You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-on to #279. Once the t-digest reduce loop is vectorized numpy (GIL-releasing) and the fold is a single order-independent k-way merge, the SpillAggregator reducer can be parallelized for another ~2β4Γ on heavy shards. This is an engine-level change to src/zagg/processing/spill.py (the reducer thread + the memory budget), not a user-side agg function β at most gated by a worker knob (e.g. streaming.reduce_workers: N). Like #279 it ships in the packaged zagg, so fleet use needs a layer redeploy; it is fully testable locally without one.
_close_block β _reduce_one on one threading.Thread; _join_reducer before the next close. So reads overlap one block's reduce, but blocks reduce strictly serially in close order, and _fold_block walks its partitions sequentially (spill.py:598-621).
Two flavors of parallelism
Flavor A β intra-block, across partitions (clean, no correctness change). _fold_block loops for key in block.partition_keys(). Partitions are disjoint morton cell-groups β within a block a cell lives in exactly one partition β so folding partitions concurrently touches disjoint cells. No associativity/order issue. Each partition worker returns its own {cell: digest} and the disjoint results are dict.update-merged single-threaded (avoid mutating shared self._digests/self._counts from N threads). Parallelizes both the partition read-back I/O and the (post-#279) numpy CPU. Realistic degree = Lambda vCPUs (~2 near 1.8 GB, up to 6 at 10 GB).
Flavor B β inter-block, parallel block reduces (rides on #279 Phase 3).
The same cell recurs in every block, so folding two blocks concurrently into shared self._digests[cell] is racy and order-dependent. With #279's k-way merge, each block reduces independently to its own per-block-per-cell digest (no shared state), then a single k-way merge per cell combines across blocks at the end β parallel-safe and block-order-invariant.
Central design question β the memory budget
_default_block_bytes / _BUILD_MULT (spill.py:344-367) are derived assuming one partition reduced at a time, at ~3Γ its spilled bytes, alongside the 25%-of-memory read buffer. P-way parallelism multiplies that peak working set by P β it would blow the Lambda memory ceiling unless the threshold is re-derived (shrink block_bytes by ~P, or gate the parallelism degree on measured free memory / vCPU count). This re-derivation + re-measuring the replay peaks (the #217 phase-3 slabs) is the substantive design work here, and it couples to the deployment memory/vCPU tier. This is why it is its own PR, not part of #279.
Proposed phasing (once unblocked)
Flavor A β parallelize _fold_block across partitions; per-partition workers return disjoint {cell: digest}; merge single-threaded. Memory-budget re-derivation as the gating design decision (measure peak vs _BUILD_MULT Γ P).
Parallelism degree: fixed knob (reduce_workers), or auto from os.cpu_count() / memory budget? Recommendation: auto-derive, capped, with an override knob.
π€ from Claude
Summary
Follow-on to #279. Once the t-digest reduce loop is vectorized numpy (GIL-releasing) and the fold is a single order-independent k-way merge, the
SpillAggregatorreducer can be parallelized for another ~2β4Γ on heavy shards. This is an engine-level change tosrc/zagg/processing/spill.py(the reducer thread + the memory budget), not a user-side agg function β at most gated by a worker knob (e.g.streaming.reduce_workers: N). Like #279 it ships in the packaged zagg, so fleet use needs a layer redeploy; it is fully testable locally without one.Blocked by #279 β specifically:
Current model
_close_blockβ_reduce_oneon onethreading.Thread;_join_reducerbefore the next close. So reads overlap one block's reduce, but blocks reduce strictly serially in close order, and_fold_blockwalks its partitions sequentially (spill.py:598-621).Two flavors of parallelism
Flavor A β intra-block, across partitions (clean, no correctness change).
_fold_blockloopsfor key in block.partition_keys(). Partitions are disjoint morton cell-groups β within a block a cell lives in exactly one partition β so folding partitions concurrently touches disjoint cells. No associativity/order issue. Each partition worker returns its own{cell: digest}and the disjoint results aredict.update-merged single-threaded (avoid mutating sharedself._digests/self._countsfrom N threads). Parallelizes both the partition read-back I/O and the (post-#279) numpy CPU. Realistic degree = Lambda vCPUs (~2 near 1.8 GB, up to 6 at 10 GB).Flavor B β inter-block, parallel block reduces (rides on #279 Phase 3).
The same cell recurs in every block, so folding two blocks concurrently into shared
self._digests[cell]is racy and order-dependent. With #279's k-way merge, each block reduces independently to its own per-block-per-cell digest (no shared state), then a single k-way merge per cell combines across blocks at the end β parallel-safe and block-order-invariant.Central design question β the memory budget
_default_block_bytes/_BUILD_MULT(spill.py:344-367) are derived assuming one partition reduced at a time, at ~3Γ its spilled bytes, alongside the 25%-of-memory read buffer. P-way parallelism multiplies that peak working set by P β it would blow the Lambda memory ceiling unless the threshold is re-derived (shrinkblock_bytesby ~P, or gate the parallelism degree on measured free memory / vCPU count). This re-derivation + re-measuring the replay peaks (the #217 phase-3 slabs) is the substantive design work here, and it couples to the deployment memory/vCPU tier. This is why it is its own PR, not part of #279.Proposed phasing (once unblocked)
_fold_blockacross partitions; per-partition workers return disjoint{cell: digest}; merge single-threaded. Memory-budget re-derivation as the gating design decision (measure peak vs_BUILD_MULT Γ P).Open questions
reduce_workers), or auto fromos.cpu_count()/ memory budget? Recommendation: auto-derive, capped, with an override knob.