temporal-sdk
Safe HaskellNone
LanguageHaskell2010

Temporal.Workflow.Saga

Synopsis

Documentation

data SagaT (m :: Type -> Type) a Source #

The saga pattern is a failure management pattern that helps establish consistency in distributed applications, and coordinates transactions between multiple services to attempt to maintain data consistency.

If you’re wondering if the saga pattern is right for your scenario, ask yourself:

Does your logic involve multiple steps, some of which span machines, services, shards, or databases, for which partial execution is undesirable?

Turns out, this is exactly where sagas are useful. Maybe you are checking inventory, charging a user’s credit card, and then fulfilling the order. Maybe you are managing a supply chain.

The saga pattern is helpful because it basically functions as a state machine storing program progress, preventing multiple credit card charges, reverting if necessary, and knowing exactly how to safely resume in a consistent state in the event of power loss.

There are many “do it all, or don’t bother” software applications in the real-world:

  • If you successfully charge the user for an item but your fulfillment service reports that the item is out of stock, you’re going to have upset users if you don’t refund the charge. If you have the opposite problem and accidentally deliver items “for free,” you’ll be out of business.
  • If the machine coordinating a machine learning data processing pipeline crashes but the follower machines carry on processing the data with nowhere to report their data to, you may have a very expensive compute resources bill on your hands.

In all of these cases having some sort of “progress tracking” and compensation code to deal with these “do-it-all-or-don’t-do-any-of-it” tasks is exactly what the saga pattern provides. In saga parlance, these sorts of “all or nothing” tasks are called long-running transactions. This doesn’t necessarily mean such actions run for a “long” time, just that they require more steps in logical time than something running locally interacting with a single database.

A saga is composed of two parts:

  1. Defined behavior for “going backwards” if you need to “undo” something (i.e., compensations)
  2. Behavior for striving towards forward progress (i.e., saving state to know where to recover from in the face of failure). This second part is often called the “orchestration logic” of the saga. For Temporal, execution of the orchestration logic is handled by the Temporal server, and the saga monad transformer here simply needs to handle the compensation logic.

Lastly, note that compensation actions are still subject to the same restrictions as any other workflow code. This means that compensation Actions run within a Workflow monad can be timed out, and they can be retried. Make sure in this case that whatever retry / timeout policies you have in place for your workflow are appropriate for the compensation actions you are running.

For more information on the saga pattern, see the following resources:

Instances

Instances details
MonadTrans SagaT Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

lift :: Monad m => m a -> SagaT m a #

MonadAccum w m => MonadAccum w (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

look :: SagaT m w #

add :: w -> SagaT m () #

accum :: (w -> (a, w)) -> SagaT m a #

MonadError e m => MonadError e (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

throwError :: e -> SagaT m a #

catchError :: SagaT m a -> (e -> SagaT m a) -> SagaT m a #

MonadReader r m => MonadReader r (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

ask :: SagaT m r #

local :: (r -> r) -> SagaT m a -> SagaT m a #

reader :: (r -> a) -> SagaT m a #

MonadSelect w m => MonadSelect w (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

select :: ((a -> w) -> a) -> SagaT m a #

MonadState s m => MonadState s (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

get :: SagaT m s #

put :: s -> SagaT m () #

state :: (s -> (a, s)) -> SagaT m a #

MonadWriter w m => MonadWriter w (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

writer :: (a, w) -> SagaT m a #

tell :: w -> SagaT m () #

listen :: SagaT m a -> SagaT m (a, w) #

pass :: SagaT m (a, w -> w) -> SagaT m a #

MonadIO m => MonadIO (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

liftIO :: IO a -> SagaT m a #

Contravariant m => Contravariant (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

contramap :: (a' -> a) -> SagaT m a -> SagaT m a' #

(>$) :: b -> SagaT m b -> SagaT m a #

MonadCatch m => MonadCatch (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

catch :: (HasCallStack, Exception e) => SagaT m a -> (e -> SagaT m a) -> SagaT m a #

MonadThrow m => MonadThrow (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

throwM :: (HasCallStack, Exception e) => e -> SagaT m a #

MonadPlus m => Alternative (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

empty :: SagaT m a #

(<|>) :: SagaT m a -> SagaT m a -> SagaT m a #

some :: SagaT m a -> SagaT m [a] #

many :: SagaT m a -> SagaT m [a] #

Monad m => Applicative (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

pure :: a -> SagaT m a #

(<*>) :: SagaT m (a -> b) -> SagaT m a -> SagaT m b #

liftA2 :: (a -> b -> c) -> SagaT m a -> SagaT m b -> SagaT m c #

(*>) :: SagaT m a -> SagaT m b -> SagaT m b #

(<*) :: SagaT m a -> SagaT m b -> SagaT m a #

Functor m => Functor (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

fmap :: (a -> b) -> SagaT m a -> SagaT m b #

(<$) :: a -> SagaT m b -> SagaT m a #

Monad m => Monad (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

(>>=) :: SagaT m a -> (a -> SagaT m b) -> SagaT m b #

(>>) :: SagaT m a -> SagaT m b -> SagaT m b #

return :: a -> SagaT m a #

MonadPlus m => MonadPlus (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

mzero :: SagaT m a #

mplus :: SagaT m a -> SagaT m a -> SagaT m a #

MonadFail m => MonadFail (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

fail :: String -> SagaT m a #

MonadFix m => MonadFix (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

mfix :: (a -> SagaT m a) -> SagaT m a #

MonadLogger m => MonadLogger (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

monadLoggerLog :: ToLogStr msg => Loc -> LogSource -> LogLevel -> msg -> SagaT m () #

MonadCont m => MonadCont (SagaT m) Source # 
Instance details

Defined in Temporal.Workflow.Saga

Methods

callCC :: ((a -> SagaT m b) -> SagaT m a) -> SagaT m a #

runSaga :: (MonadCatch m, MonadLogger m) => (SomeException -> m ()) -> SagaT m a -> m a Source #

compensated Source #

Arguments

:: MonadCatch m 
=> m ()

The compensation action to run if this saga step fails.

This action will be run in the event of any subsequent failure, not just the failure of this saga step.

Saga compensation actions will be run in reverse order of their addition to the saga.

Lastly, note that all sync exceptions thrown by the compensation action will be swallowed.

-> m a

The saga step to run.

-> SagaT m a