Class LockingSagaRepository<T>

java.lang.Object
org.axonframework.modelling.saga.repository.LockingSagaRepository<T>
All Implemented Interfaces:
SagaRepository<T>
Direct Known Subclasses:
AnnotatedSagaRepository

public abstract class LockingSagaRepository<T> extends Object implements SagaRepository<T>
Abstract implementation of a saga repository that locks access to a saga while the saga is being operated on.

The lock is obtained before the saga is loaded or created and held until the ProcessingContext completes, so only one processing context at a time operates on a given saga.

The default PessimisticLockFactory hands out a lock owned by the thread that acquired it, which means the lock must be released on that same thread. Axon Framework 4 got that for free, because the unit of work was bound to the thread through a thread local. In Axon Framework 5 it holds when the ProcessingContext runs its completion handlers on the invoking thread, which is what a TransactionManager answering true to requiresSameThreadInvocations() arranges. Both SpringTransactionManager and EntityManagerTransactionManager do. Without one, the release can happen on another thread, the lock is never released, and the saga becomes permanently unreachable; that failure is logged as an error naming the saga and both threads, since nothing can recover the lock at that point. A saga repository therefore needs a transaction manager that requires same-thread invocations, in the same way JdbcSagaStore needs a transaction-aware ConnectionProvider.

Since:
3.0
Author:
Rene de Waele
  • Constructor Details

  • Method Details

    • load

      public @Nullable Saga<T> load(String sagaIdentifier, ProcessingContext context)
      Loads a known Saga instance by its unique identifier. Due to the concurrent nature of Sagas, it is not unlikely for a Saga to have ceased to exist after it has been found based on associations. Therefore, a repository should return null in case a Saga doesn't exists, as opposed to throwing an exception.

      This implementation locks access to sagas with the given sagaIdentifier and releases the lock when the given context completes, on both commit and rollback.

      Specified by:
      load in interface SagaRepository<T>
      Parameters:
      sagaIdentifier - The unique identifier of the Saga to load
      context - the ProcessingContext the loaded Saga is managed in
      Returns:
      The Saga instance, or null if no such saga exists
    • createInstance

      public Saga<T> createInstance(String sagaIdentifier, Supplier<T> factoryMethod, ProcessingContext context)
      Creates a new Saga instance. The returned Saga will delegate event handling to the instance supplied by the given factoryMethod.

      This implementation locks access to sagas with the given sagaIdentifier and releases the lock when the given context completes, on both commit and rollback.

      Specified by:
      createInstance in interface SagaRepository<T>
      Parameters:
      sagaIdentifier - the identifier to use for the new saga instance
      factoryMethod - Used to create a new Saga delegate
      context - the ProcessingContext the new Saga is managed in
      Returns:
      a new Saga instance wrapping an instance of type T
    • doLoad

      protected abstract @Nullable Saga<T> doLoad(String sagaIdentifier, ProcessingContext context)
      Loads a known Saga instance by its unique identifier. Due to the concurrent nature of Sagas, it is not unlikely for a Saga to have ceased to exist after it has been found based on associations. Therefore, a repository should return null in case a Saga doesn't exists, as opposed to throwing an exception.
      Parameters:
      sagaIdentifier - The unique identifier of the Saga to load
      context - the ProcessingContext the loaded Saga is managed in
      Returns:
      The Saga instance, or null if no such saga exists
    • doCreateInstance

      protected abstract Saga<T> doCreateInstance(String sagaIdentifier, Supplier<T> factoryMethod, ProcessingContext context)
      Creates a new Saga instance. The returned Saga will delegate event handling to the instance supplied by the given factoryMethod.
      Parameters:
      sagaIdentifier - the identifier to use for the new saga instance
      factoryMethod - Used to create a new Saga delegate
      context - the ProcessingContext the new Saga is managed in
      Returns:
      a new Saga instance wrapping an instance of type T