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

chore: add session pool options for multiplexed session. #2960

Merged
merged 28 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
edc5bbf
fix: prevent illegal negative timeout values into thread sleep() meth…
arpan14 Feb 6, 2023
49a85df
Merge pull request #1 from arpan14/retryerror
arpan14 Feb 8, 2023
4cd497b
Fixing lint issues.
arpan14 Feb 8, 2023
4a6aa8e
Merge branch 'googleapis:main' into main
arpan14 Mar 13, 2023
b2aa09d
Merge branch 'googleapis:main' into main
arpan14 Mar 15, 2023
8d6d71e
Merge branch 'googleapis:main' into main
arpan14 May 9, 2023
77e6e7d
Merge branch 'googleapis:main' into main
arpan14 Jul 17, 2023
e8b7fad
Merge branch 'googleapis:main' into main
arpan14 Jul 25, 2023
8aa84e1
Merge branch 'googleapis:main' into main
arpan14 Oct 10, 2023
57fd405
Merge branch 'googleapis:main' into main
arpan14 Oct 27, 2023
1253563
Merge branch 'googleapis:main' into main
arpan14 Nov 20, 2023
d4f6a60
Merge branch 'googleapis:main' into main
arpan14 Dec 15, 2023
3efaf7c
Merge branch 'googleapis:main' into main
arpan14 Dec 26, 2023
f41b39f
Merge branch 'googleapis:main' into main
arpan14 Jan 3, 2024
7e3287f
Merge branch 'googleapis:main' into main
arpan14 Jan 13, 2024
7edd24d
Merge branch 'googleapis:main' into main
arpan14 Feb 13, 2024
fe3649b
Merge branch 'googleapis:main' into main
arpan14 Feb 20, 2024
ae0dbc9
Merge branch 'googleapis:main' into main
arpan14 Feb 23, 2024
99cd112
Merge branch 'googleapis:main' into main
arpan14 Feb 23, 2024
7da9d8d
Merge branch 'googleapis:main' into main
arpan14 Feb 27, 2024
03cfaed
Merge branch 'googleapis:main' into main
arpan14 Mar 7, 2024
52662f8
Merge branch 'googleapis:main' into main
arpan14 Mar 18, 2024
cba2a00
Merge branch 'googleapis:main' into main
arpan14 Mar 21, 2024
b035607
chore: add session pool options for multiplexed session.
arpan14 Mar 21, 2024
e59cb3a
Update google-cloud-spanner/src/main/java/com/google/cloud/spanner/Se…
arpan14 Mar 21, 2024
61ae225
Update google-cloud-spanner/src/main/java/com/google/cloud/spanner/Se…
arpan14 Mar 21, 2024
f2f341e
fix: comments.
arpan14 Mar 21, 2024
c5652b8
chore: lint fix.
arpan14 Mar 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ public class SessionPoolOptions {
/** Property for allowing mocking of session maintenance clock. */
private final Clock poolMaintainerClock;

private final Duration waitForMultiplexedSession;
private final boolean useMultiplexedSession;
private final Duration multiplexedSessionMaintenanceDuration;

private SessionPoolOptions(Builder builder) {
// minSessions > maxSessions is only possible if the user has only set a value for maxSessions.
// We allow that to prevent code that only sets a value for maxSessions to break if the
Expand All @@ -93,6 +97,9 @@ private SessionPoolOptions(Builder builder) {
this.randomizePositionQPSThreshold = builder.randomizePositionQPSThreshold;
this.inactiveTransactionRemovalOptions = builder.inactiveTransactionRemovalOptions;
this.poolMaintainerClock = builder.poolMaintainerClock;
this.useMultiplexedSession = builder.useMultiplexedSession;
this.multiplexedSessionMaintenanceDuration = builder.multiplexedSessionMaintenanceDuration;
this.waitForMultiplexedSession = builder.waitForMultiplexedSession;
}

@Override
Expand Down Expand Up @@ -123,7 +130,11 @@ public boolean equals(Object o) {
&& Objects.equals(this.randomizePositionQPSThreshold, other.randomizePositionQPSThreshold)
&& Objects.equals(
this.inactiveTransactionRemovalOptions, other.inactiveTransactionRemovalOptions)
&& Objects.equals(this.poolMaintainerClock, other.poolMaintainerClock);
&& Objects.equals(this.poolMaintainerClock, other.poolMaintainerClock)
&& Objects.equals(this.useMultiplexedSession, other.useMultiplexedSession)
&& Objects.equals(
this.multiplexedSessionMaintenanceDuration, other.multiplexedSessionMaintenanceDuration)
&& Objects.equals(this.waitForMultiplexedSession, other.waitForMultiplexedSession);
}

@Override
Expand All @@ -148,7 +159,10 @@ public int hashCode() {
this.releaseToPosition,
this.randomizePositionQPSThreshold,
this.inactiveTransactionRemovalOptions,
this.poolMaintainerClock);
this.poolMaintainerClock,
this.useMultiplexedSession,
this.multiplexedSessionMaintenanceDuration,
this.waitForMultiplexedSession);
}

