3030import com .google .spanner .v1 .PartitionReadRequest ;
3131import com .google .spanner .v1 .PartitionResponse ;
3232import com .google .spanner .v1 .TransactionSelector ;
33+ import java .time .Clock ;
34+ import java .time .Duration ;
35+ import java .time .Instant ;
3336import java .util .List ;
3437import java .util .Map ;
38+ import java .util .concurrent .atomic .AtomicReference ;
39+ import java .util .concurrent .locks .ReentrantLock ;
3540import javax .annotation .Nullable ;
41+ import javax .annotation .concurrent .GuardedBy ;
3642
3743/** Default implementation for Batch Client interface. */
3844public class BatchClientImpl implements BatchClient {
3945 private final SessionClient sessionClient ;
46+
4047 private final boolean isMultiplexedSessionEnabled ;
4148
49+ /** Lock to protect the multiplexed session. */
50+ private final ReentrantLock multiplexedSessionLock = new ReentrantLock ();
51+
52+ /** The duration before we try to replace the multiplexed session. The default is 7 days. */
53+ private final Duration sessionExpirationDuration ;
54+
55+ /** The expiration date/time of the current multiplexed session. */
56+ @ GuardedBy ("multiplexedSessionLock" )
57+ private final AtomicReference <Instant > expirationDate ;
58+
59+ @ GuardedBy ("multiplexedSessionLock" )
60+ private final AtomicReference <SessionImpl > multiplexedSessionReference ;
61+
62+ private final Clock clock ;
63+
4264 BatchClientImpl (SessionClient sessionClient , boolean isMultiplexedSessionEnabled ) {
4365 this .sessionClient = checkNotNull (sessionClient );
4466 this .isMultiplexedSessionEnabled = isMultiplexedSessionEnabled ;
67+ this .sessionExpirationDuration =
68+ Duration .ofMillis (
69+ sessionClient
70+ .getSpanner ()
71+ .getOptions ()
72+ .getSessionPoolOptions ()
73+ .getMultiplexedSessionMaintenanceDuration ()
74+ .toMillis ());
75+ // Initialize the expiration date to the start of time to avoid unnecessary null checks.
76+ // This also ensured that a new session is created on first request.
77+ this .expirationDate = new AtomicReference <>(Instant .MIN );
78+ this .multiplexedSessionReference = new AtomicReference <>();
79+ clock = Clock .systemUTC ();
4580 }
4681
4782 @ Override
@@ -54,7 +89,7 @@ public String getDatabaseRole() {
5489 public BatchReadOnlyTransaction batchReadOnlyTransaction (TimestampBound bound ) {
5590 SessionImpl session ;
5691 if (isMultiplexedSessionEnabled ) {
57- session = sessionClient . createMultiplexedSession ();
92+ session = getMultiplexedSession ();
5893 } else {
5994 session = sessionClient .createSession ();
6095 }
@@ -99,6 +134,20 @@ public BatchReadOnlyTransaction batchReadOnlyTransaction(BatchTransactionId batc
99134 batchTransactionId );
100135 }
101136
137+ private SessionImpl getMultiplexedSession () {
138+ this .multiplexedSessionLock .lock ();
139+ try {
140+ if (this .clock .instant ().isAfter (this .expirationDate .get ())
141+ || this .multiplexedSessionReference .get () == null ) {
142+ this .multiplexedSessionReference .set (this .sessionClient .createMultiplexedSession ());
143+ this .expirationDate .set (this .clock .instant ().plus (this .sessionExpirationDuration ));
144+ }
145+ return this .multiplexedSessionReference .get ();
146+ } finally {
147+ this .multiplexedSessionLock .unlock ();
148+ }
149+ }
150+
102151 private static class BatchReadOnlyTransactionImpl extends MultiUseReadOnlyTransaction
103152 implements BatchReadOnlyTransaction {
104153 private final String sessionName ;
0 commit comments