Skip to content
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

feat: allow specifying how many idle GRPC channels to keep #837

Merged
merged 7 commits into from Dec 27, 2019

Conversation

schmidt-sebastian
Copy link
Contributor

@schmidt-sebastian schmidt-sebastian commented Dec 26, 2019

This PR lets users influence the memory profile of their Firestore usage:

  • They can set the minimum client pool size to 0 to close all GRPC channels when Firestore is completely idle
  • They can use a larger number to reduce the startup costs of new GRPC channels.

This PR aims to preserve the current behavior by always leaving at least one GRPC channel if no option is provided.

b/146363799

@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label Dec 26, 2019
@schmidt-sebastian schmidt-sebastian force-pushed the mrschmidt/idlechannels branch 2 times, most recently from d8ae845 to 8d94521 Compare December 26, 2019 11:53
@codecov
Copy link

codecov bot commented Dec 26, 2019

Codecov Report

Merging #837 into master will decrease coverage by <.01%.
The diff coverage is 89.28%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #837      +/-   ##
==========================================
- Coverage   88.83%   88.82%   -0.01%     
==========================================
  Files          27       27              
  Lines       16725    16765      +40     
  Branches     1151     1156       +5     
==========================================
+ Hits        14858    14892      +34     
- Misses       1862     1868       +6     
  Partials        5        5
Impacted Files Coverage Δ
types/firestore.d.ts 0% <0%> (ø) ⬆️
dev/src/index.ts 98.74% <100%> (+0.01%) ⬆️
dev/src/types.ts 100% <100%> (ø) ⬆️
dev/src/pool.ts 96.61% <97.87%> (+1.03%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5c870e6...0e34859. Read the comment docs.

@schmidt-sebastian schmidt-sebastian force-pushed the mrschmidt/idlechannels branch 2 times, most recently from bb88a59 to 470e450 Compare December 26, 2019 12:01
@schmidt-sebastian schmidt-sebastian added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Dec 26, 2019
@kokoro-team kokoro-team removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Dec 26, 2019
@schmidt-sebastian schmidt-sebastian added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Dec 26, 2019
@kokoro-team kokoro-team removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Dec 26, 2019
Copy link
Contributor

@mikelehen mikelehen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly nits...

As an aside, I will just point out that it seems unfortunate that we have to expose this to developers and make them make a trade-off between latency, memory, and the ability for the client to 100% shut down (if I'm understanding correctly). I worry that the only way people will find out about this options is when they run into some problem, diagnose it, and then go googling for it and find a post telling them to change this. That means many people will just suffer through the problem (because googling / solving problems is hard) and those that do solve it will have had to go on an unwanted adventure to diagnose / solve the problem.

All that is to say: We should do our best to make this "just work" for as many use cases as possible so 99.9% of people don't need to go on this adventure. :-)

dev/src/pool.ts Outdated
this.activeClients.forEach((requestCount, client) => {
if (!selectedClient && requestCount < this.concurrentOperationLimit) {
for (const [client, requestCount] of this.activeClients) {
// Bin pack requests to reduce the maximize the number of idle clients as
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"reduce the maximize the ..."? Also, I don't do bin packing enough for this comment to 100% make sense right away. And I had to think more than 3 seconds to figure out what "selectedRequestCount" was.

Perhaps rename selectedRequestCount to selectedClientRequestCount (long/verbose, I know, sorry!) and the comment to something like:

Use the "most-full" client that can still accommodate the required requestCount in order to maximize the number of idle clients as operations start to complete.

Don't feel very strongly though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your comment is not only grammatically correct, but also more helpful. Thanks!

@@ -77,7 +85,7 @@ export class ClientPool<T> {
selectedClient = client;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The log line above is going to get spewed multiple times now. I think it should be extracted from the for-loop.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I moved it outside the loop.

dev/src/pool.ts Outdated
assert(requestCount > 0, 'No active request');
this.activeClients.set(client, requestCount! - 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems odd you need the ! but I assume you have your reasons...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uhm. I hope I had my reasons when I added it first, but they are not immediately apparent now. Removed.

dev/src/pool.ts Outdated
// least one active client as a single client can never exceed the
// concurrent operation limit by itself).
// - Use `maxIdleClients:0` to shut down the client pool completely when all
// clients are idle.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW I read this comment 2 or 3 times, and read the code once, and the code still makes more sense to me than the comment. So it might be worth rephrasing / paring down the comment. I'm not 100% sure what it's trying to get across. Is it justifying the existence of the maxIdleClients setting (in which maybe the bulk of this comment should go there?)? Or is there actually some nuance to this code that needs 10 lines of explanation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to justify that the code doesn't just count the number of idle clients, but instead counts the number of idle "request slots". If this comment doesn't help, then I am ok with removing it.

dev/src/types.ts Outdated
/**
* The maximum number of idle GRPC channels to keep. A smaller number of idle
* channels reduces memory usage but increases request latency for clients
* with fluctuating request rates. Defaults to 1.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Elsewhere you document that setting it to 0 allows the client pool to shut down completely. Is that a behavior that should be documented here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good suggestion. Added (here and in the other two places...)

Copy link
Contributor Author

@schmidt-sebastian schmidt-sebastian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your review. There was no rush for this :)

The default setting should keep the existing behavior and work for most users. There have been a couple complaints about our memory usage from very heavy users. I don't expect maxIdleChannels to be an often used setting, but it should allow users that want to tune memory usage to do so.

dev/src/pool.ts Outdated
this.activeClients.forEach((requestCount, client) => {
if (!selectedClient && requestCount < this.concurrentOperationLimit) {
for (const [client, requestCount] of this.activeClients) {
// Bin pack requests to reduce the maximize the number of idle clients as
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your comment is not only grammatically correct, but also more helpful. Thanks!

@@ -77,7 +85,7 @@ export class ClientPool<T> {
selectedClient = client;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I moved it outside the loop.

dev/src/pool.ts Outdated
assert(requestCount > 0, 'No active request');
this.activeClients.set(client, requestCount! - 1);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uhm. I hope I had my reasons when I added it first, but they are not immediately apparent now. Removed.

dev/src/pool.ts Outdated
// least one active client as a single client can never exceed the
// concurrent operation limit by itself).
// - Use `maxIdleClients:0` to shut down the client pool completely when all
// clients are idle.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to justify that the code doesn't just count the number of idle clients, but instead counts the number of idle "request slots". If this comment doesn't help, then I am ok with removing it.

dev/src/types.ts Outdated
/**
* The maximum number of idle GRPC channels to keep. A smaller number of idle
* channels reduces memory usage but increases request latency for clients
* with fluctuating request rates. Defaults to 1.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good suggestion. Added (here and in the other two places...)

@schmidt-sebastian schmidt-sebastian merged commit 37e93da into master Dec 27, 2019
@schmidt-sebastian schmidt-sebastian deleted the mrschmidt/idlechannels branch January 3, 2020 00:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes This human has signed the Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants