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

com.scalar.db.sql.springdata.twopc.ScalarDbTwoPcRepository Maven / Gradle / Ivy

package com.scalar.db.sql.springdata.twopc;

import com.scalar.db.sql.springdata.ScalarDbRepository;
import com.scalar.db.sql.springdata.exception.ScalarDbNonTransientException;
import com.scalar.db.sql.springdata.exception.ScalarDbTransientException;
import com.scalar.db.sql.springdata.exception.ScalarDbUnknownTransactionStateException;
import java.util.Collection;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.transaction.annotation.Transactional;

/**
 * Spring Data fragment repository interface for two phase commit transaction in ScalarDB. The
 * default Spring Transaction manager doesn't support two phase commit, so
 * {@code @Transaction(transactionManager = "scalarDbSuspendableTransactionManager")} is also needed
 * for the primitive APIs.
 *
 * @param  a model class
 */
@NoRepositoryBean
public interface ScalarDbTwoPcRepository
    extends CrudRepository, ScalarDbRepository {

  // Primitive 2PC APIs

  String begin();

  void prepare();

  void validate();

  void suspend();

  void commit();

  void rollback();

  void join(String transactionId);

  void resume(String transactionId);

  // High level abstract 2PC APIs

  //   Common

  /**
   * Execute oneshot operations in two-phase commit mode. The operations passed as an argument are
   * executed in a transaction. All the transactional operations (e.g, BEGIN, PREPARE and COMMIT)
   * are automatically executed.
   *
   * @param oneshotOperations CRUD operations to be executed in a transaction
   * @param  a result type returned from {@code oneshotOperations}
   * @return result of the CRUD operations
   */
  @Transactional(transactionManager = "scalarDbSuspendableTransactionManager")
  default  T executeOneshotOperations(@Nonnull Supplier oneshotOperations) {
    begin();
    T result = oneshotOperations.get();
    prepare();
    validate();
    commit();
    return result;
  }

  //   API for coordinators

  /**
   * Execute a 2PC transaction on the coordinator service. See {@code executeTwoPcTransaction}
   * above. This method starts a 2PC transaction with an auto generated transaction ID.
   *
   * @param executionPhaseOps execution phase task containing both local and remote operations
   *     (local CRUD, remote service call and so on.) This task is also supposed to start
   *     transactions with a given transaction ID on remote participants. Local and remote
   *     operations will be rolled back if any exception is thrown
   * @param remotePrepareCommitPhaseOps a collection of remote 2PC operations (prepare, validate,
   *     commit and rollback) for all the remote participants
   * @param  a result type returned from {@code executionPhaseOps}
   * @throws ScalarDbUnknownTransactionStateException if the 2PC transaction fails due to a
   *     non-transient cause with an unknown final status. The exception contains {@code
   *     transactionId}. Note that the final 2PC transaction status is unknown. Whether the
   *     transaction is actually committed or not needs to be decided by the application side (e.g.
   *     check if the target record is expectedly updated)
   * @throws ScalarDbNonTransientException if the 2PC transaction fails due to a non-transient
   *     cause. The exception contains {@code transactionId}
   * @throws ScalarDbTransientException if the 2PC transaction fails due to a transient cause. The
   *     exception contains {@code transactionId}
   * @return a return value from {@code executionPhaseOps} that can contain any result of local
   *     operation and/or remote operations
   */
  @Transactional(transactionManager = "scalarDbSuspendableTransactionManager")
  default  TwoPcResult executeTwoPcTransaction(
      ExecutionPhaseOperations executionPhaseOps,
      Collection remotePrepareCommitPhaseOps) {
    String transactionId = begin();

    return TwoPcOperationsProcessor.create()
        .execute(
            transactionId,
            executionPhaseOps,
            LocalPrepareCommitPhaseOperations.createSerializable(
                this::prepare, this::validate, this::commit, this::rollback),
            RemotePrepareCommitOperationsProcessor.create(true, remotePrepareCommitPhaseOps));
  }

  //   API for participants

  /**
   * Join the transaction, execute the CRUD operations and suspend the transaction on the
   * participant service. This API should be called first, and then {@code
   * prepareTransactionOnParticipant} and following APIs are supposed to be called.
   *
   * @param transactionId transaction ID
   * @param crudOperations CRUD operations as a part of execution phase
   * @param  a result type returned from {@code crudOperations}
   * @return result of the CRUD operations
   */
  @Transactional(transactionManager = "scalarDbSuspendableTransactionManager")
  default  T joinTransactionOnParticipant(
      String transactionId, @Nonnull Supplier crudOperations) {
    join(transactionId);

    T result = crudOperations.get();

    suspend();

    return result;
  }

  /**
   * Resume the transaction, execute the CRUD operations and suspend the transaction on the
   * participant service. This API can be called after {@code joinTransactionOnParticipant} before
   * {@code prepareTransactionOnParticipant} if needed.
   *
   * @param transactionId transaction ID
   * @param crudOperations CRUD operations as a part of execution phase
   * @param  a result type returned from {@code crudOperations}
   * @return result of the CRUD operations
   */
  @Transactional(transactionManager = "scalarDbSuspendableTransactionManager")
  default  T resumeTransactionOnParticipant(
      String transactionId, @Nonnull Supplier crudOperations) {
    resume(transactionId);

    T result = crudOperations.get();

    suspend();

    return result;
  }

  /**
   * Prepare the transaction and suspend the transaction on the participant service. This API should
   * be called after {@code joinTransactionOnParticipant}, and then {@code
   * validateTransactionOnParticipant} and following APIs are supposed to be called.
   *
   * @param transactionId transaction ID
   */
  @Transactional(transactionManager = "scalarDbSuspendableTransactionManager")
  default void prepareTransactionOnParticipant(String transactionId) {
    resume(transactionId);

    prepare();

    suspend();
  }

  /**
   * Validate the transaction and suspend the transaction on the participant service. This API
   * should be called after {@code prepareTransactionOnParticipant}, and then {@code
   * commitTransactionOnParticipant} or {@code rollbackTransactionOnParticipant} is supposed to be
   * called. This is needed only if `scalar.db.consensus_commit.isolation_level` is `SERIALIZABLE`
   * and `scalar.db.consensus_commit.serializable_strategy` is `EXTRA_READ`
   *
   * @param transactionId transaction ID
   */
  @Transactional(transactionManager = "scalarDbSuspendableTransactionManager")
  default void validateTransactionOnParticipant(String transactionId) {
    resume(transactionId);

    validate();

    suspend();
  }

  /**
   * Commit the transaction on the participant service. This API should be called after {@code
   * prepareTransactionOnParticipant} or {@code validateTransactionOnParticipant}, depending on the
   * transaction manager configurations.
   *
   * @param transactionId transaction ID
   */
  @Transactional(transactionManager = "scalarDbSuspendableTransactionManager")
  default void commitTransactionOnParticipant(String transactionId) {
    resume(transactionId);

    commit();
  }

  /**
   * Rollback the transaction on the participant service. This API should be called after {@code
   * prepareTransactionOnParticipant} or {@code validateTransactionOnParticipant}, depending on the
   * transaction manager configurations.
   *
   * @param transactionId transaction ID
   */
  @Transactional(transactionManager = "scalarDbSuspendableTransactionManager")
  default void rollbackTransactionOnParticipant(String transactionId) {
    resume(transactionId);

    rollback();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy