@@ -51,6 +51,11 @@ public class CrudHandler {
5151 // Whether the transaction is in read-only mode or not.
5252 private final boolean readOnly ;
5353
54+ // Whether the transaction is in one-operation mode or not. One-operation mode refers to executing
55+ // a CRUD operation directly through `DistributedTransactionManager` without explicitly beginning
56+ // a transaction.
57+ private final boolean oneOperation ;
58+
5459 private final List <ConsensusCommitScanner > scanners = new ArrayList <>();
5560
5661 @ SuppressFBWarnings ("EI_EXPOSE_REP2" )
@@ -60,14 +65,16 @@ public CrudHandler(
6065 TransactionTableMetadataManager tableMetadataManager ,
6166 boolean isIncludeMetadataEnabled ,
6267 ParallelExecutor parallelExecutor ,
63- boolean readOnly ) {
68+ boolean readOnly ,
69+ boolean oneOperation ) {
6470 this .storage = checkNotNull (storage );
6571 this .snapshot = checkNotNull (snapshot );
6672 this .tableMetadataManager = tableMetadataManager ;
6773 this .isIncludeMetadataEnabled = isIncludeMetadataEnabled ;
6874 this .mutationConditionsValidator = new MutationConditionsValidator (snapshot .getId ());
6975 this .parallelExecutor = parallelExecutor ;
7076 this .readOnly = readOnly ;
77+ this .oneOperation = oneOperation ;
7178 }
7279
7380 @ VisibleForTesting
@@ -78,14 +85,16 @@ public CrudHandler(
7885 boolean isIncludeMetadataEnabled ,
7986 MutationConditionsValidator mutationConditionsValidator ,
8087 ParallelExecutor parallelExecutor ,
81- boolean readOnly ) {
88+ boolean readOnly ,
89+ boolean oneOperation ) {
8290 this .storage = checkNotNull (storage );
8391 this .snapshot = checkNotNull (snapshot );
8492 this .tableMetadataManager = tableMetadataManager ;
8593 this .isIncludeMetadataEnabled = isIncludeMetadataEnabled ;
8694 this .mutationConditionsValidator = mutationConditionsValidator ;
8795 this .parallelExecutor = parallelExecutor ;
8896 this .readOnly = readOnly ;
97+ this .oneOperation = oneOperation ;
8998 }
9099
91100 public Optional <Result > get (Get originalGet ) throws CrudException {
@@ -102,11 +111,17 @@ public Optional<Result> get(Get originalGet) throws CrudException {
102111 key = new Snapshot .Key (get );
103112 }
104113
105- readUnread (key , get );
106-
107- return snapshot
108- .getResult (key , get )
109- .map (r -> new FilteredResult (r , originalProjections , metadata , isIncludeMetadataEnabled ));
114+ if (isSnapshotReadRequired ()) {
115+ readUnread (key , get );
116+ return snapshot
117+ .getResult (key , get )
118+ .map (r -> new FilteredResult (r , originalProjections , metadata , isIncludeMetadataEnabled ));
119+ } else {
120+ Optional <TransactionResult > result = read (key , get );
121+ return snapshot
122+ .mergeResult (key , result , get .getConjunctions ())
123+ .map (r -> new FilteredResult (r , originalProjections , metadata , isIncludeMetadataEnabled ));
124+ }
110125 }
111126
112127 // Only for a Get with index, the argument `key` is null
@@ -120,7 +135,7 @@ void readUnread(@Nullable Snapshot.Key key, Get get) throws CrudException {
120135 // Although this class is not thread-safe, this method is actually thread-safe, so we call it
121136 // concurrently in the implicit pre-read
122137 @ VisibleForTesting
123- void read (@ Nullable Snapshot .Key key , Get get ) throws CrudException {
138+ Optional < TransactionResult > read (@ Nullable Snapshot .Key key , Get get ) throws CrudException {
124139 Optional <TransactionResult > result = getFromStorage (get );
125140 if (!result .isPresent () || result .get ().isCommitted ()) {
126141 if (result .isPresent () || get .getConjunctions ().isEmpty ()) {
@@ -142,8 +157,8 @@ void read(@Nullable Snapshot.Key key, Get get) throws CrudException {
142157 }
143158 }
144159 }
145- snapshot . putIntoGetSet (get , result );
146- return ;
160+ putIntoGetSetInSnapshot (get , result );
161+ return result ;
147162 }
148163 throw new UncommittedRecordException (
149164 get ,
@@ -204,7 +219,7 @@ private LinkedHashMap<Snapshot.Key, TransactionResult> scanInternal(Scan scan)
204219 }
205220 }
206221
207- snapshot . putIntoScanSet (scan , results );
222+ putIntoScanSetInSnapshot (scan , results );
208223
209224 return results ;
210225 }
@@ -263,9 +278,43 @@ private void putIntoReadSetInSnapshot(Snapshot.Key key, Optional<TransactionResu
263278 }
264279 }
265280
281+ private boolean isSnapshotReadRequired () {
282+ // In one-operation mode, we don't need snapshot read
283+ return !oneOperation ;
284+ }
285+
286+ private boolean isValidationOrSnapshotReadRequired () {
287+ return snapshot .isValidationRequired () || isSnapshotReadRequired ();
288+ }
289+
290+ private void putIntoGetSetInSnapshot (Get get , Optional <TransactionResult > result ) {
291+ // If neither validation nor snapshot read is required, we don't need to put the result into
292+ // the get set
293+ if (isValidationOrSnapshotReadRequired ()) {
294+ snapshot .putIntoGetSet (get , result );
295+ }
296+ }
297+
298+ private void putIntoScanSetInSnapshot (
299+ Scan scan , LinkedHashMap <Snapshot .Key , TransactionResult > results ) {
300+ // If neither validation nor snapshot read is required, we don't need to put the results into
301+ // the scan set
302+ if (isValidationOrSnapshotReadRequired ()) {
303+ snapshot .putIntoScanSet (scan , results );
304+ }
305+ }
306+
307+ private void putIntoScannerSetInSnapshot (
308+ Scan scan , LinkedHashMap <Snapshot .Key , TransactionResult > results ) {
309+ // if validation is not required, we don't need to put the results into the scanner set
310+ if (snapshot .isValidationRequired ()) {
311+ snapshot .putIntoScannerSet (scan , results );
312+ }
313+ }
314+
266315 private void verifyNoOverlap (Scan scan , Map <Snapshot .Key , TransactionResult > results ) {
267- // In read-only mode, we don't need to verify the overlap
268- if (!readOnly ) {
316+ // In either read-only mode or one-operation mode, we don't need to verify the overlap
317+ if (!readOnly && ! oneOperation ) {
269318 snapshot .verifyNoOverlap (scan , results );
270319 }
271320 }
@@ -432,7 +481,7 @@ private class ConsensusCommitStorageScanner extends AbstractTransactionCrudOpera
432481 private final List <String > originalProjections ;
433482 private final Scanner scanner ;
434483
435- private final LinkedHashMap <Snapshot .Key , TransactionResult > results = new LinkedHashMap <>() ;
484+ @ Nullable private final LinkedHashMap <Snapshot .Key , TransactionResult > results ;
436485 private final AtomicBoolean fullyScanned = new AtomicBoolean ();
437486 private final AtomicBoolean closed = new AtomicBoolean ();
438487
@@ -441,6 +490,14 @@ public ConsensusCommitStorageScanner(Scan scan, List<String> originalProjections
441490 this .scan = scan ;
442491 this .originalProjections = originalProjections ;
443492 scanner = scanFromStorage (scan );
493+
494+ if (isValidationOrSnapshotReadRequired ()) {
495+ results = new LinkedHashMap <>();
496+ } else {
497+ // If neither validation nor snapshot read is required, we don't need to put the results
498+ // into the scan set
499+ results = null ;
500+ }
444501 }
445502
446503 @ Override
@@ -456,7 +513,10 @@ public Optional<Result> one() throws CrudException {
456513 Snapshot .Key key = new Snapshot .Key (scan , r .get ());
457514 TransactionResult result = new TransactionResult (r .get ());
458515 processScanResult (key , scan , result );
459- results .put (key , result );
516+
517+ if (results != null ) {
518+ results .put (key , result );
519+ }
460520
461521 TableMetadata metadata = getTableMetadata (scan );
462522 return Optional .of (
@@ -499,10 +559,10 @@ public void close() {
499559 if (fullyScanned .get ()) {
500560 // If the scanner is fully scanned, we can treat it as a normal scan, and put the results
501561 // into the scan set
502- snapshot . putIntoScanSet (scan , results );
562+ putIntoScanSetInSnapshot (scan , results );
503563 } else {
504564 // If the scanner is not fully scanned, put the results into the scanner set
505- snapshot . putIntoScannerSet (scan , results );
565+ putIntoScannerSetInSnapshot (scan , results );
506566 }
507567
508568 verifyNoOverlap (scan , results );
0 commit comments