Repository navigation
feat(worker): serve trip plans from PID GTFS instead of Golemio - #56
Merged
Merged
Conversation
Every /api/trips/:tripId miss spent the shared Golemio budget (20 calls / 8 s) through the singleton gateway Durable Object, so a cold iOS launch took ~8 s to load ~20 plans, and the cost grew with every user. Trip plans are static GTFS data, and Golemio's trip endpoint is a view of PID's public feed. build:index now also builds every trip's plan into apps/web/public/data/trips/v1/<route>.json (one file per route prefix of the trip id, ~900 files, ~160 MB) with the same normalization as toTrip. The Worker reads them through the ASSETS binding, caches a few parsed files and many plans per isolate, and asks the gateway only for ids the deployed feed lacks. A daily scheduled deploy refreshes the data. Checked against production: every live vehicle's trip id is in the feed, and the built plans match /api/trips byte for byte. The HTTP contract is unchanged; the bucket format lives in packages/contract. Also points the integration suite at apps/*, which the move to apps/ left behind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
🚀 Preview deployed: https://preview-56.tablo.run |
# Conflicts: # apps/worker/src/api.ts # apps/worker/test/api.test.ts
Trip plans now have one source: the GTFS built into the deploy. The Golemio route that served them (fetchTrip, the gateway's trip cache and getTrip RPC, PidTripResponse, toTrip) only lingered as a fallback for ids the deployed feed lacks, which the daily rebuild and the feed's two weeks of lookahead make rare, so it goes. /api/trips/:tripId now answers 404 for an id the feed lacks, and 503 when its bucket can't be read or holds malformed data, so clients retry a data bug instead of treating it as a missing trip. The GTFS helpers move from golemio/normalize.ts into gtfs/buckets.ts, and the trip-plan tests build from GTFS rows instead of a Golemio fixture. The HTTP contract is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # apps/worker/src/golemio/schema.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Every
GET /api/trips/:tripIdmiss spent the one Golemio token's budget (20 calls / 8 s) through the singleton gateway Durable Object. A cold iOS launch took ~8 s to load ~20 vehicle plans, and that cost grows with every user, which matters for the public release. Trip plans are static timetable data, and Golemio's trip endpoint is just a view of PID's public GTFS feed.What
build:index(same download, one unzip) now also writes every trip's plan toapps/web/public/data/trips/v1/<route>.json. There is one file per route prefix of the trip id: ~900 files, ~160 MB, about 6 s and 2.3 GB of RAM. Output is deterministic, so routes that didn't change upload nothing.TripBucketV1,tripBucketKeyandtripFromBucketinpackages/contract/src/trip-bucket.ts. The builder is inapps/worker/src/gtfs/buckets.ts; it applies the normalizationtoTripdid (time parsing, ASW ids, rounding).ASSETS. It keeps up to 3 parsed files and 512 plans per isolate and validates each plan againstTripbefore serving it. Trips never reach Golemio or the gateway DO. An id the deployed feed lacks returns 404TripNotFound; an unreadable or malformed file returns 503UpstreamUnavailable, which isn't cached, so the next request retries.fetchTrip, the gateway's trip cache andgetTripRPC,PidTripResponse,toTripand the Golemio trip fixture). Trips have one source of truth. The GTFS helpers moved togtfs/buckets.ts, and the trip tests now build from GTFS rows.deploy.ymlalso runs daily at 03:30 UTC (after PID publishes the feed) and on manual dispatch.scripts/lib/csv.tsnow has a lazyiterCsvgenerator;parseCsvis unchanged.vitest.integration.config.tsnow points atapps/*. The move toapps/left it matching no files.docs/adr/0001-trip-plans-from-gtfs.mdexplains why I used static assets over R2, D1, KV or a Worker cron, anddocs/DEPLOY.mdis updated.The HTTP contract is unchanged (
contract:checkpasses, andwire-shape.jsonand the iOS fixtures didn't change)./api/trips/:id/vehicleand/api/vehiclesstill go to Golemio through the gateway.Verification
/api/tripsbyte for byte on all 19 trips I compared. The other requests in that sample got 503 because I had used up production's Golemio budget.bun run testpasses (140 tests).toTripexpectation: order, normalization, shape rounding and dedupe, time fallbacks.GOLEMIO_API_TOKEN=dummy bun run test:integrationpasses (4 tests). The new test gets a plan from local workerd with a dummy Golemio token, so the plan can only have come from the assets.typecheck,lint,fmt:check,build:webandverify:pwapass. No trip files end up in the PWA precache.Notes
🤖 Generated with Claude Code