Marketplace scaling
Marketplace scaling and launch checklist
A marketplace 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 8 signals and hold the performance targets below.
Last checked
Rules to apply at launch
- Pagination: Search, seller lists, orders, messages and reviews use keyset pagination on an indexed (sort_key, id) cursor; page size defaults to 24 and is capped at 50; no OFFSET and no count(*) of the full result set (show 'many' beyond a bound).
- Search: Search is a single indexed query: tsvector + GIN for text, btree for category/price/status, a precomputed rank_key for ordering; facet counts come from a bounded query cached 60 s per filter combination.
- Caching: Home, category and listing pages are rendered with ISR and edge cache (stale-while-revalidate); listing mutations revalidate the page tag; prices shown are display-only and always re-read from the database at checkout.
- Idempotency: All order mutations are compare-and-set updates (UPDATE orders SET status = to WHERE id = $1 AND status = from) inside one transaction with the order_events insert; zero rows updated means the transition was already applied or invalid.
- Rate limits: Per-IP and per-user limits on search, listing creation, message send, checkout creation, report submission and signup, stored in Postgres token buckets; new-account limits are stricter than established-account limits; exceeded limits return 429 and write nothing.
- Fanout: Message fan-out is O(1): insert the message and increment one unread counter on the conversation row; inbox is one indexed range read; realtime delivery is polling or SSE per open thread only.
- Realtime: Open conversation pages hold one SSE stream fed by LISTEN/NOTIFY on the conversation id, falling back to 10 s polling; closed tabs consume nothing.
- Storage: Per-seller storage quota, max 12 images per listing, 10 MB per image; unreferenced uploads are swept after 24 h.
Performance targets
| What to measure | Target |
|---|---|
| Search results API | p95 < 250 ms at 1M published listings, one indexed query plus one cached facet read |
| Listing page LCP | < 2.5 s; TTFB < 200 ms from edge cache; images served as responsive widths |
| Order state transition | p99 < 100 ms, one compare-and-set update plus one event insert |
| Message send | p99 < 80 ms, two writes in one transaction |
| Escrow release throughput | release job clears 1,000 due orders per run within 5 minutes, bounded by the per-run value cap |
What to watch
- Metric gmv take rate and fee cents: gross merchandise value, platform fee and net per day and category; the business health numbers.
- Metric order state funnel: counts and dwell time per state; stuck pending_payment, delivered or disputed orders are visible.
- Metric search latency and zero result rate: p95 latency and share of queries with no results, per category.
- Log money-movement audit log: every charge, transfer, refund and hold with order id, idempotency key and actor; no card data or tokens.
- Trace checkout to release trace: one trace id from checkout through webhook to transfer.
- Slo checkout success: 99.5% of checkout attempts reach a definitive paid or failed state within 60 s.
- Alert ledger drift or double payout: nonzero ledger drift, more than one payout per order, or a failed transfer retry loop pages immediately.
- Alert dispute and chargeback rate: dispute rate above 1% of orders over 7 days, or a seller above 3x the platform average, triggers review and payout hold.
Recurring work
- process_listing_media: runs queue (jobs kind=media). Guard: magic-byte and dimension check, max pixels, resource-limited container, idempotent by object_key, per-seller daily cap.
- refresh_rank_keys: runs cron 15m. Guard: bounded batch of changed listings, set-based update, never a full table rewrite in one transaction.
- rollup_seller_ratings: runs cron 5m. Guard: only sellers with reviews newer than the last run, upsert rating_avg and rating_count.
- expire_unpaid_orders: runs cron 5m. Guard: cancels pending_payment orders older than 30 min and releases nothing else; idempotent by status predicate.
- enforce_acceptance_deadlines: runs cron 15m. Guard: orders past accept_by are auto-cancelled and refunded through the state machine, capped batch.
- rollup_seller_stats: runs cron hourly. Guard: recomputes today and yesterday only, upsert by (seller_id, day).
Add when the numbers say so
- Read replicas: Route search, listing detail and seller storefront reads to read replicas once primary CPU or search p95 crosses the trigger; money paths always read the primary.
- Search: Move to a dedicated search engine (OpenSearch/Typesense) with geo and learned ranking once Postgres search misses its latency budget.
Deferred until it is needed
| Deferred | Adopt it when |
|---|---|
| Dedicated search engine (OpenSearch/Typesense) with geo and learned ranking | > 5M listings or search p95 > 300 ms. |
| Read replicas for search and storefront reads | primary CPU > 60% sustained or read p95 > 200 ms. |
| Dedicated websocket tier for messaging and presence | > 5k concurrent open conversations. |
| Multi-currency presentment and cross-border payouts | first non-home-country market launch. |
| ML fraud scoring and personalised ranking | chargeback rate > 0.5% or > 100k monthly active buyers. |
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 marketplace?
- Not before a measured trigger fires. For example, for "Dedicated search engine (OpenSearch/Typesense) with geo and learned ranking": > 5M listings or search p95 > 300 ms.
- What performance targets should a marketplace aim for?
- Search results API: p95 < 250 ms at 1M published listings, one indexed query plus one cached facet read. Listing page LCP: < 2.5 s; TTFB < 200 ms from edge cache; images served as responsive widths. Order state transition: p99 < 100 ms, one compare-and-set update plus one event insert.
- 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