Forge64 logo
All app types

Booking system scaling

Booking system scaling and launch checklist

A booking system stays fast by paginating every list, indexing the main questions, and deferring heavy work until a measured trigger fires. At launch, apply 8 rules, watch 9 signals and hold the performance targets below.

Last checked

Rules to apply at launch

  • Idempotency: Overlap prevention is a database exclusion constraint on (resource_id, during) for held and confirmed rows; booking is attempt-insert-and-catch-23P01, never check-then-insert; a concurrency test firing N parallel requests for one slot must yield exactly one success.
  • Caching: Availability is computed on read for a bounded window (<= 31 days, one resource set) from rules minus time off minus booked ranges using the GiST index; results are cached at the edge for 15 s per (service, resource, window) and are advisory only; the exclusion constraint remains authoritative.
  • Pagination: Booking lists, customer lists and history use keyset pagination on an indexed (sort_key, id) cursor, page size 25 capped at 100; calendar views read a bounded time range per resource, never the whole table.
  • Rate limits: Token-bucket limits in Postgres per IP and per email on availability, booking creation, manage-link actions and reminder resends; per-customer cap on simultaneous held and future bookings per business; exceeded limits return 429 and hold no slot.
  • Data growth: Holds are short (10 min) and bounded per customer and per IP so slot hoarding cannot starve inventory; expired holds are cleaned up lazily at insert and by cron, keeping the exclusion index small.
  • Data growth: bookings and booking_events grow monotonically: partition by month on the booking start past the stated thresholds, anonymise old customers, and serve dashboards from utilisation_daily rollups instead of scanning bookings.
  • Realtime: Staff calendars refresh via SSE fed by LISTEN/NOTIFY on the resource id or 15 s polling with ETag; customers see slot loss on the confirm step through the 409 response, not a live feed.
  • Fanout: Business-wide events (holiday closure, staff leave) are one set-based insert into time_off for all affected resources plus a single job that cancels or reschedules impacted bookings in batches of 100.

Performance targets

Performance targets for a booking system
What to measureTarget
Availability APIp95 < 200 ms for a 14-day window per resource set, one GiST-backed range query plus rules expansion in memory
Booking creation (hold)p99 < 100 ms, one insert, <= 5 queries
Booking page LCP< 2.5 s on 4G mobile; shell edge cached, slots hydrated client-side
Calendar day/week viewp95 < 300 ms for 50 resources, one range read per week
Reminder timelinessp99 reminder sent within 2 minutes of send_at
Double-booking rateexactly 0 overlapping held or confirmed rows, verified by a nightly invariant query

What to watch

  • Metric booking conversion funnel: availability viewed -> hold created -> payment started -> confirmed, plus hold expiry rate.
  • Metric slot conflict rate: share of booking attempts rejected by the exclusion constraint; spikes mean stale availability cache or hoarding.
  • Metric reminder lag s and failure ratio: now minus send_at for the oldest pending reminder, send failures by channel.
  • Metric no show and cancellation rate: per business and per service; drives deposit policy advice.
  • Log booking event log: booking id, business id, transition, actor type; no customer names, emails, phones or notes.
  • Trace availability to confirmation trace: one trace id across availability query, hold, payment and webhook confirmation.
  • Slo booking availability: 99.9% of booking API calls return a definitive success or slot-taken response; 99% of reminders sent within 2 minutes.
  • Alert overlap invariant violation: any overlapping active bookings on one resource pages immediately (should be impossible).

Recurring work

  • send_reminders: runs cron 1m (claims reminders where send_at <= now()). Guard: SKIP LOCKED batch of 200, skips cancelled bookings, per-business daily cap, per-customer opt-out, attempt cap 3, idempotent by (booking_id, channel, send_at).
  • expire_holds: runs cron 1m. Guard: sets held bookings past hold_expires_at to expired in bounded batches, status predicate makes it idempotent, cancels the unpaid payment intent.
  • materialise_series: runs cron hourly + on series create/edit. Guard: extends each active series to the 90-day horizon, max 200 occurrences per run per series, conflicts recorded per occurrence not fatal, advisory lock per series.
  • mark_no_shows: runs cron 5m. Guard: confirmed bookings ended more than no_show_grace_min ago without check-in become no_show; applies the deposit forfeit rule once; bounded batch.
  • rollup_utilisation: runs cron hourly. Guard: recompute today and yesterday only, upsert by (resource_id, day).
  • sync_calendars: runs queue (phase 2). Guard: per-connection backoff, token refresh failures disable the connection, API call budget per business per day.

Add when the numbers say so

  • Read replicas: Serve public availability and calendar range reads from a read replica once primary CPU or availability p95 crosses the trigger; booking writes and holds always hit the primary.
  • Storage: Shard the very largest businesses by business_id or move hot calendars to a dedicated tenant database once a single business exceeds the stated write rate.

Deferred until it is needed

Work to defer for a booking system, with the trigger for adopting it
DeferredAdopt it when
Two-way Google/Outlook calendar syncpaying businesses request it or > 20% of bookings are double-booked externally.
SMS provider integration at scaleno-show rate > 8% after email reminders.
Waitlist and auto-fill of cancelled slotscancellations > 10% of bookings.
Read replicas for availability and calendar readsprimary CPU > 60% sustained or availability p95 > 300 ms.
Route-aware multi-location scheduling and dynamic pricingmobile-service businesses or utilisation below 50% at peak demand.

On Forge64

Forge64 matches your description to one of 12 reference architectures, writes the plan and data model into the project, generates the app, and runs a quality gate over the code before you see it. Each app gets its own Postgres database and sign-in with no per-user fee. Hosting on Forge Cloud and Forge Payments are rolling out.

Every metered resource on Forge64 has a hard cap, so a traffic spike stops at your limit instead of becoming a surprise bill. This is a reference design for the category, not a promise that every item is generated today. Built-ins such as email, background jobs, file uploads and search are coming soon, so anything here that needs them is something you add yourself for now.

FAQ

When should I worry about scale for a booking system?
Not before a measured trigger fires. For example, for "Two-way Google/Outlook calendar sync": paying businesses request it or > 20% of bookings are double-booked externally.
What performance targets should a booking system aim for?
Availability API: p95 < 200 ms for a 14-day window per resource set, one GiST-backed range query plus rules expansion in memory. Booking creation (hold): p99 < 100 ms, one insert, <= 5 queries. Booking page LCP: < 2.5 s on 4G mobile; shell edge cached, slots hydrated client-side.
Does Forge64 cap my usage costs?
Yes. Every metered resource has a hard cap, and paid plans add an overage spend cap that you set.

Build your app with Forge64

One prompt to a running app with auth, database, hosting and payments. Start free, no credit card required.

Start building for free