@@ -289,13 +289,108 @@ After EC2 has written shards to R2:
289289
290290## 6. Things NOT to do on EC2
291291
292- - Don't push to ` h:main ` (Github). Push to ` e: ` (EC2-mediator remote)
293- only. CI deploy on ` h ` would break due to ` workspace:* ` pyrmts deps.
292+ - ~~ Don't push to ` h:main ` (Github). Push to ` e: ` (EC2-mediator remote)
293+ only. CI deploy on ` h ` would break due to ` workspace:* ` pyrmts deps.~~
294+ No longer applies (2026-05-25): pyrmts has a ` dist ` branch on GitHub
295+ and ` gbfs/api/ ` is on ` pds gh ` refs, so ` git push h main ` triggers a
296+ clean CI deploy. Push to ` h: ` freely.
294297- Don't run the live CFW workers from EC2. Keep ` wrangler deploy ` on
295298 the laptop (or skip it entirely until the build is verified).
296299- Don't delete any of the legacy/cascade/PoC data on R2 — keep through
297300 the validation period.
298301
302+ ## 7. Rebuild: parquet layout for read-side pruning (EC2, follow-up)
303+
304+ > Added 2026-05-25, after CFW OOMs surfaced on ` /api/avail-v2[/cells] `
305+ > sub-day queries.
306+
307+ ### Diagnosis
308+
309+ ` ctbk avail-v2-build ` wrote shards with pyarrow defaults:
310+ - ` num_row_groups = 1 ` per shard (default ` row_group_size=1M ` ; v2 1h
311+ shards have ~ 956K rows, so they fit in a single RG).
312+ - Rows sorted ` (cell, dt_sec) ` (see ` avail_v2.py:272 ` ).
313+
314+ Together this defeats hyparquet's row-group ` dt ` -stats pruning:
315+ even when the CFW reader passes ` binCol='dt' + range=… ` (now wired in
316+ ` avail_geo.ts ` + ` avail_pyrmts.ts ` ), there's only one RG, and its
317+ ` dt ` -stats span the entire shard period. So every query loads the
318+ whole decompressed shard (~ 130 MB for v2 1h) → blows the 128 MB CFW
319+ free-tier memory limit on any sub-day query.
320+
321+ This is the unaddressed half of pyrmts SPEC.md "Open questions" §
322+ ("Row-group sizing" + "Sort order"). The reader has the pruning
323+ primitive; the writer doesn't yet have a convention or helper.
324+
325+ ### Changes
326+
327+ In ` ctbk/avail_v2.py ` :
328+
329+ 1 . ** Resort rows by ` (dt, cell) ` ** instead of ` (cell, dt) ` . h3's
330+ resolution-in-high-bits property is nice-to-have for resolution
331+ pruning but pyrmts doesn't currently do that pruning, so dt-first
332+ is the right primary key for now. (Lines 272 + 457; both shard
333+ writers.)
334+ 2 . ** Set ` row_group_size ` ** in the ` pq.write_table(...) ` call (line
335+ 296). Start with ` 8192 ` ; tune if needed. Goal: each RG covers a
336+ narrow ` dt ` window so stats prune effectively.
337+ 3 . (Optional, defer if it's not necessary for the OOM fix) ** Sort
338+ monoid output similarly** in the cascade step — if cascaded tiers
339+ inherit the input shape this happens for free, otherwise mirror
340+ the change.
341+
342+ ### Rebuild driver
343+
344+ All built (tier × period) pairs need re-write. From EC2:
345+
346+ ``` bash
347+ # Idempotent re-runs are fine — they overwrite. Order doesn't matter
348+ # semantically but build finest tiers first so cascade has its inputs.
349+ ctbk avail-v2-build --tier 1m --force # ~521 shards across 49 dates
350+ # Then cascade up
351+ ctbk avail-v2-build --tier 2m --derive-from 1m --force
352+ # … continue through the ladder up to 1y
353+ ```
354+
355+ (If ` --force ` doesn't exist yet on ` avail-v2-build ` , add it — flag to
356+ overwrite existing R2 objects rather than skip-if-exists.)
357+
358+ Total rebuild scope: 18 tiers × shards-per-tier. Embarrassingly
359+ parallel per (tier, period); current 1m build took ~ hours on EC2,
360+ expect similar.
361+
362+ ### Validation
363+
364+ After rebuild, the OOM-reproducer should now succeed:
365+
366+ ``` bash
367+ curl -A ' ctbk-probe' \
368+ " https://ctbk-gbfs-api.ryan-0dc.workers.dev/api/avail-v2?from=2026-05-22T00:00:00Z&to=2026-05-23T00:00:00Z&bbox=40.74,-74.00,40.78,-73.96&bin_budget=24&cell_budget=200"
369+ ```
370+
371+ Expect 200 + small JSON body (~ few KB). If still OOMs: inspect the
372+ shard's ` pqm ` output — ` num_row_groups ` should be hundreds, each
373+ row group's ` dt ` stats should span minutes-to-low-hours, not the
374+ full shard period.
375+
376+ ### Note: read-side patches already landed
377+
378+ - ` gbfs/api/src/avail_geo.ts ` threads ` { binCol, range } ` to
379+ ` fetchSegmentRows ` (commit ` 7ddb7104 ` ).
380+ - ` gbfs/api/src/avail_pyrmts.ts ` (shadow mode) threads the same opts
381+ to ` fetchShardData ` (next commit).
382+
383+ Both are no-ops until this rebuild lands; once it does, they take
384+ effect without further CFW changes.
385+
386+ ### Upstream pyrmts follow-up
387+
388+ Open SPEC.md questions §321/§323 ("Row-group sizing", "Sort order")
389+ become tractable now that ctbk has a real layout pin. Worth proposing
390+ a ` pyrmts.write_tier_parquet(rows, pyramid, …) ` helper that handles
391+ RG sizing + binCol-first sort by default, so future consumers don't
392+ hit this trap.
393+
299394## Open
300395
301396- Histogram column format: ` pa.json_extension_type() ` (pyarrow ≥18)
0 commit comments