Skip to content

Chunk-range temporal reads via hidefix (drop whole-granule fetch, eventually h5py)Β #222

Description

@espg

πŸ€– from Claude

Follow-up to #220/#221 (which put xarray + h5netcdf + h5py in the Lambda layer to unblock the temporal worker): replace the temporal reader's whole-granule fetch with chunk-range reads, most likely via hidefix β€” which would also let h5py/libhdf5 leave the layer again.

Why

zagg.temporal.open_dataset's non-.zarr S3 branch GETs the entire object into memory before opening (obstore.get(...).bytes() β†’ xr.open_dataset(BytesIO)). Correct, but each temporal worker pulls full MERRA-2 daily granules (hundreds of MB) to read one storm's bbox sliver. The pre-zagg orchestrator (jbbutler/antarctic_AR_dataset#1) averaged ~7 MB/storm with range reads; the first post-#221 slice run will measure what whole-file fetching costs by comparison β€” that number sizes the payoff here.

What hidefix already provides (verified against the local checkout, v0.13.0)

  • src/reader/s3.rs: async chunk-range S3 reader β€” ranged GETs, up to 16 concurrent, adjacent chunks coalesced into single requests, any S3-compatible endpoint with caller-supplied credentials. Exactly the read model needed.
  • Codecs: gzip/deflate + shuffle β€” covers MERRA-2 .nc4.
  • Serializable chunk indexes β€” the same machinery zagg's spatial sidecar index backend already consumes through h5coro-hidefix.

The gaps (all in the Python/xarray glue, python/hidefix/xarray.py)

  1. The xarray entrypoint is local-path only; the S3 reader is unreachable from engine="hidefix".
  2. No prebuilt-index injection β€” upstream has the exact TODO in place: # TODO: allow take an existing index, maybe from a serialized object.
  3. HidefixDataStore opens netCDF4.Dataset alongside the hidefix index for metadata/attributes β€” adopting the engine as shipped would put libnetcdf and libhdf5 back in the layer (heavier than the h5py it replaces).

Two routes (design fork β€” hence discuss)

A. Upstream the glue (three well-scoped PRs to gauteh/hidefix): accept (s3 url, serialized index) in the engine; implement the serialized-index TODO; derive metadata from hidefix's own index instead of netCDF4-python. Cleanest long-term (one engine, everyone benefits); waits on upstream review.

B. zagg-side adapter: a thin temporal reader over the Rust S3Reader β€” per-granule chunk indexes built orchestrator-side and distributed alongside the granule URLs, workers do coalesced range GETs. Mirrors the spatial sidecar design one-to-one; waits on nobody; adds a zagg-owned read path to maintain.

A hybrid is plausible: (B) now behind the READERS registry (reader: hidefix_s3), fold into (A) as upstream lands.

Open questions

  1. Route A, B, or hybrid?
  2. Index placement: per-granule chunk-index sidecars hung off the temporal granule catalog (the downstream repo's phase-B index is the natural host) vs. inline in the event payload?
  3. Payoff gate: wait for the slice-run bytes-per-storm measurement before committing, or proceed on the PR-1 ~7 MB/storm prior?
  4. Layer end-state: once chunk-range reads land, do h5py/h5netcdf leave the layer (mask files could also ship as indexed NetCDF or Zarr), or stay as the fallback engine?

Refs: #220, #221 (layer state + measured sizes), #218/#219 (shared 250 MB budget; async-tiff precedent for lean Rust readers on the fleet), gauteh/hidefix (S3Reader + the serialized-index TODO).


Amendments (maintainer-ratified; see thread)

  • Route A ratified β€” three upstream PRs to gauteh/hidefix: (1) wire the merged S3Reader (Add reader for S3 (picks up #26)Β gauteh/hidefix#47 β€” Rust-level only today) through the Python bindings + xarray entrypoint; (2) implement the serialized-index TODO; (3) capture attributes/dimension metadata in the Rust index so the engine drops netCDF4-python entirely (it is a metadata stand-in, not a requirement). Route B is dropped β€” it was the h5coro-hidefix spatial pattern cloned onto the temporal path, and serves only as design precedent now.
  • Index placement ratified β€” mirror the spatial pattern: inline and sidecar options through zagg's existing index-backend mechanism and config templates.
  • Remaining open question: the payoff gate (question 3) β€” the post-Add xarray/h5netcdf/h5py to the Lambda layer (issue #220)Β #221 slice run's bytes-per-storm measurement lands soon either way.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions