Skip zarr metadata consolidation by default (opt-in)#193
Conversation
espg
left a comment
There was a problem hiding this comment.
🤖 from Claude (review)
Fresh-context adversarial review of the opt-in-consolidation change. Verdict: clean — no must-fix / should-fix. One low-severity inline note on the read-parity test; everything else checked out.
What I verified (independently, not on faith):
-
Lambda gate correctness — clean. Traced
_finalize_fn→LambdaExecutor.finalize()(dispatch.py:390):finalize()callsself._finalize_fn(), discards its return, and returns a freshRunReport()that the caller also discards. So the no-op path dispatches nomode:"finalize"invoke, and nothing downstream keys off a real finalize — thefinalize_sbracket (runner.py:1524) still runs (~0 s), and cost/summary accounting readsreport.cost/ per-cell results, never the finalize result. This exactly mirrors the temporal path'sfinalize_fn=lambda: None(runner.py:722). Local path: workers already write withconsolidated=False, so gating the standaloneconsolidate_metadatacall still yields a valid store. Defaultfalseconfirmed end-to-end (config accessor → both backends). -
Store validity + reader parity — clean.
git grepacrosssrc/+ notebooks: no reader opens withconsolidated=True/use_consolidated; readers navigate to paths directly. The parity test assertsconsolidated_metadata is Noneon a fresh store, then byte-identical reads after consolidating the same store. See the one inline note: the store is a CSR-subgroup store rather than a literalShardingCodecstore — faithful for the consolidation claim (codec-independent), worth a clarifying comment. -
Third notebook — correct.
rasterized_zarr.ipynbreads the same regenerated published store; one-kwarg diff, valid JSON,nbformat_minor 4(no cell-id requirement — pre-existing, untouched). I also checked a fourthconsolidated=Truereader,bench/layout_access.ipynb: correctly left alone — its stores come frombench/layout_materialize.py:141, which always callsconsolidate_metadataitself, so that pair is self-consistent and unaffected by the config gate. -
Config validator — clean. Exact mirror of
output.aoi_mask(validator placed adjacent,boolaccessor withdefault=False). Non-bool rejected and tested.outputhas no allowed-keys whitelist to register the new key in, and no otheroutput.*reader needs to honor it. -
Test quality — solid.
TestConsolidationGatedrives the real_run_local/_run_lambdaand counts realconsolidate_metadata/_invoke_lambda_finalizecalls (0 off, 1 on) across both backends × both flag states — a dropped gate or re-enabled default would fail them; not tautological (both branches genuinely reachfinalize()). -
Conventions — clean. Title-only commits (
phase 1/2 of issue #191); Phase 3 deferred honestly with rationale; pre-existing N818/format drift + pyarrow collection gap correctly flagged-not-fixed.runner.pyis well over ~1000 lines but that's pre-existing (+23 here), not this PR's remit.
What I ran: uv run pytest tests/test_config.py::TestConsolidateMetadataFlag tests/test_runner.py::TestConsolidationGate tests/test_readers.py::TestReadParityWithoutConsolidation -q → 8 passed. Notebook JSON validated for all three (nbformat 4, one-line diffs, cell structure intact).
No side-effecting directives raised — the PR body's #188 merge-order note is already moot since the open_store rewire is present on main (base 5004df4); whichever the author lands, keep both the open_store call and consolidated=False.
Generated by Claude Code
Closes #191
What / approach
Metadata consolidation (
zarr.consolidate_metadata, run as a finalize step after every AOI dispatch) costs ~70 s/run and produces a consolidated-metadata blob that nothing in zagg's read path consults — readers navigate to specific paths in a few GETs, and the write path already passesconsolidated=Falseeverywhere. This makes consolidation opt-in via a newoutput.consolidate_metadataflag (defaultfalse), gated dispatcher-side so the finalize invoke is skipped entirely when off.src/zagg/config.py): addedoutput.consolidate_metadata(defaultfalse) with a boolean validator (mirrorsoutput.aoi_mask) and aget_consolidate_metadata()accessor (mirrorsget_aoi_mask()).src/zagg/runner.py_run_local): theconsolidate_metadata(zarr_store, zarr_format=3)call now runs only when the flag is true._run_lambda): themode: "finalize"invoke is gated dispatcher-side — when the flag is off the executor gets a no-opfinalize_fn(mirroring the temporal path_run_lambda_events, which already skips finalize because tabular output has no metadata to consolidate), so no finalize Lambda is dispatched. When on,_invoke_lambda_finalizeruns byte-identically to before. Themode: "finalize"handler indeployment/aws/lambda_handler.pyis unchanged — it still consolidates when invoked (which now only happens on the opt-in path), so it needed no edit.Behavior when the flag is true is identical to the pre-change path (still consolidates, both backends).
Notebooks
The example notebooks read the published
atl06_cycle22_fullsphere.zarrstore withxr.open_dataset(..., consolidated=True). Since that store will be regenerated without consolidation (opt-out, no backward-compat needed), those reads are flipped toconsolidated=False.consolidated=Falseis a safe v3 read regardless (readers do lazy metadata reads natively). Edited via a surgical JSON round-trip (indent=1, ensure_ascii=False— verified identical to the repo's on-disk formatting), so each notebook has a one-line diff and cell ids/outputs are untouched.Phases
output.consolidate_metadataconfig flag (config accessor/validator; local + Lambda dispatcher-side gate).consolidated=False+ read-parity test.How it was tested
uv run pytest -q(excluding two pre-existingpyarrow-gated modules — see below): 1443 passed, 20 skipped in 401 s.test_readers test_config test_runner test_output): 419 passed.uv run ruff checkanduv run ruff format --checkon every touched file: clean.New tests:
tests/test_config.py::TestConsolidateMetadataFlag— accessor default off, accessor true, validator rejects non-bool.tests/test_runner.py::TestConsolidationGate— local runner skipsconsolidate_metadataby default and calls it when enabled; Lambda dispatcher skips_invoke_lambda_finalizeby default and calls it when enabled.tests/test_readers.py::TestReadParityWithoutConsolidation— builds a two-shard located t-digest CSR store with no consolidated metadata (the default now), assertsread_tensors/read_raw_values/read_locationsreach the data, then consolidates the same store and asserts every reader returns byte-identical results. Also pins that a freshly written store carries no consolidated-metadata blob (root.metadata.consolidated_metadata is None) yet is fully navigable.Questions for review
notebooks/rasterized_zarr.ipynbreads the same regenerated store withconsolidated=True, so it's flipped too (a one-line kwarg change), otherwise it would break against the non-consolidated store. Call out if you'd rather scope this PR to only the two the issue enumerated.zarr==3.2.1'sconsolidate_metadata(store, path, zarr_format)exposes no concurrency parameter; the only lever is the globalzarr.config['async.concurrency'](default 10), whose effect on the ~4,102-object serial-GET path can't be verified without the production store the issue measured against. Per the issue's guidance ("if it doesn't expose concurrency cleanly, don't fight it — note the finding and defer; do not add a dependency"), it's deferred to Parallelize the opt-in metadata-consolidation path (deferred from #191/PR #193) #194. The opt-in path is off by default, so it's not a default foot-gun.ruff check src testsreports 1 pre-existingN818insrc/zagg/registry.py(UnknownCapability), andruff format --check src testswould reformat 7 files I did not touch (src/zagg/__init__.py,src/zagg/__main__.py,src/zagg/catalog/sources.py,src/zagg/viz/crs.py,tests/test_catalog.py,tests/test_extract.py,tests/test_integration.py). All of my touched files are clean.tests/test_shardmap.pyandtests/test_viz.pyfail to collect underuv sync --extra testbecause theyimport pyarrow, which thetestextra doesn't install (present onorigin/maintoo). Excluded from the run above; unrelated to this change.Note on the earlier #188 merge-order caveat: PR #188 has since merged to
main, and this branch is based on5004df4(post-merge), so the notebook cells already carry both theopen_storerewire andconsolidated=False— no rebase needed.