Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 34s
notify.sx: reminders + digests as durable flows over an injected transport. A flow requests delivery (suspend); the host dispatch sends and resumes with the outcome. At-least-once + idempotent (transport dedups by msg id; replay logs outcomes). Retry rides suspend/resume with distinct per-attempt tags, bounded by maxn. Digest delivers a batch with per-message outcomes. 182/182 green. Delivery core is the delivery-on-sx extraction seam. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
39 lines
2.8 KiB
Plaintext
39 lines
2.8 KiB
Plaintext
;; lib/events/notify.sx — durable notification delivery flows over an injected
|
|
;; transport (lib/flow).
|
|
;;
|
|
;; Reminders and digests are durable `flow`s: a flow `request`s delivery (a
|
|
;; suspend point), the HOST performs the actual send via an injected `dispatch`
|
|
;; (the transport — email/push/etc.), and resumes the flow with the outcome.
|
|
;; Because flow uses deterministic replay, a completed delivery is never re-run
|
|
;; on recovery; the host owns IO and persistence.
|
|
;;
|
|
;; Delivery is AT-LEAST-ONCE with idempotency. Each message carries an id (the
|
|
;; idempotency key). Two protections stop double-delivery:
|
|
;; 1. The transport dedups by id — a re-send of a delivered id is a no-op
|
|
;; that still reports ok, so a retry never produces two pings.
|
|
;; 2. flow's replay log records each resolved request, so recovery replays the
|
|
;; logged outcome instead of re-issuing the send.
|
|
;;
|
|
;; Retry/backoff rides flow suspend/resume: each attempt issues a request with a
|
|
;; DISTINCT tag `(deliver <id> <n>)` — distinct tags keep deterministic replay
|
|
;; correct across retries. The dispatch returns (ok info) to finish or
|
|
;; (retry reason) to try again, bounded by `maxn` (then (failed id reason)).
|
|
;;
|
|
;; A message is a 3-element list (id recipient body). The transport is generic
|
|
;; and injected — when feed/notify lands, both consumers share one transport,
|
|
;; so this delivery core is a candidate for extraction to `delivery-on-sx`.
|
|
;;
|
|
;; The Scheme flow source below loads into a flow env (see lib/flow/api.sx).
|
|
;; `ev/notify-run` prepends it to a caller program and evaluates in the shared
|
|
;; flow env.
|
|
|
|
(define
|
|
ev-notify-flows-src
|
|
"(define (ev-msg-id m) (car m))\n (define (ev-msg-recipient m) (car (cdr m)))\n (define (ev-msg-body m) (car (cdr (cdr m))))\n (define (ev-mem x xs)\n (if (null? xs) #f (if (equal? x (car xs)) #t (ev-mem x (cdr xs)))))\n (define (ev-notify-attempt m n maxn)\n (let ((r (request (list (quote deliver) (ev-msg-id m) n) m)))\n (if (eq? (car r) (quote ok))\n (list (quote delivered) (ev-msg-id m) n)\n (if (>= n maxn)\n (list (quote failed) (ev-msg-id m) (car (cdr r)))\n (ev-notify-attempt m (+ n 1) maxn)))))\n (define (ev-deliver-reminder maxn)\n (flow-node (lambda (m) (ev-notify-attempt m 1 maxn))))\n (define (ev-digest-step ms maxn)\n (if (null? ms)\n (list)\n (cons (ev-notify-attempt (car ms) 1 maxn)\n (ev-digest-step (cdr ms) maxn))))\n (define (ev-deliver-digest maxn)\n (flow-node (lambda (ms) (ev-digest-step ms maxn))))")
|
|
|
|
;; Run a Scheme flow program with the notify flows preloaded, in the shared
|
|
;; flow env. Returns the program's value (SX-native).
|
|
(define
|
|
ev/notify-run
|
|
(fn (prog) (flow-run (str ev-notify-flows-src "\n" prog))))
|