Skip to content

Make v2→v3 re-encode safer: prefix-safe omit_nodes and lower peak memory#1

Closed
wietzesuijker wants to merge 2 commits into
d-v-b:perf/direct-zarr-iofrom
wietzesuijker:pr-92
Closed

Make v2→v3 re-encode safer: prefix-safe omit_nodes and lower peak memory#1
wietzesuijker wants to merge 2 commits into
d-v-b:perf/direct-zarr-iofrom
wietzesuijker:pr-92

Conversation

@wietzesuijker

Copy link
Copy Markdown

Aims to make the v2→v3 re-encode path a bit safer and easier to operate, without changing its intent (still a re-encode, not a raw-bytes copy). The changes focus on reducing memory spikes during copying, making “omit” behavior reproducible, and avoiding private APIs that could break on dependency upgrades.

What changed (and why):

  • omit_nodes is normalized and subtree-safe (in zarrio.reencode_group) so foo won’t accidentally match foobar; the normalized omit list is recorded in output root attrs (_eopf_geozarr.omit_nodes) to make results easier to audit and reproduce.
  • Array copying is now chunk-by-chunk (driven by the destination chunk grid) instead of new_array[...] = old_array[...], which lowers peak memory and is typically friendlier to object stores.
  • The S2 converter no longer imports private xarray/zarr helpers; it uses small local routines for time-unit mapping and chunk/shard sizing to reduce “upgrade surprise” risk.
  • add_multiscales_metadata_to_parent is annotated as DataTree | None, matching its real skip behavior.

@d-v-b, hope this is somewhat helpful / feel free to cherry-pick or discard

@d-v-b d-v-b changed the base branch from main to perf/direct-zarr-io December 16, 2025 14:18
Comment thread src/eopf_geozarr/zarrio.py Outdated

chunk_shape = tuple(new_array.metadata.chunk_grid.chunk_shape)
# Iterate using the destination chunk grid to bound peak memory.
regions_iter = _iter_chunk_regions(tuple(new_array.shape), chunk_shape)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can use the _iter_shard_regions method defined on the new array, it's private API in zarr but it's not likely to change

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we don't want to depend on private API and instead want to use our own version, we need to implement tests

@d-v-b

d-v-b commented Dec 16, 2025

Copy link
Copy Markdown
Owner

hi @wietzesuijker thanks for this!

@codecov-commenter

Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

Thanks for integrating Codecov - We've got you covered ☂️

Comment thread src/eopf_geozarr/zarrio.py Outdated
existing = root_attrs.get("_eopf_geozarr")
eopf_meta: dict[str, object] = dict(existing) if isinstance(existing, dict) else {}
eopf_meta["omit_nodes"] = sorted(omit_nodes)
root_attrs["_eopf_geozarr"] = eopf_meta

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to store the names of missing groups as metadata?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asking because, presuming we have access to the original data, we can find the names of omitted groups by checking against the original data

@d-v-b

d-v-b commented Dec 16, 2025

Copy link
Copy Markdown
Owner
  • Array copying is now chunk-by-chunk (driven by the destination chunk grid) instead of new_array[...] = old_array[...], which lowers peak memory and is typically friendlier to object stores.

this will use extra IO if the source chunks are misaligned to the destination chunks, because the same source chunk will be read multiple times. But if the source array fits in memory, eagerly fetching it reads each chunk exactly once, so I think that's a better approach until we have an actual memory budget problem.

@wietzesuijker wietzesuijker force-pushed the pr-92 branch 8 times, most recently from 04a3290 to da6a42d Compare December 16, 2025 17:55
@wietzesuijker wietzesuijker changed the title Make omit_nodes reproducible, reduce v2→v3 conversion peak memory, and remove private API dependencies Make omit_nodes reproducible and reduce v2→v3 conversion peak memory Dec 16, 2025
@wietzesuijker wietzesuijker changed the title Make omit_nodes reproducible and reduce v2→v3 conversion peak memory Make v2→v3 re-encode safer: prefix-safe omit_nodes and lower peak memory Dec 16, 2025
# If chunking differs, writing by destination chunk regions can re-read source chunks.
# Prefer eager copy for smaller arrays to minimize IO; fall back to chunk-by-chunk when
# the array is too large to comfortably materialize.
eager_copy_max_bytes = 256 * 1024 * 1024

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i do not understand the reasoning for this optimization. what memory problem are we solving here?

@wietzesuijker

Copy link
Copy Markdown
Author

Sorry let's discard

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants