GCounterDouble
A grow-only counter over Double — the exact-precision sibling of GCounter.
Same lattice as GCounter (a per-replica map, piece is elementwise max), but the slot values are Double. It exists so a monotonic OTLP DOUBLE_SUM metric folds into a grow-only counter without truncation or fixed-point scaling.
The one wrinkle: value determinism
value sums the per-replica slots, and floating-point + is not associative, so summing in map-iteration order could yield a slightly different value on two replicas that hold identical converged state. The merged state always converges (elementwise max is order-independent); only the derived value is order-sensitive. value therefore sums in canonical ReplicaId order so every replica computes the same number. (Very large magnitude differences can still lose low-order bits — the honest floating-point limit, analogous to HyperLogLog's estimation error.)
Samples
val phone = ReplicaId("phone")
val watch = ReplicaId("watch")
// Each device independently accumulates fractional seconds of CPU time.
var onPhone = GCounterDouble.ZERO
onPhone = onPhone.piece(onPhone.inc(phone, 0.75).delta)
var onWatch = GCounterDouble.ZERO
onWatch = onWatch.piece(onWatch.inc(watch, 0.5).delta)
// Merge either direction — the total is the same, to the bit.
val total = onPhone.piece(onWatch).value // 1.25
check(total == 1.25)Properties
Functions
The causal Dots this state has delivered — (author, author-seq) per op.
The per-author high-water of dots this state delivered and has since compacted away without retaining their identities.
The join: elementwise max of the two count maps.