com.scalar.database.transaction.consensuscommit.CrudHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scalardb Show documentation
Show all versions of scalardb Show documentation
A universal transaction manager that achieves database-agnostic transactions and distributed transactions that span multiple databases
package com.scalar.database.transaction.consensuscommit;
import static com.google.common.base.Preconditions.checkNotNull;
import com.scalar.database.api.Consistency;
import com.scalar.database.api.Delete;
import com.scalar.database.api.DistributedStorage;
import com.scalar.database.api.Get;
import com.scalar.database.api.Put;
import com.scalar.database.api.Result;
import com.scalar.database.api.Scan;
import com.scalar.database.api.Scanner;
import com.scalar.database.exception.storage.ExecutionException;
import com.scalar.database.exception.transaction.CrudException;
import com.scalar.database.exception.transaction.CrudRuntimeException;
import com.scalar.database.exception.transaction.UncommittedRecordException;
import com.scalar.database.io.Key;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.annotation.concurrent.ThreadSafe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** */
@ThreadSafe
public class CrudHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CrudHandler.class);
private final DistributedStorage storage;
private final Snapshot snapshot;
public CrudHandler(DistributedStorage storage, Snapshot snapshot) {
this.storage = checkNotNull(storage);
this.snapshot = checkNotNull(snapshot);
}
public Optional get(Get get) throws CrudException {
Optional result;
Snapshot.Key key = new Snapshot.Key(get);
result = snapshot.get(key);
if (result.isPresent()) {
return Optional.of(result.get());
}
result = getFromStorage(get);
if (!result.isPresent()) {
return Optional.empty();
}
if (result.get().isCommitted()) {
snapshot.put(key, result.get());
return Optional.of(result.get());
}
throw new UncommittedRecordException(result.get(), "this record needs recovery");
}
public List scan(Scan scan) throws CrudException {
// NOTICE : scan needs to always look at storage first since no primary key is specified
List results = new ArrayList<>();
List uncommitted = new ArrayList<>();
for (Result r : getFromStorage(scan)) {
TransactionResult result = new TransactionResult(r);
if (!result.isCommitted()) {
uncommitted.add(result);
continue;
}
results.add(result);
}
if (uncommitted.size() > 0) {
throw new UncommittedRecordException(uncommitted, "these record needs recovery");
}
// update snapshots
results.forEach(
r -> {
Snapshot.Key key =
getSnapshotKey(r, scan)
.orElseThrow(() -> new CrudRuntimeException("can' get a snapshot key"));
if (snapshot.get(key).isPresent()) {
LOGGER.warn("scanned records are already in snapshot. overwriting snapshot...");
}
snapshot.put(key, (TransactionResult) r);
});
return results;
}
public void put(Put put) {
snapshot.put(new Snapshot.Key(put), put);
}
public void delete(Delete delete) {
snapshot.put(new Snapshot.Key(delete), delete);
}
private Optional getFromStorage(Get get) throws CrudException {
try {
get.withConsistency(Consistency.LINEARIZABLE);
return storage.get(get).map(r -> new TransactionResult(r));
} catch (ExecutionException e) {
throw new CrudException("get failed.", e);
}
}
private Scanner getFromStorage(Scan scan) throws CrudException {
try {
scan.withConsistency(Consistency.LINEARIZABLE);
return storage.scan(scan);
} catch (ExecutionException e) {
throw new CrudException("scan failed.", e);
}
}
private Optional getSnapshotKey(Result result, Scan scan) {
Optional partitionKey = result.getPartitionKey();
Optional clusteringKey = result.getClusteringKey();
if (!partitionKey.isPresent() || !clusteringKey.isPresent()) {
return Optional.empty();
}
return Optional.of(
new Snapshot.Key(
scan.forNamespace().get(),
scan.forTable().get(),
partitionKey.get(),
clusteringKey.get()));
}
public Snapshot getSnapshot() {
return snapshot;
}
}