Skip to content

Promote dev to main: inline AMA operations drain, sparser crons - #300

Merged
CaliCastle merged 4 commits into
mainfrom
dev
Aug 23, 2026
Merged

Promote dev to main: inline AMA operations drain, sparser crons#300
CaliCastle merged 4 commits into
mainfrom
dev

Conversation

@CaliCastle

@CaliCastle CaliCastle commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Release

Promotes #299 to production.

Neon compute fix: the */5 AMA work cron kept the database endpoint awake 24/7 (~183 CU-hrs/month). Durable operations (booking emails, Finalizing Booking recovery, refunds, artifact updates) now drain inline via waitUntil in the same invocation that enqueues them, so guest-facing work starts immediately. The cron drops to */30 (media reconcile to hourly) as a pure clock fallback for reminders, retry backoff, and expired Slot Hold bookkeeping — letting Neon autosuspend between wakeups (~85% compute reduction expected).

Review hardening included: webhook kick gated on booking_created, drain loops past full batches under a 240s budget, mutating routes at the platform-default 300s maxDuration.

Note: the new cron schedules take effect with this production deployment.

Greptile Summary

The PR starts due AMA durable operations within the mutation invocation that enqueues them, while retaining less-frequent cron sweeps for time-based and recovery work.

  • Adds waitUntil-backed, multi-batch operation draining with a 240-second start budget.
  • Triggers draining after successful booking-management actions and newly created Stripe bookings.
  • Raises affected mutation routes to the platform’s 300-second duration limit.
  • Changes the AMA worker cadence from five to thirty minutes and media reconciliation from fifteen minutes to hourly.

Confidence Score: 5/5

The PR appears safe to merge, with inline processing covering mutation-triggered AMA work and existing cron paths retaining scheduled recovery duties.

Durable-operation leasing protects concurrent drains, all changed enqueueing mutation paths trigger the runner, and the reduced schedules do not establish a broken correctness contract.

Important Files Changed

Filename Overview
lib/ama/booking/server.ts Adds a bounded waitUntil drain that repeatedly runs full operation batches while relying on existing lease-based concurrency controls.
lib/ama/booking/http.ts Adds a typed webhook-outcome callback after successful event processing without changing signature verification or retry responses.
app/api/ama/stripe/webhook/route.ts Starts inline processing only when webhook handling creates a booking and therefore enqueues finalization work.
app/api/ama/manage/[token]/cancel/route.ts Starts operation draining after a successful guest cancellation and raises the route duration limit.
app/api/ama/manage/[token]/reschedule/route.ts Starts operation draining after a successful guest reschedule and raises the route duration limit.
app/api/admin/ama/bookings/[bookingId]/route.ts Starts operation draining after successful owner booking actions.
app/api/admin/ama/operations/[operationId]/route.ts Starts the runner after successful retry or resolution actions, including a harmless empty pass for resolution-only actions.
vercel.json Reduces AMA and media maintenance frequency, intentionally increasing clock-driven latency while allowing database autosuspension.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  M[AMA mutation] --> E[Enqueue durable operation]
  E --> K[kickAmaOperations]
  K --> W[waitUntil inline drain]
  W --> R[Runner: batches of 10]
  R --> P[Calendar / Meeting / Email / Stripe]
  C[30-minute AMA cron] --> R
  C --> H[Expired hold cleanup]
  C --> T[Reminders and retry backoff]
  MC[Hourly media cron] --> MR[Media reconciliation]
Loading

Reviews (1): Last reviewed commit: "perf(ama): drain durable operations inli..." | Re-trigger Greptile

Context used (4)

The */5 AMA work cron kept the Neon endpoint awake around the clock:
each run issues an unconditional hold-release UPDATE plus a claimDue
query, resetting the 5-minute autosuspend timer every cycle (~183
CU-hrs/month of always-on compute).

Mutating endpoints now kick a background drain of the durable
operations queue via waitUntil in the same invocation that enqueued
the work, so booking emails, Finalizing Booking recovery, refunds,
and admin-triggered retries start immediately instead of waiting for
the next sweep. The lease-based runner already tolerates concurrent
drains, so the kick and the scheduled sweep cannot double-execute.

With request-triggered work handled inline, the cron becomes a pure
clock fallback (reminders, retry backoff, expired Slot Hold
bookkeeping) and drops to */30, with media reconcile hourly and
aligned so wakeups consolidate. Reminders and retries may now land up
to 30 minutes later; everything guest-facing gets faster, and the
database can suspend between wakeups.
Address review feedback on the inline operations drain:

- The Stripe webhook handler now surfaces the processed outcome via an
  onOutcome callback, and the route kicks the drain only for
  booking_created. Duplicate, ignored, orphaned, booking-exists, and
  hold-release deliveries return 200 without enqueueing work, so they
  no longer start a runner pass.
- A single runner pass caps at its batch size, so older due work could
  crowd out the operation the triggering mutation just enqueued. The
  kick now re-runs the drain while full batches come back, bounded by
  a 30-second inline budget; leftovers stay with the scheduled sweep.
A single slow first pass (ten due operations with provider calls) could
exhaust the 30-second outer budget before the drain reached the
operation the triggering mutation enqueued, deferring it to the
scheduled sweep.

Raise the mutating routes to the platform-default 300s maxDuration
(the explicit 60 had actually lowered it) and extend the drain budget
to 240s, so 240s of passes plus one worst-case 45s pass still finish
inside the ceiling. Passes are claim-ordered by nextAttemptAt, so each
one moves the queue strictly toward the newest operation.
@CaliCastle
CaliCastle merged commit 2205b93 into main Aug 23, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant