22
22
import com .google .spanner .v1 .ReadRequest .OrderBy ;
23
23
import com .google .spanner .v1 .RequestOptions .Priority ;
24
24
import com .google .spanner .v1 .TransactionOptions .IsolationLevel ;
25
+ import com .google .spanner .v1 .TransactionOptions .ReadWrite .ReadLockMode ;
25
26
import java .io .Serializable ;
26
27
import java .time .Duration ;
27
28
import java .util .Objects ;
@@ -155,9 +156,36 @@ public static TransactionOption commitStats() {
155
156
* process in the commit phase (when any needed locks are acquired). The validation process
156
157
* succeeds only if there are no conflicting committed transactions (that committed mutations to
157
158
* the read data at a commit timestamp after the read timestamp).
159
+ *
160
+ * @deprecated Use {@link Options#readLockMode(ReadLockMode)} instead.
158
161
*/
162
+ @ Deprecated
159
163
public static TransactionOption optimisticLock () {
160
- return OPTIMISTIC_LOCK_OPTION ;
164
+ return Options .readLockMode (ReadLockMode .OPTIMISTIC );
165
+ }
166
+
167
+ /**
168
+ * Specifying this instructs the transaction to request {@link ReadLockMode} from the backend.
169
+ *
170
+ * <p>This option controls the locking behavior for read operations and queries within a
171
+ * read-write transaction. It works in conjunction with the transaction's {@link IsolationLevel}.
172
+ *
173
+ * <ul>
174
+ * <li>{@link ReadLockMode#OPTIMISTIC}: Reads and queries do not acquire locks during the
175
+ * transaction's execution phase. Instead, Spanner validates at commit time that the data
176
+ * read has not been modified by other transactions since the read occurred. This mode can
177
+ * reduce lock contention and improve read performance but may result in more transaction
178
+ * aborts under high write contention on the same data.
179
+ * <li>{@link ReadLockMode#PESSIMISTIC}: The transaction acquires locks on data as it's
180
+ * accessed. For example, Data Manipulation Language (DML) statements and {@code SELECT ...
181
+ * FOR UPDATE} queries will take locks. This approach blocks other transactions from
182
+ * modifying the locked data until the current transaction completes, which can prevent
183
+ * conflicts and reduce commit-time aborts, but may increase lock wait times and overall
184
+ * contention.
185
+ * </ul>
186
+ */
187
+ public static TransactionOption readLockMode (ReadLockMode readLockMode ) {
188
+ return new ReadLockModeOption (readLockMode );
161
189
}
162
190
163
191
/**
@@ -367,16 +395,6 @@ void appendToOptions(Options options) {
367
395
}
368
396
}
369
397
370
- /** Option to request Optimistic Concurrency Control for read/write transactions. */
371
- static final class OptimisticLockOption extends InternalOption implements TransactionOption {
372
- @ Override
373
- void appendToOptions (Options options ) {
374
- options .withOptimisticLock = true ;
375
- }
376
- }
377
-
378
- static final OptimisticLockOption OPTIMISTIC_LOCK_OPTION = new OptimisticLockOption ();
379
-
380
398
/** Option to request the transaction to be excluded from change streams. */
381
399
static final class ExcludeTxnFromChangeStreamsOption extends InternalOption
382
400
implements UpdateTransactionOption {
@@ -516,6 +534,20 @@ void appendToOptions(Options options) {
516
534
}
517
535
}
518
536
537
+ /** Option to set read lock mode for read/write transactions. */
538
+ static final class ReadLockModeOption extends InternalOption implements TransactionOption {
539
+ private final ReadLockMode readLockMode ;
540
+
541
+ public ReadLockModeOption (ReadLockMode readLockMode ) {
542
+ this .readLockMode = readLockMode ;
543
+ }
544
+
545
+ @ Override
546
+ void appendToOptions (Options options ) {
547
+ options .readLockMode = readLockMode ;
548
+ }
549
+ }
550
+
519
551
private boolean withCommitStats ;
520
552
521
553
private Duration maxCommitDelay ;
@@ -530,7 +562,6 @@ void appendToOptions(Options options) {
530
562
private String tag ;
531
563
private String etag ;
532
564
private Boolean validateOnly ;
533
- private Boolean withOptimisticLock ;
534
565
private Boolean withExcludeTxnFromChangeStreams ;
535
566
private Boolean dataBoostEnabled ;
536
567
private DirectedReadOptions directedReadOptions ;
@@ -540,6 +571,7 @@ void appendToOptions(Options options) {
540
571
private Boolean lastStatement ;
541
572
private IsolationLevel isolationLevel ;
542
573
private XGoogSpannerRequestId reqId ;
574
+ private ReadLockMode readLockMode ;
543
575
544
576
// Construction is via factory methods below.
545
577
private Options () {}
@@ -644,10 +676,6 @@ Boolean validateOnly() {
644
676
return validateOnly ;
645
677
}
646
678
647
- Boolean withOptimisticLock () {
648
- return withOptimisticLock ;
649
- }
650
-
651
679
Boolean withExcludeTxnFromChangeStreams () {
652
680
return withExcludeTxnFromChangeStreams ;
653
681
}
@@ -704,6 +732,10 @@ IsolationLevel isolationLevel() {
704
732
return isolationLevel ;
705
733
}
706
734
735
+ ReadLockMode readLockMode () {
736
+ return readLockMode ;
737
+ }
738
+
707
739
@ Override
708
740
public String toString () {
709
741
StringBuilder b = new StringBuilder ();
@@ -740,9 +772,6 @@ public String toString() {
740
772
if (validateOnly != null ) {
741
773
b .append ("validateOnly: " ).append (validateOnly ).append (' ' );
742
774
}
743
- if (withOptimisticLock != null ) {
744
- b .append ("withOptimisticLock: " ).append (withOptimisticLock ).append (' ' );
745
- }
746
775
if (withExcludeTxnFromChangeStreams != null ) {
747
776
b .append ("withExcludeTxnFromChangeStreams: " )
748
777
.append (withExcludeTxnFromChangeStreams )
@@ -772,6 +801,9 @@ public String toString() {
772
801
if (reqId != null ) {
773
802
b .append ("requestId: " ).append (reqId .toString ());
774
803
}
804
+ if (readLockMode != null ) {
805
+ b .append ("readLockMode: " ).append (readLockMode ).append (' ' );
806
+ }
775
807
return b .toString ();
776
808
}
777
809
@@ -807,15 +839,15 @@ public boolean equals(Object o) {
807
839
&& Objects .equals (tag (), that .tag ())
808
840
&& Objects .equals (etag (), that .etag ())
809
841
&& Objects .equals (validateOnly (), that .validateOnly ())
810
- && Objects .equals (withOptimisticLock (), that .withOptimisticLock ())
811
842
&& Objects .equals (withExcludeTxnFromChangeStreams (), that .withExcludeTxnFromChangeStreams ())
812
843
&& Objects .equals (dataBoostEnabled (), that .dataBoostEnabled ())
813
844
&& Objects .equals (directedReadOptions (), that .directedReadOptions ())
814
845
&& Objects .equals (orderBy (), that .orderBy ())
815
846
&& Objects .equals (isLastStatement (), that .isLastStatement ())
816
847
&& Objects .equals (lockHint (), that .lockHint ())
817
848
&& Objects .equals (isolationLevel (), that .isolationLevel ())
818
- && Objects .equals (reqId (), that .reqId ());
849
+ && Objects .equals (reqId (), that .reqId ())
850
+ && Objects .equals (readLockMode (), that .readLockMode ());
819
851
}
820
852
821
853
@ Override
@@ -857,9 +889,6 @@ public int hashCode() {
857
889
if (validateOnly != null ) {
858
890
result = 31 * result + validateOnly .hashCode ();
859
891
}
860
- if (withOptimisticLock != null ) {
861
- result = 31 * result + withOptimisticLock .hashCode ();
862
- }
863
892
if (withExcludeTxnFromChangeStreams != null ) {
864
893
result = 31 * result + withExcludeTxnFromChangeStreams .hashCode ();
865
894
}
@@ -887,6 +916,9 @@ public int hashCode() {
887
916
if (reqId != null ) {
888
917
result = 31 * result + reqId .hashCode ();
889
918
}
919
+ if (readLockMode != null ) {
920
+ result = 31 * result + readLockMode .hashCode ();
921
+ }
890
922
return result ;
891
923
}
892
924
0 commit comments