New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
perf: reduce memory usage upon running server tests #3949
perf: reduce memory usage upon running server tests #3949
Conversation
server/routes/api/documents.test.ts
Outdated
|
||
const server = getTestServer(); | ||
afterAll(() => disconnectdb().then(() => server.close())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This plugged up the leaks detected using --detect-leaks
flag as shown here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recently (as part of this issue) changed this to use a single centralized test server – the thinking was that it would be fast than spinning up an instance/dbconnection in each file. I'm actually pretty surprised this works though and the rest of the suite doesn't fail after the server is closed? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recently (as part of this issue) changed this to use a single centralized test server – the thinking was that it would be fast than spinning up an instance/dbconnection in each file.
Looks like this is unreachable. Not sure why, likely due to how jest
handles running multiple test files 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I'll admit I didn't explicitly check that. Must be a limitation of jest module scope, in that case how about we change getTestServer
to the following:
export function getTestServer() {
const app = webService();
const testServer = new TestServer(app.callback());
testServer.cleanup = async () => {
await sequelize.close();
await testServer.close();
};
return testServer;
}
Then we just need to add the following in individual test files
afterAll(server.cleanup);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right though, this cleanup makes a huge difference 🙏
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The more we can centralize setup and teardown the less future similar mistakes, I'm okay centralizing – particularly as moving into the suites would also require importing sequelize
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about 👇
export function getTestServer() {
const app = webService();
const server = new TestServer(app.callback());
server.disconnect = () => {
await sequelize.close();
await server.close();
};
return server;
}
export function getTestdb() {
const db = sequelize;
db.flush = () => { /* flush! */ };
db.seed = () => { /* seed! */ };
db.disconnect () => { /* disconnect! */ };
};
And,
// Usage(in server/routes/api/)
import { getTestServer, getTestdb } from "@server/test/utils"
const db = getTestdb();
const server = getTestServer();
afterAll(server.disconnect);
beforeEach(db.flush);
beforeEach(db.seed);
//////////////////////////////////////////////////////////////////////////
// Usage(in server/{commands, models...})
import { getTestdb } from "@server/test/utils";
const db = getTestdb();
afterAll(db.disconnect);
beforeEach(db.flush);
beforeEach(db.seed);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, seems clean and sensible 👍 . But getTestdb
-> getTestDatabase
And don't include seed
, I'm trying to gradually get rid of it in favor of individual factories.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And don't include seed, I'm trying to gradually get rid of it in favor of individual factories.
So, we keep seed
as it is in current scope? Also, are you exploring an equivalent of factory_bot
? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay – include seed if you're prepared to make the changes 😆
server/test/setup.ts
Outdated
@@ -18,6 +18,7 @@ if (process.env.DATABASE_URL_TEST) { | |||
require("@server/database/sequelize"); | |||
|
|||
jest.mock("bull"); | |||
jest.mock("i18next-http-backend"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fixed the leak for shared/i18n/index.test.ts
.
@@ -1,8 +1,8 @@ | |||
import Abstract from "./Abstract"; | |||
import { URL_REGEX } from "./Abstract"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The embeds were problematic in node@16.16.0
. It occurred that maybe this would fix it since we don't really require the entire component for tests. I tried running it multiple times and the leaks came to halt, although I didn't spot considerable difference in heap usage.
On the other hand, in node@16.10.0
and node@14.19.3
, these seemed to behave sanely with --detect-leaks
flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oof, this is a painful change to make across all embeds though – and now there is an implicit requirement of both exports just for test compatibility.
I think that the /shared/
tests should just be split out into their own test:shared
command that runs separately and if they're a touch leaky it at least won't affect the other server tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to split these out now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't have any conflicts, but heads up: #3951
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets remove these changes in embeds, otherwise you killed it – memory behavior is great now!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh..almost forgot! Thanks for pointing out. Reverted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are just the sample changes for quick feedback. Once aligned, these changes would be propagated across all the tests.
Ran into an issue after purging and re-pulling the postgres docker image 👇
Did you ever run into this issue @tommoor ? |
You can run |
Shoot! How did I forget this 😆 |
dd590ff
to
48f4ee9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed for tests under server/routes
. I'll pause here for your review @tommoor so far 😆
No red flags! |
8b4c8b1
to
7430110
Compare
Closes #3939
This attempts to plug the memory leaks that occur upon running
yarn test:server
. I almost completely relied upon the--detect-leaks
flag to spot and fix them. This flag gives room for the GC to sweep by awaiting for a few ticks in between.Following are the results collected after making changes across the board:
node@16.16
node@16.10
node@14.19