DDD.Outbox.DeadLetters (vicander v0.1.1)

Copy Markdown View Source

Inspect and resolve dead-lettered outbox events.

An event is dead-lettered when delivery kept failing (an infrastructure error, an exception, an event type that can't be resolved) and the poller ran out of retries. Business rejections are not dead letters: they are recorded as rejected in event_deliveries.

A dead letter is unresolved until someone decides what to do with it:

  • replay/2 - clear the failure state; the poller retries it. Handlers that already succeeded for the event are skipped.
  • keep/2 - acknowledge it and leave it dead-lettered (nothing more will happen).
  • discard/2 - drop it for good.

DDD.Outbox.DeadLetterHandler applies a policy to new dead letters automatically; the mix vic.outbox.* tasks call these functions for manual work.

Summary

Functions

Resolve the dead letter as discarded.

One dead letter by its UUID string, or nil.

Acknowledge the dead letter and leave it as it is.

Dead letters, oldest first. Only unresolved ones unless all: true. limit: defaults to 100.

Make the poller retry the event from scratch. Counts in replays.

Replay every unresolved dead letter. Returns how many were replayed.

Types

dead_letter()

@type dead_letter() :: %{
  id: String.t(),
  event_type: String.t(),
  aggregate_type: String.t(),
  aggregate_id: String.t(),
  payload: map(),
  attempts: non_neg_integer(),
  last_error: String.t() | nil,
  failed_at: DateTime.t(),
  resolution: String.t() | nil,
  resolved_at: DateTime.t() | nil,
  replays: non_neg_integer()
}

Functions

discard(repo, id)

@spec discard(module(), String.t()) :: :ok | {:error, :not_found}

Resolve the dead letter as discarded.

get(repo, id)

@spec get(module(), String.t()) :: dead_letter() | nil

One dead letter by its UUID string, or nil.

keep(repo, id)

@spec keep(module(), String.t()) :: :ok | {:error, :not_found}

Acknowledge the dead letter and leave it as it is.

list(repo, opts \\ [])

@spec list(module(), keyword()) :: [dead_letter()]

Dead letters, oldest first. Only unresolved ones unless all: true. limit: defaults to 100.

replay(repo, id)

@spec replay(module(), String.t()) :: :ok | {:error, :not_found}

Make the poller retry the event from scratch. Counts in replays.

replay_all(repo)

@spec replay_all(module()) :: non_neg_integer()

Replay every unresolved dead letter. Returns how many were replayed.