# `StatifierOban.Timer.Key`
[🔗](https://github.com/riddler/statifier_oban/blob/v0.17.3/lib/statifier_oban/timer/key.ex#L1)

Derives the two scoped keys a durable-timer store needs, per ADR-0054
decision 3 as amended by ADR-0059, and `docs/durable-timers.md` ("Keying
your store") in statifier-ex.

The dedup key is the compact `{scope, ordinal}` pair ADR-0059 decision 3
blesses; the cancellation key is `{scope, send_id}`, which ADR-0059 leaves
untouched. Because `send_id` is row data rather than a dedup-key component
here, a cancel matches stored rows through `cancels?/3`, against the row's
stored effect.

`scope` is always an argument and never derived: Statifier's counters
restart for every chart execution, so `send_1` (or ordinal `1`) from one
execution addresses the same row as its twin from an unrelated execution
unless the store keeps them apart. It is `ctx.session_id` (spec 5.10's
`_sessionid`) for a live session, or the host's own durable execution id
for a process-less host.

# `error`

```elixir
@type error() :: :invalid_scope | :missing_send_id | :missing_ordinal
```

# `scope`

```elixir
@type scope() :: String.t()
```

# `cancellation_key`

```elixir
@spec cancellation_key(
  scope(),
  Statifier.Effect.SendDelayed.t() | Statifier.Effect.Cancel.t()
) ::
  {:ok, StatifierOban.Timer.CancellationKey.t()} | {:error, error()}
```

# `cancels?`

```elixir
@spec cancels?(
  StatifierOban.Timer.CancellationKey.t(),
  scope(),
  Statifier.Effect.SendDelayed.t()
) ::
  boolean()
```

Whether a cancellation key addresses the stored effect under a scope.

Spec 6.3 cancels every delayed send under a sendid; the stored effect's
`send_id` is row data, not a dedup-key component, so the match reads it
off the row rather than off the key.

# `dedup_key`

```elixir
@spec dedup_key(scope(), Statifier.Effect.SendDelayed.t()) ::
  {:ok, StatifierOban.Timer.DedupKey.t()} | {:error, error()}
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
