Chronicle.Transactions.UnitOfWork (cratis_chronicle v3.16.1)

Copy Markdown View Source

Buffers event appends and commits them as a single Chronicle append-many operation.

Use begin/1 to create a unit of work and make it current for the calling process. Calls to Chronicle.append/3, Chronicle.append_many/3, or a transactional event sequence are buffered until commit/1 is called.

Summary

Functions

Enrolls a Chronicle.ReadModels.DecisionRead as a decision dependency of the unit of work.

Begins a new unit of work and makes it current for the calling process.

Returns a specification to start this module under a supervisor.

Commits the buffered events.

Returns the correlation id for a unit of work.

Returns the current unit of work for the calling process.

Runs a function inside a unit of work and commits it, returning {:ok, result} on success.

Returns the merged concurrency scopes enrolled via add_decision_read/2, keyed by event source id.

Returns the buffered events in insertion order.

Returns whether the calling process currently has an active unit of work.

Returns whether any decision read has been enrolled via add_decision_read/2.

Returns whether the unit of work has been committed or rolled back.

Returns whether the unit of work completed successfully.

Returns the last committed sequence number if available.

Registers a callback that runs when the unit of work is committed or rolled back.

Rolls back the unit of work and clears the buffered events.

Sets an existing unit of work as current for the calling process.

Tries to get a unit of work by correlation id.

Runs a function inside a unit of work and commits it if the function succeeds. Rolls the unit of work back if the function raises. The function's return value is returned as-is and committed whatever it is. When this returns, the unit of work is completed and its process stopped.

Types

commit_fun()

@type commit_fun() :: (map() -> {:ok, map()} | {:error, term()})

on_completed()

@type on_completed() :: (t() -> any())

t()

@type t() :: pid()

Functions

add_decision_read(unit_of_work, read)

@spec add_decision_read(t(), Chronicle.ReadModels.DecisionRead.t()) :: :ok

Enrolls a Chronicle.ReadModels.DecisionRead as a decision dependency of the unit of work.

Reads of the same event source merge: the commit guards against anything appended since the earliest of their boundaries, across the union of their event types. Raises ArgumentError when the same event source already carries an explicit :concurrency_scope from a buffered event — the two guards cannot both apply, and silently picking one would hide that the caller asked for both.

add_event(unit_of_work, event_sequence_id, event, opts \\ [])

Adds an event to the unit of work.

begin(opts \\ [])

@spec begin(keyword()) :: t()

Begins a new unit of work and makes it current for the calling process.

The caller owns the unit of work: complete it with commit/1 or rollback/1, and stop its process with Agent.stop/1 when done. decide/2 and with_unit_of_work/2 do both for you.

child_spec(arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

commit(unit_of_work)

@spec commit(t()) :: :ok | {:error, term()}

Commits the buffered events.

When the commit itself fails ({:error, reason}, for example when the kernel is unreachable), the unit of work stays open and current: retry commit/1, or call rollback/1.

correlation_id(unit_of_work)

@spec correlation_id(t()) :: Chronicle.Correlation.CorrelationId.t()

Returns the correlation id for a unit of work.

current()

@spec current() :: t()

Returns the current unit of work for the calling process.

decide(fun, opts \\ [])

@spec decide((t() -> result), keyword()) :: {:ok, result} | {:error, term()}
when result: any()

Runs a function inside a unit of work and commits it, returning {:ok, result} on success.

Unlike with_unit_of_work/2, a conflict never raises: when the function enrolled at least one decision read (Chronicle.ReadModels.DecisionReads.get/3) and the commit is rejected because one of them is no longer current, this returns {:error, %Chronicle.ReadModels.DecisionConflict{}} instead. Any other commit failure is returned as {:error, reason}, after rolling the unit of work back so later appends from the process are not buffered into it.

Returning {:error, reason} from fun rejects the decision: the unit of work is rolled back, nothing is written, and {:error, reason} is returned as-is. The unit of work is rolled back, and the error re-raised, if fun itself raises. When decide/2 returns, its unit of work is completed and its process stopped, so don't keep the unit of work beyond fun.

Chronicle.decide(fn _unit_of_work ->
  {:ok, read} = Chronicle.ReadModels.DecisionReads.get(MyApp.ReadModels.Seat, "seat-5")
  if read.instance == nil, do: Chronicle.EventSequences.EventLog.append("seat-5", %SeatBooked{})
end)

get_decision_reads(unit_of_work)

@spec get_decision_reads(t()) :: %{
  required(String.t()) => Chronicle.Events.ConcurrencyScope.t()
}

Returns the merged concurrency scopes enrolled via add_decision_read/2, keyed by event source id.

get_events(unit_of_work)

Returns the buffered events in insertion order.

has_current?()

@spec has_current?() :: boolean()

Returns whether the calling process currently has an active unit of work.

has_decision_reads?(unit_of_work)

@spec has_decision_reads?(t()) :: boolean()

Returns whether any decision read has been enrolled via add_decision_read/2.

is_completed?(unit_of_work)

@spec is_completed?(t()) :: boolean()

Returns whether the unit of work has been committed or rolled back.

is_success?(unit_of_work)

@spec is_success?(t()) :: boolean()

Returns whether the unit of work completed successfully.

last_committed_sequence_number(unit_of_work)

@spec last_committed_sequence_number(t()) :: non_neg_integer() | nil

Returns the last committed sequence number if available.

on_completed(unit_of_work, callback)

@spec on_completed(t(), on_completed()) :: :ok

Registers a callback that runs when the unit of work is committed or rolled back.

rollback(unit_of_work)

@spec rollback(t()) :: :ok

Rolls back the unit of work and clears the buffered events.

set_current(unit_of_work)

@spec set_current(t()) :: t()

Sets an existing unit of work as current for the calling process.

try_get_for(correlation_id)

@spec try_get_for(Chronicle.Correlation.CorrelationId.t() | String.t()) ::
  {:ok, t()} | :error

Tries to get a unit of work by correlation id.

with_unit_of_work(fun, opts \\ [])

@spec with_unit_of_work((t() -> result), keyword()) :: result when result: any()

Runs a function inside a unit of work and commits it if the function succeeds. Rolls the unit of work back if the function raises. The function's return value is returned as-is and committed whatever it is. When this returns, the unit of work is completed and its process stopped.