Sharded output (zarr ShardingCodec)¶
zagg can bundle a dispatch shard's inner read-chunks into one zarr
ShardingCodec shard object instead of writing them as many independent
regular chunk objects. This decouples the write/dispatch granularity (the
unit a Lambda worker processes) from the read granularity (the small chunk a
reader partial-decodes), without drowning the reader in millions of tiny objects
at global/dense scale.
It builds directly on chunk_inner — it changes only the storage form of the
inner chunks, not the grid geometry. For HEALPix grids it is the default
whenever chunk_inner gives K > 1, on both store layouts (issue #215 flat,
issue #236 hive); an explicit sharded: false opts out. For rectilinear
grids it stays opt-in.
How it relates to chunk_inner¶
chunk_inner sets the geometry: the shard is the dispatch unit (HEALPix
parent_order; rectilinear chunk_shape), the inner chunk is the smaller
read chunk (HEALPix chunk_inner order, e.g. 13 = a 64×64 = 4096-cell chunk;
rectilinear chunk_inner = [inner_h, inner_w]), and one shard owns
K = (shard cells) / (inner-chunk cells) inner chunks.
sharded only picks how those K inner chunks are stored:
sharded: false |
sharded: true (HEALPix default at K > 1) |
|
|---|---|---|
| storage | K regular chunk objects per shard |
1 shard object per shard |
| object count | high (empties absent) | low (~K× fewer) |
| sub-shard sparsity | absent objects | shard-index entries inside the object |
| reader 64×64 access | one object per chunk | byte-range partial decode within the shard |
With zarr-shard == dispatch-shard, each worker writes exactly one whole shard
object, alone — the canonical single-writer pattern: no read-modify-write, no
cross-worker contention. Empty inner chunks (e.g. a track corridor crossing only
part of a shard) are omitted from the shard index, so sub-shard sparsity is
preserved inside the object.
Config¶
The knob lives on the grid/chunk block, next to chunk_inner:
# HEALPix
output:
grid:
type: healpix
parent_order: 8 # dispatch shard
child_order: 19 # leaf cell order
chunk_inner: 13 # inner read chunk (64×64 = 4096 cells) -> K = 4^(13-8) = 1024
sharded: true
# Rectilinear
output:
grid:
type: rectilinear
crs: EPSG:3031
resolution: 5000
bounds: [-3200000, -3200000, 3200000, 3200000]
chunk_shape: [256, 256] # dispatch shard tile
chunk_inner: [64, 64] # inner read chunk -> K = (256/64)^2 = 16
sharded: true
sharded only does something when chunk_inner makes K > 1 (a shard with
more than one inner chunk). At K == 1 there is nothing to bundle:
- HEALPix silently no-ops it (issue #215) — since
shardedis the default, a single-chunk grid must not fail at construction. An explicitsharded: trueatK == 1likewise validates and no-ops: the output is byte-identical to the unsharded write (one chunk == one object either way). - Rectilinear (where
shardedis opt-in) rejectsK == 1at grid construction with a clear message — an explicit flag that cannot do anything is a config mistake there, not a default to tolerate.
Storage layout¶
- The dense per-cell arrays (
<group>/<varname>) carry asharding_indexedcodec: the outer chunk is the whole shard, the inner chunk is the read chunk. Inner codecs stay bytes-only/uncompressed (zagg's policy), so the on-disk bytes for a populated inner chunk are identical to the regular path. resolution: chunkcompanion arrays and (HEALPix) the 1-D coordinate / (rect) the 1-Dx/yarrays are not sharded — they keep their regular layout.- The worker writes the whole shard in one
set_block_selectionper dense array (block selection is shard-granular on a sharded array), so a single shard object is produced per dispatch shard. - Under the hive store layout (issue #236) the same applies inside each
leaf: every dense per-cell array (and each ragged field's vlen array, issue
#209) is ONE
ShardingCodecobject spanning the whole leaf, written at leaf block 0 — hive output is byte-identical to the flat sharded shard region.
Reader note¶
This is currently a writer-side feature plus offline round-trip read-back.
Consuming sharded stores from the higher-level read helpers (shard-index /
byte-range reads instead of list_prefix enumeration) is tracked as a
follow-up; the underlying zarr partial-decode of a 64×64 chunk within a shard
already works through the standard zarr array API.