use of com.scalar.db.transaction.consensuscommit.ParallelExecutor.ParallelExecutorTask in project scalardb by scalar-labs.
the class Snapshot method toSerializableWithExtraRead.
@VisibleForTesting
void toSerializableWithExtraRead(DistributedStorage storage) throws ExecutionException, CommitConflictException {
if (!isExtraReadEnabled()) {
return;
}
List<ParallelExecutorTask> tasks = new ArrayList<>();
// Read set by scan is re-validated to check if there is no anti-dependency
for (Map.Entry<Scan, List<Key>> entry : scanSet.entrySet()) {
tasks.add(() -> {
Map<Key, TransactionResult> currentReadMap = new HashMap<>();
Set<Key> validatedReadSet = new HashSet<>();
Scanner scanner = null;
try {
Scan scan = entry.getKey();
// only get tx_id and tx_version columns because we use only them to compare
scan.clearProjections();
scan.withProjection(Attribute.ID).withProjection(Attribute.VERSION);
ScalarDbUtils.addProjectionsForKeys(scan, getTableMetadata(scan));
scanner = storage.scan(scan);
for (Result result : scanner) {
TransactionResult transactionResult = new TransactionResult(result);
// Ignore records that this transaction has prepared (and that are in the write set)
if (transactionResult.getId().equals(id)) {
continue;
}
currentReadMap.put(new Key(scan, result), transactionResult);
}
} finally {
if (scanner != null) {
try {
scanner.close();
} catch (IOException e) {
LOGGER.warn("failed to close the scanner", e);
}
}
}
for (Key key : entry.getValue()) {
if (writeSet.containsKey(key) || deleteSet.containsKey(key)) {
continue;
}
// Check if read records are not changed
TransactionResult latestResult = currentReadMap.get(key);
if (isChanged(Optional.of(latestResult), readSet.get(key))) {
throwExceptionDueToAntiDependency();
}
validatedReadSet.add(key);
}
// Check if the size of a read set by scan is not changed
if (currentReadMap.size() != validatedReadSet.size()) {
throwExceptionDueToAntiDependency();
}
});
}
// Calculate read set validated by scan
Set<Key> validatedReadSetByScan = new HashSet<>();
for (List<Key> values : scanSet.values()) {
validatedReadSetByScan.addAll(values);
}
// Read set by get is re-validated to check if there is no anti-dependency
for (Map.Entry<Key, Optional<TransactionResult>> entry : readSet.entrySet()) {
Key key = entry.getKey();
if (writeSet.containsKey(key) || deleteSet.containsKey(key) || validatedReadSetByScan.contains(key)) {
continue;
}
tasks.add(() -> {
// only get tx_id and tx_version columns because we use only them to compare
Get get = new Get(key.getPartitionKey(), key.getClusteringKey().orElse(null)).withProjection(Attribute.ID).withProjection(Attribute.VERSION).withConsistency(Consistency.LINEARIZABLE).forNamespace(key.getNamespace()).forTable(key.getTable());
Optional<TransactionResult> latestResult = storage.get(get).map(TransactionResult::new);
// Check if a read record is not changed
if (isChanged(latestResult, entry.getValue())) {
throwExceptionDueToAntiDependency();
}
});
}
parallelExecutor.validate(tasks);
}
Aggregations