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

com.scalar.db.transaction.consensuscommit.RecoveryHandler 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
Show newest version
package com.scalar.db.transaction.consensuscommit;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.VisibleForTesting;
import com.scalar.db.api.DistributedStorage;
import com.scalar.db.api.Mutation;
import com.scalar.db.api.Selection;
import com.scalar.db.api.TransactionState;
import com.scalar.db.exception.storage.ExecutionException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.List;
import java.util.Optional;
import javax.annotation.concurrent.ThreadSafe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@ThreadSafe
public class RecoveryHandler {
  static final long TRANSACTION_LIFETIME_MILLIS = 15000;
  private static final Logger logger = LoggerFactory.getLogger(RecoveryHandler.class);
  private final DistributedStorage storage;
  private final Coordinator coordinator;
  private final TransactionTableMetadataManager tableMetadataManager;

  @SuppressFBWarnings("EI_EXPOSE_REP2")
  public RecoveryHandler(
      DistributedStorage storage,
      Coordinator coordinator,
      TransactionTableMetadataManager tableMetadataManager) {
    this.storage = checkNotNull(storage);
    this.coordinator = checkNotNull(coordinator);
    this.tableMetadataManager = checkNotNull(tableMetadataManager);
  }

  // lazy recovery in read phase
  public void recover(Selection selection, TransactionResult result) {
    logger.debug("recovering for {}", result.getId());

    Optional state;
    try {
      state = coordinator.getState(result.getId());
    } catch (CoordinatorException e) {
      logger.warn("can't get coordinator state. transaction ID: {}", result.getId(), e);
      return;
    }

    if (state.isPresent()) {
      if (state.get().getState().equals(TransactionState.COMMITTED)) {
        rollforwardRecord(selection, result);
      } else {
        rollbackRecord(selection, result);
      }
    } else {
      abortIfExpired(selection, result);
    }
  }

  @VisibleForTesting
  void rollbackRecord(Selection selection, TransactionResult result) {
    logger.debug(
        "rollback for {}, {} mutated by {}",
        selection.getPartitionKey(),
        selection.getClusteringKey(),
        result.getId());
    try {
      RollbackMutationComposer composer =
          new RollbackMutationComposer(result.getId(), storage, tableMetadataManager);
      composer.add(selection, result);
      mutate(composer.get(), result.getId());
    } catch (Exception e) {
      logger.warn("rolling back a record failed. transaction ID: {}", result.getId(), e);
      // ignore since the record is recovered lazily
    }
  }

  @VisibleForTesting
  void rollforwardRecord(Selection selection, TransactionResult result) {
    logger.debug(
        "rollforward for {}, {} mutated by {}",
        selection.getPartitionKey(),
        selection.getClusteringKey(),
        result.getId());
    CommitMutationComposer composer = new CommitMutationComposer(result.getId());
    composer.add(selection, result);
    mutate(composer.get(), result.getId());
  }

  private void abortIfExpired(Selection selection, TransactionResult result) {
    long current = System.currentTimeMillis();
    if (current <= result.getPreparedAt() + TRANSACTION_LIFETIME_MILLIS) {
      return;
    }

    try {
      coordinator.putState(new Coordinator.State(result.getId(), TransactionState.ABORTED));
      rollbackRecord(selection, result);
    } catch (CoordinatorException e) {
      logger.warn("coordinator tries to abort {}, but failed", result.getId(), e);
    }
  }

  private void mutate(List mutations, String transactionId) {
    if (mutations.isEmpty()) {
      return;
    }
    try {
      storage.mutate(mutations);
    } catch (ExecutionException e) {
      logger.warn(
          "mutation in recovery failed. the record will be eventually recovered. transaction ID: {}",
          transactionId,
          e);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy