-
Notifications
You must be signed in to change notification settings - Fork 132
refactor: move multiplexed session handling to separate class #3063
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
Conversation
…a-spanner into mux-benchmark-experiments
…a-spanner into mux-benchmark-experiments
…a-spanner into mux-benchmark-experiments
* It is enough with one executor to maintain the multiplexed sessions in all the clients, as they | ||
* do not need to be updated often, and the maintenance task is light. | ||
*/ | ||
private static final ScheduledExecutorService MAINTAINER_SERVICE = |
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 will be a new thread which will maintain multiplexed sessions? If yes, can you share if re-using the existing thread was a harder implementation?
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.
It would have been slightly harder, but that was not my main reason for not using the same thread. My reasoning for creating a separate maintainer and executor was:
- This separates multiplexed sessions completely from the session pool. That means that once all operations work on multiplexed sessions, we can just remove the entire session pool implementation.
- This maintainer uses a
ScheduledThreadPoolExecutor
with zero core threads, and only runs the task once every 10 minutes. The additional resource usage is therefore minimal.
* This flag is set to true if the server return UNIMPLEMENTED when we try to create a multiplexed | ||
* session. | ||
*/ | ||
private final AtomicBoolean unimplemented = new AtomicBoolean(false); |
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.
Nit: The formatting looks off. Also, should we add a TODO that this is a temporary measure and we can remove it in future when this has a bit of increased usage?
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 added a TODO for removing.
I'm not sure what you mean with the formatting being off (?) The code is formatted using the normal formatter, and I don't see anything strange with this line or the ones directly above.
|
||
package com.google.cloud.spanner; | ||
|
||
import static com.google.cloud.spanner.SessionImpl.NO_CHANNEL_HINT; |
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.
Given we are initializing this to -1, can there be an edge case where the value gets decremented to -1? Would using a boxed integer and setting null
be a better default than -1?
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 think that there's a bit of confusion between this value and the numCurrentSingleUseTransactions
:
- The channel hint that is generated will never be -1. It is always calculated by getting the first clear bit from index 0. That means that it will always be >= 0.
- The
numCurrentSingleUseTransactions
gets incremented and decremented based on transactions being started and ended. That value should also never get to -1. In theory it could if we were 'over-closing' single-use transactions, but even then we have a safe-guard in theonReadDone()
method that only decrements it the first time the method is called, and ignores any second call.
One other scenario that is a bit more probable (but still not much) is that reads never call onReadDone
. This could cause numCurrentSingleUseTransactions
to get higher than it should. The effect of this would be that we would fall back to using a random channel hint more often than we in the ideal case should. (The same method is also used to end spans
, which means that it would also cause spans
not to be ended. That is already the case in the current regular session implementation.)
static class MultiplexedSessionTransaction extends SessionImpl { | ||
private final MultiplexedSessionDatabaseClient client; | ||
|
||
private final boolean singleUse; |
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 is the channel hint managed for multi-use transactions?
- Given we will always set the
options
atSessionImpl
, the random hint logic that was introduced inAbstractReadContext
becomes dead code. Do we clean it up here itself? Or do that in a separate PR? I'm fine either ways.
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.
- Channel hint for multi-use read-only transactions are always random. In theory we could use the same strategy for them as for single-use read-only transactions, but I think we should not, because it would make the 'channel-hint-bitset' depend on read-only transactions always being closed. Failure to close read-only transactions has been a common source of session leaks in the past, and that is exactly what we want to get rid of with multiplexed sessions. Adding a new dependency on closing all read-only transactions here could in theory give us a bit better latency if someone is running 1qps multi-use read-only transactions, but I don't think the benefit of that outweighs the potential downside.
- The code for assigning a random hint in
AbstractReadContext
is still used. We only create anoptions
instance if the channel hint is not the special valueNO_CHANNEL_HINT
. Seejava-spanner/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java
Line 125 in 5979f2d
if (channelHint == NO_CHANNEL_HINT) {
2.1.MultiplexedSessionDatabaseClient
returnsNO_CHANNEL_HINT
if we should just use a random channel:Line 315 in 5979f2d
return NO_CHANNEL_HINT;
2.2. That value is then passed to the constructor ofSessionImpl
.
2.3.SessionImpl
checks if the channel hint isNO_CHANNEL_HINT
, and if so does not create anoptions
instance, but instead callssessionReference.getOptions()
.
2.4.sessionReference.getOptions()
returns null for multiplexed sessions and the affiliated channel for regular sessions.
...e-cloud-spanner/src/main/java/com/google/cloud/spanner/MultiplexedSessionDatabaseClient.java
Show resolved
Hide resolved
…a-spanner into mux-benchmark-experiments
} | ||
|
||
@Override | ||
public CursorState tryNext() throws SpannerException { | ||
return delegate.tryNext(); | ||
return getDelegate().tryNext(); |
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.
👍
Refactors the multiplexed session implementation to use its own class instead of being combined with the existing session pool. This simplifies the code path for multiplexed sessions, and makes it possible to entirely remove the
SessionPool
class when all operations can be executed on multiplexed sessions.