Runs the transactional outbox (runtime v2).
An event's :effects are written to the store's outbox in the same transaction as
its state (see game.gm.store). This namespace executes them afterwards, and applies
their :on-success / :on-failure callbacks, with these guarantees:
:failed. Anything that does not tell us what happened (401/403, 408, 429, 5xx,
a timeout, a dropped connection) is UNCERTAIN: the effect stays :pending, is
retried with capped backoff, never failed automatically, and logs a loud
EFFECT_STUCK once it is old enough that a person should look.dispatch-fn, and the store marks the callback applied in the same
transaction as the state change they make.DELIVERY. An executor is a fn (effect) -> outcome, or {:run f :delivery d}. The
default, :at-least-once, is right for anything idempotent (a ledger batch under its
key, a PUT, a read): uncertain outcomes retry. :at-most-once is for calls that must
not be repeated blindly (they cost a credit, publish something live): an uncertain
outcome becomes a definite :failed with error :unknown-outcome, unless the
executor says nothing was applied ({:retry true :safe true}, e.g. connection
refused), and an effect whose earlier claim was abandoned (the process died mid-call;
see :abandoned? on claim-effects!) is failed the same way WITHOUT calling the
provider again.
CALLBACK HOOK. Callback events are stored when the effect is emitted, before its
outcome exists. :callback-event-fn (effect event) -> event runs when the callback
is applied, with the completed row, so a GM can put the outcome into the event's data.
Times are epoch milliseconds from now-fn, so everything here is testable without
sleeping.
Runs the transactional outbox (runtime v2).
An event's `:effects` are written to the store's outbox in the same transaction as
its state (see game.gm.store). This namespace executes them afterwards, and applies
their `:on-success` / `:on-failure` callbacks, with these guarantees:
- AT-LEAST-ONCE EXECUTION, EXACTLY-ONCE IN EFFECT. A crash at any point leaves the
row pending; the next sweep re-runs it, and MoM's ledger answers a repeated
idempotency key with the original transaction instead of a second one.
- OUTCOMES ARE DEFINITE OR THEY ARE NOT. MoM saying no (bad request, unknown
currency or player, insufficient balance, key reused with another payload) is
`:failed`. Anything that does not tell us what happened (401/403, 408, 429, 5xx,
a timeout, a dropped connection) is UNCERTAIN: the effect stays `:pending`, is
retried with capped backoff, never failed automatically, and logs a loud
EFFECT_STUCK once it is old enough that a person should look.
- CALLBACKS RUN EXACTLY ONCE. They are dispatched locally (no HTTP hop) through an
injected `dispatch-fn`, and the store marks the callback applied in the same
transaction as the state change they make.
DELIVERY. An executor is a fn `(effect) -> outcome`, or `{:run f :delivery d}`. The
default, `:at-least-once`, is right for anything idempotent (a ledger batch under its
key, a PUT, a read): uncertain outcomes retry. `:at-most-once` is for calls that must
not be repeated blindly (they cost a credit, publish something live): an uncertain
outcome becomes a definite `:failed` with error `:unknown-outcome`, unless the
executor says nothing was applied (`{:retry true :safe true}`, e.g. connection
refused), and an effect whose earlier claim was abandoned (the process died mid-call;
see `:abandoned?` on claim-effects!) is failed the same way WITHOUT calling the
provider again.
CALLBACK HOOK. Callback events are stored when the effect is emitted, before its
outcome exists. `:callback-event-fn` `(effect event) -> event` runs when the callback
is applied, with the completed row, so a GM can put the outcome into the event's data.
Times are epoch milliseconds from `now-fn`, so everything here is testable without
sleeping.(apply-callback! {:keys [store dispatch-fn callback-event-fn] :as ctx} effect)Applies the effect's on-success / on-failure events as local dispatches and marks the
callback applied, all in one transaction. A callback that reports failure changes
nothing but is still marked applied (it would fail the same way again). Its own
:effects are ignored: no cascades. Each event passes through :callback-event-fn
first. Returns the store's result, whose :result is one outcome per event
{:type :success :data :events} (:data/:events from the handler, so a caller
waiting on the effect can answer with them), or {:skipped :not-applicable} if it was
already applied by someone else.
Applies the effect's on-success / on-failure events as local dispatches and marks the
callback applied, all in one transaction. A callback that reports failure changes
nothing but is still marked applied (it would fail the same way again). Its own
`:effects` are ignored: no cascades. Each event passes through `:callback-event-fn`
first. Returns the store's result, whose `:result` is one outcome per event
`{:type :success :data :events}` (`:data`/`:events` from the handler, so a caller
waiting on the effect can answer with them), or {:skipped :not-applicable} if it was
already applied by someone else.(backoff-ms attempts)Delay before attempt number attempts + 1: 1s, 2s, 4s ... capped at 5 minutes.
Delay before attempt number `attempts` + 1: 1s, 2s, 4s ... capped at 5 minutes.
(classify-notify-response status body)MoM's answer to a notification write -> an outcome. The endpoint is idempotent, so an uncertain answer is simply retried.
200 succeeded (:written / :existing / :recipients kept)
400, 404 failed: MoM looked and said no (unknown player, malformed target)
anything else uncertain: stays pending and retries with backoff
MoM's answer to a notification write -> an outcome. The endpoint is idempotent, so an uncertain answer is simply retried. 200 succeeded (`:written` / `:existing` / `:recipients` kept) 400, 404 failed: MoM looked and said no (unknown player, malformed target) anything else uncertain: stays pending and retries with backoff
(classify-response status body)MoM's answer to a ledger batch -> an outcome.
{:status :succeeded :result ..} 200, including an idempotent replay {:status :failed :error ..} 400, 409, and the 404s that name a missing currency or player: MoM looked and said no {:retry true :error ..} anything else: we do not know what happened
MoM's answer to a ledger batch -> an outcome.
{:status :succeeded :result ..} 200, including an idempotent replay
{:status :failed :error ..} 400, 409, and the 404s that name a missing
currency or player: MoM looked and said no
{:retry true :error ..} anything else: we do not know what happened(execute-effect executors effect)Runs effect with the executor for its type and returns an outcome (see the ns
docstring for delivery). An exception is uncertain, never a failure. A type with no
executor (player/notify for now) is recorded as failed/unsupported so its on-failure
callback can run.
Runs `effect` with the executor for its type and returns an outcome (see the ns docstring for delivery). An exception is uncertain, never a failure. A type with no executor (player/notify for now) is recorded as failed/unsupported so its on-failure callback can run.
(ledger-executor {:keys [mom-url game-name auth-headers-fn http-post timeout-ms]
:or {http-post http/post timeout-ms 10000}})An executor for ledger/batch effects: POSTs the batch to MoM under the effect's own
idempotency key. opts: :mom-url, :game-name, :auth-headers-fn (a fn returning the
headers map), and optionally :http-post (default clj-http), for tests.
An executor for `ledger/batch` effects: POSTs the batch to MoM under the effect's own idempotency key. `opts`: :mom-url, :game-name, :auth-headers-fn (a fn returning the headers map), and optionally :http-post (default clj-http), for tests.
(make-ctx opts)The context the functions above take. Required: :store, :dispatch-fn, :executors ({effect-type executor}, see DELIVERY). Optional: :worker-id, :now-fn, :lease-ms, :batch, :log-fn, :callback-event-fn, :retention ({:receipts-ms :journal-ms :outbox-ms :limit}, see store/prune!) and :prune-interval-ms.
The context the functions above take. Required: :store, :dispatch-fn, :executors
({effect-type executor}, see DELIVERY). Optional: :worker-id, :now-fn, :lease-ms,
:batch, :log-fn, :callback-event-fn, :retention ({:receipts-ms :journal-ms :outbox-ms
:limit}, see store/prune!) and :prune-interval-ms.(notify-executor {:keys [mom-url game-name auth-headers-fn http-post timeout-ms]
:or {http-post http/post timeout-ms 10000}})An executor for player/notify effects: writes the notification to the targeted seats'
inboxes through MoM (POST /api/v1/games/:g/notifications). Inbox only: MoM never pushes
for a game that runs its own runtime, and the consumers poll their inbox. opts as for
ledger-executor.
An executor for `player/notify` effects: writes the notification to the targeted seats' inboxes through MoM (`POST /api/v1/games/:g/notifications`). Inbox only: MoM never pushes for a game that runs its own runtime, and the consumers poll their inbox. `opts` as for `ledger-executor`.
(prune! {:keys [store retention now-fn]})One retention pass (see store/prune!): batches until a table comes back short, at most
max-prune-batches, so a first pass over a large backlog never holds a long lock.
Uses (:retention ctx) ({:receipts-ms :journal-ms :outbox-ms :limit}) and returns the
totals {:receipts :journal :outbox}, or nil when no retention is configured.
One retention pass (see store/prune!): batches until a table comes back short, at most
`max-prune-batches`, so a first pass over a large backlog never holds a long lock.
Uses `(:retention ctx)` ({:receipts-ms :journal-ms :outbox-ms :limit}) and returns the
totals {:receipts :journal :outbox}, or nil when no retention is configured.(run-effects! {:keys [store] :as ctx} effect-keys)Runs the effects with these keys (an event's own, normally) right now, on the
calling thread, and returns [{:key :type :status :result :error :callback}] in the
order given. A status of :pending means the provider's answer was uncertain and the
sweeper owns it now; the caller must not treat that as a failure. :callback is the
data and events the callbacks produced, for a caller that answers with them.
Runs the effects with these keys (an event's own, normally) right now, on the
calling thread, and returns [{:key :type :status :result :error :callback}] in the
order given. A status of :pending means the provider's answer was uncertain and the
sweeper owns it now; the caller must not treat that as a failure. `:callback` is the
data and events the callbacks produced, for a caller that answers with them.(start-worker! ctx interval-ms)Sweeps once now (recovering anything a previous process left behind), then every
interval-ms on a daemon thread. Returns a no-arg fn that stops it.
Sweeps once now (recovering anything a previous process left behind), then every `interval-ms` on a daemon thread. Returns a no-arg fn that stops it.
(sweep! {:keys [store batch] :as ctx})One recovery pass: runs every due effect and applies every unapplied callback, in any state a crash may have left them. Safe to run from many workers at once. Returns {:effects n :callbacks n}.
One recovery pass: runs every due effect and applies every unapplied callback, in
any state a crash may have left them. Safe to run from many workers at once.
Returns {:effects n :callbacks n}.The caller envelope on a callback event. Effect data never establishes a caller.
The caller envelope on a callback event. Effect data never establishes a caller.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |