From 588e8d991012f96966aebe4c468a94d3de695558 Mon Sep 17 00:00:00 2001 From: Toshihiro Suzuki Date: Fri, 7 Nov 2025 23:11:42 +0900 Subject: [PATCH] Unify Get/Scan preparation logic for transaction reads and Serializable validation (#3113) --- .../consensuscommit/ConsensusCommitUtils.java | 155 +++ .../consensuscommit/CrudHandler.java | 312 +---- .../transaction/consensuscommit/Snapshot.java | 88 +- .../ConsensusCommitUtilsTest.java | 588 ++++++++++ .../consensuscommit/CrudHandlerTest.java | 1044 +++++------------ .../consensuscommit/SnapshotTest.java | 255 ++-- ...nsusCommitSpecificIntegrationTestBase.java | 2 +- 7 files changed, 1249 insertions(+), 1195 deletions(-) diff --git a/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitUtils.java b/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitUtils.java index 4ca0f509cc..6ad6f9f9fc 100644 --- a/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitUtils.java +++ b/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitUtils.java @@ -1,12 +1,22 @@ package com.scalar.db.transaction.consensuscommit; import com.google.common.collect.ImmutableMap; +import com.scalar.db.api.AndConditionSet; import com.scalar.db.api.ConditionBuilder; +import com.scalar.db.api.ConditionSetBuilder; +import com.scalar.db.api.ConditionalExpression; +import com.scalar.db.api.Consistency; +import com.scalar.db.api.Get; +import com.scalar.db.api.GetBuilder; import com.scalar.db.api.Insert; +import com.scalar.db.api.LikeExpression; import com.scalar.db.api.MutationCondition; import com.scalar.db.api.Operation; import com.scalar.db.api.Put; import com.scalar.db.api.PutBuilder; +import com.scalar.db.api.Scan; +import com.scalar.db.api.ScanBuilder; +import com.scalar.db.api.Selection; import com.scalar.db.api.TableMetadata; import com.scalar.db.api.Update; import com.scalar.db.api.UpdateIf; @@ -352,4 +362,149 @@ static TransactionTableMetadata getTransactionTableMetadata( } return metadata; } + + static Get prepareGetForStorage(Get get, TableMetadata metadata) { + GetBuilder.BuildableGetOrGetWithIndexFromExisting builder = + Get.newBuilder(get).clearProjections().consistency(Consistency.LINEARIZABLE); + + if (!get.getConjunctions().isEmpty()) { + // If there are conjunctions, we need to convert them to include conditions on the before + // image + Set converted = convertConjunctions(get.getConjunctions(), metadata); + return builder.clearConditions().whereOr(converted).build(); + } + + return builder.build(); + } + + static Scan prepareScanForStorage(Scan scan, TableMetadata metadata) { + ScanBuilder.BuildableScanOrScanAllFromExisting builder = + Scan.newBuilder(scan).clearProjections().consistency(Consistency.LINEARIZABLE); + + if (scan.getLimit() > 0) { + // Since the recovery process and the conjunction processing may exclude some records from + // the scan result, it is necessary to perform the scan without a limit. + builder.limit(0); + } + + if (!scan.getConjunctions().isEmpty()) { + // If there are conjunctions, we need to convert them to include conditions on the before + // image + Set converted = convertConjunctions(scan.getConjunctions(), metadata); + return builder.clearConditions().whereOr(converted).build(); + } + + return builder.build(); + } + + /** + * Converts the given conjunctions to include conditions on before images. + * + *

This is necessary because we might miss prepared records whose before images match the + * original conditions when reading from storage. For example, suppose we have the following + * records in storage: + * + *

+   *   | partition_key | clustering_key | column | status    | before_column | before_status  |
+   *   |---------------|----------------|--------|-----------|---------------|----------------|
+   *   | 0             | 0              | 1000   | COMMITTED |               |                |
+   *   | 0             | 1              | 200    | PREPARED  | 1000          | COMMITTED      |
+   * 
+ * + * If we scan records with the condition "column = 1000" without converting the condition + * (conjunction), we only get the first record, not the second one, because the condition does not + * match. However, the second record has not been committed yet, so we should still retrieve it, + * considering the possibility that the record will be rolled back. + * + *

To handle such cases, we convert the conjunctions to include conditions on the before image. + * For example, if the original condition is: + * + *

+   *   column = 1000
+   * 
+ * + * We convert it to: + * + *
+   *   column = 1000 OR before_column = 1000
+   * 
+ * + *

Here are more examples: + * + *

Example 1: + * + *

+   *   {@code column >= 500 AND column < 1000}
+   * 
+ * + * becomes: + * + *
+   *   {@code (column >= 500 AND column < 1000) OR (before_column >= 500 AND before_column < 1000)}
+   * 
+ * + *

Example 2: + * + *

+   *   {@code column1 = 500 OR column2 != 1000}
+   * 
+ * + * becomes: + * + *
+   *   {@code column1 = 500 OR column2 != 1000 OR before_column1 = 500 OR before_column2 != 1000}
+   * 
+ * + * This way, we can ensure that prepared records whose before images satisfy the original scan + * conditions are not missed during the scan. + * + * @param conjunctions the conjunctions to convert + * @param metadata the table metadata of the target table + * @return the converted conjunctions + */ + private static Set convertConjunctions( + Set conjunctions, TableMetadata metadata) { + Set converted = new HashSet<>(conjunctions.size() * 2); + + // Keep the original conjunctions + conjunctions.forEach( + c -> converted.add(ConditionSetBuilder.andConditionSet(c.getConditions()).build())); + + // Add conditions on the before image + for (Selection.Conjunction conjunction : conjunctions) { + Set conditions = new HashSet<>(conjunction.getConditions().size()); + for (ConditionalExpression condition : conjunction.getConditions()) { + String columnName = condition.getColumn().getName(); + + if (metadata.getPartitionKeyNames().contains(columnName) + || metadata.getClusteringKeyNames().contains(columnName)) { + // If the condition is on the primary key, we don't need to convert it + conditions.add(condition); + continue; + } + + // Convert the condition to use the before image column + ConditionalExpression convertedCondition; + if (condition instanceof LikeExpression) { + LikeExpression likeExpression = (LikeExpression) condition; + convertedCondition = + ConditionBuilder.buildLikeExpression( + likeExpression.getColumn().copyWith(Attribute.BEFORE_PREFIX + columnName), + likeExpression.getOperator(), + likeExpression.getEscape()); + } else { + convertedCondition = + ConditionBuilder.buildConditionalExpression( + condition.getColumn().copyWith(Attribute.BEFORE_PREFIX + columnName), + condition.getOperator()); + } + + conditions.add(convertedCondition); + } + + converted.add(ConditionSetBuilder.andConditionSet(conditions).build()); + } + + return converted; + } } diff --git a/core/src/main/java/com/scalar/db/transaction/consensuscommit/CrudHandler.java b/core/src/main/java/com/scalar/db/transaction/consensuscommit/CrudHandler.java index ef4ba8b056..df6e688be0 100644 --- a/core/src/main/java/com/scalar/db/transaction/consensuscommit/CrudHandler.java +++ b/core/src/main/java/com/scalar/db/transaction/consensuscommit/CrudHandler.java @@ -4,16 +4,11 @@ import static com.scalar.db.transaction.consensuscommit.ConsensusCommitOperationAttributes.isImplicitPreReadEnabled; import com.google.common.annotations.VisibleForTesting; -import com.scalar.db.api.AndConditionSet; -import com.scalar.db.api.ConditionBuilder; -import com.scalar.db.api.ConditionSetBuilder; -import com.scalar.db.api.ConditionalExpression; import com.scalar.db.api.Consistency; import com.scalar.db.api.Delete; import com.scalar.db.api.DistributedStorage; import com.scalar.db.api.Get; import com.scalar.db.api.GetBuilder; -import com.scalar.db.api.LikeExpression; import com.scalar.db.api.Operation; import com.scalar.db.api.Put; import com.scalar.db.api.Result; @@ -31,13 +26,11 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.IOException; import java.util.ArrayList; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -88,10 +81,7 @@ public CrudHandler( this.parallelExecutor = checkNotNull(parallelExecutor); } - public Optional get(Get originalGet, TransactionContext context) throws CrudException { - List originalProjections = new ArrayList<>(originalGet.getProjections()); - Get get = (Get) prepareStorageSelection(originalGet); - + public Optional get(Get get, TransactionContext context) throws CrudException { TableMetadata metadata = getTableMetadata(get, context.transactionId); Snapshot.Key key; @@ -103,43 +93,46 @@ public Optional get(Get originalGet, TransactionContext context) throws } if (isSnapshotReadRequired(context)) { - readUnread(key, get, context); + readUnread(key, get, context, metadata); return context .snapshot .getResult(key, get) - .map(r -> new FilteredResult(r, originalProjections, metadata, isIncludeMetadataEnabled)); + .map( + r -> new FilteredResult(r, get.getProjections(), metadata, isIncludeMetadataEnabled)); } else { - Optional result = read(key, get, context); + Optional result = read(key, get, context, metadata); return context .snapshot .mergeResult(key, result, get.getConjunctions()) - .map(r -> new FilteredResult(r, originalProjections, metadata, isIncludeMetadataEnabled)); + .map( + r -> new FilteredResult(r, get.getProjections(), metadata, isIncludeMetadataEnabled)); } } // Only for a Get with index, the argument `key` is null @VisibleForTesting - void readUnread(@Nullable Snapshot.Key key, Get get, TransactionContext context) + void readUnread( + @Nullable Snapshot.Key key, Get get, TransactionContext context, TableMetadata metadata) throws CrudException { if (!context.snapshot.containsKeyInGetSet(get)) { - read(key, get, context); + read(key, get, context, metadata); } } // Although this class is not thread-safe, this method is actually thread-safe, so we call it // concurrently in the implicit pre-read @VisibleForTesting - Optional read(@Nullable Snapshot.Key key, Get get, TransactionContext context) + Optional read( + @Nullable Snapshot.Key key, Get get, TransactionContext context, TableMetadata metadata) throws CrudException { - Optional result = getFromStorage(get, context); + Optional result = getFromStorage(get, metadata, context.transactionId); if (result.isPresent() && !result.get().isCommitted()) { // Lazy recovery if (key == null) { // Only for a Get with index, the argument `key` is null. In that case, create a key from // the result - TableMetadata tableMetadata = getTableMetadata(get, context.transactionId); - key = new Snapshot.Key(get, result.get(), tableMetadata); + key = new Snapshot.Key(get, result.get(), metadata); } result = executeRecovery(key, get, result.get(), context); @@ -168,8 +161,7 @@ Optional read(@Nullable Snapshot.Key key, Get get, Transactio if (result.isPresent()) { // Only when we can get the record with the Get with index, we can put it into the read // set - TableMetadata tableMetadata = getTableMetadata(get, context.transactionId); - key = new Snapshot.Key(get, result.get(), tableMetadata); + key = new Snapshot.Key(get, result.get(), metadata); putIntoReadSetInSnapshot(key, result, context); } } @@ -205,20 +197,17 @@ private Optional executeRecovery( return recoveryResult.recoveredResult; } - public List scan(Scan originalScan, TransactionContext context) throws CrudException { - List originalProjections = new ArrayList<>(originalScan.getProjections()); - Scan scan = (Scan) prepareStorageSelection(originalScan); - LinkedHashMap results = scanInternal(scan, context); - verifyNoOverlap(scan, results, context); - + public List scan(Scan scan, TransactionContext context) throws CrudException { TableMetadata metadata = getTableMetadata(scan, context.transactionId); + LinkedHashMap results = scanInternal(scan, context, metadata); + verifyNoOverlap(scan, results, context); return results.values().stream() - .map(r -> new FilteredResult(r, originalProjections, metadata, isIncludeMetadataEnabled)) + .map(r -> new FilteredResult(r, scan.getProjections(), metadata, isIncludeMetadataEnabled)) .collect(Collectors.toList()); } private LinkedHashMap scanInternal( - Scan scan, TransactionContext context) throws CrudException { + Scan scan, TransactionContext context, TableMetadata metadata) throws CrudException { Optional> resultsInSnapshot = context.snapshot.getResults(scan); if (resultsInSnapshot.isPresent()) { @@ -227,20 +216,10 @@ private LinkedHashMap scanInternal( LinkedHashMap results = new LinkedHashMap<>(); - Scanner scanner = null; - try { - if (scan.getLimit() > 0) { - // Since recovery and conjunctions may delete some records from the scan result, it is - // necessary to perform the scan without a limit. - scanner = scanFromStorage(Scan.newBuilder(scan).limit(0).build(), context); - } else { - scanner = scanFromStorage(scan, context); - } - + try (Scanner scanner = scanFromStorage(scan, metadata, context.transactionId)) { for (Result r : scanner) { TransactionResult result = new TransactionResult(r); - TableMetadata tableMetadata = getTableMetadata(scan, context.transactionId); - Snapshot.Key key = new Snapshot.Key(scan, r, tableMetadata); + Snapshot.Key key = new Snapshot.Key(scan, r, metadata); Optional processedScanResult = processScanResult(key, scan, result, context); processedScanResult.ifPresent(res -> results.put(key, res)); @@ -262,14 +241,8 @@ private LinkedHashMap scanInternal( exception.getMessage()), exception, context.transactionId); - } finally { - if (scanner != null) { - try { - scanner.close(); - } catch (IOException e) { - logger.warn("Failed to close the scanner. Transaction ID: {}", context.transactionId, e); - } - } + } catch (IOException e) { + logger.warn("Failed to close the scanner. Transaction ID: {}", context.transactionId, e); } putIntoScanSetInSnapshot(scan, results, context); @@ -305,21 +278,17 @@ private Optional processScanResult( return ret; } - public TransactionCrudOperable.Scanner getScanner(Scan originalScan, TransactionContext context) + public TransactionCrudOperable.Scanner getScanner(Scan scan, TransactionContext context) throws CrudException { - List originalProjections = new ArrayList<>(originalScan.getProjections()); - Scan scan = (Scan) prepareStorageSelection(originalScan); - + TableMetadata metadata = getTableMetadata(scan, context.transactionId); ConsensusCommitScanner scanner; - Optional> resultsInSnapshot = context.snapshot.getResults(scan); if (resultsInSnapshot.isPresent()) { scanner = - new ConsensusCommitSnapshotScanner( - scan, originalProjections, context, resultsInSnapshot.get()); + new ConsensusCommitSnapshotScanner(scan, context, metadata, resultsInSnapshot.get()); } else { - scanner = new ConsensusCommitStorageScanner(scan, originalProjections, context); + scanner = new ConsensusCommitStorageScanner(scan, context, metadata); } context.scanners.add(scanner); @@ -386,6 +355,7 @@ private boolean isOverlapVerificationRequired(TransactionContext context) { } public void put(Put put, TransactionContext context) throws CrudException { + TableMetadata metadata = getTableMetadata(put, context.transactionId); Snapshot.Key key = new Snapshot.Key(put); if (put.getCondition().isPresent() @@ -398,7 +368,7 @@ public void put(Put put, TransactionContext context) throws CrudException { if (put.getCondition().isPresent()) { if (isImplicitPreReadEnabled(put) && !context.snapshot.containsKeyInReadSet(key)) { - read(key, createGet(key), context); + read(key, createGet(key), context, metadata); } mutationConditionsValidator.checkIfConditionIsSatisfied( put, context.snapshot.getResult(key).orElse(null), context); @@ -408,11 +378,12 @@ public void put(Put put, TransactionContext context) throws CrudException { } public void delete(Delete delete, TransactionContext context) throws CrudException { + TableMetadata metadata = getTableMetadata(delete, context.transactionId); Snapshot.Key key = new Snapshot.Key(delete); if (delete.getCondition().isPresent()) { if (!context.snapshot.containsKeyInReadSet(key)) { - read(key, createGet(key), context); + read(key, createGet(key), context, metadata); } mutationConditionsValidator.checkIfConditionIsSatisfied( delete, context.snapshot.getResult(key).orElse(null), context); @@ -431,7 +402,9 @@ public void readIfImplicitPreReadEnabled(TransactionContext context) throws Crud if (isImplicitPreReadEnabled(put)) { Snapshot.Key key = entry.getKey(); if (!context.snapshot.containsKeyInReadSet(key)) { - tasks.add(() -> read(key, createGet(key), context)); + Get get = createGet(key); + TableMetadata metadata = getTableMetadata(get, context.transactionId); + tasks.add(() -> read(key, get, context, metadata)); } } } @@ -440,7 +413,9 @@ public void readIfImplicitPreReadEnabled(TransactionContext context) throws Crud for (Map.Entry entry : context.snapshot.getDeleteSet()) { Snapshot.Key key = entry.getKey(); if (!context.snapshot.containsKeyInReadSet(key)) { - tasks.add(() -> read(key, createGet(key), context)); + Get get = createGet(key); + TableMetadata metadata = getTableMetadata(get, context.transactionId); + tasks.add(() -> read(key, get, context, metadata)); } } @@ -456,7 +431,7 @@ private Get createGet(Snapshot.Key key) { .table(key.getTable()) .partitionKey(key.getPartitionKey()); key.getClusteringKey().ifPresent(buildableGet::clusteringKey); - return (Get) prepareStorageSelection(buildableGet.build()); + return buildableGet.consistency(Consistency.LINEARIZABLE).build(); } /** @@ -544,186 +519,41 @@ void waitForRecoveryCompletion(TransactionContext context) throws CrudException // Although this class is not thread-safe, this method is actually thread-safe because the storage // is thread-safe @VisibleForTesting - Optional getFromStorage(Get get, TransactionContext context) + Optional getFromStorage(Get get, TableMetadata metadata, String transactionId) throws CrudException { try { - if (get.getConjunctions().isEmpty()) { - // If there are no conjunctions, we can read the record directly - return storage.get(get).map(TransactionResult::new); - } else { - // If there are conjunctions, we need to convert them to include conditions on the before - // image - Set converted = convertConjunctions(get, get.getConjunctions(), context); - Get convertedGet = Get.newBuilder(get).clearConditions().whereOr(converted).build(); - return storage.get(convertedGet).map(TransactionResult::new); - } + return storage + .get(ConsensusCommitUtils.prepareGetForStorage(get, metadata)) + .map(TransactionResult::new); } catch (ExecutionException e) { throw new CrudException( CoreError.CONSENSUS_COMMIT_READING_RECORD_FROM_STORAGE_FAILED.buildMessage( e.getMessage()), e, - context.transactionId); + transactionId); } } - private Scanner scanFromStorage(Scan scan, TransactionContext context) throws CrudException { + private Scanner scanFromStorage(Scan scan, TableMetadata metadata, String transactionId) + throws CrudException { try { - if (scan.getConjunctions().isEmpty()) { - // If there are no conjunctions, we can read the record directly - return storage.scan(scan); - } else { - // If there are conjunctions, we need to convert them to include conditions on the before - // image - Set converted = convertConjunctions(scan, scan.getConjunctions(), context); - Scan convertedScan = Scan.newBuilder(scan).clearConditions().whereOr(converted).build(); - return storage.scan(convertedScan); - } + return storage.scan(ConsensusCommitUtils.prepareScanForStorage(scan, metadata)); } catch (ExecutionException e) { throw new CrudException( CoreError.CONSENSUS_COMMIT_SCANNING_RECORDS_FROM_STORAGE_FAILED.buildMessage( e.getMessage()), e, - context.transactionId); + transactionId); } } - /** - * Converts the given conjunctions to include conditions on before images. - * - *

This is necessary because we might miss prepared records whose before images match the - * original conditions when reading from storage. For example, suppose we have the following - * records in storage: - * - *

-   *   | partition_key | clustering_key | column | status    | before_column | before_status  |
-   *   |---------------|----------------|--------|-----------|---------------|----------------|
-   *   | 0             | 0              | 1000   | COMMITTED |               |                |
-   *   | 0             | 1              | 200    | PREPARED  | 1000          | COMMITTED      |
-   * 
- * - * If we scan records with the condition "column = 1000" without converting the condition - * (conjunction), we only get the first record, not the second one, because the condition does not - * match. However, the second record has not been committed yet, so we should still retrieve it, - * considering the possibility that the record will be rolled back. - * - *

To handle such cases, we convert the conjunctions to include conditions on the before image. - * For example, if the original condition is: - * - *

-   *   column = 1000
-   * 
- * - * We convert it to: - * - *
-   *   column = 1000 OR before_column = 1000
-   * 
- * - *

Here are more examples: - * - *

Example 1: - * - *

-   *   {@code column >= 500 AND column < 1000}
-   * 
- * - * becomes: - * - *
-   *   {@code (column >= 500 AND column < 1000) OR (before_column >= 500 AND before_column < 1000)}
-   * 
- * - *

Example 2: - * - *

-   *   {@code column1 = 500 OR column2 != 1000}
-   * 
- * - * becomes: - * - *
-   *   {@code column1 = 500 OR column2 != 1000 OR before_column1 = 500 OR before_column2 != 1000}
-   * 
- * - * This way, we can ensure that prepared records whose before images satisfy the original scan - * conditions are not missed during the scan. - * - * @param selection the selection to convert - * @param conjunctions the conjunctions to convert - * @param context the transaction context - * @return the converted conjunctions - */ - private Set convertConjunctions( - Selection selection, Set conjunctions, TransactionContext context) + private TableMetadata getTableMetadata(Operation operation, String transactionId) throws CrudException { - TableMetadata metadata = getTableMetadata(selection, context.transactionId); - - Set converted = new HashSet<>(conjunctions.size() * 2); - - // Keep the original conjunctions - conjunctions.forEach( - c -> converted.add(ConditionSetBuilder.andConditionSet(c.getConditions()).build())); - - // Add conditions on the before image - for (Selection.Conjunction conjunction : conjunctions) { - Set conditions = new HashSet<>(conjunction.getConditions().size()); - for (ConditionalExpression condition : conjunction.getConditions()) { - String columnName = condition.getColumn().getName(); - - if (metadata.getPartitionKeyNames().contains(columnName) - || metadata.getClusteringKeyNames().contains(columnName)) { - // If the condition is on the primary key, we don't need to convert it - conditions.add(condition); - continue; - } - - // Convert the condition to use the before image column - ConditionalExpression convertedCondition; - if (condition instanceof LikeExpression) { - LikeExpression likeExpression = (LikeExpression) condition; - convertedCondition = - ConditionBuilder.buildLikeExpression( - likeExpression.getColumn().copyWith(Attribute.BEFORE_PREFIX + columnName), - likeExpression.getOperator(), - likeExpression.getEscape()); - } else { - convertedCondition = - ConditionBuilder.buildConditionalExpression( - condition.getColumn().copyWith(Attribute.BEFORE_PREFIX + columnName), - condition.getOperator()); - } - - conditions.add(convertedCondition); - } - - converted.add(ConditionSetBuilder.andConditionSet(conditions).build()); - } - - return converted; - } - - private Selection prepareStorageSelection(Selection selection) { - if (selection instanceof Get) { - return Get.newBuilder((Get) selection) - .clearProjections() - .consistency(Consistency.LINEARIZABLE) - .build(); - } else { - assert selection instanceof Scan; - - return Scan.newBuilder((Scan) selection) - .clearProjections() - .consistency(Consistency.LINEARIZABLE) - .build(); - } - } - - private TransactionTableMetadata getTransactionTableMetadata( - Operation operation, String transactionId) throws CrudException { assert operation.forFullTableName().isPresent(); try { - return ConsensusCommitUtils.getTransactionTableMetadata(tableMetadataManager, operation); + return ConsensusCommitUtils.getTransactionTableMetadata(tableMetadataManager, operation) + .getTableMetadata(); } catch (ExecutionException e) { throw new CrudException( CoreError.GETTING_TABLE_METADATA_FAILED.buildMessage(operation.forFullTableName().get()), @@ -732,19 +562,13 @@ private TransactionTableMetadata getTransactionTableMetadata( } } - private TableMetadata getTableMetadata(Operation operation, String transactionId) - throws CrudException { - TransactionTableMetadata metadata = getTransactionTableMetadata(operation, transactionId); - return metadata.getTableMetadata(); - } - @NotThreadSafe private class ConsensusCommitStorageScanner extends AbstractTransactionCrudOperableScanner implements ConsensusCommitScanner { private final Scan scan; - private final List originalProjections; private final TransactionContext context; + private final TableMetadata metadata; private final Scanner scanner; @Nullable private final LinkedHashMap results; @@ -753,19 +577,12 @@ private class ConsensusCommitStorageScanner extends AbstractTransactionCrudOpera private final AtomicBoolean closed = new AtomicBoolean(); public ConsensusCommitStorageScanner( - Scan scan, List originalProjections, TransactionContext context) - throws CrudException { + Scan scan, TransactionContext context, TableMetadata metadata) throws CrudException { this.scan = scan; - this.originalProjections = originalProjections; this.context = context; + this.metadata = metadata; - if (scan.getLimit() > 0) { - // Since recovery and conjunctions may delete some records, it is necessary to perform the - // scan without a limit. - scanner = scanFromStorage(Scan.newBuilder(scan).limit(0).build(), context); - } else { - scanner = scanFromStorage(scan, context); - } + scanner = scanFromStorage(scan, metadata, context.transactionId); if (isValidationOrSnapshotReadRequired(context) || isOverlapVerificationRequired(context)) { results = new LinkedHashMap<>(); @@ -791,8 +608,7 @@ public Optional one() throws CrudException { return Optional.empty(); } - TableMetadata tableMetadata = getTableMetadata(scan, context.transactionId); - Snapshot.Key key = new Snapshot.Key(scan, r.get(), tableMetadata); + Snapshot.Key key = new Snapshot.Key(scan, r.get(), metadata); TransactionResult result = new TransactionResult(r.get()); Optional processedScanResult = @@ -811,11 +627,10 @@ public Optional one() throws CrudException { fullyScanned.set(true); } - TableMetadata metadata = getTableMetadata(scan, context.transactionId); return Optional.of( new FilteredResult( processedScanResult.get(), - originalProjections, + scan.getProjections(), metadata, isIncludeMetadataEnabled)); } @@ -887,8 +702,8 @@ private class ConsensusCommitSnapshotScanner extends AbstractTransactionCrudOper implements ConsensusCommitScanner { private final Scan scan; - private final List originalProjections; private final TransactionContext context; + private final TableMetadata metadata; private final Iterator> resultsIterator; private final LinkedHashMap results = new LinkedHashMap<>(); @@ -896,17 +711,17 @@ private class ConsensusCommitSnapshotScanner extends AbstractTransactionCrudOper public ConsensusCommitSnapshotScanner( Scan scan, - List originalProjections, TransactionContext context, + TableMetadata metadata, LinkedHashMap resultsInSnapshot) { this.scan = scan; - this.originalProjections = originalProjections; this.context = context; + this.metadata = metadata; resultsIterator = resultsInSnapshot.entrySet().iterator(); } @Override - public Optional one() throws CrudException { + public Optional one() { if (!resultsIterator.hasNext()) { return Optional.empty(); } @@ -914,14 +729,13 @@ public Optional one() throws CrudException { Map.Entry entry = resultsIterator.next(); results.put(entry.getKey(), entry.getValue()); - TableMetadata metadata = getTableMetadata(scan, context.transactionId); return Optional.of( new FilteredResult( - entry.getValue(), originalProjections, metadata, isIncludeMetadataEnabled)); + entry.getValue(), scan.getProjections(), metadata, isIncludeMetadataEnabled)); } @Override - public List all() throws CrudException { + public List all() { List results = new ArrayList<>(); while (true) { diff --git a/core/src/main/java/com/scalar/db/transaction/consensuscommit/Snapshot.java b/core/src/main/java/com/scalar/db/transaction/consensuscommit/Snapshot.java index 7bfcad1635..775c4e80d8 100644 --- a/core/src/main/java/com/scalar/db/transaction/consensuscommit/Snapshot.java +++ b/core/src/main/java/com/scalar/db/transaction/consensuscommit/Snapshot.java @@ -542,10 +542,11 @@ void toSerializable(DistributedStorage storage) // Get set is re-validated to check if there is no anti-dependency for (Map.Entry> entry : getSet.entrySet()) { Get get = entry.getKey(); + TableMetadata metadata = getTableMetadata(get); - if (ScalarDbUtils.isSecondaryIndexSpecified(get, getTableMetadata(get))) { + if (ScalarDbUtils.isSecondaryIndexSpecified(get, metadata)) { // For Get with index - tasks.add(() -> validateGetWithIndexResult(storage, get, entry.getValue())); + tasks.add(() -> validateGetWithIndexResult(storage, get, entry.getValue(), metadata)); } else { // For other Get @@ -554,7 +555,7 @@ void toSerializable(DistributedStorage storage) continue; } - tasks.add(() -> validateGetResult(storage, get, entry.getValue())); + tasks.add(() -> validateGetResult(storage, get, entry.getValue(), metadata)); } } @@ -591,25 +592,12 @@ private void validateScanResults( throws ExecutionException, ValidationConflictException { Scanner scanner = null; try { - // Only get tx_id and primary key columns because we use only them to compare - TableMetadata tableMetadata = getTableMetadata(scan); - scan = - Scan.newBuilder(scan) - .clearProjections() - .projection(Attribute.ID) - .projections(tableMetadata.getPartitionKeyNames()) - .projections(tableMetadata.getClusteringKeyNames()) - .build(); - - if (scan.getLimit() == 0) { - scanner = storage.scan(scan); - } else { - // Get a scanner without the limit if the scan has a limit - scanner = storage.scan(Scan.newBuilder(scan).limit(0).build()); - } + TableMetadata metadata = getTableMetadata(scan); + + scanner = storage.scan(ConsensusCommitUtils.prepareScanForStorage(scan, metadata)); // Initialize the iterator for the latest scan results - Optional latestResult = scanner.one(); + Optional latestResult = getNextResult(scanner, scan); // Initialize the iterator for the original scan results Iterator> originalResultIterator = @@ -620,13 +608,13 @@ private void validateScanResults( // Compare the records of the iterators while (latestResult.isPresent() && originalResultEntry != null) { TransactionResult latestTxResult = new TransactionResult(latestResult.get()); - Key key = new Key(scan, latestTxResult, tableMetadata); + Key key = new Key(scan, latestTxResult, metadata); if (latestTxResult.getId() != null && latestTxResult.getId().equals(id)) { // The record is inserted/deleted/updated by this transaction // Skip the record of the latest scan results - latestResult = scanner.one(); + latestResult = getNextResult(scanner, scan); if (originalResultEntry.getKey().equals(key)) { // The record is updated by this transaction @@ -660,7 +648,7 @@ private void validateScanResults( } // Proceed to the next record - latestResult = scanner.one(); + latestResult = getNextResult(scanner, scan); originalResultEntry = Iterators.getNext(originalResultIterator, null); } @@ -699,7 +687,7 @@ private void validateScanResults( // The record is inserted/deleted by this transaction // Skip the record - latestResult = scanner.one(); + latestResult = getNextResult(scanner, scan); } else { // The record is inserted by another transaction throwExceptionDueToAntiDependency(); @@ -716,8 +704,30 @@ private void validateScanResults( } } + private Optional getNextResult(Scanner scanner, Scan scan) throws ExecutionException { + Optional next = scanner.one(); + if (!next.isPresent()) { + return next; + } + + if (!scan.getConjunctions().isEmpty()) { + // Because we also get records whose before images match the conjunctions, we need to check if + // the current status of the records actually match the conjunctions. + next = + next.filter( + r -> + ScalarDbUtils.columnsMatchAnyOfConjunctions( + r.getColumns(), scan.getConjunctions())); + } + + return next.isPresent() ? next : getNextResult(scanner, scan); + } + private void validateGetWithIndexResult( - DistributedStorage storage, Get get, Optional originalResult) + DistributedStorage storage, + Get get, + Optional originalResult, + TableMetadata metadata) throws ExecutionException, ValidationConflictException { assert get.forNamespace().isPresent() && get.forTable().isPresent(); @@ -738,22 +748,34 @@ private void validateGetWithIndexResult( .build(); LinkedHashMap results = new LinkedHashMap<>(1); - TableMetadata tableMetadata = getTableMetadata(scanWithIndex); - originalResult.ifPresent( - r -> results.put(new Snapshot.Key(scanWithIndex, r, tableMetadata), r)); + originalResult.ifPresent(r -> results.put(new Snapshot.Key(scanWithIndex, r, metadata), r)); // Validate the result to check if there is no anti-dependency validateScanResults(storage, scanWithIndex, results, false); } private void validateGetResult( - DistributedStorage storage, Get get, Optional originalResult) + DistributedStorage storage, + Get get, + Optional originalResult, + TableMetadata metadata) throws ExecutionException, ValidationConflictException { - // Only get the tx_id column because we use only them to compare - get = Get.newBuilder(get).clearProjections().projection(Attribute.ID).build(); - // Check if a read record is not changed - Optional latestResult = storage.get(get).map(TransactionResult::new); + Optional latestResult = + storage + .get(ConsensusCommitUtils.prepareGetForStorage(get, metadata)) + .map(TransactionResult::new); + + if (!get.getConjunctions().isEmpty()) { + // Because we also get records whose before images match the conjunctions, we need to check if + // the current status of the records actually match the conjunctions. + latestResult = + latestResult.filter( + r -> + ScalarDbUtils.columnsMatchAnyOfConjunctions( + r.getColumns(), get.getConjunctions())); + } + if (isChanged(latestResult, originalResult)) { throwExceptionDueToAntiDependency(); } diff --git a/core/src/test/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitUtilsTest.java b/core/src/test/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitUtilsTest.java index 5ccd98ea53..a17a2f21d9 100644 --- a/core/src/test/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitUtilsTest.java +++ b/core/src/test/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitUtilsTest.java @@ -1,14 +1,20 @@ package com.scalar.db.transaction.consensuscommit; +import static com.scalar.db.api.ConditionBuilder.column; +import static com.scalar.db.api.ConditionSetBuilder.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.scalar.db.api.Consistency; +import com.scalar.db.api.Get; +import com.scalar.db.api.Scan; import com.scalar.db.api.TableMetadata; import com.scalar.db.io.Column; import com.scalar.db.io.DataType; import com.scalar.db.io.IntColumn; +import com.scalar.db.io.Key; import com.scalar.db.io.TextColumn; import java.util.HashMap; import java.util.HashSet; @@ -700,4 +706,586 @@ public void createAfterImageColumnsFromBeforeImage_versionColumnWithNonZero_shou assertThat(columns.get("tx_version").hasNullValue()).isFalse(); assertThat(columns.get("tx_version").getIntValue()).isEqualTo(5); } + + @Test + public void prepareGetForStorage_GetWithoutConjunctions_shouldReturnPreparedGet() { + // Arrange + Get get = + Get.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofText("pk", "key1")) + .projections("col1", "col2") + .build(); + + TableMetadata metadata = + TableMetadata.newBuilder() + .addColumn("pk", DataType.TEXT) + .addColumn("col1", DataType.INT) + .addColumn("col2", DataType.TEXT) + .addPartitionKey("pk") + .build(); + + // Act + Get actual = ConsensusCommitUtils.prepareGetForStorage(get, metadata); + + // Assert + assertThat(actual) + .isEqualTo( + Get.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofText("pk", "key1")) + .consistency(Consistency.LINEARIZABLE) + .build()); + } + + @Test + public void prepareGetForStorage_GetWithConjunctions_shouldConvertConjunctions() { + // Arrange + TableMetadata metadata = + ConsensusCommitUtils.buildTransactionTableMetadata( + TableMetadata.newBuilder() + .addColumn("pk", DataType.INT) + .addColumn("ck", DataType.INT) + .addColumn("col1", DataType.INT) + .addColumn("col2", DataType.INT) + .addColumn("col3", DataType.INT) + .addColumn("col4", DataType.TEXT) + .addPartitionKey("pk") + .addClusteringKey("ck") + .addSecondaryIndex("col3") + .build()); + + // Act Assert + + // Single condition + assertThat( + ConsensusCommitUtils.prepareGetForStorage( + Get.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .clusteringKey(Key.ofInt("ck", 200)) + .where(column("col1").isEqualToInt(10)) + .build(), + metadata)) + .isEqualTo( + Get.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .clusteringKey(Key.ofInt("ck", 200)) + .where(column("col1").isEqualToInt(10)) + .or(column(Attribute.BEFORE_PREFIX + "col1").isEqualToInt(10)) + .consistency(Consistency.LINEARIZABLE) + .build()); + + // AND condition + assertThat( + ConsensusCommitUtils.prepareGetForStorage( + Get.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .clusteringKey(Key.ofInt("ck", 200)) + .where(column("col1").isEqualToInt(10)) + .and(column("col2").isGreaterThanInt(20)) + .build(), + metadata)) + .isEqualTo( + Get.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .clusteringKey(Key.ofInt("ck", 200)) + .where( + condition(column("col1").isEqualToInt(10)) + .and(column("col2").isGreaterThanInt(20)) + .build()) + .or( + condition(column(Attribute.BEFORE_PREFIX + "col1").isEqualToInt(10)) + .and(column(Attribute.BEFORE_PREFIX + "col2").isGreaterThanInt(20)) + .build()) + .consistency(Consistency.LINEARIZABLE) + .build()); + + // OR condition + assertThat( + ConsensusCommitUtils.prepareGetForStorage( + Get.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .clusteringKey(Key.ofInt("ck", 200)) + .where(column("col1").isEqualToInt(10)) + .or(column("col2").isEqualToInt(30)) + .build(), + metadata)) + .isEqualTo( + Get.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .clusteringKey(Key.ofInt("ck", 200)) + .where(column("col1").isEqualToInt(10)) + .or(column(Attribute.BEFORE_PREFIX + "col1").isEqualToInt(10)) + .or(column("col2").isEqualToInt(30)) + .or(column(Attribute.BEFORE_PREFIX + "col2").isEqualToInt(30)) + .consistency(Consistency.LINEARIZABLE) + .build()); + + // Complex condition (AND + OR) + assertThat( + ConsensusCommitUtils.prepareGetForStorage( + Get.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .clusteringKey(Key.ofInt("ck", 200)) + .where( + condition(column("col1").isGreaterThanInt(10)) + .and(column("col1").isLessThanInt(20)) + .build()) + .or( + condition(column("col2").isGreaterThanInt(30)) + .and(column("col2").isLessThanOrEqualToInt(40)) + .build()) + .build(), + metadata)) + .isEqualTo( + Get.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .clusteringKey(Key.ofInt("ck", 200)) + .where( + condition(column("col1").isGreaterThanInt(10)) + .and(column("col1").isLessThanInt(20)) + .build()) + .or( + condition(column(Attribute.BEFORE_PREFIX + "col1").isGreaterThanInt(10)) + .and(column(Attribute.BEFORE_PREFIX + "col1").isLessThanInt(20)) + .build()) + .or( + condition(column("col2").isGreaterThanInt(30)) + .and(column("col2").isLessThanOrEqualToInt(40)) + .build()) + .or( + condition(column(Attribute.BEFORE_PREFIX + "col2").isGreaterThanInt(30)) + .and(column(Attribute.BEFORE_PREFIX + "col2").isLessThanOrEqualToInt(40)) + .build()) + .consistency(Consistency.LINEARIZABLE) + .build()); + + // LIKE expression (should be converted) + assertThat( + ConsensusCommitUtils.prepareGetForStorage( + Get.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .clusteringKey(Key.ofInt("ck", 200)) + .where(column("col4").isLikeText("pattern%")) + .build(), + metadata)) + .isEqualTo( + Get.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .clusteringKey(Key.ofInt("ck", 200)) + .where(column("col4").isLikeText("pattern%")) + .or(column(Attribute.BEFORE_PREFIX + "col4").isLikeText("pattern%")) + .consistency(Consistency.LINEARIZABLE) + .build()); + + // LIKE expression with escape (should be converted) + assertThat( + ConsensusCommitUtils.prepareGetForStorage( + Get.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .clusteringKey(Key.ofInt("ck", 200)) + .where(column("col4").isLikeText("%pattern%", "\\")) + .build(), + metadata)) + .isEqualTo( + Get.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .clusteringKey(Key.ofInt("ck", 200)) + .where(column("col4").isLikeText("%pattern%", "\\")) + .or(column(Attribute.BEFORE_PREFIX + "col4").isLikeText("%pattern%", "\\")) + .consistency(Consistency.LINEARIZABLE) + .build()); + } + + @Test + public void prepareScanForStorage_ScanWithoutConjunctionsAndLimit_shouldReturnPreparedScan() { + // Arrange + Scan scan = + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofText("pk", "key1")) + .projections("col1", "col2") + .build(); + + TableMetadata metadata = + TableMetadata.newBuilder() + .addColumn("pk", DataType.TEXT) + .addColumn("col1", DataType.INT) + .addColumn("col2", DataType.TEXT) + .addPartitionKey("pk") + .build(); + + // Act + Scan actual = ConsensusCommitUtils.prepareScanForStorage(scan, metadata); + + // Assert + assertThat(actual) + .isEqualTo( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofText("pk", "key1")) + .consistency(Consistency.LINEARIZABLE) + .build()); + } + + @Test + public void prepareScanForStorage_ScanWithLimit_shouldRemoveLimit() { + // Arrange + Scan scan = + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofText("pk", "key1")) + .limit(10) + .build(); + + TableMetadata metadata = + TableMetadata.newBuilder().addColumn("pk", DataType.TEXT).addPartitionKey("pk").build(); + + // Act + Scan actual = ConsensusCommitUtils.prepareScanForStorage(scan, metadata); + + // Assert + assertThat(actual) + .isEqualTo( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofText("pk", "key1")) + .consistency(Consistency.LINEARIZABLE) + .build()); + } + + @Test + public void prepareScanForStorage_ScanWithConjunctions_shouldConvertConjunctions() { + // Arrange + TableMetadata metadata = + ConsensusCommitUtils.buildTransactionTableMetadata( + TableMetadata.newBuilder() + .addColumn("pk", DataType.INT) + .addColumn("ck", DataType.INT) + .addColumn("col1", DataType.INT) + .addColumn("col2", DataType.INT) + .addColumn("col3", DataType.INT) + .addColumn("col4", DataType.TEXT) + .addPartitionKey("pk") + .addClusteringKey("ck") + .addSecondaryIndex("col3") + .build()); + + // Act Assert + + // Single condition + assertThat( + ConsensusCommitUtils.prepareScanForStorage( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .where(column("col1").isEqualToInt(10)) + .build(), + metadata)) + .isEqualTo( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .where(column("col1").isEqualToInt(10)) + .or(column(Attribute.BEFORE_PREFIX + "col1").isEqualToInt(10)) + .consistency(Consistency.LINEARIZABLE) + .build()); + + // AND condition + assertThat( + ConsensusCommitUtils.prepareScanForStorage( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .where(column("col1").isEqualToInt(10)) + .and(column("col2").isGreaterThanInt(20)) + .build(), + metadata)) + .isEqualTo( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .where( + condition(column("col1").isEqualToInt(10)) + .and(column("col2").isGreaterThanInt(20)) + .build()) + .or( + condition(column(Attribute.BEFORE_PREFIX + "col1").isEqualToInt(10)) + .and(column(Attribute.BEFORE_PREFIX + "col2").isGreaterThanInt(20)) + .build()) + .consistency(Consistency.LINEARIZABLE) + .build()); + + // OR condition + assertThat( + ConsensusCommitUtils.prepareScanForStorage( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .where(column("col1").isEqualToInt(10)) + .or(column("col2").isEqualToInt(30)) + .build(), + metadata)) + .isEqualTo( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .where(column("col1").isEqualToInt(10)) + .or(column(Attribute.BEFORE_PREFIX + "col1").isEqualToInt(10)) + .or(column("col2").isEqualToInt(30)) + .or(column(Attribute.BEFORE_PREFIX + "col2").isEqualToInt(30)) + .consistency(Consistency.LINEARIZABLE) + .build()); + + // Complex condition (AND + OR) + assertThat( + ConsensusCommitUtils.prepareScanForStorage( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .where( + condition(column("col1").isGreaterThanInt(10)) + .and(column("col1").isLessThanInt(20)) + .build()) + .or( + condition(column("col2").isGreaterThanInt(30)) + .and(column("col2").isLessThanOrEqualToInt(40)) + .build()) + .build(), + metadata)) + .isEqualTo( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .where( + condition(column("col1").isGreaterThanInt(10)) + .and(column("col1").isLessThanInt(20)) + .build()) + .or( + condition(column(Attribute.BEFORE_PREFIX + "col1").isGreaterThanInt(10)) + .and(column(Attribute.BEFORE_PREFIX + "col1").isLessThanInt(20)) + .build()) + .or( + condition(column("col2").isGreaterThanInt(30)) + .and(column("col2").isLessThanOrEqualToInt(40)) + .build()) + .or( + condition(column(Attribute.BEFORE_PREFIX + "col2").isGreaterThanInt(30)) + .and(column(Attribute.BEFORE_PREFIX + "col2").isLessThanOrEqualToInt(40)) + .build()) + .consistency(Consistency.LINEARIZABLE) + .build()); + + // LIKE expression (should be converted) + assertThat( + ConsensusCommitUtils.prepareScanForStorage( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .where(column("col4").isLikeText("pattern%")) + .build(), + metadata)) + .isEqualTo( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .where(column("col4").isLikeText("pattern%")) + .or(column(Attribute.BEFORE_PREFIX + "col4").isLikeText("pattern%")) + .consistency(Consistency.LINEARIZABLE) + .build()); + + // LIKE expression with escape (should be converted) + assertThat( + ConsensusCommitUtils.prepareScanForStorage( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .where(column("col4").isLikeText("%pattern%", "\\")) + .build(), + metadata)) + .isEqualTo( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("pk", 100)) + .where(column("col4").isLikeText("%pattern%", "\\")) + .or(column(Attribute.BEFORE_PREFIX + "col4").isLikeText("%pattern%", "\\")) + .consistency(Consistency.LINEARIZABLE) + .build()); + + // Condition on partition key (should not be converted) + assertThat( + ConsensusCommitUtils.prepareScanForStorage( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .all() + .where(column("pk").isEqualToInt(100)) + .build(), + metadata)) + .isEqualTo( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .all() + .where(column("pk").isEqualToInt(100)) + .consistency(Consistency.LINEARIZABLE) + .build()); + + // Condition on partition key and other columns (only other columns should be converted) + assertThat( + ConsensusCommitUtils.prepareScanForStorage( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .all() + .where(column("pk").isEqualToInt(50)) + .and(column("col1").isEqualToInt(10)) + .build(), + metadata)) + .isEqualTo( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .all() + .where( + condition(column("pk").isEqualToInt(50)) + .and(column("col1").isEqualToInt(10)) + .build()) + .or( + condition(column("pk").isEqualToInt(50)) + .and(column(Attribute.BEFORE_PREFIX + "col1").isEqualToInt(10)) + .build()) + .consistency(Consistency.LINEARIZABLE) + .build()); + + assertThat( + ConsensusCommitUtils.prepareScanForStorage( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .all() + .where(column("pk").isEqualToInt(50)) + .or(column("col1").isEqualToInt(10)) + .build(), + metadata)) + .isEqualTo( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .all() + .where(column("pk").isEqualToInt(50)) + .or(column("col1").isEqualToInt(10)) + .or(column(Attribute.BEFORE_PREFIX + "col1").isEqualToInt(10)) + .consistency(Consistency.LINEARIZABLE) + .build()); + + // Condition on clustering key (should not be converted) + assertThat( + ConsensusCommitUtils.prepareScanForStorage( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .all() + .where(column("ck").isGreaterThanInt(150)) + .build(), + metadata)) + .isEqualTo( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .all() + .where(column("ck").isGreaterThanInt(150)) + .consistency(Consistency.LINEARIZABLE) + .build()); + + // Condition on clustering key and other columns (only other columns should be converted) + assertThat( + ConsensusCommitUtils.prepareScanForStorage( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .all() + .where(column("ck").isEqualToInt(150)) + .and(column("col1").isEqualToInt(10)) + .build(), + metadata)) + .isEqualTo( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .all() + .where( + condition(column("ck").isEqualToInt(150)) + .and(column("col1").isEqualToInt(10)) + .build()) + .or( + condition(column("ck").isEqualToInt(150)) + .and(column(Attribute.BEFORE_PREFIX + "col1").isEqualToInt(10)) + .build()) + .consistency(Consistency.LINEARIZABLE) + .build()); + + assertThat( + ConsensusCommitUtils.prepareScanForStorage( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .all() + .where(column("ck").isEqualToInt(150)) + .or(column("col1").isEqualToInt(10)) + .build(), + metadata)) + .isEqualTo( + Scan.newBuilder() + .namespace("ns") + .table("tbl") + .all() + .where(column("ck").isEqualToInt(150)) + .or(column("col1").isEqualToInt(10)) + .or(column(Attribute.BEFORE_PREFIX + "col1").isEqualToInt(10)) + .consistency(Consistency.LINEARIZABLE) + .build()); + } } diff --git a/core/src/test/java/com/scalar/db/transaction/consensuscommit/CrudHandlerTest.java b/core/src/test/java/com/scalar/db/transaction/consensuscommit/CrudHandlerTest.java index 0a2af0fedd..4cb4a6a5e9 100644 --- a/core/src/test/java/com/scalar/db/transaction/consensuscommit/CrudHandlerTest.java +++ b/core/src/test/java/com/scalar/db/transaction/consensuscommit/CrudHandlerTest.java @@ -76,6 +76,8 @@ public class CrudHandlerTest { private static final String ANY_TEXT_3 = "text3"; private static final String ANY_TEXT_4 = "text4"; private static final String ANY_TEXT_5 = "text5"; + private static final int ANY_INT_1 = 100; + private static final int ANY_INT_2 = 200; private static final TableMetadata TABLE_METADATA = ConsensusCommitUtils.buildTransactionTableMetadata( @@ -148,7 +150,7 @@ private Scan prepareCrossPartitionScan() { .namespace(ANY_NAMESPACE_NAME) .table(ANY_TABLE_NAME) .all() - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) + .where(column(ANY_NAME_4).isEqualToInt(ANY_INT_1)) .build(); } @@ -167,6 +169,7 @@ private TransactionResult prepareResult( .put(ANY_NAME_1, TextColumn.of(ANY_NAME_1, partitionKeyColumnValue)) .put(ANY_NAME_2, TextColumn.of(ANY_NAME_2, clusteringKeyColumnValue)) .put(ANY_NAME_3, TextColumn.of(ANY_NAME_3, ANY_TEXT_3)) + .put(ANY_NAME_4, IntColumn.of(ANY_NAME_4, ANY_INT_1)) .put(Attribute.ID, TextColumn.of(Attribute.ID, ANY_ID_2)) .put(Attribute.STATE, IntColumn.of(Attribute.STATE, state.get())) .put(Attribute.VERSION, IntColumn.of(Attribute.VERSION, 2)) @@ -183,11 +186,10 @@ private TransactionResult prepareResult( public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudException { // Arrange Get get = prepareGet(); - Get getForStorage = toGetForStorageFrom(get); Snapshot.Key key = new Snapshot.Key(get); Optional expected = Optional.of(prepareResult(TransactionState.COMMITTED)); - when(snapshot.containsKeyInGetSet(getForStorage)).thenReturn(true); - when(snapshot.getResult(key, getForStorage)).thenReturn(expected); + when(snapshot.containsKeyInGetSet(get)).thenReturn(true); + when(snapshot.getResult(key, get)).thenReturn(expected); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); @@ -211,10 +213,10 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept Get getForStorage = toGetForStorageFrom(get); Optional expected = Optional.of(prepareResult(TransactionState.COMMITTED)); Optional transactionResult = expected.map(e -> (TransactionResult) e); - Snapshot.Key key = new Snapshot.Key(getForStorage); - when(snapshot.containsKeyInGetSet(getForStorage)).thenReturn(false); + Snapshot.Key key = new Snapshot.Key(get); + when(snapshot.containsKeyInGetSet(get)).thenReturn(false); when(storage.get(getForStorage)).thenReturn(expected); - when(snapshot.getResult(key, getForStorage)).thenReturn(transactionResult); + when(snapshot.getResult(key, get)).thenReturn(transactionResult); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); @@ -229,7 +231,7 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept expected.get(), Collections.emptyList(), TABLE_METADATA, false))); verify(storage).get(getForStorage); verify(snapshot).putIntoReadSet(key, Optional.of((TransactionResult) expected.get())); - verify(snapshot).putIntoGetSet(getForStorage, Optional.of((TransactionResult) expected.get())); + verify(snapshot).putIntoGetSet(get, Optional.of((TransactionResult) expected.get())); } @Test @@ -241,10 +243,10 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept Get getForStorage = toGetForStorageFrom(get); Optional expected = Optional.of(prepareResult(TransactionState.COMMITTED)); Optional transactionResult = expected.map(e -> (TransactionResult) e); - Snapshot.Key key = new Snapshot.Key(getForStorage); - when(snapshot.containsKeyInGetSet(getForStorage)).thenReturn(false); + Snapshot.Key key = new Snapshot.Key(get); + when(snapshot.containsKeyInGetSet(get)).thenReturn(false); when(storage.get(getForStorage)).thenReturn(expected); - when(snapshot.getResult(key, getForStorage)).thenReturn(transactionResult); + when(snapshot.getResult(key, get)).thenReturn(transactionResult); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, true, false); @@ -259,7 +261,7 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept expected.get(), Collections.emptyList(), TABLE_METADATA, false))); verify(storage).get(getForStorage); verify(snapshot, never()).putIntoReadSet(any(), any()); - verify(snapshot).putIntoGetSet(getForStorage, Optional.of((TransactionResult) expected.get())); + verify(snapshot).putIntoGetSet(get, Optional.of((TransactionResult) expected.get())); } @Test @@ -271,10 +273,10 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept Get getForStorage = toGetForStorageFrom(get); Optional expected = Optional.of(prepareResult(TransactionState.COMMITTED)); Optional transactionResult = expected.map(e -> (TransactionResult) e); - Snapshot.Key key = new Snapshot.Key(getForStorage); - when(snapshot.containsKeyInGetSet(getForStorage)).thenReturn(false); + Snapshot.Key key = new Snapshot.Key(get); + when(snapshot.containsKeyInGetSet(get)).thenReturn(false); when(storage.get(getForStorage)).thenReturn(expected); - when(snapshot.mergeResult(key, transactionResult, getForStorage.getConjunctions())) + when(snapshot.mergeResult(key, transactionResult, get.getConjunctions())) .thenReturn(transactionResult); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, true, true); @@ -302,10 +304,10 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept Get getForStorage = toGetForStorageFrom(get); Optional expected = Optional.of(prepareResult(TransactionState.COMMITTED)); Optional transactionResult = expected.map(e -> (TransactionResult) e); - Snapshot.Key key = new Snapshot.Key(getForStorage); - when(snapshot.containsKeyInGetSet(getForStorage)).thenReturn(false); + Snapshot.Key key = new Snapshot.Key(get); + when(snapshot.containsKeyInGetSet(get)).thenReturn(false); when(storage.get(getForStorage)).thenReturn(expected); - when(snapshot.mergeResult(key, transactionResult, getForStorage.getConjunctions())) + when(snapshot.mergeResult(key, transactionResult, get.getConjunctions())) .thenReturn(transactionResult); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SERIALIZABLE, true, true); @@ -321,7 +323,7 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept expected.get(), Collections.emptyList(), TABLE_METADATA, false))); verify(storage).get(getForStorage); verify(snapshot, never()).putIntoReadSet(any(), any()); - verify(snapshot).putIntoGetSet(getForStorage, Optional.of((TransactionResult) expected.get())); + verify(snapshot).putIntoGetSet(get, Optional.of((TransactionResult) expected.get())); } @Test @@ -329,14 +331,19 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept get_GetWithConjunction_GetNotExistsInSnapshotAndRecordInStorageCommitted_InOneOperationMode_ValidationRequired_ShouldReturnFromStorageAndUpdateSnapshot() throws CrudException, ExecutionException { // Arrange - ConditionalExpression condition = column(ANY_NAME_3).isEqualToText(ANY_TEXT_3); + ConditionalExpression condition = column(ANY_NAME_4).isEqualToInt(ANY_INT_1); Get get = Get.newBuilder(prepareGet()).where(condition).build(); - Get getForStorage = toGetForStorageFrom(get); + Get getForStorage = + Get.newBuilder(toGetForStorageFrom(get)) + .clearConditions() + .where(condition) + .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isEqualToInt(ANY_INT_1)) + .build(); Optional expected = Optional.of(prepareResult(TransactionState.COMMITTED)); Optional transactionResult = expected.map(e -> (TransactionResult) e); - Snapshot.Key key = new Snapshot.Key(getForStorage); - when(snapshot.containsKeyInGetSet(getForStorage)).thenReturn(false); - when(storage.get(any())).thenReturn(expected); + Snapshot.Key key = new Snapshot.Key(get); + when(snapshot.containsKeyInGetSet(get)).thenReturn(false); + when(storage.get(getForStorage)).thenReturn(expected); when(snapshot.mergeResult( key, transactionResult, Collections.singleton(Selection.Conjunction.of(condition)))) .thenReturn(transactionResult); @@ -352,15 +359,9 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept Optional.of( new FilteredResult( expected.get(), Collections.emptyList(), TABLE_METADATA, false))); - verify(storage) - .get( - Get.newBuilder(getForStorage) - .clearConditions() - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .build()); + verify(storage).get(getForStorage); verify(snapshot, never()).putIntoReadSet(any(), any()); - verify(snapshot).putIntoGetSet(getForStorage, Optional.of((TransactionResult) expected.get())); + verify(snapshot).putIntoGetSet(get, Optional.of((TransactionResult) expected.get())); } @Test @@ -372,10 +373,10 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept Get getForStorage = toGetForStorageFrom(get); Optional expected = Optional.of(prepareResult(TransactionState.COMMITTED)); Optional transactionResult = expected.map(e -> (TransactionResult) e); - Snapshot.Key key = new Snapshot.Key(getForStorage); - when(snapshot.containsKeyInGetSet(getForStorage)).thenReturn(false); - when(storage.get(any())).thenReturn(expected); - when(snapshot.mergeResult(key, transactionResult, getForStorage.getConjunctions())) + Snapshot.Key key = new Snapshot.Key(get); + when(snapshot.containsKeyInGetSet(get)).thenReturn(false); + when(storage.get(getForStorage)).thenReturn(expected); + when(snapshot.mergeResult(key, transactionResult, get.getConjunctions())) .thenReturn(transactionResult); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.READ_COMMITTED, false, false); @@ -401,13 +402,12 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept // Arrange Get get = Get.newBuilder(prepareGet()).build(); Get getForStorage = toGetForStorageFrom(get); - Optional expected = Optional.of(prepareResult(TransactionState.COMMITTED)); Optional transactionResult = expected.map(e -> (TransactionResult) e); - Snapshot.Key key = new Snapshot.Key(getForStorage); - when(snapshot.containsKeyInGetSet(getForStorage)).thenReturn(false); - when(storage.get(any())).thenReturn(expected); - when(snapshot.mergeResult(key, transactionResult, getForStorage.getConjunctions())) + Snapshot.Key key = new Snapshot.Key(get); + when(snapshot.containsKeyInGetSet(get)).thenReturn(false); + when(storage.get(getForStorage)).thenReturn(expected); + when(snapshot.mergeResult(key, transactionResult, get.getConjunctions())) .thenReturn(transactionResult); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.READ_COMMITTED, true, false); @@ -436,13 +436,13 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept Get getForStorage = toGetForStorageFrom(get); result = prepareResult(TransactionState.PREPARED); when(storage.get(getForStorage)).thenReturn(Optional.of(result)); - when(snapshot.containsKeyInGetSet(getForStorage)).thenReturn(false); + when(snapshot.containsKeyInGetSet(get)).thenReturn(false); TransactionResult expected = mock(TransactionResult.class); when(expected.getContainedColumnNames()).thenReturn(Collections.singleton(ANY_NAME_1)); when(expected.getAsObject(ANY_NAME_1)).thenReturn(ANY_TEXT_1); - when(snapshot.getResult(key, getForStorage)).thenReturn(Optional.of(expected)); + when(snapshot.getResult(key, get)).thenReturn(Optional.of(expected)); TransactionResult recoveredResult = mock(TransactionResult.class); @SuppressWarnings("unchecked") @@ -450,7 +450,7 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept when(recoveryExecutor.execute( key, - getForStorage, + get, new TransactionResult(result), ANY_ID_1, RecoveryExecutor.RecoveryType.RETURN_LATEST_RESULT_AND_RECOVER)) @@ -467,12 +467,12 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept verify(recoveryExecutor) .execute( key, - getForStorage, + get, new TransactionResult(result), ANY_ID_1, RecoveryExecutor.RecoveryType.RETURN_LATEST_RESULT_AND_RECOVER); verify(snapshot).putIntoReadSet(key, Optional.of(recoveredResult)); - verify(snapshot).putIntoGetSet(getForStorage, Optional.of(recoveredResult)); + verify(snapshot).putIntoGetSet(get, Optional.of(recoveredResult)); assertThat(actual) .isEqualTo( @@ -490,7 +490,7 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept Get getForStorage = toGetForStorageFrom(get); result = prepareResult(TransactionState.PREPARED); when(storage.get(getForStorage)).thenReturn(Optional.of(result)); - when(snapshot.containsKeyInGetSet(getForStorage)).thenReturn(false); + when(snapshot.containsKeyInGetSet(get)).thenReturn(false); TransactionResult expected = mock(TransactionResult.class); when(expected.getContainedColumnNames()).thenReturn(Collections.singleton(ANY_NAME_1)); @@ -504,13 +504,13 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept when(recoveryExecutor.execute( key, - getForStorage, + get, transactionResult, ANY_ID_1, RecoveryExecutor.RecoveryType.RETURN_COMMITTED_RESULT_AND_RECOVER)) .thenReturn(new RecoveryExecutor.Result(key, Optional.of(recoveredResult), recoveryFuture)); - when(snapshot.mergeResult(key, Optional.of(recoveredResult), getForStorage.getConjunctions())) + when(snapshot.mergeResult(key, Optional.of(recoveredResult), get.getConjunctions())) .thenReturn(Optional.of(expected)); TransactionContext context = @@ -524,7 +524,7 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept verify(recoveryExecutor) .execute( key, - getForStorage, + get, transactionResult, ANY_ID_1, RecoveryExecutor.RecoveryType.RETURN_COMMITTED_RESULT_AND_RECOVER); @@ -547,7 +547,7 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept Get getForStorage = toGetForStorageFrom(get); result = prepareResult(TransactionState.PREPARED); when(storage.get(getForStorage)).thenReturn(Optional.of(result)); - when(snapshot.containsKeyInGetSet(getForStorage)).thenReturn(false); + when(snapshot.containsKeyInGetSet(get)).thenReturn(false); TransactionResult expected = mock(TransactionResult.class); when(expected.getContainedColumnNames()).thenReturn(Collections.singleton(ANY_NAME_1)); @@ -561,13 +561,13 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept when(recoveryExecutor.execute( key, - getForStorage, + get, transactionResult, ANY_ID_1, RecoveryExecutor.RecoveryType.RETURN_COMMITTED_RESULT_AND_NOT_RECOVER)) .thenReturn(new RecoveryExecutor.Result(key, Optional.of(recoveredResult), recoveryFuture)); - when(snapshot.mergeResult(key, Optional.of(recoveredResult), getForStorage.getConjunctions())) + when(snapshot.mergeResult(key, Optional.of(recoveredResult), get.getConjunctions())) .thenReturn(Optional.of(expected)); TransactionContext context = @@ -581,7 +581,7 @@ public void get_GetExistsInSnapshot_ShouldReturnFromSnapshot() throws CrudExcept verify(recoveryExecutor) .execute( key, - getForStorage, + get, transactionResult, ANY_ID_1, RecoveryExecutor.RecoveryType.RETURN_COMMITTED_RESULT_AND_NOT_RECOVER); @@ -600,7 +600,7 @@ public void get_GetNotExistsInSnapshotAndRecordNotExistsInStorage_ShouldReturnEm // Arrange Get get = prepareGet(); Get getForStorage = toGetForStorageFrom(get); - when(snapshot.containsKeyInGetSet(getForStorage)).thenReturn(false); + when(snapshot.containsKeyInGetSet(get)).thenReturn(false); when(storage.get(getForStorage)).thenReturn(Optional.empty()); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); @@ -609,6 +609,9 @@ public void get_GetNotExistsInSnapshotAndRecordNotExistsInStorage_ShouldReturnEm Optional result = handler.get(get, context); // Assert + verify(storage).get(getForStorage); + verify(snapshot).putIntoReadSet(new Snapshot.Key(get), Optional.empty()); + verify(snapshot).putIntoGetSet(get, Optional.empty()); assertThat(result.isPresent()).isFalse(); } @@ -618,7 +621,7 @@ public void get_GetNotExistsInSnapshotAndExceptionThrownInStorage_ShouldThrowCru // Arrange Get get = prepareGet(); Get getForStorage = toGetForStorageFrom(get); - when(snapshot.containsKeyInGetSet(getForStorage)).thenReturn(false); + when(snapshot.containsKeyInGetSet(get)).thenReturn(false); ExecutionException toThrow = mock(ExecutionException.class); when(storage.get(getForStorage)).thenThrow(toThrow); TransactionContext context = @@ -634,22 +637,21 @@ public void get_GetNotExistsInSnapshotAndExceptionThrownInStorage_ShouldThrowCru public void get_CalledTwice_SecondTimeShouldReturnTheSameFromSnapshot() throws ExecutionException, CrudException { // Arrange - Get originalGet = prepareGet(); - Get getForStorage = toGetForStorageFrom(originalGet); - Get get1 = prepareGet(); - Get get2 = prepareGet(); + Get get = prepareGet(); + Get getForStorage = toGetForStorageFrom(get); + Get anotherGet = prepareGet(); Result result = prepareResult(TransactionState.COMMITTED); Optional expected = Optional.of(new TransactionResult(result)); - Snapshot.Key key = new Snapshot.Key(getForStorage); - when(snapshot.containsKeyInGetSet(getForStorage)).thenReturn(false).thenReturn(true); - when(snapshot.getResult(key, getForStorage)).thenReturn(expected).thenReturn(expected); + Snapshot.Key key = new Snapshot.Key(get); + when(snapshot.containsKeyInGetSet(get)).thenReturn(false).thenReturn(true); + when(snapshot.getResult(key, get)).thenReturn(expected).thenReturn(expected); when(storage.get(getForStorage)).thenReturn(Optional.of(result)); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - Optional results1 = handler.get(get1, context); - Optional results2 = handler.get(get2, context); + Optional results1 = handler.get(get, context); + Optional results2 = handler.get(anotherGet, context); // Assert verify(snapshot).putIntoReadSet(key, expected); @@ -659,7 +661,7 @@ public void get_CalledTwice_SecondTimeShouldReturnTheSameFromSnapshot() new FilteredResult( expected.get(), Collections.emptyList(), TABLE_METADATA, false))); assertThat(results1).isEqualTo(results2); - verify(storage, never()).get(originalGet); + verify(storage, never()).get(get); verify(storage).get(getForStorage); } @@ -667,24 +669,23 @@ public void get_CalledTwice_SecondTimeShouldReturnTheSameFromSnapshot() public void get_CalledTwice_ReadCommittedIsolation_BothShouldReturnFromStorage() throws ExecutionException, CrudException { // Arrange - Get originalGet = prepareGet(); - Get getForStorage = toGetForStorageFrom(originalGet); - Get get1 = prepareGet(); - Get get2 = prepareGet(); + Get get = prepareGet(); + Get getForStorage = toGetForStorageFrom(get); + Get anotherGet = prepareGet(); Result result = prepareResult(TransactionState.COMMITTED); Optional expected = Optional.of(new TransactionResult(result)); - Snapshot.Key key = new Snapshot.Key(getForStorage); - when(snapshot.containsKeyInGetSet(getForStorage)).thenReturn(false); + Snapshot.Key key = new Snapshot.Key(get); + when(snapshot.containsKeyInGetSet(get)).thenReturn(false); when(snapshot.mergeResult( - key, Optional.of(new TransactionResult(result)), getForStorage.getConjunctions())) + key, Optional.of(new TransactionResult(result)), get.getConjunctions())) .thenReturn(expected); when(storage.get(getForStorage)).thenReturn(Optional.of(result)); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.READ_COMMITTED, false, false); // Act - Optional results1 = handler.get(get1, context); - Optional results2 = handler.get(get2, context); + Optional results1 = handler.get(get, context); + Optional results2 = handler.get(anotherGet, context); // Assert verify(storage, times(2)).get(getForStorage); @@ -701,10 +702,9 @@ public void get_CalledTwice_ReadCommittedIsolation_BothShouldReturnFromStorage() public void get_CalledTwiceUnderRealSnapshot_SecondTimeShouldReturnTheSameFromSnapshot() throws ExecutionException, CrudException { // Arrange - Get originalGet = prepareGet(); - Get getForStorage = toGetForStorageFrom(originalGet); - Get get1 = prepareGet(); - Get get2 = prepareGet(); + Get get = prepareGet(); + Get getForStorage = toGetForStorageFrom(get); + Get anotherGet = prepareGet(); Result result = prepareResult(TransactionState.COMMITTED); Optional expected = Optional.of(new TransactionResult(result)); snapshot = new Snapshot(ANY_ID_1, Isolation.SNAPSHOT, tableMetadataManager, parallelExecutor); @@ -713,8 +713,8 @@ public void get_CalledTwiceUnderRealSnapshot_SecondTimeShouldReturnTheSameFromSn new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - Optional results1 = handler.get(get1, context); - Optional results2 = handler.get(get2, context); + Optional results1 = handler.get(get, context); + Optional results2 = handler.get(anotherGet, context); // Assert assertThat(results1) @@ -723,7 +723,7 @@ public void get_CalledTwiceUnderRealSnapshot_SecondTimeShouldReturnTheSameFromSn new FilteredResult( expected.get(), Collections.emptyList(), TABLE_METADATA, false))); assertThat(results1).isEqualTo(results2); - verify(storage, never()).get(originalGet); + verify(storage, never()).get(get); verify(storage).get(getForStorage); } @@ -731,10 +731,9 @@ public void get_CalledTwiceUnderRealSnapshot_SecondTimeShouldReturnTheSameFromSn public void get_CalledTwiceUnderRealSnapshot_ReadCommittedIsolation_BothShouldReturnFromStorage() throws ExecutionException, CrudException { // Arrange - Get originalGet = prepareGet(); - Get getForStorage = toGetForStorageFrom(originalGet); - Get get1 = prepareGet(); - Get get2 = prepareGet(); + Get get = prepareGet(); + Get getForStorage = toGetForStorageFrom(get); + Get anotherGet = prepareGet(); Result result = prepareResult(TransactionState.COMMITTED); Optional expected = Optional.of(new TransactionResult(result)); snapshot = @@ -744,8 +743,8 @@ public void get_CalledTwiceUnderRealSnapshot_ReadCommittedIsolation_BothShouldRe new TransactionContext(ANY_ID_1, snapshot, Isolation.READ_COMMITTED, false, false); // Act - Optional results1 = handler.get(get1, context); - Optional results2 = handler.get(get2, context); + Optional results1 = handler.get(get, context); + Optional results2 = handler.get(anotherGet, context); // Assert verify(storage, times(2)).get(getForStorage); @@ -770,9 +769,8 @@ public void get_ForNonExistingTable_ShouldThrowIllegalArgumentException() .partitionKey(partitionKey) .clusteringKey(clusteringKey) .build(); - Get getForStorage = toGetForStorageFrom(get); - when(tableMetadataManager.getTransactionTableMetadata(getForStorage)).thenReturn(null); + when(tableMetadataManager.getTransactionTableMetadata(get)).thenReturn(null); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); @@ -787,14 +785,14 @@ public void get_DifferentGetButSameRecordReturned_ShouldNotOverwriteReadSet() throws ExecutionException, CrudException { // Arrange Get get1 = prepareGet(); - Get get2 = Get.newBuilder(get1).where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)).build(); + Get get2 = Get.newBuilder(get1).where(column(ANY_NAME_4).isEqualToInt(ANY_INT_1)).build(); Get getForStorage1 = toGetForStorageFrom(get1); Get getForStorage2 = Get.newBuilder(get2) .clearProjections() .clearConditions() - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) + .where(column(ANY_NAME_4).isEqualToInt(ANY_INT_1)) + .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isEqualToInt(ANY_INT_1)) .consistency(Consistency.LINEARIZABLE) .build(); Result result = prepareResult(TransactionState.COMMITTED); @@ -845,11 +843,11 @@ void scanOrGetScanner_ResultGivenFromStorage_ShouldUpdateSnapshotAndReturn(ScanT List results = scanOrGetScanner(scan, scanType, context); // Assert + verify(storage).scan(scanForStorage); verify(scanner).close(); verify(snapshot).putIntoReadSet(key, Optional.of(expected)); - verify(snapshot) - .putIntoScanSet(scanForStorage, Maps.newLinkedHashMap(ImmutableMap.of(key, expected))); - verify(snapshot).verifyNoOverlap(scanForStorage, ImmutableMap.of(key, expected)); + verify(snapshot).putIntoScanSet(scan, Maps.newLinkedHashMap(ImmutableMap.of(key, expected))); + verify(snapshot).verifyNoOverlap(scan, ImmutableMap.of(key, expected)); assertThat(results.size()).isEqualTo(1); assertThat(results.get(0)) .isEqualTo(new FilteredResult(expected, Collections.emptyList(), TABLE_METADATA, false)); @@ -878,9 +876,9 @@ void scanOrGetScanner_ResultGivenFromStorage_InReadOnlyMode_ShouldUpdateSnapshot List results = scanOrGetScanner(scan, scanType, context); // Assert + verify(storage).scan(scanForStorage); verify(snapshot, never()).putIntoReadSet(any(), any()); - verify(snapshot) - .putIntoScanSet(scanForStorage, Maps.newLinkedHashMap(ImmutableMap.of(key, expected))); + verify(snapshot).putIntoScanSet(scan, Maps.newLinkedHashMap(ImmutableMap.of(key, expected))); verify(snapshot, never()).verifyNoOverlap(any(), any()); assertThat(results.size()).isEqualTo(1); assertThat(results.get(0)) @@ -910,6 +908,7 @@ void scanOrGetScanner_ResultGivenFromStorage_InReadOnlyMode_ShouldUpdateSnapshot List results = scanOrGetScanner(scan, scanType, context); // Assert + verify(storage).scan(scanForStorage); verify(snapshot, never()).putIntoReadSet(any(), any()); verify(snapshot, never()).putIntoScanSet(any(), any()); verify(snapshot, never()).verifyNoOverlap(any(), any()); @@ -942,9 +941,9 @@ void scanOrGetScanner_ResultGivenFromStorage_InReadOnlyMode_ShouldUpdateSnapshot List results = scanOrGetScanner(scan, scanType, context); // Assert + verify(storage).scan(scanForStorage); verify(snapshot, never()).putIntoReadSet(any(), any()); - verify(snapshot) - .putIntoScanSet(scanForStorage, Maps.newLinkedHashMap(ImmutableMap.of(key, expected))); + verify(snapshot).putIntoScanSet(scan, Maps.newLinkedHashMap(ImmutableMap.of(key, expected))); verify(snapshot, never()).verifyNoOverlap(any(), any()); assertThat(results.size()).isEqualTo(1); assertThat(results.get(0)) @@ -980,7 +979,7 @@ void scanOrGetScanner_ResultGivenFromStorage_InReadOnlyMode_ShouldUpdateSnapshot when(recoveryExecutor.execute( key, - scanForStorage, + scan, new TransactionResult(result), ANY_ID_1, RecoveryExecutor.RecoveryType.RETURN_LATEST_RESULT_AND_RECOVER)) @@ -993,12 +992,12 @@ void scanOrGetScanner_ResultGivenFromStorage_InReadOnlyMode_ShouldUpdateSnapshot List results = scanOrGetScanner(scan, scanType, context); // Assert + verify(storage).scan(scanForStorage); verify(scanner).close(); verify(snapshot).putIntoReadSet(key, Optional.of(recoveredResult)); verify(snapshot) - .putIntoScanSet( - scanForStorage, Maps.newLinkedHashMap(ImmutableMap.of(key, recoveredResult))); - verify(snapshot).verifyNoOverlap(scanForStorage, ImmutableMap.of(key, recoveredResult)); + .putIntoScanSet(scan, Maps.newLinkedHashMap(ImmutableMap.of(key, recoveredResult))); + verify(snapshot).verifyNoOverlap(scan, ImmutableMap.of(key, recoveredResult)); assertThat(results) .containsExactly( @@ -1034,7 +1033,7 @@ void scanOrGetScanner_ResultGivenFromStorage_InReadOnlyMode_ShouldUpdateSnapshot when(recoveryExecutor.execute( key, - scanForStorage, + scan, new TransactionResult(result), ANY_ID_1, RecoveryExecutor.RecoveryType.RETURN_COMMITTED_RESULT_AND_RECOVER)) @@ -1047,10 +1046,11 @@ void scanOrGetScanner_ResultGivenFromStorage_InReadOnlyMode_ShouldUpdateSnapshot List results = scanOrGetScanner(scan, scanType, context); // Assert + verify(storage).scan(scanForStorage); verify(scanner).close(); verify(snapshot).putIntoReadSet(key, Optional.of(recoveredResult)); verify(snapshot, never()).putIntoScanSet(any(), any()); - verify(snapshot).verifyNoOverlap(scanForStorage, ImmutableMap.of(key, recoveredResult)); + verify(snapshot).verifyNoOverlap(scan, ImmutableMap.of(key, recoveredResult)); assertThat(results) .containsExactly( @@ -1086,7 +1086,7 @@ void scanOrGetScanner_ResultGivenFromStorage_InReadOnlyMode_ShouldUpdateSnapshot when(recoveryExecutor.execute( key, - scanForStorage, + scan, new TransactionResult(result), ANY_ID_1, RecoveryExecutor.RecoveryType.RETURN_COMMITTED_RESULT_AND_NOT_RECOVER)) @@ -1099,6 +1099,7 @@ void scanOrGetScanner_ResultGivenFromStorage_InReadOnlyMode_ShouldUpdateSnapshot List results = scanOrGetScanner(scan, scanType, context); // Assert + verify(storage).scan(scanForStorage); verify(scanner).close(); verify(snapshot, never()).putIntoReadSet(any(), any()); verify(snapshot, never()).putIntoScanSet(any(), any()); @@ -1114,10 +1115,9 @@ void scanOrGetScanner_ResultGivenFromStorage_InReadOnlyMode_ShouldUpdateSnapshot void scanOrGetScanner_CalledTwice_SecondTimeShouldReturnTheSameFromSnapshot(ScanType scanType) throws ExecutionException, CrudException, IOException { // Arrange - Scan originalScan = prepareScan(); - Scan scanForStorage = toScanForStorageFrom(originalScan); - Scan scan1 = prepareScan(); - Scan scan2 = prepareScan(); + Scan scan = prepareScan(); + Scan scanForStorage = toScanForStorageFrom(scan); + Scan anotherScan = prepareScan(); result = prepareResult(TransactionState.COMMITTED); TransactionResult expected = new TransactionResult(result); if (scanType == ScanType.SCAN) { @@ -1126,27 +1126,26 @@ void scanOrGetScanner_CalledTwice_SecondTimeShouldReturnTheSameFromSnapshot(Scan when(scanner.one()).thenReturn(Optional.of(result)).thenReturn(Optional.empty()); } when(storage.scan(scanForStorage)).thenReturn(scanner); - Snapshot.Key key = new Snapshot.Key(scanForStorage, result, TABLE_METADATA); - when(snapshot.getResults(scanForStorage)) + Snapshot.Key key = new Snapshot.Key(scan, result, TABLE_METADATA); + when(snapshot.getResults(scan)) .thenReturn(Optional.empty()) .thenReturn(Optional.of(Maps.newLinkedHashMap(ImmutableMap.of(key, expected)))); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - List results1 = scanOrGetScanner(scan1, scanType, context); - List results2 = scanOrGetScanner(scan2, scanType, context); + List results1 = scanOrGetScanner(scan, scanType, context); + List results2 = scanOrGetScanner(anotherScan, scanType, context); // Assert verify(scanner).close(); verify(snapshot).putIntoReadSet(key, Optional.of(expected)); - verify(snapshot) - .putIntoScanSet(scanForStorage, Maps.newLinkedHashMap(ImmutableMap.of(key, expected))); + verify(snapshot).putIntoScanSet(scan, Maps.newLinkedHashMap(ImmutableMap.of(key, expected))); assertThat(results1.size()).isEqualTo(1); assertThat(results1.get(0)) .isEqualTo(new FilteredResult(expected, Collections.emptyList(), TABLE_METADATA, false)); assertThat(results1).isEqualTo(results2); - verify(storage, never()).scan(originalScan); + verify(storage, never()).scan(scan); verify(storage).scan(scanForStorage); } @@ -1155,10 +1154,9 @@ void scanOrGetScanner_CalledTwice_SecondTimeShouldReturnTheSameFromSnapshot(Scan void scan_CalledTwiceUnderRealSnapshot_SecondTimeShouldReturnTheSameFromSnapshot( ScanType scanType) throws ExecutionException, CrudException, IOException { // Arrange - Scan originalScan = prepareScan(); - Scan scanForStorage = toScanForStorageFrom(originalScan); - Scan scan1 = prepareScan(); - Scan scan2 = prepareScan(); + Scan scan = prepareScan(); + Scan scanForStorage = toScanForStorageFrom(scan); + Scan anotherScan = prepareScan(); result = prepareResult(TransactionState.COMMITTED); TransactionResult expected = new TransactionResult(result); snapshot = new Snapshot(ANY_ID_1, Isolation.SNAPSHOT, tableMetadataManager, parallelExecutor); @@ -1172,8 +1170,8 @@ void scan_CalledTwiceUnderRealSnapshot_SecondTimeShouldReturnTheSameFromSnapshot new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - List results1 = scanOrGetScanner(scan1, scanType, context); - List results2 = scanOrGetScanner(scan2, scanType, context); + List results1 = scanOrGetScanner(scan, scanType, context); + List results2 = scanOrGetScanner(anotherScan, scanType, context); // Assert assertThat(results1.size()).isEqualTo(1); @@ -1182,7 +1180,7 @@ void scan_CalledTwiceUnderRealSnapshot_SecondTimeShouldReturnTheSameFromSnapshot assertThat(results1).isEqualTo(results2); verify(scanner).close(); - verify(storage, never()).scan(originalScan); + verify(storage, never()).scan(scan); verify(storage).scan(scanForStorage); } @@ -1205,7 +1203,7 @@ void scanOrGetScanner_GetCalledAfterScan_ShouldReturnFromStorage(ScanType scanTy Get getForStorage = toGetForStorageFrom(get); Optional transactionResult = Optional.of(new TransactionResult(result)); when(storage.get(getForStorage)).thenReturn(Optional.of(result)); - when(snapshot.getResult(key, getForStorage)).thenReturn(transactionResult); + when(snapshot.getResult(key, get)).thenReturn(transactionResult); when(snapshot.getResult(key)).thenReturn(transactionResult); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); @@ -1229,7 +1227,8 @@ void scanOrGetScanner_GetCalledAfterScan_ShouldReturnFromStorage(ScanType scanTy void scanOrGetScanner_GetCalledAfterScanUnderRealSnapshot_ShouldReturnFromStorage( ScanType scanType) throws ExecutionException, CrudException, IOException { // Arrange - Scan scan = toScanForStorageFrom(prepareScan()); + Scan scan = prepareScan(); + Scan scanForStorage = toScanForStorageFrom(scan); result = prepareResult(TransactionState.COMMITTED); snapshot = new Snapshot(ANY_ID_1, Isolation.SNAPSHOT, tableMetadataManager, parallelExecutor); if (scanType == ScanType.SCAN) { @@ -1237,7 +1236,8 @@ void scanOrGetScanner_GetCalledAfterScanUnderRealSnapshot_ShouldReturnFromStorag } else { when(scanner.one()).thenReturn(Optional.of(result)).thenReturn(Optional.empty()); } - when(storage.scan(scan)).thenReturn(scanner); + when(storage.scan(scanForStorage)).thenReturn(scanner); + Get get = prepareGet(); Get getForStorage = toGetForStorageFrom(get); when(storage.get(getForStorage)).thenReturn(Optional.of(result)); @@ -1249,7 +1249,7 @@ void scanOrGetScanner_GetCalledAfterScanUnderRealSnapshot_ShouldReturnFromStorag Optional result = handler.get(get, context); // Assert - verify(storage).scan(scan); + verify(storage).scan(scanForStorage); verify(storage).get(getForStorage); verify(scanner).close(); @@ -1337,7 +1337,12 @@ void scanOrGetScanner_CalledAfterDeleteUnderRealSnapshot_ShouldThrowIllegalArgum ScanType scanType) throws ExecutionException, CrudException, IOException { // Arrange Scan scan = prepareCrossPartitionScan(); - Scan scanForStorage = toScanForStorageFrom(scan); + Scan scanForStorage = + Scan.newBuilder(toScanForStorageFrom(scan)) + .clearConditions() + .where(column(ANY_NAME_4).isEqualToInt(ANY_INT_1)) + .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isEqualToInt(ANY_INT_1)) + .build(); result = prepareResult(TransactionState.COMMITTED); Snapshot.Key key = new Snapshot.Key(scan, result, TABLE_METADATA); if (scanType == ScanType.SCAN) { @@ -1354,12 +1359,12 @@ void scanOrGetScanner_CalledAfterDeleteUnderRealSnapshot_ShouldThrowIllegalArgum List results = scanOrGetScanner(scan, scanType, context); // Assert + verify(storage).scan(scanForStorage); verify(scanner).close(); verify(snapshot).putIntoReadSet(key, Optional.of(transactionResult)); verify(snapshot) - .putIntoScanSet( - scanForStorage, Maps.newLinkedHashMap(ImmutableMap.of(key, transactionResult))); - verify(snapshot).verifyNoOverlap(scanForStorage, ImmutableMap.of(key, transactionResult)); + .putIntoScanSet(scan, Maps.newLinkedHashMap(ImmutableMap.of(key, transactionResult))); + verify(snapshot).verifyNoOverlap(scan, ImmutableMap.of(key, transactionResult)); assertThat(results.size()).isEqualTo(1); assertThat(results.get(0)) .isEqualTo( @@ -1373,10 +1378,15 @@ void scanOrGetScanner_CalledAfterDeleteUnderRealSnapshot_ShouldThrowIllegalArgum ScanType scanType) throws ExecutionException, IOException, CrudException { // Arrange Scan scan = prepareCrossPartitionScan(); - Scan scanForStorage = toScanForStorageFrom(scan); + Scan scanForStorage = + Scan.newBuilder(toScanForStorageFrom(scan)) + .clearConditions() + .where(column(ANY_NAME_4).isEqualToInt(ANY_INT_1)) + .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isEqualToInt(ANY_INT_1)) + .build(); result = prepareResult(TransactionState.PREPARED); - Snapshot.Key key = new Snapshot.Key(scanForStorage, result, TABLE_METADATA); + Snapshot.Key key = new Snapshot.Key(scan, result, TABLE_METADATA); if (scanType == ScanType.SCAN) { when(scanner.iterator()).thenReturn(Collections.singletonList(result).iterator()); } else { @@ -1386,10 +1396,10 @@ void scanOrGetScanner_CalledAfterDeleteUnderRealSnapshot_ShouldThrowIllegalArgum TransactionResult recoveredResult = mock(TransactionResult.class); - when(recoveredResult.getContainedColumnNames()).thenReturn(Collections.singleton(ANY_NAME_3)); - when(recoveredResult.getAsObject(ANY_NAME_3)).thenReturn(ANY_TEXT_3); + when(recoveredResult.getContainedColumnNames()).thenReturn(Collections.singleton(ANY_NAME_4)); + when(recoveredResult.getAsObject(ANY_NAME_4)).thenReturn(ANY_INT_1); when(recoveredResult.getColumns()) - .thenReturn(ImmutableMap.of(ANY_NAME_3, TextColumn.of(ANY_NAME_3, ANY_TEXT_3))); + .thenReturn(ImmutableMap.of(ANY_NAME_4, IntColumn.of(ANY_NAME_4, ANY_INT_1))); when(snapshot.getResult(key)).thenReturn(Optional.of(new TransactionResult(result))); @@ -1398,7 +1408,7 @@ void scanOrGetScanner_CalledAfterDeleteUnderRealSnapshot_ShouldThrowIllegalArgum when(recoveryExecutor.execute( key, - scanForStorage, + scan, new TransactionResult(result), ANY_ID_1, RecoveryExecutor.RecoveryType.RETURN_LATEST_RESULT_AND_RECOVER)) @@ -1408,22 +1418,15 @@ void scanOrGetScanner_CalledAfterDeleteUnderRealSnapshot_ShouldThrowIllegalArgum new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - List results = scanOrGetScanner(scanForStorage, scanType, context); + List results = scanOrGetScanner(scan, scanType, context); // Assert - verify(storage) - .scan( - Scan.newBuilder(scanForStorage) - .clearConditions() - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .build()); + verify(storage).scan(scanForStorage); verify(scanner).close(); verify(snapshot).putIntoReadSet(key, Optional.of(recoveredResult)); verify(snapshot) - .putIntoScanSet( - scanForStorage, Maps.newLinkedHashMap(ImmutableMap.of(key, recoveredResult))); - verify(snapshot).verifyNoOverlap(scanForStorage, ImmutableMap.of(key, recoveredResult)); + .putIntoScanSet(scan, Maps.newLinkedHashMap(ImmutableMap.of(key, recoveredResult))); + verify(snapshot).verifyNoOverlap(scan, ImmutableMap.of(key, recoveredResult)); assertThat(results) .containsExactly( @@ -1437,10 +1440,15 @@ void scanOrGetScanner_CalledAfterDeleteUnderRealSnapshot_ShouldThrowIllegalArgum ScanType scanType) throws ExecutionException, IOException, CrudException { // Arrange Scan scan = prepareCrossPartitionScan(); - Scan scanForStorage = toScanForStorageFrom(scan); + Scan scanForStorage = + Scan.newBuilder(toScanForStorageFrom(scan)) + .clearConditions() + .where(column(ANY_NAME_4).isEqualToInt(ANY_INT_1)) + .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isEqualToInt(ANY_INT_1)) + .build(); result = prepareResult(TransactionState.PREPARED); - Snapshot.Key key = new Snapshot.Key(scanForStorage, result, TABLE_METADATA); + Snapshot.Key key = new Snapshot.Key(scan, result, TABLE_METADATA); if (scanType == ScanType.SCAN) { when(scanner.iterator()).thenReturn(Collections.singletonList(result).iterator()); } else { @@ -1450,10 +1458,10 @@ void scanOrGetScanner_CalledAfterDeleteUnderRealSnapshot_ShouldThrowIllegalArgum TransactionResult recoveredResult = mock(TransactionResult.class); - when(recoveredResult.getContainedColumnNames()).thenReturn(Collections.singleton(ANY_NAME_3)); - when(recoveredResult.getAsObject(ANY_NAME_3)).thenReturn(ANY_TEXT_4); + when(recoveredResult.getContainedColumnNames()).thenReturn(Collections.singleton(ANY_NAME_4)); + when(recoveredResult.getAsObject(ANY_NAME_4)).thenReturn(ANY_INT_2); when(recoveredResult.getColumns()) - .thenReturn(ImmutableMap.of(ANY_NAME_3, TextColumn.of(ANY_NAME_3, ANY_TEXT_4))); + .thenReturn(ImmutableMap.of(ANY_NAME_4, IntColumn.of(ANY_NAME_4, ANY_INT_2))); when(snapshot.getResult(key)).thenReturn(Optional.of(new TransactionResult(result))); @@ -1462,7 +1470,7 @@ void scanOrGetScanner_CalledAfterDeleteUnderRealSnapshot_ShouldThrowIllegalArgum when(recoveryExecutor.execute( key, - scanForStorage, + scan, new TransactionResult(result), ANY_ID_1, RecoveryExecutor.RecoveryType.RETURN_LATEST_RESULT_AND_RECOVER)) @@ -1472,20 +1480,14 @@ void scanOrGetScanner_CalledAfterDeleteUnderRealSnapshot_ShouldThrowIllegalArgum new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - List results = scanOrGetScanner(scanForStorage, scanType, context); + List results = scanOrGetScanner(scan, scanType, context); // Assert - verify(storage) - .scan( - Scan.newBuilder(scanForStorage) - .clearConditions() - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .build()); + verify(storage).scan(scanForStorage); verify(scanner).close(); verify(snapshot, never()).putIntoReadSet(any(), any()); - verify(snapshot).putIntoScanSet(scanForStorage, Maps.newLinkedHashMap()); - verify(snapshot).verifyNoOverlap(scanForStorage, ImmutableMap.of()); + verify(snapshot).putIntoScanSet(scan, Maps.newLinkedHashMap()); + verify(snapshot).verifyNoOverlap(scan, ImmutableMap.of()); assertThat(results).isEmpty(); } @@ -1495,10 +1497,8 @@ void scanOrGetScanner_CalledAfterDeleteUnderRealSnapshot_ShouldThrowIllegalArgum void scanOrGetScanner_WithLimit_ShouldReturnLimitedResults(ScanType scanType) throws CrudException, ExecutionException, IOException { // Arrange - Scan scanWithoutLimit = prepareScan(); - Scan scanWithoutLimitForStorage = toScanForStorageFrom(scanWithoutLimit); - Scan scanWithLimit = Scan.newBuilder(scanWithoutLimit).limit(2).build(); - Scan scanWithLimitForStorage = toScanForStorageFrom(scanWithLimit); + Scan scanWithLimit = Scan.newBuilder(prepareScan()).limit(2).build(); + Scan scanForStorage = Scan.newBuilder(toScanForStorageFrom(scanWithLimit)).limit(0).build(); Result result1 = prepareResult(ANY_TEXT_1, ANY_TEXT_2, TransactionState.COMMITTED); Result result2 = prepareResult(ANY_TEXT_1, ANY_TEXT_3, TransactionState.COMMITTED); @@ -1518,7 +1518,7 @@ void scanOrGetScanner_WithLimit_ShouldReturnLimitedResults(ScanType scanType) .thenReturn(Optional.of(result2)) .thenReturn(Optional.empty()); } - when(storage.scan(scanWithoutLimitForStorage)).thenReturn(scanner); + when(storage.scan(scanForStorage)).thenReturn(scanner); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); @@ -1535,6 +1535,7 @@ void scanOrGetScanner_WithLimit_ShouldReturnLimitedResults(ScanType scanType) .isEqualTo( new FilteredResult(transactionResult2, Collections.emptyList(), TABLE_METADATA, false)); + verify(storage).scan(scanForStorage); verify(scanner).close(); verify(snapshot).putIntoReadSet(key1, Optional.of(transactionResult1)); verify(snapshot).putIntoReadSet(key2, Optional.of(transactionResult2)); @@ -1542,7 +1543,7 @@ void scanOrGetScanner_WithLimit_ShouldReturnLimitedResults(ScanType scanType) @SuppressWarnings("unchecked") ArgumentCaptor> resultsCaptor = ArgumentCaptor.forClass(LinkedHashMap.class); - verify(snapshot).putIntoScanSet(eq(scanWithLimitForStorage), resultsCaptor.capture()); + verify(snapshot).putIntoScanSet(eq(scanWithLimit), resultsCaptor.capture()); LinkedHashMap capturedResults = resultsCaptor.getValue(); assertThat(capturedResults).hasSize(2); @@ -1554,10 +1555,9 @@ void scanOrGetScanner_WithLimit_ShouldReturnLimitedResults(ScanType scanType) void scanOrGetScanner_WithLimitExceedingAvailableResults_ShouldReturnAllAvailableResults( ScanType scanType) throws CrudException, ExecutionException, IOException { // Arrange - Scan scanWithoutLimit = prepareScan(); Scan scanWithLimit = - Scan.newBuilder(scanWithoutLimit).limit(5).build(); // Limit higher than available results - Scan scanForStorage = toScanForStorageFrom(scanWithoutLimit); + Scan.newBuilder(prepareScan()).limit(5).build(); // Limit higher than available results + Scan scanForStorage = Scan.newBuilder(toScanForStorageFrom(scanWithLimit)).limit(0).build(); Result result = prepareResult(TransactionState.COMMITTED); Snapshot.Key key1 = new Snapshot.Key(scanWithLimit, result, TABLE_METADATA); @@ -1579,6 +1579,7 @@ void scanOrGetScanner_WithLimitExceedingAvailableResults_ShouldReturnAllAvailabl // Assert assertThat(results).hasSize(1); + verify(storage).scan(scanForStorage); verify(scanner).close(); verify(snapshot).putIntoReadSet(key1, Optional.of(transactionResult1)); } @@ -1589,10 +1590,8 @@ void scanOrGetScanner_WithLimitExceedingAvailableResults_ShouldReturnAllAvailabl scanOrGetScanner_WithLimit_UncommittedResult_ShouldCallRecoveryExecutorWithReturnLatestResultAndRecover( ScanType scanType) throws ExecutionException, IOException, CrudException { // Arrange - Scan scanWithoutLimit = prepareScan(); - Scan scanWithLimit = Scan.newBuilder(scanWithoutLimit).limit(2).build(); - Scan scanForStorageWithLimit = toScanForStorageFrom(scanWithLimit); - Scan scanForStorageWithoutLimit = toScanForStorageFrom(scanWithoutLimit); + Scan scanWithLimit = Scan.newBuilder(prepareScan()).limit(2).build(); + Scan scanForStorage = Scan.newBuilder(toScanForStorageFrom(scanWithLimit)).limit(0).build(); Result uncommittedResult1 = prepareResult(ANY_TEXT_1, ANY_TEXT_2, TransactionState.DELETED); Result uncommittedResult2 = prepareResult(ANY_TEXT_1, ANY_TEXT_3, TransactionState.PREPARED); @@ -1614,7 +1613,7 @@ void scanOrGetScanner_WithLimitExceedingAvailableResults_ShouldReturnAllAvailabl .thenReturn(Optional.of(uncommittedResult3)) .thenReturn(Optional.empty()); } - when(storage.scan(scanForStorageWithoutLimit)).thenReturn(scanner); + when(storage.scan(scanForStorage)).thenReturn(scanner); TransactionResult recoveredResult1 = mock(TransactionResult.class); when(recoveredResult1.getContainedColumnNames()).thenReturn(Collections.singleton(ANY_NAME_3)); @@ -1633,14 +1632,14 @@ void scanOrGetScanner_WithLimitExceedingAvailableResults_ShouldReturnAllAvailabl when(recoveryExecutor.execute( key1, - scanForStorageWithLimit, + scanWithLimit, new TransactionResult(uncommittedResult1), ANY_ID_1, RecoveryExecutor.RecoveryType.RETURN_LATEST_RESULT_AND_RECOVER)) .thenReturn(new RecoveryExecutor.Result(key1, Optional.empty(), recoveryFuture)); when(recoveryExecutor.execute( key2, - scanForStorageWithLimit, + scanWithLimit, new TransactionResult(uncommittedResult2), ANY_ID_1, RecoveryExecutor.RecoveryType.RETURN_LATEST_RESULT_AND_RECOVER)) @@ -1648,7 +1647,7 @@ void scanOrGetScanner_WithLimitExceedingAvailableResults_ShouldReturnAllAvailabl new RecoveryExecutor.Result(key2, Optional.of(recoveredResult1), recoveryFuture)); when(recoveryExecutor.execute( key3, - scanForStorageWithLimit, + scanWithLimit, new TransactionResult(uncommittedResult3), ANY_ID_1, RecoveryExecutor.RecoveryType.RETURN_LATEST_RESULT_AND_RECOVER)) @@ -1662,18 +1661,17 @@ void scanOrGetScanner_WithLimitExceedingAvailableResults_ShouldReturnAllAvailabl List results = scanOrGetScanner(scanWithLimit, scanType, context); // Assert - verify(storage).scan(scanForStorageWithoutLimit); + verify(storage).scan(scanForStorage); verify(scanner).close(); verify(snapshot).putIntoReadSet(key2, Optional.of(recoveredResult1)); verify(snapshot).putIntoReadSet(key3, Optional.of(recoveredResult2)); verify(snapshot) .putIntoScanSet( - scanForStorageWithLimit, + scanWithLimit, Maps.newLinkedHashMap(ImmutableMap.of(key2, recoveredResult1, key3, recoveredResult2))); verify(snapshot) .verifyNoOverlap( - scanForStorageWithLimit, - ImmutableMap.of(key2, recoveredResult1, key3, recoveredResult2)); + scanWithLimit, ImmutableMap.of(key2, recoveredResult1, key3, recoveredResult2)); assertThat(results) .containsExactly( @@ -1776,11 +1774,12 @@ public void getScanner_ExecutionExceptionThrownByScannerOne_ShouldThrowCrudExcep actualScanner.close(); // Assert + verify(storage).scan(scanForStorage); verify(scanner).close(); verify(snapshot).putIntoReadSet(key1, Optional.of(txResult1)); verify(snapshot) - .putIntoScannerSet(scanForStorage, Maps.newLinkedHashMap(ImmutableMap.of(key1, txResult1))); - verify(snapshot).verifyNoOverlap(scanForStorage, ImmutableMap.of(key1, txResult1)); + .putIntoScannerSet(scan, Maps.newLinkedHashMap(ImmutableMap.of(key1, txResult1))); + verify(snapshot).verifyNoOverlap(scan, ImmutableMap.of(key1, txResult1)); assertThat(actualResult) .hasValue(new FilteredResult(txResult1, Collections.emptyList(), TABLE_METADATA, false)); @@ -1810,6 +1809,7 @@ public void getScanner_ExecutionExceptionThrownByScannerOne_ShouldThrowCrudExcep actualScanner.close(); // Assert + verify(storage).scan(scanForStorage); verify(snapshot, never()).putIntoReadSet(any(), any()); verify(snapshot, never()).putIntoScannerSet(any(), any()); verify(snapshot, never()).verifyNoOverlap(any(), any()); @@ -1843,9 +1843,10 @@ public void getScanner_ExecutionExceptionThrownByScannerOne_ShouldThrowCrudExcep actualScanner.close(); // Assert + verify(storage).scan(scanForStorage); verify(snapshot, never()).putIntoReadSet(any(), any()); verify(snapshot) - .putIntoScannerSet(scanForStorage, Maps.newLinkedHashMap(ImmutableMap.of(key1, txResult1))); + .putIntoScannerSet(scan, Maps.newLinkedHashMap(ImmutableMap.of(key1, txResult1))); verify(snapshot, never()).verifyNoOverlap(any(), any()); assertThat(actualResult) @@ -1865,7 +1866,7 @@ public void put_PutWithoutConditionGiven_ShouldCallAppropriateMethods() throws C spied.put(put, context); // Assert - verify(spied, never()).readUnread(any(), any(), any()); + verify(spied, never()).readUnread(any(), any(), any(), any()); verify(snapshot, never()).getResult(any()); verify(mutationConditionsValidator, never()) .checkIfConditionIsSatisfied(any(Put.class), any(), any()); @@ -1891,13 +1892,6 @@ public void put_PutWithoutConditionGiven_ShouldCallAppropriateMethods() throws C when(result.isCommitted()).thenReturn(true); when(snapshot.getResult(key)).thenReturn(Optional.of(result)); - Get getForKey = - Get.newBuilder() - .namespace(key.getNamespace()) - .table(key.getTable()) - .partitionKey(key.getPartitionKey()) - .build(); - CrudHandler spied = spy(handler); TransactionContext context = @@ -1907,7 +1901,7 @@ public void put_PutWithoutConditionGiven_ShouldCallAppropriateMethods() throws C spied.put(put, context); // Assert - verify(spied, never()).readUnread(key, getForKey, context); + verify(spied, never()).readUnread(any(), any(), any(), any()); verify(snapshot).getResult(key); verify(mutationConditionsValidator).checkIfConditionIsSatisfied(put, result, context); verify(snapshot).putIntoWriteSet(key, put); @@ -1945,13 +1939,13 @@ public void put_PutWithoutConditionGiven_ShouldCallAppropriateMethods() throws C TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); - doReturn(Optional.empty()).when(spied).getFromStorage(getForKey, context); + doReturn(Optional.empty()).when(spied).getFromStorage(getForKey, TABLE_METADATA, ANY_ID_1); // Act spied.put(put, context); // Assert - verify(spied).read(key, getForKey, context); + verify(spied).read(key, getForKey, context, TABLE_METADATA); verify(snapshot).getResult(key); verify(mutationConditionsValidator).checkIfConditionIsSatisfied(put, result, context); verify(snapshot).putIntoWriteSet(key, put); @@ -1975,13 +1969,6 @@ public void put_PutWithoutConditionGiven_ShouldCallAppropriateMethods() throws C when(result.isCommitted()).thenReturn(true); when(snapshot.getResult(key)).thenReturn(Optional.of(result)); - Get getForKey = - Get.newBuilder() - .namespace(key.getNamespace()) - .table(key.getTable()) - .partitionKey(key.getPartitionKey()) - .build(); - CrudHandler spied = spy(handler); TransactionContext context = @@ -1991,7 +1978,7 @@ public void put_PutWithoutConditionGiven_ShouldCallAppropriateMethods() throws C spied.put(put, context); // Assert - verify(spied, never()).readUnread(key, getForKey, context); + verify(spied, never()).readUnread(any(), any(), any(), any()); verify(snapshot).getResult(key); verify(mutationConditionsValidator).checkIfConditionIsSatisfied(put, result, context); verify(snapshot).putIntoWriteSet(key, put); @@ -2036,7 +2023,7 @@ public void delete_DeleteWithoutConditionGiven_ShouldCallAppropriateMethods() spied.delete(delete, context); // Assert - verify(spied, never()).readUnread(any(), any(), any()); + verify(spied, never()).readUnread(any(), any(), any(), any()); verify(snapshot, never()).getResult(any()); verify(mutationConditionsValidator, never()) .checkIfConditionIsSatisfied(any(Delete.class), any(), any()); @@ -2060,13 +2047,6 @@ public void delete_DeleteWithConditionGiven_WithResultInReadSet_ShouldCallApprop when(result.isCommitted()).thenReturn(true); when(snapshot.getResult(key)).thenReturn(Optional.of(result)); - Get getForKey = - Get.newBuilder() - .namespace(key.getNamespace()) - .table(key.getTable()) - .partitionKey(key.getPartitionKey()) - .build(); - CrudHandler spied = spy(handler); TransactionContext context = @@ -2076,7 +2056,7 @@ public void delete_DeleteWithConditionGiven_WithResultInReadSet_ShouldCallApprop spied.delete(delete, context); // Assert - verify(spied, never()).readUnread(key, getForKey, context); + verify(spied, never()).readUnread(any(), any(), any(), any()); verify(snapshot).getResult(key); verify(mutationConditionsValidator).checkIfConditionIsSatisfied(delete, result, context); verify(snapshot).putIntoDeleteSet(key, delete); @@ -2110,13 +2090,13 @@ public void delete_DeleteWithConditionGiven_WithoutResultInReadSet_ShouldCallApp TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); - doReturn(Optional.empty()).when(spied).getFromStorage(getForKey, context); + doReturn(Optional.empty()).when(spied).getFromStorage(getForKey, TABLE_METADATA, ANY_ID_1); // Act spied.delete(delete, context); // Assert - verify(spied).read(key, getForKey, context); + verify(spied).read(key, getForKey, context, TABLE_METADATA); verify(snapshot).getResult(key); verify(mutationConditionsValidator).checkIfConditionIsSatisfied(delete, null, context); verify(snapshot).putIntoDeleteSet(key, delete); @@ -2137,15 +2117,16 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() .table(key.getTable()) .partitionKey(key.getPartitionKey()) .build(); + Get getForStorage = toGetForStorageFrom(getForKey); when(snapshot.containsKeyInGetSet(getForKey)).thenReturn(true); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - handler.readUnread(key, getForKey, context); + handler.readUnread(key, getForKey, context, TABLE_METADATA); // Assert - verify(storage, never()).get(any()); + verify(storage, never()).get(getForStorage); verify(snapshot, never()).putIntoReadSet(any(Snapshot.Key.class), any(Optional.class)); verify(snapshot, never()).putIntoGetSet(any(Get.class), any(Optional.class)); } @@ -2165,16 +2146,17 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() .table(key.getTable()) .partitionKey(key.getPartitionKey()) .build(); + Get getForStorage = toGetForStorageFrom(getForKey); when(snapshot.containsKeyInGetSet(getForKey)).thenReturn(false); - when(storage.get(any())).thenReturn(Optional.empty()); + when(storage.get(getForStorage)).thenReturn(Optional.empty()); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - handler.readUnread(key, getForKey, context); + handler.readUnread(key, getForKey, context, TABLE_METADATA); // Assert - verify(storage).get(any()); + verify(storage).get(getForStorage); verify(snapshot).putIntoReadSet(key, Optional.empty()); verify(snapshot).putIntoGetSet(getForKey, Optional.empty()); } @@ -2193,26 +2175,24 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() .namespace(key.getNamespace()) .table(key.getTable()) .partitionKey(key.getPartitionKey()) - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_1)) + .where(column(ANY_NAME_4).isEqualToInt(ANY_INT_1)) + .build(); + Get getForStorage = + Get.newBuilder(toGetForStorageFrom(getForKey)) + .clearConditions() + .where(column(ANY_NAME_4).isEqualToInt(ANY_INT_1)) + .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isEqualToInt(ANY_INT_1)) .build(); when(snapshot.containsKeyInGetSet(getForKey)).thenReturn(false); - when(storage.get(any())).thenReturn(Optional.empty()); + when(storage.get(getForStorage)).thenReturn(Optional.empty()); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - handler.readUnread(key, getForKey, context); + handler.readUnread(key, getForKey, context, TABLE_METADATA); // Assert - verify(storage) - .get( - Get.newBuilder() - .namespace(key.getNamespace()) - .table(key.getTable()) - .partitionKey(key.getPartitionKey()) - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_1)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_1)) - .build()); + verify(storage).get(getForStorage); verify(snapshot, never()).putIntoReadSet(key, Optional.empty()); verify(snapshot).putIntoGetSet(getForKey, Optional.empty()); } @@ -2226,26 +2206,25 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() when(key.getNamespace()).thenReturn(ANY_NAMESPACE_NAME); when(key.getTable()).thenReturn(ANY_TABLE_NAME); when(key.getPartitionKey()).thenReturn(Key.ofText(ANY_NAME_1, ANY_TEXT_1)); - - when(result.getInt(Attribute.STATE)).thenReturn(TransactionState.COMMITTED.get()); - when(storage.get(any())).thenReturn(Optional.of(result)); - Get getForKey = Get.newBuilder() .namespace(key.getNamespace()) .table(key.getTable()) .partitionKey(key.getPartitionKey()) .build(); + Get getForStorage = toGetForStorageFrom(getForKey); when(snapshot.containsKeyInGetSet(getForKey)).thenReturn(false); + when(result.getInt(Attribute.STATE)).thenReturn(TransactionState.COMMITTED.get()); + when(storage.get(getForStorage)).thenReturn(Optional.of(result)); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - handler.readUnread(key, getForKey, context); + handler.readUnread(key, getForKey, context, TABLE_METADATA); // Assert - verify(storage).get(any()); + verify(storage).get(getForStorage); verify(snapshot).putIntoReadSet(key, Optional.of(new TransactionResult(result))); verify(snapshot).putIntoGetSet(getForKey, Optional.of(new TransactionResult(result))); } @@ -2259,17 +2238,16 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() when(key.getNamespace()).thenReturn(ANY_NAMESPACE_NAME); when(key.getTable()).thenReturn(ANY_TABLE_NAME); when(key.getPartitionKey()).thenReturn(Key.ofText(ANY_NAME_1, ANY_TEXT_1)); - - when(result.getInt(Attribute.STATE)).thenReturn(TransactionState.PREPARED.get()); - when(storage.get(any())).thenReturn(Optional.of(result)); - Get getForKey = Get.newBuilder() .namespace(key.getNamespace()) .table(key.getTable()) .partitionKey(key.getPartitionKey()) .build(); + Get getForStorage = toGetForStorageFrom(getForKey); when(snapshot.containsKeyInGetSet(getForKey)).thenReturn(false); + when(result.getInt(Attribute.STATE)).thenReturn(TransactionState.PREPARED.get()); + when(storage.get(getForStorage)).thenReturn(Optional.of(result)); TransactionResult recoveredResult = mock(TransactionResult.class); @SuppressWarnings("unchecked") @@ -2287,10 +2265,10 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - handler.readUnread(key, getForKey, context); + handler.readUnread(key, getForKey, context, TABLE_METADATA); // Assert - verify(storage).get(getForKey); + verify(storage).get(getForStorage); verify(recoveryExecutor) .execute( key, @@ -2311,17 +2289,17 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() when(key.getNamespace()).thenReturn(ANY_NAMESPACE_NAME); when(key.getTable()).thenReturn(ANY_TABLE_NAME); when(key.getPartitionKey()).thenReturn(Key.ofText(ANY_NAME_1, ANY_TEXT_1)); - - when(result.getInt(Attribute.STATE)).thenReturn(TransactionState.PREPARED.get()); - when(storage.get(any())).thenReturn(Optional.of(result)); - Get getForKey = Get.newBuilder() .namespace(key.getNamespace()) .table(key.getTable()) .partitionKey(key.getPartitionKey()) + .consistency(Consistency.LINEARIZABLE) .build(); + Get getForStorage = toGetForStorageFrom(getForKey); when(snapshot.containsKeyInGetSet(getForKey)).thenReturn(false); + when(result.getInt(Attribute.STATE)).thenReturn(TransactionState.PREPARED.get()); + when(storage.get(getForStorage)).thenReturn(Optional.of(result)); Optional recoveredRecord = Optional.empty(); @SuppressWarnings("unchecked") @@ -2339,10 +2317,10 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - handler.readUnread(key, getForKey, context); + handler.readUnread(key, getForKey, context, TABLE_METADATA); // Assert - verify(storage).get(getForKey); + verify(storage).get(getForStorage); verify(recoveryExecutor) .execute( key, @@ -2363,25 +2341,30 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() when(key.getNamespace()).thenReturn(ANY_NAMESPACE_NAME); when(key.getTable()).thenReturn(ANY_TABLE_NAME); when(key.getPartitionKey()).thenReturn(Key.ofText(ANY_NAME_1, ANY_TEXT_1)); - - when(result.getInt(Attribute.STATE)).thenReturn(TransactionState.PREPARED.get()); - when(storage.get(any())).thenReturn(Optional.of(result)); - Get getWithConjunction = Get.newBuilder() .namespace(key.getNamespace()) .table(key.getTable()) .partitionKey(key.getPartitionKey()) - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) + .where(column(ANY_NAME_4).isEqualToInt(ANY_INT_1)) + .build(); + Get getForStorage = + Get.newBuilder(getWithConjunction) + .clearConditions() + .where(column(ANY_NAME_4).isEqualToInt(ANY_INT_1)) + .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isEqualToInt(ANY_INT_1)) + .consistency(Consistency.LINEARIZABLE) .build(); when(snapshot.containsKeyInGetSet(getWithConjunction)).thenReturn(false); + when(result.getInt(Attribute.STATE)).thenReturn(TransactionState.PREPARED.get()); + when(storage.get(getForStorage)).thenReturn(Optional.of(result)); TransactionResult recoveredResult = mock(TransactionResult.class); - when(recoveredResult.getContainedColumnNames()).thenReturn(Collections.singleton(ANY_NAME_3)); - when(recoveredResult.getAsObject(ANY_NAME_3)).thenReturn(ANY_TEXT_3); + when(recoveredResult.getContainedColumnNames()).thenReturn(Collections.singleton(ANY_NAME_4)); + when(recoveredResult.getAsObject(ANY_NAME_4)).thenReturn(ANY_INT_1); when(recoveredResult.getColumns()) - .thenReturn(ImmutableMap.of(ANY_NAME_3, TextColumn.of(ANY_NAME_3, ANY_TEXT_3))); + .thenReturn(ImmutableMap.of(ANY_NAME_4, IntColumn.of(ANY_NAME_4, ANY_INT_1))); @SuppressWarnings("unchecked") Future recoveryFuture = mock(Future.class); @@ -2398,16 +2381,10 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - handler.readUnread(key, getWithConjunction, context); + handler.readUnread(key, getWithConjunction, context, TABLE_METADATA); // Assert - verify(storage) - .get( - Get.newBuilder(getWithConjunction) - .clearConditions() - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .build()); + verify(storage).get(getForStorage); verify(recoveryExecutor) .execute( key, @@ -2428,25 +2405,29 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() when(key.getNamespace()).thenReturn(ANY_NAMESPACE_NAME); when(key.getTable()).thenReturn(ANY_TABLE_NAME); when(key.getPartitionKey()).thenReturn(Key.ofText(ANY_NAME_1, ANY_TEXT_1)); - - when(result.getInt(Attribute.STATE)).thenReturn(TransactionState.PREPARED.get()); - when(storage.get(any())).thenReturn(Optional.of(result)); - Get getWithConjunction = Get.newBuilder() .namespace(key.getNamespace()) .table(key.getTable()) .partitionKey(key.getPartitionKey()) - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) + .where(column(ANY_NAME_4).isEqualToInt(ANY_INT_1)) + .build(); + Get getForStorage = + Get.newBuilder(toGetForStorageFrom(getWithConjunction)) + .clearConditions() + .where(column(ANY_NAME_4).isEqualToInt(ANY_INT_1)) + .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isEqualToInt(ANY_INT_1)) .build(); when(snapshot.containsKeyInGetSet(getWithConjunction)).thenReturn(false); + when(result.getInt(Attribute.STATE)).thenReturn(TransactionState.PREPARED.get()); + when(storage.get(getForStorage)).thenReturn(Optional.of(result)); TransactionResult recoveredResult = mock(TransactionResult.class); - when(recoveredResult.getContainedColumnNames()).thenReturn(Collections.singleton(ANY_NAME_3)); - when(recoveredResult.getAsObject(ANY_NAME_3)).thenReturn(ANY_TEXT_4); + when(recoveredResult.getContainedColumnNames()).thenReturn(Collections.singleton(ANY_NAME_4)); + when(recoveredResult.getAsObject(ANY_NAME_4)).thenReturn(ANY_INT_2); when(recoveredResult.getColumns()) - .thenReturn(ImmutableMap.of(ANY_NAME_3, TextColumn.of(ANY_NAME_3, ANY_TEXT_4))); + .thenReturn(ImmutableMap.of(ANY_NAME_4, IntColumn.of(ANY_NAME_4, ANY_INT_2))); @SuppressWarnings("unchecked") Future recoveryFuture = mock(Future.class); @@ -2463,16 +2444,10 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - handler.readUnread(key, getWithConjunction, context); + handler.readUnread(key, getWithConjunction, context, TABLE_METADATA); // Assert - verify(storage) - .get( - Get.newBuilder(getWithConjunction) - .clearConditions() - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .build()); + verify(storage).get(getForStorage); verify(recoveryExecutor) .execute( key, @@ -2495,16 +2470,17 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() .table(ANY_TABLE_NAME) .indexKey(Key.ofText(ANY_NAME_3, ANY_TEXT_1)) .build(); + Get getForStorage = toGetForStorageFrom(getWithIndex); when(snapshot.containsKeyInGetSet(getWithIndex)).thenReturn(false); - when(storage.get(any())).thenReturn(Optional.empty()); + when(storage.get(getForStorage)).thenReturn(Optional.empty()); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - handler.readUnread(null, getWithIndex, context); + handler.readUnread(null, getWithIndex, context, TABLE_METADATA); // Assert - verify(storage).get(any()); + verify(storage).get(getForStorage); verify(snapshot, never()).putIntoReadSet(any(), any()); verify(snapshot).putIntoGetSet(getWithIndex, Optional.empty()); } @@ -2520,24 +2496,24 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() ImmutableMap.of( ANY_NAME_1, TextColumn.of(ANY_NAME_1, ANY_TEXT_1), ANY_NAME_2, TextColumn.of(ANY_NAME_2, ANY_TEXT_2))); - when(storage.get(any())).thenReturn(Optional.of(result)); - Get getWithIndex = Get.newBuilder() .namespace(ANY_NAMESPACE_NAME) .table(ANY_TABLE_NAME) .indexKey(Key.ofText(ANY_NAME_3, ANY_TEXT_1)) .build(); + Get getForStorage = toGetForStorageFrom(getWithIndex); when(snapshot.containsKeyInGetSet(getWithIndex)).thenReturn(false); + when(storage.get(getForStorage)).thenReturn(Optional.of(result)); TransactionContext context = new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - handler.readUnread(null, getWithIndex, context); + handler.readUnread(null, getWithIndex, context, TABLE_METADATA); // Assert - verify(storage).get(any()); + verify(storage).get(getForStorage); verify(snapshot) .putIntoReadSet( new Snapshot.Key(getWithIndex, result, TABLE_METADATA), @@ -2556,15 +2532,15 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() ImmutableMap.of( ANY_NAME_1, TextColumn.of(ANY_NAME_1, ANY_TEXT_1), ANY_NAME_2, TextColumn.of(ANY_NAME_2, ANY_TEXT_2))); - when(storage.get(any())).thenReturn(Optional.of(result)); - Get getWithIndex = Get.newBuilder() .namespace(ANY_NAMESPACE_NAME) .table(ANY_TABLE_NAME) .indexKey(Key.ofText(ANY_NAME_3, ANY_TEXT_1)) .build(); + Get getForStorage = toGetForStorageFrom(getWithIndex); when(snapshot.containsKeyInGetSet(getWithIndex)).thenReturn(false); + when(storage.get(getForStorage)).thenReturn(Optional.of(result)); Snapshot.Key key = new Snapshot.Key(getWithIndex, result, TABLE_METADATA); @@ -2584,10 +2560,10 @@ public void readUnread_GetContainedInGetSet_ShouldCallAppropriateMethods() new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); // Act - handler.readUnread(key, getWithIndex, context); + handler.readUnread(key, getWithIndex, context, TABLE_METADATA); // Assert - verify(storage).get(getWithIndex); + verify(storage).get(getForStorage); verify(recoveryExecutor) .execute( key, @@ -2751,478 +2727,6 @@ public void readIfImplicitPreReadEnabled_ShouldCallAppropriateMethods() assertThat(transactionIdCaptor.getValue()).isEqualTo(ANY_ID_1); } - @Test - public void get_WithConjunctions_ShouldConvertConjunctions() - throws CrudException, ExecutionException { - // Arrange - when(result.getInt(Attribute.STATE)).thenReturn(TransactionState.COMMITTED.get()); - when(storage.get(any())).thenReturn(Optional.of(result)); - TransactionContext context = - new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); - - // Act - handler.get( - Get.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .clusteringKey(Key.ofText(ANY_NAME_2, ANY_TEXT_2)) - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .build(), - context); - handler.get( - Get.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .clusteringKey(Key.ofText(ANY_NAME_2, ANY_TEXT_2)) - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .and(column(ANY_NAME_4).isEqualToInt(10)) - .build(), - context); - handler.get( - Get.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .clusteringKey(Key.ofText(ANY_NAME_2, ANY_TEXT_2)) - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(ANY_NAME_4).isEqualToInt(20)) - .build(), - context); - handler.get( - Get.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .clusteringKey(Key.ofText(ANY_NAME_2, ANY_TEXT_2)) - .where( - condition(column(ANY_NAME_3).isNotEqualToText(ANY_TEXT_3)) - .and(column(ANY_NAME_3).isNotEqualToText(ANY_TEXT_4)) - .build()) - .or( - condition(column(ANY_NAME_4).isGreaterThanInt(30)) - .and(column(ANY_NAME_4).isLessThanOrEqualToInt(40)) - .build()) - .build(), - context); - handler.get( - Get.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .clusteringKey(Key.ofText(ANY_NAME_2, ANY_TEXT_2)) - .where( - condition(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(ANY_NAME_3).isEqualToText(ANY_TEXT_4)) - .build()) - .and( - condition(column(ANY_NAME_4).isLessThanOrEqualToInt(50)) - .or(column(ANY_NAME_4).isGreaterThanInt(60)) - .build()) - .build(), - context); - handler.get( - Get.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .clusteringKey(Key.ofText(ANY_NAME_2, ANY_TEXT_2)) - .where(column(ANY_NAME_3).isLikeText(ANY_TEXT_3)) - .or(column(ANY_NAME_3).isLikeText(ANY_TEXT_4)) - .build(), - context); - - // Assert - verify(storage) - .get( - Get.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .clusteringKey(Key.ofText(ANY_NAME_2, ANY_TEXT_2)) - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .consistency(Consistency.LINEARIZABLE) - .build()); - verify(storage) - .get( - Get.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .clusteringKey(Key.ofText(ANY_NAME_2, ANY_TEXT_2)) - .where( - condition(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .and(column(ANY_NAME_4).isEqualToInt(10)) - .build()) - .or( - condition( - column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .and(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isEqualToInt(10)) - .build()) - .consistency(Consistency.LINEARIZABLE) - .build()); - verify(storage) - .get( - Get.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .clusteringKey(Key.ofText(ANY_NAME_2, ANY_TEXT_2)) - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(ANY_NAME_4).isEqualToInt(20)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isEqualToInt(20)) - .consistency(Consistency.LINEARIZABLE) - .build()); - verify(storage) - .get( - Get.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .clusteringKey(Key.ofText(ANY_NAME_2, ANY_TEXT_2)) - .where( - condition(column(ANY_NAME_3).isNotEqualToText(ANY_TEXT_3)) - .and(column(ANY_NAME_3).isNotEqualToText(ANY_TEXT_4)) - .build()) - .or( - condition(column(ANY_NAME_4).isGreaterThanInt(30)) - .and(column(ANY_NAME_4).isLessThanOrEqualToInt(40)) - .build()) - .or( - condition( - column(Attribute.BEFORE_PREFIX + ANY_NAME_3) - .isNotEqualToText(ANY_TEXT_3)) - .and( - column(Attribute.BEFORE_PREFIX + ANY_NAME_3) - .isNotEqualToText(ANY_TEXT_4)) - .build()) - .or( - condition(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isGreaterThanInt(30)) - .and( - column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isLessThanOrEqualToInt(40)) - .build()) - .consistency(Consistency.LINEARIZABLE) - .build()); - verify(storage) - .get( - Get.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .clusteringKey(Key.ofText(ANY_NAME_2, ANY_TEXT_2)) - .where( - condition(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .and(column(ANY_NAME_4).isLessThanOrEqualToInt(50)) - .build()) - .or( - condition(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .and(column(ANY_NAME_4).isGreaterThanInt(60)) - .build()) - .or( - condition(column(ANY_NAME_3).isEqualToText(ANY_TEXT_4)) - .and(column(ANY_NAME_4).isLessThanOrEqualToInt(50)) - .build()) - .or( - condition(column(ANY_NAME_3).isEqualToText(ANY_TEXT_4)) - .and(column(ANY_NAME_4).isGreaterThanInt(60)) - .build()) - .or( - condition( - column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .and( - column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isLessThanOrEqualToInt(50)) - .build()) - .or( - condition( - column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .and(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isGreaterThanInt(60)) - .build()) - .or( - condition( - column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_4)) - .and( - column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isLessThanOrEqualToInt(50)) - .build()) - .or( - condition( - column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_4)) - .and(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isGreaterThanInt(60)) - .build()) - .consistency(Consistency.LINEARIZABLE) - .build()); - verify(storage) - .get( - Get.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .clusteringKey(Key.ofText(ANY_NAME_2, ANY_TEXT_2)) - .where(column(ANY_NAME_3).isLikeText(ANY_TEXT_3)) - .or(column(ANY_NAME_3).isLikeText(ANY_TEXT_4)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isLikeText(ANY_TEXT_3)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isLikeText(ANY_TEXT_4)) - .consistency(Consistency.LINEARIZABLE) - .build()); - } - - @Test - public void scan_WithConjunctions_ShouldConvertConjunctions() - throws CrudException, ExecutionException { - // Arrange - when(result.getInt(Attribute.STATE)).thenReturn(TransactionState.COMMITTED.get()); - when(result.getColumns()) - .thenReturn( - ImmutableMap.of( - ANY_NAME_1, TextColumn.of(ANY_NAME_1, ANY_TEXT_1), - ANY_NAME_2, TextColumn.of(ANY_NAME_2, ANY_TEXT_2))); - when(scanner.iterator()).thenReturn(Collections.singletonList(result).iterator()); - when(storage.scan(any())).thenReturn(scanner); - TransactionContext context = - new TransactionContext(ANY_ID_1, snapshot, Isolation.SNAPSHOT, false, false); - - // Act - handler.scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .build(), - context); - handler.scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .and(column(ANY_NAME_4).isEqualToInt(10)) - .build(), - context); - handler.scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(ANY_NAME_4).isEqualToInt(20)) - .build(), - context); - handler.scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .where( - condition(column(ANY_NAME_3).isNotEqualToText(ANY_TEXT_3)) - .and(column(ANY_NAME_3).isNotEqualToText(ANY_TEXT_4)) - .build()) - .or( - condition(column(ANY_NAME_4).isGreaterThanInt(30)) - .and(column(ANY_NAME_4).isLessThanOrEqualToInt(40)) - .build()) - .build(), - context); - handler.scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .where( - condition(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(ANY_NAME_3).isEqualToText(ANY_TEXT_4)) - .build()) - .and( - condition(column(ANY_NAME_4).isLessThanOrEqualToInt(50)) - .or(column(ANY_NAME_4).isGreaterThanInt(60)) - .build()) - .build(), - context); - handler.scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .where(column(ANY_NAME_3).isLikeText(ANY_TEXT_3)) - .or(column(ANY_NAME_3).isLikeText(ANY_TEXT_4)) - .build(), - context); - handler.scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .all() - .where(column(ANY_NAME_1).isGreaterThanText(ANY_TEXT_3)) - .and(column(ANY_NAME_2).isLessThanOrEqualToText(ANY_TEXT_4)) - .build(), - context); - handler.scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .all() - .where(column(ANY_NAME_1).isGreaterThanText(ANY_TEXT_3)) - .and(column(ANY_NAME_3).isEqualToText(ANY_TEXT_4)) - .build(), - context); - - // Assert - verify(storage) - .scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .consistency(Consistency.LINEARIZABLE) - .build()); - verify(storage) - .scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .where( - condition(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .and(column(ANY_NAME_4).isEqualToInt(10)) - .build()) - .or( - condition( - column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .and(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isEqualToInt(10)) - .build()) - .consistency(Consistency.LINEARIZABLE) - .build()); - verify(storage) - .scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .where(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(ANY_NAME_4).isEqualToInt(20)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isEqualToInt(20)) - .consistency(Consistency.LINEARIZABLE) - .build()); - verify(storage) - .scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .where( - condition(column(ANY_NAME_3).isNotEqualToText(ANY_TEXT_3)) - .and(column(ANY_NAME_3).isNotEqualToText(ANY_TEXT_4)) - .build()) - .or( - condition(column(ANY_NAME_4).isGreaterThanInt(30)) - .and(column(ANY_NAME_4).isLessThanOrEqualToInt(40)) - .build()) - .or( - condition( - column(Attribute.BEFORE_PREFIX + ANY_NAME_3) - .isNotEqualToText(ANY_TEXT_3)) - .and( - column(Attribute.BEFORE_PREFIX + ANY_NAME_3) - .isNotEqualToText(ANY_TEXT_4)) - .build()) - .or( - condition(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isGreaterThanInt(30)) - .and( - column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isLessThanOrEqualToInt(40)) - .build()) - .consistency(Consistency.LINEARIZABLE) - .build()); - verify(storage) - .scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .where( - condition(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .and(column(ANY_NAME_4).isLessThanOrEqualToInt(50)) - .build()) - .or( - condition(column(ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .and(column(ANY_NAME_4).isGreaterThanInt(60)) - .build()) - .or( - condition(column(ANY_NAME_3).isEqualToText(ANY_TEXT_4)) - .and(column(ANY_NAME_4).isLessThanOrEqualToInt(50)) - .build()) - .or( - condition(column(ANY_NAME_3).isEqualToText(ANY_TEXT_4)) - .and(column(ANY_NAME_4).isGreaterThanInt(60)) - .build()) - .or( - condition( - column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .and( - column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isLessThanOrEqualToInt(50)) - .build()) - .or( - condition( - column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_3)) - .and(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isGreaterThanInt(60)) - .build()) - .or( - condition( - column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_4)) - .and( - column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isLessThanOrEqualToInt(50)) - .build()) - .or( - condition( - column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_4)) - .and(column(Attribute.BEFORE_PREFIX + ANY_NAME_4).isGreaterThanInt(60)) - .build()) - .consistency(Consistency.LINEARIZABLE) - .build()); - verify(storage) - .scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .where(column(ANY_NAME_3).isLikeText(ANY_TEXT_3)) - .or(column(ANY_NAME_3).isLikeText(ANY_TEXT_4)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isLikeText(ANY_TEXT_3)) - .or(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isLikeText(ANY_TEXT_4)) - .consistency(Consistency.LINEARIZABLE) - .build()); - verify(storage) - .scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .all() - .where(column(ANY_NAME_1).isGreaterThanText(ANY_TEXT_3)) - .and(column(ANY_NAME_2).isLessThanOrEqualToText(ANY_TEXT_4)) - .consistency(Consistency.LINEARIZABLE) - .build()); - verify(storage) - .scan( - Scan.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .all() - .where( - condition(column(ANY_NAME_1).isGreaterThanText(ANY_TEXT_3)) - .and(column(ANY_NAME_3).isEqualToText(ANY_TEXT_4)) - .build()) - .or( - condition(column(ANY_NAME_1).isGreaterThanText(ANY_TEXT_3)) - .and(column(Attribute.BEFORE_PREFIX + ANY_NAME_3).isEqualToText(ANY_TEXT_4)) - .build()) - .consistency(Consistency.LINEARIZABLE) - .build()); - } - private List scanOrGetScanner(Scan scan, ScanType scanType, TransactionContext context) throws CrudException { if (scanType == ScanType.SCAN) { diff --git a/core/src/test/java/com/scalar/db/transaction/consensuscommit/SnapshotTest.java b/core/src/test/java/com/scalar/db/transaction/consensuscommit/SnapshotTest.java index 9d8b2dfa85..fbe900f70c 100644 --- a/core/src/test/java/com/scalar/db/transaction/consensuscommit/SnapshotTest.java +++ b/core/src/test/java/com/scalar/db/transaction/consensuscommit/SnapshotTest.java @@ -173,7 +173,6 @@ private Get prepareGet() { .table(ANY_TABLE_NAME) .partitionKey(partitionKey) .clusteringKey(clusteringKey) - .consistency(Consistency.LINEARIZABLE) .build(); } @@ -185,7 +184,6 @@ private Get prepareAnotherGet() { .table(ANY_TABLE_NAME) .partitionKey(partitionKey) .clusteringKey(clusteringKey) - .consistency(Consistency.LINEARIZABLE) .build(); } @@ -206,7 +204,6 @@ private Scan prepareScan() { .table(ANY_TABLE_NAME) .partitionKey(partitionKey) .start(clusteringKey) - .consistency(Consistency.LINEARIZABLE) .build(); } @@ -216,7 +213,6 @@ private Scan prepareScanWithLimit(int limit) { .table(ANY_TABLE_NAME) .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) .limit(limit) - .consistency(Consistency.LINEARIZABLE) .build(); } @@ -254,7 +250,6 @@ private Put preparePut(String partitionKeyColumnValue, String clusteringKeyColum .clusteringKey(Key.ofText(ANY_NAME_2, clusteringKeyColumnValue)) .textValue(ANY_NAME_3, ANY_TEXT_3) .textValue(ANY_NAME_4, ANY_TEXT_4) - .consistency(Consistency.LINEARIZABLE) .build(); } @@ -266,7 +261,6 @@ private Put preparePutWithPartitionKeyOnly() { .partitionKey(partitionKey) .textValue(ANY_NAME_3, ANY_TEXT_3) .textValue(ANY_NAME_4, ANY_TEXT_4) - .consistency(Consistency.LINEARIZABLE) .build(); } @@ -282,7 +276,6 @@ private Put preparePutWithIntColumns() { .value(IntColumn.of(ANY_NAME_6, ANY_INT_1)) .value(IntColumn.of(ANY_NAME_7, ANY_INT_1)) .value(IntColumn.ofNull(ANY_NAME_8)) - .consistency(Consistency.LINEARIZABLE) .build(); } @@ -307,7 +300,6 @@ private Delete prepareDelete(String partitionKeyColumnValue, String clusteringKe .table(ANY_TABLE_NAME) .partitionKey(Key.ofText(ANY_NAME_1, partitionKeyColumnValue)) .clusteringKey(Key.ofText(ANY_NAME_2, clusteringKeyColumnValue)) - .consistency(Consistency.LINEARIZABLE) .build(); } @@ -319,7 +311,6 @@ private Delete prepareAnotherDelete() { .table(ANY_TABLE_NAME) .partitionKey(partitionKey) .clusteringKey(clusteringKey) - .consistency(Consistency.LINEARIZABLE) .build(); } @@ -982,14 +973,15 @@ public void toSerializable_ReadSetNotChanged_ShouldProcessWithoutExceptions() snapshot.putIntoGetSet(get, Optional.of(txResult)); snapshot.putIntoWriteSet(new Snapshot.Key(put), put); DistributedStorage storage = mock(DistributedStorage.class); - Get getWithProjections = Get.newBuilder(prepareAnotherGet()).projection(Attribute.ID).build(); - when(storage.get(getWithProjections)).thenReturn(Optional.of(txResult)); + Get getForStorage = + Get.newBuilder(prepareAnotherGet()).consistency(Consistency.LINEARIZABLE).build(); + when(storage.get(getForStorage)).thenReturn(Optional.of(txResult)); // Act Assert assertThatCode(() -> snapshot.toSerializable(storage)).doesNotThrowAnyException(); // Assert - verify(storage).get(getWithProjections); + verify(storage).get(getForStorage); } @Test @@ -1004,15 +996,16 @@ public void toSerializable_ReadSetUpdated_ShouldThrowValidationConflictException snapshot.putIntoWriteSet(new Snapshot.Key(put), put); DistributedStorage storage = mock(DistributedStorage.class); TransactionResult changedTxResult = prepareResult(ANY_ID + "x"); - Get getWithProjections = Get.newBuilder(prepareAnotherGet()).projection(Attribute.ID).build(); - when(storage.get(getWithProjections)).thenReturn(Optional.of(changedTxResult)); + Get getForStorage = + Get.newBuilder(prepareAnotherGet()).consistency(Consistency.LINEARIZABLE).build(); + when(storage.get(getForStorage)).thenReturn(Optional.of(changedTxResult)); // Act Assert assertThatThrownBy(() -> snapshot.toSerializable(storage)) .isInstanceOf(ValidationConflictException.class); // Assert - verify(storage).get(getWithProjections); + verify(storage).get(getForStorage); } @Test @@ -1026,15 +1019,16 @@ public void toSerializable_ReadSetExtended_ShouldThrowValidationConflictExceptio snapshot.putIntoWriteSet(new Snapshot.Key(put), put); DistributedStorage storage = mock(DistributedStorage.class); TransactionResult txResult = prepareResult(ANY_ID); - Get getWithProjections = Get.newBuilder(prepareAnotherGet()).projection(Attribute.ID).build(); - when(storage.get(getWithProjections)).thenReturn(Optional.of(txResult)); + Get getForStorage = + Get.newBuilder(prepareAnotherGet()).consistency(Consistency.LINEARIZABLE).build(); + when(storage.get(getForStorage)).thenReturn(Optional.of(txResult)); // Act Assert assertThatThrownBy(() -> snapshot.toSerializable(storage)) .isInstanceOf(ValidationConflictException.class); // Assert - verify(storage).get(getWithProjections); + verify(storage).get(getForStorage); } @Test @@ -1046,19 +1040,18 @@ public void toSerializable_GetSetWithGetWithIndex_ShouldProcessWithoutExceptions TransactionResult txResult = prepareResult(ANY_ID + "x"); snapshot.putIntoGetSet(getWithIndex, Optional.of(txResult)); DistributedStorage storage = mock(DistributedStorage.class); - Scan scanWithIndex = - Scan.newBuilder(prepareScanWithIndex()) - .projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2) - .build(); + Scan scanForStorage = + Scan.newBuilder(prepareScanWithIndex()).consistency(Consistency.LINEARIZABLE).build(); + Scanner scanner = mock(Scanner.class); when(scanner.one()).thenReturn(Optional.of(txResult)).thenReturn(Optional.empty()); - when(storage.scan(scanWithIndex)).thenReturn(scanner); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatCode(() -> snapshot.toSerializable(storage)).doesNotThrowAnyException(); // Assert - verify(storage).scan(scanWithIndex); + verify(storage).scan(scanForStorage); } @Test @@ -1072,23 +1065,22 @@ public void toSerializable_GetSetWithGetWithIndex_ShouldProcessWithoutExceptions TransactionResult result2 = prepareResult(ANY_ID + "xx", ANY_TEXT_1, ANY_TEXT_3); snapshot.putIntoGetSet(getWithIndex, Optional.of(result1)); DistributedStorage storage = mock(DistributedStorage.class); - Scan scanWithIndex = - Scan.newBuilder(prepareScanWithIndex()) - .projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2) - .build(); + Scan scanForStorage = + Scan.newBuilder(prepareScanWithIndex()).consistency(Consistency.LINEARIZABLE).build(); + Scanner scanner = mock(Scanner.class); when(scanner.one()) .thenReturn(Optional.of(result1)) .thenReturn(Optional.of(result2)) .thenReturn(Optional.empty()); - when(storage.scan(scanWithIndex)).thenReturn(scanner); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatThrownBy(() -> snapshot.toSerializable(storage)) .isInstanceOf(ValidationConflictException.class); // Assert - verify(storage).scan(scanWithIndex); + verify(storage).scan(scanForStorage); } @Test @@ -1102,22 +1094,21 @@ public void toSerializable_GetSetWithGetWithIndex_ShouldProcessWithoutExceptions TransactionResult result2 = prepareResult(ANY_ID, ANY_TEXT_1, ANY_TEXT_3); snapshot.putIntoGetSet(getWithIndex, Optional.of(result1)); DistributedStorage storage = mock(DistributedStorage.class); - Scan scanWithIndex = - Scan.newBuilder(prepareScanWithIndex()) - .projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2) - .build(); + Scan scanForStorage = + Scan.newBuilder(prepareScanWithIndex()).consistency(Consistency.LINEARIZABLE).build(); + Scanner scanner = mock(Scanner.class); when(scanner.one()) .thenReturn(Optional.of(result1)) .thenReturn(Optional.of(result2)) .thenReturn(Optional.empty()); - when(storage.scan(scanWithIndex)).thenReturn(scanner); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatCode(() -> snapshot.toSerializable(storage)).doesNotThrowAnyException(); // Assert - verify(storage).scan(scanWithIndex); + verify(storage).scan(scanForStorage); } @Test @@ -1132,15 +1123,15 @@ public void toSerializable_ScanSetNotChanged_ShouldProcessWithoutExceptions() DistributedStorage storage = mock(DistributedStorage.class); Scanner scanner = mock(Scanner.class); when(scanner.one()).thenReturn(Optional.of(txResult)).thenReturn(Optional.empty()); - Scan scanWithProjections = - Scan.newBuilder(prepareScan()).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).build(); - when(storage.scan(scanWithProjections)).thenReturn(scanner); + Scan scanForStorage = + Scan.newBuilder(prepareScan()).consistency(Consistency.LINEARIZABLE).build(); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatCode(() -> snapshot.toSerializable(storage)).doesNotThrowAnyException(); // Assert - verify(storage).scan(scanWithProjections); + verify(storage).scan(scanForStorage); } @Test @@ -1156,16 +1147,16 @@ public void toSerializable_ScanSetUpdated_ShouldThrowValidationConflictException TransactionResult changedTxResult = prepareResult(ANY_ID + "x"); Scanner scanner = mock(Scanner.class); when(scanner.one()).thenReturn(Optional.of(changedTxResult)).thenReturn(Optional.empty()); - Scan scanWithProjections = - Scan.newBuilder(prepareScan()).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).build(); - when(storage.scan(scanWithProjections)).thenReturn(scanner); + Scan scanForStorage = + Scan.newBuilder(prepareScan()).consistency(Consistency.LINEARIZABLE).build(); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatThrownBy(() -> snapshot.toSerializable(storage)) .isInstanceOf(ValidationConflictException.class); // Assert - verify(storage).scan(scanWithProjections); + verify(storage).scan(scanForStorage); } @Test @@ -1181,15 +1172,15 @@ public void toSerializable_ScanSetUpdatedByMyself_ShouldProcessWithoutExceptions TransactionResult changedTxResult = prepareResult(ANY_ID); Scanner scanner = mock(Scanner.class); when(scanner.one()).thenReturn(Optional.of(changedTxResult)).thenReturn(Optional.empty()); - Scan scanWithProjections = - Scan.newBuilder(prepareScan()).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).build(); - when(storage.scan(scanWithProjections)).thenReturn(scanner); + Scan scanForStorage = + Scan.newBuilder(prepareScan()).consistency(Consistency.LINEARIZABLE).build(); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatCode(() -> snapshot.toSerializable(storage)).doesNotThrowAnyException(); // Assert - verify(storage).scan(scanWithProjections); + verify(storage).scan(scanForStorage); } @Test @@ -1204,16 +1195,16 @@ public void toSerializable_ScanSetExtended_ShouldThrowValidationConflictExceptio TransactionResult txResult = new TransactionResult(result); Scanner scanner = mock(Scanner.class); when(scanner.one()).thenReturn(Optional.of(txResult)).thenReturn(Optional.empty()); - Scan scanWithProjections = - Scan.newBuilder(prepareScan()).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).build(); - when(storage.scan(scanWithProjections)).thenReturn(scanner); + Scan scanForStorage = + Scan.newBuilder(prepareScan()).consistency(Consistency.LINEARIZABLE).build(); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatThrownBy(() -> snapshot.toSerializable(storage)) .isInstanceOf(ValidationConflictException.class); // Assert - verify(storage).scan(scanWithProjections); + verify(storage).scan(scanForStorage); } @Test @@ -1233,16 +1224,16 @@ public void toSerializable_ScanSetExtended_ShouldThrowValidationConflictExceptio .thenReturn(Optional.of(result1)) .thenReturn(Optional.of(result2)) .thenReturn(Optional.empty()); - Scan scanWithProjections = - Scan.newBuilder(prepareScan()).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).build(); - when(storage.scan(scanWithProjections)).thenReturn(scanner); + Scan scanForStorage = + Scan.newBuilder(prepareScan()).consistency(Consistency.LINEARIZABLE).build(); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatThrownBy(() -> snapshot.toSerializable(storage)) .isInstanceOf(ValidationConflictException.class); // Assert - verify(storage).scan(scanWithProjections); + verify(storage).scan(scanForStorage); } @Test @@ -1257,15 +1248,15 @@ public void toSerializable_ScanSetExtendedByMyself_ShouldProcessWithoutException TransactionResult txResult = new TransactionResult(result); Scanner scanner = mock(Scanner.class); when(scanner.one()).thenReturn(Optional.of(txResult)).thenReturn(Optional.empty()); - Scan scanWithProjections = - Scan.newBuilder(prepareScan()).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).build(); - when(storage.scan(scanWithProjections)).thenReturn(scanner); + Scan scanForStorage = + Scan.newBuilder(prepareScan()).consistency(Consistency.LINEARIZABLE).build(); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatCode(() -> snapshot.toSerializable(storage)).doesNotThrowAnyException(); // Assert - verify(storage).scan(scanWithProjections); + verify(storage).scan(scanForStorage); } @Test @@ -1285,15 +1276,15 @@ public void toSerializable_ScanSetExtendedByMyself_ShouldProcessWithoutException .thenReturn(Optional.of(result1)) .thenReturn(Optional.of(result2)) .thenReturn(Optional.empty()); - Scan scanWithProjections = - Scan.newBuilder(prepareScan()).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).build(); - when(storage.scan(scanWithProjections)).thenReturn(scanner); + Scan scanForStorage = + Scan.newBuilder(prepareScan()).consistency(Consistency.LINEARIZABLE).build(); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatCode(() -> snapshot.toSerializable(storage)).doesNotThrowAnyException(); // Assert - verify(storage).scan(scanWithProjections); + verify(storage).scan(scanForStorage); } @Test @@ -1308,16 +1299,16 @@ public void toSerializable_ScanSetDeleted_ShouldThrowValidationConflictException DistributedStorage storage = mock(DistributedStorage.class); Scanner scanner = mock(Scanner.class); when(scanner.one()).thenReturn(Optional.empty()); - Scan scanWithProjections = - Scan.newBuilder(prepareScan()).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).build(); - when(storage.scan(scanWithProjections)).thenReturn(scanner); + Scan scanForStorage = + Scan.newBuilder(prepareScan()).consistency(Consistency.LINEARIZABLE).build(); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatThrownBy(() -> snapshot.toSerializable(storage)) .isInstanceOf(ValidationConflictException.class); // Assert - verify(storage).scan(scanWithProjections); + verify(storage).scan(scanForStorage); } @Test @@ -1337,16 +1328,16 @@ public void toSerializable_ScanSetDeleted_ShouldThrowValidationConflictException DistributedStorage storage = mock(DistributedStorage.class); Scanner scanner = mock(Scanner.class); when(scanner.one()).thenReturn(Optional.of(result2)).thenReturn(Optional.empty()); - Scan scanWithProjections = - Scan.newBuilder(prepareScan()).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).build(); - when(storage.scan(scanWithProjections)).thenReturn(scanner); + Scan scanForStorage = + Scan.newBuilder(prepareScan()).consistency(Consistency.LINEARIZABLE).build(); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatThrownBy(() -> snapshot.toSerializable(storage)) .isInstanceOf(ValidationConflictException.class); // Assert - verify(storage).scan(scanWithProjections); + verify(storage).scan(scanForStorage); } @Test @@ -1361,7 +1352,6 @@ public void toSerializable_MultipleScansInScanSetExist_ShouldProcessWithoutExcep .table(ANY_TABLE_NAME) .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) .start(Key.ofText(ANY_NAME_2, ANY_TEXT_2)) - .consistency(Consistency.LINEARIZABLE) .build(); Scan scan2 = Scan.newBuilder() @@ -1369,7 +1359,6 @@ public void toSerializable_MultipleScansInScanSetExist_ShouldProcessWithoutExcep .table(ANY_TABLE_NAME) .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_2)) .start(Key.ofText(ANY_NAME_2, ANY_TEXT_1)) - .consistency(Consistency.LINEARIZABLE) .build(); Result result1 = @@ -1410,29 +1399,27 @@ public void toSerializable_MultipleScansInScanSetExist_ShouldProcessWithoutExcep Scanner scanner1 = mock(Scanner.class); when(scanner1.one()).thenReturn(Optional.of(result1)).thenReturn(Optional.empty()); - Scan scan1WithProjections = + Scan scan1ForStorage = Scan.newBuilder() .namespace(ANY_NAMESPACE_NAME) .table(ANY_TABLE_NAME) .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) .start(Key.ofText(ANY_NAME_2, ANY_TEXT_2)) .consistency(Consistency.LINEARIZABLE) - .projections(Arrays.asList(Attribute.ID, ANY_NAME_1, ANY_NAME_2)) .build(); - when(storage.scan(scan1WithProjections)).thenReturn(scanner1); + when(storage.scan(scan1ForStorage)).thenReturn(scanner1); Scanner scanner2 = mock(Scanner.class); when(scanner2.one()).thenReturn(Optional.of(result2)).thenReturn(Optional.empty()); - Scan scan2WithProjections = + Scan scan2ForStorage = Scan.newBuilder() .namespace(ANY_NAMESPACE_NAME) .table(ANY_TABLE_NAME) .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_2)) .start(Key.ofText(ANY_NAME_2, ANY_TEXT_1)) .consistency(Consistency.LINEARIZABLE) - .projections(Arrays.asList(Attribute.ID, ANY_NAME_1, ANY_NAME_2)) .build(); - when(storage.scan(scan2WithProjections)).thenReturn(scanner2); + when(storage.scan(scan2ForStorage)).thenReturn(scanner2); // Act Assert assertThatCode(() -> snapshot.toSerializable(storage)).doesNotThrowAnyException(); @@ -1450,14 +1437,14 @@ public void toSerializable_NullMetadataInReadSetNotChanged_ShouldProcessWithoutE snapshot.putIntoGetSet(get, Optional.of(result)); snapshot.putIntoWriteSet(new Snapshot.Key(put), put); DistributedStorage storage = mock(DistributedStorage.class); - Get getWithProjections = Get.newBuilder(get).projection(Attribute.ID).build(); - when(storage.get(getWithProjections)).thenReturn(Optional.of(txResult)); + Get getForStorage = Get.newBuilder(get).consistency(Consistency.LINEARIZABLE).build(); + when(storage.get(getForStorage)).thenReturn(Optional.of(txResult)); // Act Assert assertThatCode(() -> snapshot.toSerializable(storage)).doesNotThrowAnyException(); // Assert - verify(storage).get(getWithProjections); + verify(storage).get(getForStorage); } @Test @@ -1472,15 +1459,15 @@ public void toSerializable_NullMetadataInReadSetChanged_ShouldThrowValidationCon snapshot.putIntoGetSet(get, Optional.of(result)); snapshot.putIntoWriteSet(new Snapshot.Key(put), put); DistributedStorage storage = mock(DistributedStorage.class); - Get getWithProjections = Get.newBuilder(get).projection(Attribute.ID).build(); - when(storage.get(getWithProjections)).thenReturn(Optional.of(changedResult)); + Get getForStorage = Get.newBuilder(get).consistency(Consistency.LINEARIZABLE).build(); + when(storage.get(getForStorage)).thenReturn(Optional.of(changedResult)); // Act Assert assertThatThrownBy(() -> snapshot.toSerializable(storage)) .isInstanceOf(ValidationConflictException.class); // Assert - verify(storage).get(getWithProjections); + verify(storage).get(getForStorage); } @Test @@ -1494,20 +1481,20 @@ public void toSerializable_ScanWithLimitInScanSet_ShouldProcessWithoutExceptions Snapshot.Key key1 = new Snapshot.Key(scan, result1, TABLE_METADATA); snapshot.putIntoScanSet(scan, Maps.newLinkedHashMap(Collections.singletonMap(key1, result1))); DistributedStorage storage = mock(DistributedStorage.class); - Scan scanWithProjectionsWithoutLimit = - Scan.newBuilder(scan).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).limit(0).build(); + Scan scanForStorage = + Scan.newBuilder(scan).limit(0).consistency(Consistency.LINEARIZABLE).build(); Scanner scanner = mock(Scanner.class); when(scanner.one()) .thenReturn(Optional.of(result1)) .thenReturn(Optional.of(result2)) .thenReturn(Optional.empty()); - when(storage.scan(scanWithProjectionsWithoutLimit)).thenReturn(scanner); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatCode(() -> snapshot.toSerializable(storage)).doesNotThrowAnyException(); // Assert - verify(storage).scan(scanWithProjectionsWithoutLimit); + verify(storage).scan(scanForStorage); } @Test @@ -1523,22 +1510,22 @@ public void toSerializable_ScanWithLimitInScanSet_ShouldProcessWithoutExceptions Snapshot.Key key1 = new Snapshot.Key(scan, result1, TABLE_METADATA); snapshot.putIntoScanSet(scan, Maps.newLinkedHashMap(ImmutableMap.of(key1, result1))); DistributedStorage storage = mock(DistributedStorage.class); - Scan scanWithProjectionsWithoutLimit = - Scan.newBuilder(scan).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).limit(0).build(); + Scan scanForStorage = + Scan.newBuilder(scan).limit(0).consistency(Consistency.LINEARIZABLE).build(); Scanner scanner = mock(Scanner.class); when(scanner.one()) .thenReturn(Optional.of(insertedResult)) .thenReturn(Optional.of(result1)) .thenReturn(Optional.of(result2)) .thenReturn(Optional.empty()); - when(storage.scan(scanWithProjectionsWithoutLimit)).thenReturn(scanner); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatThrownBy(() -> snapshot.toSerializable(storage)) .isInstanceOf(ValidationConflictException.class); // Assert - verify(storage).scan(scanWithProjectionsWithoutLimit); + verify(storage).scan(scanForStorage); } @Test @@ -1554,21 +1541,21 @@ public void toSerializable_ScanWithLimitInScanSet_ShouldProcessWithoutExceptions Snapshot.Key key1 = new Snapshot.Key(scan, result1, TABLE_METADATA); snapshot.putIntoScanSet(scan, Maps.newLinkedHashMap(ImmutableMap.of(key1, result1))); DistributedStorage storage = mock(DistributedStorage.class); - Scan scanWithProjectionsWithoutLimit = - Scan.newBuilder(scan).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).limit(0).build(); + Scan scanForStorage = + Scan.newBuilder(scan).limit(0).consistency(Consistency.LINEARIZABLE).build(); Scanner scanner = mock(Scanner.class); when(scanner.one()) .thenReturn(Optional.of(insertedResult)) .thenReturn(Optional.of(result1)) .thenReturn(Optional.of(result2)) .thenReturn(Optional.empty()); - when(storage.scan(scanWithProjectionsWithoutLimit)).thenReturn(scanner); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatCode(() -> snapshot.toSerializable(storage)).doesNotThrowAnyException(); // Assert - verify(storage).scan(scanWithProjectionsWithoutLimit); + verify(storage).scan(scanForStorage); } @Test @@ -1586,22 +1573,22 @@ public void toSerializable_ScanWithLimitInScanSet_ShouldProcessWithoutExceptions snapshot.putIntoScanSet( scan, Maps.newLinkedHashMap(ImmutableMap.of(key1, result1, key2, result2))); DistributedStorage storage = mock(DistributedStorage.class); - Scan scanWithProjectionsWithoutLimit = - Scan.newBuilder(scan).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).limit(0).build(); + Scan scanForStorage = + Scan.newBuilder(scan).limit(0).consistency(Consistency.LINEARIZABLE).build(); Scanner scanner = mock(Scanner.class); when(scanner.one()) .thenReturn(Optional.of(result1)) .thenReturn(Optional.of(result2)) .thenReturn(Optional.of(insertedResult)) .thenReturn(Optional.empty()); - when(storage.scan(scanWithProjectionsWithoutLimit)).thenReturn(scanner); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatThrownBy(() -> snapshot.toSerializable(storage)) .isInstanceOf(ValidationConflictException.class); // Assert - verify(storage).scan(scanWithProjectionsWithoutLimit); + verify(storage).scan(scanForStorage); } @Test @@ -1619,21 +1606,22 @@ public void toSerializable_ScanWithLimitInScanSet_ShouldProcessWithoutExceptions snapshot.putIntoScanSet( scan, Maps.newLinkedHashMap(ImmutableMap.of(key1, result1, key2, result2))); DistributedStorage storage = mock(DistributedStorage.class); - Scan scanWithProjectionsWithoutLimit = - Scan.newBuilder(scan).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).limit(0).build(); + Scan scanForStorage = + Scan.newBuilder(scan).limit(0).consistency(Consistency.LINEARIZABLE).build(); + Scanner scanner = mock(Scanner.class); when(scanner.one()) .thenReturn(Optional.of(result1)) .thenReturn(Optional.of(result2)) .thenReturn(Optional.of(insertedResult)) .thenReturn(Optional.empty()); - when(storage.scan(scanWithProjectionsWithoutLimit)).thenReturn(scanner); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatCode(() -> snapshot.toSerializable(storage)).doesNotThrowAnyException(); // Assert - verify(storage).scan(scanWithProjectionsWithoutLimit); + verify(storage).scan(scanForStorage); } @Test @@ -1657,16 +1645,17 @@ public void toSerializable_ScanWithLimitInScanSet_ShouldProcessWithoutExceptions when(scanner.one()).thenReturn(Optional.of(result2)).thenReturn(Optional.empty()); DistributedStorage storage = mock(DistributedStorage.class); - Scan scanWithProjections = - Scan.newBuilder(scan).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).limit(0).build(); - when(storage.scan(scanWithProjections)).thenReturn(scanner); + Scan scanForStorage = + Scan.newBuilder(scan).limit(0).consistency(Consistency.LINEARIZABLE).build(); + + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatThrownBy(() -> snapshot.toSerializable(storage)) .isInstanceOf(ValidationConflictException.class); // Assert - verify(storage).scan(scanWithProjections); + verify(storage).scan(scanForStorage); } @Test @@ -1692,15 +1681,16 @@ public void toSerializable_ScanWithLimitInScanSet_ShouldProcessWithoutExceptions when(scanner.one()).thenReturn(Optional.of(result2)).thenReturn(Optional.empty()); DistributedStorage storage = mock(DistributedStorage.class); - Scan scanWithProjections = - Scan.newBuilder(scan).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).limit(0).build(); - when(storage.scan(scanWithProjections)).thenReturn(scanner); + Scan scanForStorage = + Scan.newBuilder(scan).limit(0).consistency(Consistency.LINEARIZABLE).build(); + + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatCode(() -> snapshot.toSerializable(storage)).doesNotThrowAnyException(); // Assert - verify(storage).scan(scanWithProjections); + verify(storage).scan(scanForStorage); } @Test @@ -1726,15 +1716,15 @@ public void toSerializable_ScanWithLimitInScanSet_ShouldProcessWithoutExceptions when(scanner.one()).thenReturn(Optional.of(result2)).thenReturn(Optional.empty()); DistributedStorage storage = mock(DistributedStorage.class); - Scan scanWithProjections = - Scan.newBuilder(scan).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).limit(0).build(); - when(storage.scan(scanWithProjections)).thenReturn(scanner); + Scan scanForStorage = + Scan.newBuilder(scan).limit(0).consistency(Consistency.LINEARIZABLE).build(); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatCode(() -> snapshot.toSerializable(storage)).doesNotThrowAnyException(); // Assert - verify(storage).scan(scanWithProjections); + verify(storage).scan(scanForStorage); } @Test @@ -1748,20 +1738,19 @@ public void toSerializable_ScannerSetNotChanged_ShouldProcessWithoutExceptions() Snapshot.Key key1 = new Snapshot.Key(scan, result1, TABLE_METADATA); snapshot.putIntoScannerSet(scan, Maps.newLinkedHashMap(ImmutableMap.of(key1, result1))); DistributedStorage storage = mock(DistributedStorage.class); - Scan scanWithProjections = - Scan.newBuilder(scan).projections(Attribute.ID, ANY_NAME_1, ANY_NAME_2).build(); + Scan scanForStorage = Scan.newBuilder(scan).consistency(Consistency.LINEARIZABLE).build(); Scanner scanner = mock(Scanner.class); when(scanner.one()) .thenReturn(Optional.of(result1)) .thenReturn(Optional.of(result2)) .thenReturn(Optional.empty()); - when(storage.scan(scanWithProjections)).thenReturn(scanner); + when(storage.scan(scanForStorage)).thenReturn(scanner); // Act Assert assertThatCode(() -> snapshot.toSerializable(storage)).doesNotThrowAnyException(); // Assert - verify(storage).scan(scanWithProjections); + verify(storage).scan(scanForStorage); } @Test @@ -1836,7 +1825,6 @@ public void toSerializable_ScannerSetNotChanged_ShouldProcessWithoutExceptions() .table(ANY_TABLE_NAME) .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) // (-infinite, infinite) - .consistency(Consistency.LINEARIZABLE) .build(); // Act Assert @@ -1859,7 +1847,6 @@ public void toSerializable_ScannerSetNotChanged_ShouldProcessWithoutExceptions() .namespace(ANY_NAMESPACE_NAME) .table(ANY_TABLE_NAME) .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) - .consistency(Consistency.LINEARIZABLE) .where(ConditionBuilder.column(ANY_NAME_3).isEqualToText(ANY_TEXT_4)) .build(); @@ -1946,7 +1933,6 @@ public void toSerializable_ScannerSetNotChanged_ShouldProcessWithoutExceptions() .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) // (-infinite, "text3"] .end(Key.ofText(ANY_NAME_2, ANY_TEXT_3), true) - .consistency(Consistency.LINEARIZABLE) .build(); Scan scan2 = Scan.newBuilder() @@ -1955,7 +1941,6 @@ public void toSerializable_ScannerSetNotChanged_ShouldProcessWithoutExceptions() .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) // (-infinite, "text2"] .end(Key.ofText(ANY_NAME_2, ANY_TEXT_2), true) - .consistency(Consistency.LINEARIZABLE) .build(); Scan scan3 = Scan.newBuilder() @@ -1964,7 +1949,6 @@ public void toSerializable_ScannerSetNotChanged_ShouldProcessWithoutExceptions() .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) // (-infinite, "text2") .end(Key.ofText(ANY_NAME_2, ANY_TEXT_2), false) - .consistency(Consistency.LINEARIZABLE) .build(); // Act Assert @@ -1997,7 +1981,6 @@ public void toSerializable_ScannerSetNotChanged_ShouldProcessWithoutExceptions() .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) // ["text1", infinite) .start(Key.ofText(ANY_NAME_2, ANY_TEXT_1), true) - .consistency(Consistency.LINEARIZABLE) .build(); Scan scan2 = Scan.newBuilder() @@ -2006,7 +1989,6 @@ public void toSerializable_ScannerSetNotChanged_ShouldProcessWithoutExceptions() .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) // ["text2", infinite) .start(Key.ofText(ANY_NAME_2, ANY_TEXT_2), true) - .consistency(Consistency.LINEARIZABLE) .build(); Scan scan3 = Scan.newBuilder() @@ -2015,7 +1997,6 @@ public void toSerializable_ScannerSetNotChanged_ShouldProcessWithoutExceptions() .partitionKey(Key.ofText(ANY_NAME_1, ANY_TEXT_1)) // ("text2", infinite) .start(Key.ofText(ANY_NAME_2, ANY_TEXT_2), false) - .consistency(Consistency.LINEARIZABLE) .build(); // Act Assert @@ -2181,12 +2162,7 @@ public void verifyNoOverlap_ScanAllGivenAndPutInWriteSetInSameTable_ShouldThrowE Snapshot.Key putKey = new Snapshot.Key(put); snapshot.putIntoWriteSet(putKey, put); Scan scanAll = - ScanAll.newBuilder() - .namespace(ANY_NAMESPACE_NAME) - .table(ANY_TABLE_NAME) - .all() - .consistency(Consistency.LINEARIZABLE) - .build(); + ScanAll.newBuilder().namespace(ANY_NAMESPACE_NAME).table(ANY_TABLE_NAME).all().build(); TransactionResult result = prepareResult(ANY_ID); Snapshot.Key key = new Snapshot.Key(scanAll, result, TABLE_METADATA); @@ -2209,12 +2185,7 @@ public void verifyNoOverlap_ScanAllGivenAndPutInWriteSetInSameTable_ShouldThrowE Snapshot.Key putKey = new Snapshot.Key(put); snapshot.putIntoWriteSet(putKey, put); Scan scanAll = - ScanAll.newBuilder() - .namespace(ANY_NAMESPACE_NAME_2) - .table(ANY_TABLE_NAME_2) - .all() - .consistency(Consistency.LINEARIZABLE) - .build(); + ScanAll.newBuilder().namespace(ANY_NAMESPACE_NAME_2).table(ANY_TABLE_NAME_2).all().build(); TransactionResult result = prepareResult(ANY_ID); Snapshot.Key key = new Snapshot.Key(scanAll, result, TABLE_METADATA); diff --git a/integration-test/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitSpecificIntegrationTestBase.java b/integration-test/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitSpecificIntegrationTestBase.java index 95610f9a87..95a0f1c9e5 100644 --- a/integration-test/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitSpecificIntegrationTestBase.java +++ b/integration-test/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitSpecificIntegrationTestBase.java @@ -7008,7 +7008,7 @@ public void scanAndUpdate_ScanWithIndexGiven_ShouldUpdate(Isolation isolation) // Act Assert DistributedTransaction transaction = begin(manager, readOnly); Optional actual = - manager.get( + transaction.get( Get.newBuilder() .namespace(namespace1) .table(TABLE_1)