Scrypath.Operator.FailedWork (scrypath v0.3.5)

Copy Markdown View Source

Failed sync work returned by Scrypath.failed_sync_work/2.

Each entry reports a stable Scrypath-owned identifier, operation kind, retryability, and summarized reason. Recovery details are exposed through an explicit Scrypath.Operator.RecoveryAction when durable replay is possible.

Enriched fields (additive, v1.3)

  • attempt / max_attempts — set from the Oban job when source == :oban; nil for Meilisearch task rows and any path without queue attempt metadata.
  • reason_class — bounded operator-facing classification: :transport, :validation, :backend_rejected, :queue_exhausted, or :unknown (default when signals are missing or ambiguous).
  • last_attempt_at — mirrors failed_at for every constructor path (soft alias for “when this failure was last observed”).

Telemetry

Each constructed row emits once:

:telemetry.execute(
  [:scrypath, :operator, :failed_work, :observed],
  %{count: 1},
  metadata
)

Required metadata keys: :reason_class, :schema, :mode.

Optional metadata keys (v1.3): :operation, :retryable? — same meanings as the struct fields.

Treat schema module atoms and other rich metadata as unsafe for low-cardinality metric labels (for example Prometheus or OTel attribute rules) unless you aggregate or sample; they are appropriate for logs, traces, and structured handlers that filter explicitly.

Rollups

reason_class_counts/1 summarizes a row list into per-class pileup counts. If you filter rows for a view, compute counts from that same filtered list; total then matches the filtered length, not an unfiltered source length.

Summary

Types

operation()

@type operation() :: :upsert | :delete | :unknown

reason_class()

@type reason_class() ::
  :transport | :validation | :backend_rejected | :queue_exhausted | :unknown

state()

@type state() :: :failed | :retrying

t()

@type t() :: %Scrypath.Operator.FailedWork{
  attempt: non_neg_integer() | nil,
  failed_at: DateTime.t() | nil,
  id: term(),
  last_attempt_at: DateTime.t() | nil,
  max_attempts: non_neg_integer() | nil,
  metadata: map(),
  mode: :inline | :manual | :oban | atom(),
  operation: operation(),
  reason: String.t() | nil,
  reason_class: reason_class() | nil,
  recovery: Scrypath.Operator.RecoveryAction.t() | nil,
  retryable?: boolean(),
  schema: module(),
  source: :meilisearch | :oban | atom(),
  state: state()
}

Functions

list(schema_module, config, operator_opts)

@spec list(module(), keyword(), keyword()) :: {:ok, [t()]} | {:error, term()}

reason_class_counts(rows)

@spec reason_class_counts([t()]) :: Scrypath.Operator.ReasonClassCounts.t()

recovery_action(failed_work)

@spec recovery_action(t()) :: Scrypath.Operator.RecoveryAction.t() | nil