All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.scalar.database.transaction.consensuscommit.CrudHandler Maven / Gradle / Ivy

Go to download

A universal transaction manager that achieves database-agnostic transactions and distributed transactions that span multiple databases

There is a newer version: 3.14.0-alpha.1
Show newest version
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;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy