2222import com .google .spanner .v1 .ReadRequest .OrderBy ;
2323import com .google .spanner .v1 .RequestOptions .Priority ;
2424import com .google .spanner .v1 .TransactionOptions .IsolationLevel ;
25+ import com .google .spanner .v1 .TransactionOptions .ReadWrite .ReadLockMode ;
2526import java .io .Serializable ;
2627import java .time .Duration ;
2728import java .util .Objects ;
@@ -155,9 +156,36 @@ public static TransactionOption commitStats() {
155156 * process in the commit phase (when any needed locks are acquired). The validation process
156157 * succeeds only if there are no conflicting committed transactions (that committed mutations to
157158 * the read data at a commit timestamp after the read timestamp).
159+ *
160+ * @deprecated Use {@link Options#readLockMode(ReadLockMode)} instead.
158161 */
162+ @ Deprecated
159163 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 );
161189 }
162190
163191 /**
@@ -367,16 +395,6 @@ void appendToOptions(Options options) {
367395 }
368396 }
369397
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-
380398 /** Option to request the transaction to be excluded from change streams. */
381399 static final class ExcludeTxnFromChangeStreamsOption extends InternalOption
382400 implements UpdateTransactionOption {
@@ -516,6 +534,20 @@ void appendToOptions(Options options) {
516534 }
517535 }
518536
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+
519551 private boolean withCommitStats ;
520552
521553 private Duration maxCommitDelay ;
@@ -530,7 +562,6 @@ void appendToOptions(Options options) {
530562 private String tag ;
531563 private String etag ;
532564 private Boolean validateOnly ;
533- private Boolean withOptimisticLock ;
534565 private Boolean withExcludeTxnFromChangeStreams ;
535566 private Boolean dataBoostEnabled ;
536567 private DirectedReadOptions directedReadOptions ;
@@ -540,6 +571,7 @@ void appendToOptions(Options options) {
540571 private Boolean lastStatement ;
541572 private IsolationLevel isolationLevel ;
542573 private XGoogSpannerRequestId reqId ;
574+ private ReadLockMode readLockMode ;
543575
544576 // Construction is via factory methods below.
545577 private Options () {}
@@ -644,10 +676,6 @@ Boolean validateOnly() {
644676 return validateOnly ;
645677 }
646678
647- Boolean withOptimisticLock () {
648- return withOptimisticLock ;
649- }
650-
651679 Boolean withExcludeTxnFromChangeStreams () {
652680 return withExcludeTxnFromChangeStreams ;
653681 }
@@ -704,6 +732,10 @@ IsolationLevel isolationLevel() {
704732 return isolationLevel ;
705733 }
706734
735+ ReadLockMode readLockMode () {
736+ return readLockMode ;
737+ }
738+
707739 @ Override
708740 public String toString () {
709741 StringBuilder b = new StringBuilder ();
@@ -740,9 +772,6 @@ public String toString() {
740772 if (validateOnly != null ) {
741773 b .append ("validateOnly: " ).append (validateOnly ).append (' ' );
742774 }
743- if (withOptimisticLock != null ) {
744- b .append ("withOptimisticLock: " ).append (withOptimisticLock ).append (' ' );
745- }
746775 if (withExcludeTxnFromChangeStreams != null ) {
747776 b .append ("withExcludeTxnFromChangeStreams: " )
748777 .append (withExcludeTxnFromChangeStreams )
@@ -772,6 +801,9 @@ public String toString() {
772801 if (reqId != null ) {
773802 b .append ("requestId: " ).append (reqId .toString ());
774803 }
804+ if (readLockMode != null ) {
805+ b .append ("readLockMode: " ).append (readLockMode ).append (' ' );
806+ }
775807 return b .toString ();
776808 }
777809
@@ -807,15 +839,15 @@ public boolean equals(Object o) {
807839 && Objects .equals (tag (), that .tag ())
808840 && Objects .equals (etag (), that .etag ())
809841 && Objects .equals (validateOnly (), that .validateOnly ())
810- && Objects .equals (withOptimisticLock (), that .withOptimisticLock ())
811842 && Objects .equals (withExcludeTxnFromChangeStreams (), that .withExcludeTxnFromChangeStreams ())
812843 && Objects .equals (dataBoostEnabled (), that .dataBoostEnabled ())
813844 && Objects .equals (directedReadOptions (), that .directedReadOptions ())
814845 && Objects .equals (orderBy (), that .orderBy ())
815846 && Objects .equals (isLastStatement (), that .isLastStatement ())
816847 && Objects .equals (lockHint (), that .lockHint ())
817848 && Objects .equals (isolationLevel (), that .isolationLevel ())
818- && Objects .equals (reqId (), that .reqId ());
849+ && Objects .equals (reqId (), that .reqId ())
850+ && Objects .equals (readLockMode (), that .readLockMode ());
819851 }
820852
821853 @ Override
@@ -857,9 +889,6 @@ public int hashCode() {
857889 if (validateOnly != null ) {
858890 result = 31 * result + validateOnly .hashCode ();
859891 }
860- if (withOptimisticLock != null ) {
861- result = 31 * result + withOptimisticLock .hashCode ();
862- }
863892 if (withExcludeTxnFromChangeStreams != null ) {
864893 result = 31 * result + withExcludeTxnFromChangeStreams .hashCode ();
865894 }
@@ -887,6 +916,9 @@ public int hashCode() {
887916 if (reqId != null ) {
888917 result = 31 * result + reqId .hashCode ();
889918 }
919+ if (readLockMode != null ) {
920+ result = 31 * result + readLockMode .hashCode ();
921+ }
890922 return result ;
891923 }
892924
0 commit comments