End-to-end sponsor example: catalog → shardmap → dual aggregation → cached reads#169
End-to-end sponsor example: catalog → shardmap → dual aggregation → cached reads#169espg wants to merge 8 commits into
Conversation
| "cat_path = DATA / \"cat_serc_atl03_007.parquet\"\n", | ||
| "catalog.to_geoparquet(str(cat_path))\n", | ||
| "\n", | ||
| "# Round-trip: later runs (and the drift tests) load the committed snapshot\n", |
There was a problem hiding this comment.
🤖 from Claude (review)
This comment claims drift tests load the committed snapshot, but no such test exists in this PR — nothing under tests/ references cat_serc_atl03_007.parquet, sm_serc_healpix_o10.json, or the two new configs (git grep serc -- tests/ is empty). More broadly, the two configs now shipped in the package (src/zagg/configs/atl03_{tdigest,gain_bias}_serc.yaml, loaded via importlib.resources in cell 6) have no committed validation coverage — test_config.py only checks named configs (atl06 / merra2_storm / atl03_waveform_chunk), not a glob over zagg.configs. The PR's "both configs load_config + validate_index_config clean" was verified manually only. Per CLAUDE.md §4, either add a small test asserting both configs load + validate_index_config clean and that their spatial_signature() match (the #89 reuse contract the notebook asserts), or soften this comment's present-tense "drift tests" claim.
There was a problem hiding this comment.
🤖 from Claude
Folded in de2c5f5: TestSercExampleConfigs (tests/test_config.py) now covers both shipped configs — load/validate, index-block validation, the issue #89 shared-spatial-signature contract, and the aoi_mask + location-channel declarations. The "drift tests" wording is softened to "later runs" in the next notebook regeneration (phase 2 re-executes the notebook top-to-bottom, so prose and outputs move together).
| "**Requirements.** `pip install \"zagg[analysis,catalog,viz]\"` (zagg ≥ 0.13; the\n", | ||
| "Binder image already provides it via the repo's `.binder/` environment).\n", | ||
| "Sections 1–3 run **anywhere, with no credentials** — CMR granule *metadata* is\n", | ||
| "anonymous. The aggregation runs in section 5 read ATL03 granule *data* and write\n", |
There was a problem hiding this comment.
🤖 from Claude (review)
Section-numbering is internally inconsistent. The overview list at the top of this cell numbers the pipeline 1. Catalog, 2. Shardmap, 3. Two aggregations, 4. Read-back — but this sentence (and cell 5's "which is exactly what section 5 does") sends the reader to section 5 for the aggregation runs, and the config walkthrough is headed ## 3. The two run configs. So aggregations are overview-step 3 yet "section 5", and no ## 4/## 5 headers exist in this phase-1 notebook, so "section 5" currently points nowhere. Either renumber the overview to match the eventual section headers or drop the concrete "section 5" pointers until those sections land (phases 2-3).
There was a problem hiding this comment.
🤖 from Claude
Agreed — the intro list is being renumbered to match the actual section headers (catalog 1, shardmap 2, configs 3, dispatch 4, execute 5, read-back 6) in the phase-2/3 regeneration, which adds the missing sections 4–7.
|
🤖 from Claude Emergency stop (machine shutdown) — state preserved. Exactly where things stand: Done / committed (branch
Local state that survives reboot (home dir, not committed by design):
Lost on reboot (scratch tmp): the venv (rebuild: Bucket: write access verified with the Precise next actions for a resumer:
|
…aude/168-sponsor-example
|
🤖 from Claude Paused before the production dispatch — two items need espg: Direction received mid-run: the notebook's executed aggregations should be real Lambda dispatches against the deployed 0.14.0 fleet (writing directly to Blocked on:
Also noting for the dispatch attempt when it happens: if worker writes to Nothing has been published to the bucket. Branch state: phase 2 committed at 9ab82f2 (executed local run); self-review pass in flight. |
| "metadata": {}, | ||
| "execution_count": null, | ||
| "outputs": [], | ||
| "source": "from io import BytesIO\n\nimport obstore\nimport pandas as pd\n\nidx_store = S3Store(BUCKET, prefix=\"zagg-index/ATL03/007\", region=REGION,\n skip_signature=True)\nlisting = obstore.list(idx_store).collect()\nprint(f\"{len(listing)} granule manifests published; first three:\")\nfor meta in listing[:3]:\n print(f\" {meta['path']} ({meta['size'] / 1024:.0f} KB)\")\n\ngranule_id = shardmap.granules[0][0][\"id\"]\nbuf = obstore.get(idx_store, f\"{granule_id}.parquet\").bytes()\nmf = pd.read_parquet(BytesIO(bytes(buf)))\nprint(f\"\\n{granule_id}: {len(mf)} chunk records\")\nmf.head()", |
There was a problem hiding this comment.
🤖 from Claude (review)
Manifest fetch will 404 when this notebook runs against the published bucket. shardmap.granules[0][0]["id"] is the full granule filename with the .h5 suffix — the committed sm_serc_healpix_o10.json stores "id": "ATL03_20190105163308_01260202_007_01.h5" — but write-back names each manifest by the granule-URL stem: src/zagg/index/inline.py:424 does PurePosixPath(urlsplit(granule_url).path).stem, so the object is ATL03_..._007_01.parquet (no .h5; confirmed by the end-to-end notebook's cell-19 output, which lists ATL03_20190105163308_01260202_007_01.parquet). As written, f"{granule_id}.parquet" requests ATL03_..._007_01.h5.parquet, which does not exist, so this cell errors. Strip the suffix, e.g. granule_id = Path(shardmap.granules[0][0]["id"]).stem (or .removesuffix(".h5")).
There was a problem hiding this comment.
🤖 from Claude
Folded in 724aa47: the fetch key is now shardmap.granules[0][0]["id"].removesuffix(".h5") with a comment documenting the stem convention — matching inline.py write-back naming.
|
🤖 from Claude (review)
Version claim contradicts the cell it cites. This line says the committed run is "a dev build of the 0.14.0 line, as the version cell shows," but the version cell (cell 2) prints |
|
🤖 from Claude (review)
This notebook carries a Binder badge but is not Binder-runnable end to end. §5 reads an ~88 GB local granule cache under |
|
🤖 from Claude Review findings 2 and 3 folded in 724aa47, in the restructured (fleet-dispatch) notebook:
|
|
🤖 from Claude Run complete — ball is with espg (label
Next session, after the live run: execute |
|
first run of tdigest (create sidecar files): Using it, same call but with sidecar backend: |
|
🤖 from Claude Sidecar-cache A/B/C benchmark (SERC 9-shard fleet, 2026-07-06 ~19:30–20:30 UTC)Method: backed up + cleared
Full per-worker tables + raw status JSONs retained locally; stores at Refs: #170 (read-path fix, planned + in progress), #171 / PR #172 (force-cold dispatch), the warm-container forensics earlier in this thread. |
|
🤖 from Claude This run: attempted the anonymous execution of
One-pass unblock — with these done, the next routine run executes and commits notebook B with no further input: (a) grant the execution role write_back-403 hardening (from the benchmark comment): deferred as out of this PR's scope. The swallow is core worker code — On the two failed sidecar shards at 19:08: the first, Branch unchanged at 724aa47; label stays Generated by Claude Code |
|
🤖 from Claude Pushed 5f047f6: Rerun note for the pre-run checklist: this changes the published store layout (one sharded object per array/shard instead of K chunk objects), so the next fleet run should |
Closes #168. (espg-directed, in-session.)
The sponsor-facing end-to-end example: two notebooks plus the pipeline behind them, on real public data — ICESat-2 ATL03 v007 over the NEON SERC AOP box (
tests/data/benchmark/AOP_NEON.geojson, temporal pin2018-10-13 .. 2025-06-01, matchingtests/data/benchmark/README.md).What this adds
notebooks/sponsor_end_to_end.ipynb— the full pipeline as fleet dispatches: CMR → STAC-geoparquet catalog (build-once, add 88 south stress test shard + benchmark #148/88S stress shard: pins, 900s timeout, chunk-boundary extraction, a-priori read plan, streaming tdigest (issue #148) #152 precedent), shardmap with the strict-AOI mask payload (Optional strict-AOI polygon clip for shardmap output (sub-shard clipping) #101) + viewer, then two production Lambda dispatches from the one shardmap (Reuse shardmaps across configs with different aggregators (relax signature guard to spatial-only) #89): located t-digest (High-resolution morton location channel for ragged/CSR fields #87 / PR High-resolution morton location channel for ragged/CSR fields #165) withinline+write_backpopulating the granule-keyed manifest cache underzagg-index/ATL03/007/(Virtual index entry point: pluggable chunk-index backends for the read path #160 / PR Virtual chunk-index backends for the read path (issue #160) #163), and gain/bias 128-bin waveforms consuming that cache through thesidecarbackend (h5coro-hidefix 0.2.0,zagg.index_backendsentry point). Read-back (§5) uses unsigned S3 reads of the published stores: density + strict-AOI maps, digest-quantile maps, an exact digest-vs-waveform cross-check, the morton location channel. Committed unexecuted by design — the live run is espg's architecture test of the 0.14.0 deployment (division of labor below).notebooks/sponsor_read_only.ipynb— the Binder-badged, zero-credential companion (anonymous store reads + a sidecar-manifest fetch). Committed unexecuted with a status note: it can only run once the fleet run populates the bucket.src/zagg/configs/atl03_tdigest_serc.yaml/atl03_gain_bias_serc.yaml— the two run configs (shared o10 spatial signature → one shardmap;output.aoi_mask: true;data_source.indexper Virtual index entry point: pluggable chunk-index backends for the read path #160; canonicalsliderule-public-corsstore URLs), guarded bytests/test_config.py::TestSercExampleConfigs.notebooks/data/— committed build-once snapshots:cat_serc_atl03_007.parquet(55 KB, 59 granules),sm_serc_healpix_o10.json(232 KB, 9 shards / 326 pairs,aoi_maskpayload, NSIDC S3 hrefs — fleet-ready as committed).Division of labor: what is already proven vs what the live run tests
Every code path in notebook A was pre-validated in-process on this exact shardmap before commit (same worker code via
backend="local", h5coroFileDriverover a complete 61-granule local cache):sidecar675 s wall at 4 local workers; per-shard worker times ~50–250 s);sidecarconsumed the 59 write-back manifests and its output was byte-identical to aninlinererun (waveform_counts,gain_hspot-check) withtotal_obsequality asserted across the pair;The reference stores are retained at
~/.cache/zagg/serc_example/for post-run comparison. So espg's live run exercises the Lambda/deployment layer specifically — dispatch, layer contents, worker IAM, store/manifest writes from the execution role — not untested notebook code.How espg runs the live test (pre-run checklist)
aws sso login --profile nasa(+export AWS_PROFILE=nasa), and EDL~/.netrcpresent.s3://sliderule-public-cors/zagg-index/ATL03/007/— the Virtual index entry point: pluggable chunk-index backends for the read path #160/Virtual chunk-index backends for the read path (issue #160) #163 execution-rolePutObjectgrant onzagg-index/*(commit 87b941e granted it in-template) must be in the deployed stack. If run 1 errors withAccessDeniedonzagg-index/*, the stack needs the update — pause there.process-shardwas last modified 2026-07-06 04:02 UTC with layerprocess-shard-deps:15(the 0.14.0 + h5coro-hidefix 0.2.0 rollout) — assumed current.jupyter nbconvert --to notebook --execute --inplace notebooks/sponsor_end_to_end.ipynb, or Run-All). §4's final cell prints per-worker min/median/max durations from the async result objects — the architecture-test numbers.Statically verified on the dispatch cells (could not be invoked from this session)
agg()kwargs against the live signature; both configsload_config+validate_configclean; the in-notebooksidecarindex override passesvalidate_index_configwith the plugin installed andsidecaris entry-point-discoverable; store/index URLs match the canonical prefixes; the committed shardmap carries NSIDC S3 hrefs (fleet-ready), 9 shards,aoi_maskpayload, and a spatial signature equal to both configs' grids. Not verifiable statically: the worker-side IAM grant (checklist item 2), the async result-object polling against the deployed worker, and live NSIDC credential exchange.Phases
How it was tested
TestSercExampleConfigs; localpre-commit(repo-pinned ruff/mypy/codespell) green on the changed files;pytest tests/test_config.py tests/test_index.py267 passed on the merged branch (with h5coro-hidefix 0.2.0 installed, exercising the entry-point discovery path).ast.parses, zero outputs, stable cell ids. Notebook A's earlier fully-executed state (local backend) is preserved in branch history at 9ab82f2 for reference.Questions for review
waveform_countsis emitted uncompressed (codecs: [bytes]): the gain/bias store will be ~1.1 GB in the bucket (568 × 2 MB raw chunks; measured on the reference run). Fine to host, but a compressed template codec would cut it ~10× — follow-up issue?