Riders request trips. Drivers move around Barcelona. The server matches them in real time using Uber's H3 hex grid — but stored as plain bigints in vanilla Postgres.
Payments run through a stubbed Stripe-style provider with idempotent webhooks enforced at the schema level.
H3 has 16 levels (0–15). Each level subdivides each hex into ~7 children. Pick two: one for matching, one for analytics.
Both computed in app code from the same (lat, lng). Stored as bigint columns on every spatial table.
An H3 cell ID is just a 64-bit integer. So “find drivers near me” becomes a btree index lookup — not a geometry operation.
No ST_DWithin. No GIST. No PostGIS. Just bigint = bigint, indexed.
H3 columns at both res 9 and res 7 on every spatial table — drivers, trips, pings, events. Computed in app, written once, indexed twice.
WITH candidate_hexes AS (
SELECT unnest($1::bigint[]) AS h3_9
)
SELECT d.id
FROM drivers d
JOIN candidate_hexes c
ON d.current_h3_9 = c.h3_9
WHERE d.status = 'available'
ORDER BY d.last_seen_at DESC
LIMIT 1
FOR UPDATE SKIP LOCKED;
gridDisk(pickup, k=2) in JS — array of nearby hexes, ~350m radius.current_h3_9 uses the partial btree. Plan: Index Scan, never Seq Scan.status = available.SKIP LOCKED means concurrent matchers grab different drivers instead of blocking on the same row.ON CONFLICT DO NOTHING a one-line correctness proof.INSERT INTO payments (
id, trip_id, payment_method_id, amount_cents,
currency, status, provider, provider_payment_id, authorized_at
)
VALUES ($1, $2, $3, $4, $5,
'authorized', 'stripe', $6, now())
ON CONFLICT (provider_payment_id) DO UPDATE SET
status = EXCLUDED.status,
authorized_at = COALESCE(payments.authorized_at, EXCLUDED.authorized_at),
updated_at = now()
WHERE payments.status NOT IN
('captured', 'refunded', 'partially_refunded');
UNIQUE on provider_payment_id is what makes ON CONFLICT work. Without it, this is a plain insert.authorized webhook arriving after captured can't move state backward. State regression prevented in SQL.Every choice optimizes for “the report can defend this in one paragraph.”
No ORM, no PostGIS, no h3-pg. Every “no” is a story for the report.
docker compose up, simulator scripts seed 500 drivers walking around Barcelona.EXPLAIN (ANALYZE, BUFFERS) before/after each index, with measured deltas.