-
Notifications
You must be signed in to change notification settings - Fork 30
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
NodeJS generator seems to generate clients with memory leak #823
Comments
Is this issue for the monolith or gapic-generator-typescript? |
By looking at the paths ( |
Thank you for the report, I'll investigate. The leak may be on various levels: the GAPIC client itself, google-gax, protobuf.js, or gRPC. With your test, I believe I will be able to figure out on which layer the memory is not reclaimed properly, and hopefully fix it. Having said that, I need to point out that the client creation is a heavy operation that involves synchronous reading of a big file from the filesystem, and we never actually tested that creating and closing the client has zero memory footprint, just because this is not something that should happen often in the process. What is the use case that requires creating more and more clients and close them? |
The issue was originally reported for the Spanner hand-written client. The use case as I understand it, is that a single application opens several different connections to Spanner using specific credentials for different users / clients / customers. |
@alexander-fenster Thanks for taking a look a this. Just to elaborate on the use case some more. I work for a security/compliance company and we're scanning the GCP accounts of our customers for compliance violations. Hence the need to be creating 1000's of these clients with different credentials. Anecdotally, (and I don't meant for this to sound like a dig) we do a similar scanning process for many other cloud services (including Azure and AWS) and this is the only node client library where we're seeing these leaks. |
Thanks @tomdee for your explanation! It totally makes sense (even though, as you could guess, this is not the common use case). I'll update the bug when I figure out where exactly the problem happens. |
BTW, It seems like the long running operations client never gets closed. That seems to contribute a large part of the problem, but there's still some other leaks there. |
This bug is blocking a p1, so it's p1 also |
@alexander-fenster do you have any updates on this issue? We'd really like to mitigate some memory leaks in production and this would be hugely beneficial to us. |
@Utsav2 One thing that I did that could make the situation better was introducing of the proto caching in And thanks for pinging me here, we'll update this issue when we have more information. |
Thanks @alexander-fenster! I can share what context I have on this leak to help with your investigation. The operations client here doesn't have a close method, which I think we could add: https://github.com/googleapis/gax-nodejs/blob/889730c1548a6dcc0b082a24c59a9278dd2296f6/src/operationsClient.ts#L63 Then we need to bump the dependency pin in this repository, and close the lro client when this client is closed. https://github.com/googleapis/gapic-generator-typescript/blob/master/templates/typescript_gapic/src/%24version/%24service_client.ts.njk#L75 |
@alexander-fenster Thanks for the update. I tried my reproduction case (only the Spanner client, not the PubSub client) with Memory Usage with v2.10.3:
Memory Usage with v2.12.0
Memory Usage with Closing Operations ClientI also created a local build of
So each of the above steps seem to help a little, but there also seems to be at least one more issue hidden somewhere. |
@alexander-fenster Is there any update on this? |
@olavloite This issue is triaged and I will investigate in late of July sprint. Will update the further info here. Thanks for the patience. |
Hello! Quick update here - I've made a fix to the generator that will close the mixin clients (i.e. Once I did some more digging to see if I could reduce the memory leakage some more, but so far haven't found an additional solution. Interestingly enough, if I add the ability to take a heap snapshot in the program, it seems to reduce the memory leakage slightly, which maybe suggests something funky going on with garbage collection (in addition to something else causing the leaks). Sample code (with the
Output without heap snapshot (over 1000 iterations, memory leakage of ~1.4MB):
Output with heap snapshot (over 1000 iterations, memory leakage of ~0.9MB):
|
@alicejli Thanks for the fix and elaborate explanation. Just to be sure: There are no additional changes that we need to do in the Spanner client to invoke any of the close methods that are added in the generated client, other than what has already been added in googleapis/nodejs-spanner#1416. Is that correct? |
Ah interesting, I didn't see that PR. I am not totally sure how the When I test this change locally, it reduces the memory leakage by ~50% as opposed to having the change just in Some additional investigation - it seems that for the
I identified this by adding a for loop set to run 10 times over every part of the constructor, and this was the only part of the constructor that generated a significant change in memory usage. It seems that even adding this.operationsClient.close() to the close() function does not quite clear out everything in the old |
The TLDR version: Once the Spanner hand-written client is using the newest generated client that includes the fix in #953, I think we have solved most of the problem. I'll test that when the client has been re-generated. |
Thanks for explaining! It looks like the PR for the newly generated client: googleapis/nodejs-spanner#1455 was just merged; let me know how your testing goes. |
@alicejli , is this good to be closed assuming this is done? |
@olavloite I'm going to close this with the assumption the memory leak issue with client generation is fixed. Please feel free to re-open if not. Thanks! |
@alicejli Sorry, I should have posted my findings here as well (copy-paste from googleapis/nodejs-spanner#1306 (comment)): With the latest changes in the generated clients the memory leak when opening and closing a new client repeatedly has been significantly reduced, but not completely eliminated. Running a simple script that opens a simple generated client, executes one request with it and then closes the client will now leak approx 2MB after 100 iterations. That is approx 1/10th of the original problem. Test scriptconst {Spanner, v1} = require('@google-cloud/spanner');
const REPETITIONS = 100;
console.log(`Running ${REPETITIONS} times`);
function main() {
async function test() {
for (let i = 0; i < REPETITIONS; i++) {
await useGapic();
global.gc();
const used = process.memoryUsage().heapUsed / 1024 / 1024;
console.log(`${i}: Current mem usage ${Math.round(used * 100) / 100} MB`);
}
}
async function useGapic() {
const client = new v1.InstanceAdminClient({
projectId: 'my-project',
keyFile: '/path/to/my-key.json',
});
await client.listInstanceConfigs({
parent: 'projects/my-project',
});
await client.close();
}
test();
}
process.on('unhandledRejection', err => {
console.error(err.message);
process.exitCode = 1;
});
main(); Results for 100 iterations
I've been trying out a number of different things, but I've not been able to exactly pinpoint the problem, although the following is clear:
Opening and closing clients like this is not a very common use case, so maybe this is something that we just have to live with, but I at least wanted to inform you. |
The NodeJS gapic clients seem to have a memory leak that can be triggered using the following steps:
This issue was first reported for the Spanner hand-written client, but seems to be more of a generic problem with the generated gapic clients.
The following simple application shows the behavior for both
PubSub
andSpanner
. The memory consumption of the application increases with approx 100KB-200KB for each iteration.index.js
package.json
Output at 50 iterations. Running this script with more iterations and/or with tight memory settings will cause an out of memory error.
The text was updated successfully, but these errors were encountered: