Liking cljdoc? Tell your friends :D

game.gm.outbox

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.

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.
raw docstring

apply-callback!clj

(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.
raw docstring

backoff-msclj

(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.
raw docstring

classify-notify-responseclj

(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
raw docstring

classify-responseclj

(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
raw docstring

default-lease-msclj


default-prune-interval-msclj


execute-effectclj

(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.
raw docstring

ledger-executorclj

(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.
raw docstring

make-ctxclj

(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.
raw docstring

max-backoff-msclj


max-prune-batchesclj


notify-executorclj

(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`.
raw docstring

prune!clj

(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.
raw docstring

run-effects!clj

(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.
raw docstring

start-worker!clj

(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.
raw docstring

stuck-after-msclj


sweep!clj

(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}.
raw docstring

system-callerclj

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.
raw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close