public Builder toBuilder() {
Expand Down Expand Up @@ -271,6 +285,18 @@ long getRandomizePositionQPSThreshold() {
return randomizePositionQPSThreshold;
}

boolean getUseMultiplexedSession() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I assume that we are making these method package-private because we don't want to expose these methods publicly yet, but that they will be made public at a later time.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, you are right. Will make it public at a later point.

return useMultiplexedSession;
}

Duration getMultiplexedSessionMaintenanceDuration() {
return multiplexedSessionMaintenanceDuration;
}

Duration getWaitForMultiplexedSession() {
return waitForMultiplexedSession;
}

public static Builder newBuilder() {
return new Builder();
}
Expand Down Expand Up @@ -467,6 +493,9 @@ public static class Builder {
*/
private long randomizePositionQPSThreshold = 0L;

private boolean useMultiplexedSession = false;
private Duration multiplexedSessionMaintenanceDuration = Duration.ofDays(7);
private Duration waitForMultiplexedSession = Duration.ofSeconds(10);
private Clock poolMaintainerClock;

private static Position getReleaseToPositionFromSystemProperty() {
Expand Down Expand Up @@ -669,6 +698,49 @@ Builder setPoolMaintainerClock(Clock poolMaintainerClock) {
return this;
}

/**
* Sets whether the client should use multiplexed session or not. If set to true, the client
* optimises and runs multiple applicable requests concurrently on a single session. A single
* multiplexed session is sufficient to handle all concurrent traffic, so we don't require to
* provision any additional sessions.
arpan14 marked this conversation as resolved.
Show resolved Hide resolved
*
* <p>When set to false, the client uses the regular session cached in the session pool for
* running 1 concurrent transaction per session. We require to provision sufficient sessions by
* making use of {@link SessionPoolOptions#minSessions} and {@link
* SessionPoolOptions#maxSessions} based on the traffic load. Failing to do so will result in
* higher latencies.
*
* @param useMultiplexedSession
arpan14 marked this conversation as resolved.
Show resolved Hide resolved
*/
Builder setUseMultiplexedSession(boolean useMultiplexedSession) {
this.useMultiplexedSession = useMultiplexedSession;
return this;
}

@VisibleForTesting
Builder setMultiplexedSessionMaintenanceDuration(
Duration multiplexedSessionMaintenanceDuration) {
this.multiplexedSessionMaintenanceDuration = multiplexedSessionMaintenanceDuration;
return this;
}

/**
* This option is only useful when {@link SessionPoolOptions#useMultiplexedSession} is set to
* true. If greater than zero, we wait for the said duration at time of client initialization to
* ensure that the multiplexed session is initialised. The default value for this is 10s.
*
* <p>If this is set to null, the client does not wait for session is initialised which means
* that the first few requests would see more latency since they will need to wait till the
* multiplexed session is initialised.
arpan14 marked this conversation as resolved.
Show resolved Hide resolved
*
* @param waitForMultiplexedSession
* @return
arpan14 marked this conversation as resolved.
Show resolved Hide resolved
*/
Builder setWaitForMultiplexedSession(Duration waitForMultiplexedSession) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think that this should somehow be combined with the waitForMinSessions option. It is illogical if the session pool blocks for multiplexed sessions, and not for other sessions, as it means that:

  1. The first read operation will not have any additional latency.
  2. The first write operations could have additional latency.

Or if we decide to keep them separate, then we should at least document this difference, so users can figure out why the first read/write transaction could get additional latency.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

  • waitForMinSessions could block for much longer since in the minimum case we create 4 RPCs to create 100 regular sessions. waitForMultiplexedSession will be much lesser since its 1 session and the latency for multiplexed session creation in general is much lesser than a regular session (due to backend enhancements)

  • Hence I think it might be ok to block for multiplexed session but may not be ok for regular sessions.

  • The first read operation will not have any additional latency. - We will also be supporting blind write in the first phase. So in that case at-least customers wont see any difference for some of the writes as well.

  • we should at least document this difference, so users can figure out why the first read/write transaction could get additional latency. - Sure I will add some note here.

this.waitForMultiplexedSession = waitForMultiplexedSession;
return this;
}

/**
* Sets whether the client should automatically execute a background query to detect the dialect
* that is used by the database or not. Set this option to true if you do not know what the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,4 +246,62 @@ public void testRandomizePositionQPSThreshold() {
IllegalArgumentException.class,
() -> SessionPoolOptions.newBuilder().setRandomizePositionQPSThreshold(-1L));
}

@Test
public void testUseMultiplexedSession() {
assertEquals(false, SessionPoolOptions.newBuilder().build().getUseMultiplexedSession());
assertEquals(
true,
SessionPoolOptions.newBuilder()
.setUseMultiplexedSession(true)
.build()
.getUseMultiplexedSession());
assertEquals(
false,
SessionPoolOptions.newBuilder()
.setUseMultiplexedSession(true)
.setUseMultiplexedSession(false)
.build()
.getUseMultiplexedSession());
}

@Test
public void testMultiplexedSessionMaintenanceDuration() {
assertEquals(
Duration.ofDays(7),
SessionPoolOptions.newBuilder().build().getMultiplexedSessionMaintenanceDuration());
assertEquals(
Duration.ofDays(2),
SessionPoolOptions.newBuilder()
.setMultiplexedSessionMaintenanceDuration(Duration.ofDays(2))
.build()
.getMultiplexedSessionMaintenanceDuration());
assertEquals(
Duration.ofDays(10),
SessionPoolOptions.newBuilder()
.setMultiplexedSessionMaintenanceDuration(Duration.ofDays(2))
.setMultiplexedSessionMaintenanceDuration(Duration.ofDays(10))
.build()
.getMultiplexedSessionMaintenanceDuration());
}

@Test
public void testWaitForMultiplexedSession() {
assertEquals(
Duration.ofSeconds(10),
SessionPoolOptions.newBuilder().build().getWaitForMultiplexedSession());
assertEquals(
Duration.ofSeconds(20),
SessionPoolOptions.newBuilder()
.setWaitForMultiplexedSession(Duration.ofSeconds(20))
.build()
.getWaitForMultiplexedSession());
assertEquals(
Duration.ofSeconds(10),
SessionPoolOptions.newBuilder()
.setWaitForMultiplexedSession(Duration.ofSeconds(2))
.setWaitForMultiplexedSession(Duration.ofSeconds(10))
.build()
.getWaitForMultiplexedSession());
}
}
Loading