-
Notifications
You must be signed in to change notification settings - Fork 119
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
fix: prevent potential thread starvation in SessionPool #85
Conversation
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.
Thank you so much for this fix. Also nice test.
We'll merge this in for now but I'm happy to also consider the inlining of BeginTransaction with the first statement. I think that PR is still in google-cloud-java and I haven't reviewed it.
@olavloite Thanks for fixing this. I am trying to understand your test and I would like to ask some questions. Assume that Then, the session pool has a config between 8 (min) and 24 (max). In your test, you set the BatchCreateSessions call to be blocked (not responding forever) after initializing. After that, you simulate a traffic with 16 reads and 24 writes. Let's say that 8 transactions get 8 sessions at first and then the 9th transaction cannot get a session. The 9th transaction will try to call BatchCreateSession to get a new one and be waiting for the new session available. The 9th transaction will be unlocked if one of the following two cases happens:
Therefore, in this test, case 1 would not happen (never returns) and case 2 will let the remaining transactions to finish their jobs. a) My question is that how is this test related to the issue that b) Another question after I read the code is that if we set the max sessions to 1500 and |
The test case does not cover that, as the problem is actually caused by
While it's not directly covered by the test case, the above would also apply the other way around. I.e. if the
The number of threads in the executor is determined based on the gRPC transport options. The default executor that will be used if you don't specify anything else is this one: https://github.com/googleapis/java-core/blob/74f8632732a47e3f68cde9a29b95da9b9547dfbb/google-cloud-core-grpc/src/main/java/com/google/cloud/grpc/GrpcTransportOptions.java#L54 A user application can change this by setting a custom executor factory like this: SpannerOptions opts =
SpannerOptions.newBuilder()
.setTransportOptions(
GrpcTransportOptions.newBuilder().setExecutorFactory(someFactory).build())
.build(); Note that the |
@olavloite Your answer is great and well-explained! 👍 Completely resolve my confusion. Thank you very much! 👏 My concern about the second question is that most people may not know about the relationship between the session pool and the thread pool behind it. If they only change |
Yes, both concerns are certainly valid. Neither of them will normally cause any real problems, but the performance could be better. The best configuration is normally to keep
|
The
SessionPool
uses the same executor for both creating sessions and preparing these for read/write transactions. This could in very specific circumstances lead to thread starvation, which again can cause the session pool to stop returning sessions, even when there are sessions available in the pool.