events: waitlist + auto-promotion + 21 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 34s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 34s
When full, ev/waitlist! queues actors FIFO (:waitlist/:unwaitlist on the booking stream; waiting fold independent of the seat fold). ev/waitlist, ev/waitlist-position, ev/leave-waitlist!. ev/cancel-promote! frees a seat and auto-promotes the head of the queue to a confirmed booking. Idempotent. 240/240 green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,9 @@
|
||||
((= (first xs) x) i)
|
||||
(else (ev-bk-index (rest xs) x (+ i 1))))))
|
||||
|
||||
(define ev-bk-append (fn (xs a) (append xs (list a))))
|
||||
(define ev-bk-remove (fn (xs a) (filter (fn (x) (not (= x a))) xs)))
|
||||
|
||||
;; ---- per-actor state association list: ((actor state) ...) in join order ----
|
||||
|
||||
(define
|
||||
@@ -269,3 +272,101 @@
|
||||
(fn
|
||||
(b occ-key capacity)
|
||||
(max 0 (- capacity (ev-booking-count b occ-key)))))
|
||||
|
||||
;; ---- waitlist ----
|
||||
;; When an occurrence is full, actors join a FIFO waitlist (:waitlist /
|
||||
;; :unwaitlist events on the same stream). Taking a seat (:booking / :hold)
|
||||
;; removes an actor from the queue, so the waitlist fold is independent of the
|
||||
;; seat fold. Cancelling/releasing a seat can auto-promote the head of the
|
||||
;; queue (a :booking appended for them).
|
||||
|
||||
(define
|
||||
ev-fold-waiting
|
||||
(fn
|
||||
(events)
|
||||
(reduce
|
||||
(fn
|
||||
(acc e)
|
||||
(let
|
||||
((typ (persist/event-type e))
|
||||
(actor (get (persist/event-data e) :actor)))
|
||||
(cond
|
||||
((= typ :waitlist) (if (ev-bk-member? actor acc) acc (ev-bk-append acc actor)))
|
||||
((= typ :unwaitlist) (ev-bk-remove acc actor))
|
||||
((= typ :booking) (ev-bk-remove acc actor))
|
||||
((= typ :hold) (ev-bk-remove acc actor))
|
||||
(else acc))))
|
||||
(list)
|
||||
events)))
|
||||
|
||||
;; The current waitlist queue (FIFO, oldest first).
|
||||
(define
|
||||
ev/waitlist
|
||||
(fn (b occ-key) (ev-fold-waiting (persist/read b (ev-booking-stream occ-key)))))
|
||||
|
||||
;; 1-based queue position for an actor (0 if not waiting).
|
||||
(define
|
||||
ev/waitlist-position
|
||||
(fn (b occ-key actor) (ev-seat-of (ev/waitlist b occ-key) actor)))
|
||||
|
||||
;; Book if a seat is free, else join the waitlist. Idempotent: already seated →
|
||||
;; :already; already queued → :already-waiting.
|
||||
(define
|
||||
ev/waitlist!
|
||||
(fn
|
||||
(b occ-key capacity actor)
|
||||
(let
|
||||
((seats (ev-booked-actors b occ-key))
|
||||
(waiting (ev/waitlist b occ-key)))
|
||||
(cond
|
||||
((ev-bk-member? actor seats)
|
||||
{:status :already :seat (ev-seat-of seats actor) :actor actor})
|
||||
((ev-bk-member? actor waiting)
|
||||
{:status :already-waiting :position (ev-seat-of waiting actor) :actor actor})
|
||||
(else
|
||||
(let
|
||||
((r (ev/book! b occ-key capacity actor)))
|
||||
(if
|
||||
(= (get r :status) :booked)
|
||||
r
|
||||
(begin
|
||||
(persist/append b (ev-booking-stream occ-key) :waitlist 0 {:actor actor})
|
||||
{:status :waitlisted
|
||||
:position (+ (len waiting) 1)
|
||||
:actor actor}))))))))
|
||||
|
||||
;; Leave the waitlist. :left or :not-waiting.
|
||||
(define
|
||||
ev/leave-waitlist!
|
||||
(fn
|
||||
(b occ-key actor)
|
||||
(if
|
||||
(ev-bk-member? actor (ev/waitlist b occ-key))
|
||||
(begin
|
||||
(persist/append b (ev-booking-stream occ-key) :unwaitlist 0 {:actor actor})
|
||||
{:status :left :actor actor})
|
||||
{:status :not-waiting :actor actor})))
|
||||
|
||||
;; Cancel a seat and, if that frees capacity, auto-promote the head of the
|
||||
;; waitlist (a confirmed booking). Returns the cancel result plus :promoted
|
||||
;; (the actor promoted, or nil).
|
||||
(define
|
||||
ev/cancel-promote!
|
||||
(fn
|
||||
(b occ-key capacity actor)
|
||||
(let
|
||||
((c (ev/cancel! b occ-key actor)))
|
||||
(if
|
||||
(= (get c :status) :cancelled)
|
||||
(let
|
||||
((waiting (ev/waitlist b occ-key))
|
||||
(seats (ev-booked-actors b occ-key)))
|
||||
(if
|
||||
(and (not (empty? waiting)) (< (len seats) capacity))
|
||||
(let
|
||||
((promoted (first waiting)))
|
||||
(begin
|
||||
(persist/append b (ev-booking-stream occ-key) :booking 0 {:actor promoted})
|
||||
{:status :cancelled :actor actor :promoted promoted}))
|
||||
{:status :cancelled :actor actor :promoted nil}))
|
||||
c))))
|
||||
|
||||
Reference in New Issue
Block a user