Distributed Aggregation And Barriers
Purpose
Get one correct, reproducible answer out of many workers, and pay as little synchronisation
for it as the answer requires. Two decisions carry the whole topic: what the combining
function is allowed to be, and where — if anywhere — every worker must wait for every other.
The failure this prevents is the aggregate that disagrees with itself. Same input, same
code, a different partition order, and the total moves in the fifth decimal place; finance
opens a reconciliation ticket nobody can reproduce, because the cause is that floating-point
addition is not associative and the shuffle is not deterministic. The second failure is the
barrier nobody named: a job of ten thousand tasks whose wall-clock time is set entirely by
two of them, where adding workers changes nothing at all.
Workflow
- Write the aggregate contract. Define identity, accumulator, merge, finish, input
domain, overflow/error policy and whether encounter order is semantically relevant.
Associativity is required for arbitrary grouping; commutativity is required only when
partials may be reordered. Neither prevents double-counting a repeated attempt.
- Rewrite aggregates that lack a mergeable sufficient state. Average becomes a
(sum, count) pair; variance becomes (n, mean, M2); a percentile becomes a mergeable
histogram; a ratio carries numerator and denominator separately.
- Choose a summary per metric and state its error. Exact where cardinality is small, an
approximate mergeable sketch where it is not — with the error in the dashboard label.
- Partition by measured cost, not merely count, when skew explains stragglers; distinguish
deterministic data skew from host faults or transient resource contention.
- Place the barriers deliberately and count them. Every barrier converts the slowest
participant into everyone's latency. Ask what breaks if this one is removed.
- Define attempt identity and output commit. Every logical partition may execute more
than once. Stage output by
(job, stage, partition, attempt) and atomically select one
successful attempt, or use a sink-specific idempotent/transactional commit protocol.
- Decide the partial-failure contract before the job runs, not during the incident:
fail the job, retry the failed tasks, or emit a partial result with an explicit
completeness record.
- Prove algebra and recovery. Property-test regrouping/reordering allowed by the
contract, inject duplicate attempts and crashes at commit boundaries, and compare against
a trusted sequential oracle. A shuffled-order example alone is not a proof.
Inspect the target JDK/toolchain, engine and sketch-library versions, input snapshot, numeric
domain and sink commit guarantees. Java records in the reference require JDK 16+; test sketches
assume project-specific JUnit/AssertJ fixtures and are not standalone programs. Do not upgrade the
target to fit an example. Deliver the aggregate/equivalence contract, evidence for merge and
recovery semantics, expected participant/completeness record and remaining validation gaps.
Decision block
Use a barrier when:
- a later stage genuinely reads the complete output of an earlier one — a global sort, a
normalisation by a total, a join needing both sides fully partitioned
- the participant set is bounded and known before the stage starts
The barrier is affordable when:
- measured max-stage latency, not merely p99/p50, fits the job SLO at the actual task count
Avoid a barrier when:
- incremental consumption preserves the required semantics and waiting adds unnecessary
exposure to stragglers; retain a required completeness gate despite a long tail
- participants can join or fail mid-stage and no epoch/membership protocol defines who
counts as a participant
- the downstream stage could consume results incrementally instead
Prefer incremental or hierarchical combination instead when the combining function is
associative and commutative, so partial results merge in any order with no global wait;
when the result is read continuously rather than at a job boundary, that is a stream and
belongs to streaming-pipeline-topologies.
Speculatively re-execute a straggler only when the task is idempotent and side-effect-free
(idempotency), only the first result is committed, and the speculative fraction is capped.
Rules
- A barrier is as fast as its slowest participant. This is the max-of-N property
scatter-gather owns inside one request, at batch scale: the expected wait grows with the
number of comparable participants and their tail behavior. With queued task waves, stage
latency is the latest completion from stage start, including scheduling and retries; it is
not simply the longest isolated task duration.
Plot the per-task duration distribution before adding workers.
- Partitioning by task count assumes tasks cost the same. When key sizes span orders of
magnitude that assumption manufactures a straggler on every run; partition by measured
cost — bytes, rows, or a prior run's duration per key. Skew's repairs are
hot-partitions-and-rebalancing.
- The combining function must be associative under the result equivalence relation.
Commutativity is additionally required for unordered arrival; ordered concatenation is a
valid associative reduce when the engine preserves encounter order. Safe when domains and
overflow are handled: exact or intentionally modular integer sum,
min, max, count, bitwise OR, set union, HyperLogLog merge. Unsafe as scalar combiners: average,
median, subtraction and division. Ordered
first/last can be associative; unordered arrival
needs an ordering key with deterministic ties. Re-execution is a
separate property: sum is associative and commutative but counts a duplicate twice.
- Floating-point addition is not associative:
(a + b) + c and a + (b + c) differ for
doubles. A distributed sum of doubles can therefore change when the
partition or merge order changes, and the difference is real money in a reconciliation
report. Do not assume two sums over the same doubles agree — order and the summation
algorithm both move the result. Three fixes, and the design must name which is used:
exact decimal/fixed-point (BigDecimal without rounding during addition, or checked
integer minor units with an explicit currency/scale and overflow policy) — normally the
right model for contractual money; compensated summation
(Kahan/Neumaier), which bounds the error without making the operation associative; or a
deterministic evaluation — fix partition boundaries, within-partition order and the merge
tree, or use an algorithm guaranteeing reproducibility across the required regroupings.
- Average is not directly reducible from per-partition averages: reduce
(sum, count) and
divide once at the end. The same
rewrite applies to variance, standard deviation, rate and any ratio — carry both terms.
- Never average percentiles — that rule is
latency-statistics. Its distributed
consequence is the design: each worker emits a histogram, the coordinator merges the
histograms, and the quantile is read once from the merged structure. A worker that emits
only its own p99 has destroyed the information needed to compute the fleet's.
- Mergeability permits hierarchical combination; it does not imply bounded state. Exact
set union merges but grows with distinct input. A summary
that cannot merge may require retaining or repartitioning raw data and concentrating final
work; it does not literally require every record to traverse one node. Choose by what is
traded: HyperLogLog for distinct counts (fixed
memory, a stated relative error, merged by per-register maximum), count-min sketch for
non-negative frequencies (one-sided over-estimation with compatible hashes and no counter
overflow), t-digest or HdrHistogram for
quantiles. Exact distinct counting needs memory proportional to cardinality — that is the
cost a sketch buys off.
- Broadcast join when the small side fits in each worker's memory alongside its working set,
measured rather than assumed; shuffle join when both sides are large. A skewed join key
sends one worker most of the rows, and the stage then runs at that worker's speed whatever
the cluster size.
- A batch's partial failure needs a decision, not a default. Retried/speculative task outputs
need one selected attempt per logical partition, while external effects need idempotency.
A partial result must carry an explicit completeness record naming what is missing; the
per-request version of that contract is
scatter-gather.
- A checkpoint needs a sink-supported commit protocol. Atomic rename works only on file
systems that guarantee the required same-filesystem rename semantics; object stores may
implement rename as copy/delete. Prefer immutable attempt outputs plus an atomic manifest,
transaction or engine-native committer. Optimize checkpoint interval from write cost,
failure rate and recovery work, then validate under injected failure.
- Never write "exactly-once aggregation". State the boundary: at-least-once task execution
plus one selected output per logical partition can provide one committed contribution per
stage. External side effects and source/sink commits need their own boundary proof.
References
Java 25 Collector contract
MapReduce: Simplified Data Processing on Large Clusters
HyperLogLog original analysis
Aggregation correctness — identity,
associativity, conditional commutativity and duplicate-attempt separation, with the
safe/unsafe operation table and floating-point
non-associativity problem and its three fixes, non-reducible aggregates rewritten as
reducible pairs, mergeable summaries with what each approximates and its error, and a
determinism test that shuffles partition order. Read before writing a combiner, or when an
aggregate does not reproduce.
Barriers, joins and partial failure —
what a barrier costs, straggler mitigation and its safety conditions, the two join shapes
with their selecting conditions and the skew failure, checkpoint placement, the
partial-failure decision, and how to test a batch job with an injected task failure. Read
when designing a job's stages, or when its wall-clock time is set by a few tasks.
1---2name: distributed-aggregation-and-barriers3description: Correct and recoverable aggregation across workers: algebraic laws, duplicate attempts, numeric reproducibility, mergeable summaries, barriers, joins, skew, checkpointing and partial results. Use when totals drift between runs, stragglers set job latency, worker percentiles are averaged, cardinality exhausts memory, or a join stalls on one task. It excludes request fan-out, streaming windows, percentile theory, message ordering and the broader hot-key repair catalogue.4---56# Distributed Aggregation And Barriers78## Purpose910Get one correct, reproducible answer out of many workers, and pay as little synchronisation11for it as the answer requires. Two decisions carry the whole topic: what the combining12function is allowed to be, and where — if anywhere — every worker must wait for every other.1314The failure this prevents is the aggregate that disagrees with itself. Same input, same15code, a different partition order, and the total moves in the fifth decimal place; finance16opens a reconciliation ticket nobody can reproduce, because the cause is that floating-point17addition is not associative and the shuffle is not deterministic. The second failure is the18barrier nobody named: a job of ten thousand tasks whose wall-clock time is set entirely by19two of them, where adding workers changes nothing at all.2021## Workflow22231. **Write the aggregate contract.** Define identity, accumulator, merge, finish, input24 domain, overflow/error policy and whether encounter order is semantically relevant.25 Associativity is required for arbitrary grouping; commutativity is required only when26 partials may be reordered. Neither prevents double-counting a repeated attempt.272. **Rewrite aggregates that lack a mergeable sufficient state.** Average becomes a `(sum,28count)` pair; variance becomes `(n, mean, M2)`; a percentile becomes a mergeable29 histogram; a ratio carries numerator and denominator separately.303. **Choose a summary per metric and state its error.** Exact where cardinality is small, an31 approximate mergeable sketch where it is not — with the error in the dashboard label.324. **Partition by measured cost**, not merely count, when skew explains stragglers; distinguish33 deterministic data skew from host faults or transient resource contention.345. **Place the barriers deliberately and count them.** Every barrier converts the slowest35 participant into everyone's latency. Ask what breaks if this one is removed.366. **Define attempt identity and output commit.** Every logical partition may execute more37 than once. Stage output by `(job, stage, partition, attempt)` and atomically select one38 successful attempt, or use a sink-specific idempotent/transactional commit protocol.397. **Decide the partial-failure contract before the job runs**, not during the incident:40 fail the job, retry the failed tasks, or emit a partial result with an explicit41 completeness record.428. **Prove algebra and recovery.** Property-test regrouping/reordering allowed by the43 contract, inject duplicate attempts and crashes at commit boundaries, and compare against44 a trusted sequential oracle. A shuffled-order example alone is not a proof.4546Inspect the target JDK/toolchain, engine and sketch-library versions, input snapshot, numeric47domain and sink commit guarantees. Java records in the reference require JDK 16+; test sketches48assume project-specific JUnit/AssertJ fixtures and are not standalone programs. Do not upgrade the49target to fit an example. Deliver the aggregate/equivalence contract, evidence for merge and50recovery semantics, expected participant/completeness record and remaining validation gaps.5152## Decision block5354```text55Use a barrier when:56- a later stage genuinely reads the complete output of an earlier one — a global sort, a57 normalisation by a total, a join needing both sides fully partitioned58- the participant set is bounded and known before the stage starts59The barrier is affordable when:60- measured max-stage latency, not merely p99/p50, fits the job SLO at the actual task count61Avoid a barrier when:62- incremental consumption preserves the required semantics and waiting adds unnecessary63 exposure to stragglers; retain a required completeness gate despite a long tail64- participants can join or fail mid-stage and no epoch/membership protocol defines who65 counts as a participant66- the downstream stage could consume results incrementally instead67Prefer incremental or hierarchical combination instead when the combining function is68 associative and commutative, so partial results merge in any order with no global wait;69 when the result is read continuously rather than at a job boundary, that is a stream and70 belongs to streaming-pipeline-topologies.71Speculatively re-execute a straggler only when the task is idempotent and side-effect-free72 (idempotency), only the first result is committed, and the speculative fraction is capped.73```7475## Rules7677- **A barrier is as fast as its slowest participant.** This is the max-of-N property78 `scatter-gather` owns inside one request, at batch scale: the expected wait grows with the79 number of comparable participants and their tail behavior. With queued task waves, stage80 latency is the latest completion from stage start, including scheduling and retries; it is81 not simply the longest isolated task duration.82 Plot the per-task duration distribution before adding workers.83- Partitioning by task _count_ assumes tasks cost the same. When key sizes span orders of84 magnitude that assumption manufactures a straggler on every run; partition by measured85 cost — bytes, rows, or a prior run's duration per key. Skew's repairs are86 `hot-partitions-and-rebalancing`.87- **The combining function must be associative** under the result equivalence relation.88 Commutativity is additionally required for unordered arrival; ordered concatenation is a89 valid associative reduce when the engine preserves encounter order. Safe when domains and90 overflow are handled: exact or intentionally modular integer sum,91 min, max, count, bitwise OR, set union, HyperLogLog merge. Unsafe as scalar combiners: average,92 median, subtraction and division. Ordered `first`/`last` can be associative; unordered arrival93 needs an ordering key with deterministic ties. Re-execution is a94 separate property: sum is associative and commutative but counts a duplicate twice.95- **Floating-point addition is not associative**: `(a + b) + c` and `a + (b + c)` differ for96 doubles. A distributed sum of doubles can therefore change when the97 partition or merge order changes, and the difference is real money in a reconciliation98 report. Do not assume two sums over the same doubles agree — order and the summation99 algorithm both move the result. Three fixes, and the design must name which is used:100 **exact decimal/fixed-point** (`BigDecimal` without rounding during addition, or checked101 integer minor units with an explicit currency/scale and overflow policy) — normally the102 right model for contractual money; **compensated summation**103 (Kahan/Neumaier), which bounds the error without making the operation associative; or a104 **deterministic evaluation** — fix partition boundaries, within-partition order and the merge105 tree, or use an algorithm guaranteeing reproducibility across the required regroupings.106- Average is not directly reducible from per-partition averages: reduce `(sum, count)` and107 divide once at the end. The same108 rewrite applies to variance, standard deviation, rate and any ratio — carry both terms.109- **Never average percentiles** — that rule is `latency-statistics`. Its distributed110 consequence is the design: each worker emits a _histogram_, the coordinator merges the111 histograms, and the quantile is read once from the merged structure. A worker that emits112 only its own p99 has destroyed the information needed to compute the fleet's.113- **Mergeability permits hierarchical combination; it does not imply bounded state.** Exact114 set union merges but grows with distinct input. A summary115 that cannot merge may require retaining or repartitioning raw data and concentrating final116 work; it does not literally require every record to traverse one node. Choose by what is117 traded: HyperLogLog for distinct counts (fixed118 memory, a stated relative error, merged by per-register maximum), count-min sketch for119 non-negative frequencies (one-sided over-estimation with compatible hashes and no counter120 overflow), t-digest or HdrHistogram for121 quantiles. Exact distinct counting needs memory proportional to cardinality — that is the122 cost a sketch buys off.123- Broadcast join when the small side fits in each worker's memory alongside its working set,124 measured rather than assumed; shuffle join when both sides are large. A skewed join key125 sends one worker most of the rows, and the stage then runs at that worker's speed whatever126 the cluster size.127- A batch's partial failure needs a decision, not a default. Retried/speculative task outputs128 need one selected attempt per logical partition, while external effects need idempotency.129 A partial result must carry an explicit completeness record naming what is missing; the130 per-request version of that contract is `scatter-gather`.131- A checkpoint needs a sink-supported commit protocol. Atomic rename works only on file132 systems that guarantee the required same-filesystem rename semantics; object stores may133 implement rename as copy/delete. Prefer immutable attempt outputs plus an atomic manifest,134 transaction or engine-native committer. Optimize checkpoint interval from write cost,135 failure rate and recovery work, then validate under injected failure.136- Never write "exactly-once aggregation". State the boundary: at-least-once task execution137 plus one selected output per logical partition can provide one committed contribution per138 stage. External side effects and source/sink commits need their own boundary proof.139140## References141142- [Java 25 `Collector` contract](https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/stream/Collector.html)143- [MapReduce: Simplified Data Processing on Large Clusters](https://research.google/pubs/mapreduce-simplified-data-processing-on-large-clusters/)144- [HyperLogLog original analysis](https://algo.inria.fr/flajolet/Publications/FlFuGaMe07.pdf)145146- [Aggregation correctness](references/aggregation-correctness.md) — identity,147 associativity, conditional commutativity and duplicate-attempt separation, with the148 safe/unsafe operation table and floating-point149 non-associativity problem and its three fixes, non-reducible aggregates rewritten as150 reducible pairs, mergeable summaries with what each approximates and its error, and a151 determinism test that shuffles partition order. Read before writing a combiner, or when an152 aggregate does not reproduce.153- [Barriers, joins and partial failure](references/barriers-joins-and-partial-failure.md) —154 what a barrier costs, straggler mitigation and its safety conditions, the two join shapes155 with their selecting conditions and the skew failure, checkpoint placement, the156 partial-failure decision, and how to test a batch job with an injected task failure. Read157 when designing a job's stages, or when its wall-clock time is set by a few tasks.