feat(gos): Sum insert-time delta gate (T=ε·|sum|/k, degenerate 1-cell case) - #526
Merged
Merged
Conversation
Implements the insert-time GOS mechanism for Sum, the degenerate 1-cell
case: on every Update, check the accumulated-since-last-crossing
magnitude against T=ε·|sum|/k (SumIsotropicThreshold); on crossing,
capture it into a pending-drain accumulator, zero the since-crossing
tracker in place, and arm the wake signal so the flush loop runs
immediately instead of waiting for a periodic tick.
- asap-precompute-go/sketches/sum.go: SetGosMode, Update's insert-time
threshold check, ConsumeWakeSignal, drainGosDelta (drained by
ComputeDeltaAgainst when GOS is active, ignoring prev entirely).
Snapshot() excludes any amount already staged for drain so the
first-ever full snapshot and a later drained delta never overlap
(avoids double-counting under the additive backend reconstruction
model) — the one wire-format wrinkle GOS introduces; the 16-byte
{sum,count} payload shape itself is unchanged, and the non-GOS path
(gosEpsilon<=0) is byte-identical to before.
- asap-precompute-go/sketches/gos_threshold.go: SumIsotropicThreshold
(T=ε·N/k, the linear-f case with no √(d·w) term).
- asap-precompute-go/precompute.go: subWindowShouldEmit/
subWindowMarkEmitted bypass the old periodic divergence gate for
AggKindSum when GosDeltaEpsilon>0, mirroring the existing
Count-Sketch bypass. applyGosMode already worked generically via its
structural SetGosMode interface assert, so it needed no change.
- opentelemetry-collector-contrib-patch/processor/asapedgeprocessor:
prime SetGosMode at Sum series creation (warm_sketch.go); extend
config_validate.go's gos_delta_epsilon check to accept family=sum
alongside family=countsketch.
Tests: sum_gos_test.go covers empty-drain-returns-nil, wake fires once
and clears (and doesn't re-arm for a small follow-up insert once the
threshold has grown), and a telescoping reconstruction test applying
every drained delta onto a fresh target and confirming the
reconstructed total (plus the known, bounded sub-threshold residual)
matches the true cumulative sum. gos_sum_test.go is the processor-level
end-to-end proof (SubWindowInterval unset, WindowDuration long) that
ConsumeMetrics alone — no manual wakeSubWindow() — produces a flush.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
zzylol
changed the base branch from
split/pr-gos-countsketch
to
split/pr-gos-ddsketch
July 17, 2026 02:39
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of the GOS per-family stack (base: CountSketch
split/pr-gos-countsketch/ #524). Sum is the degenerate one-cell case (design §11 / derivations §8.1):T=ε·|sum|/k. No sketchlib-go change (Sum is a pure scalar in asap-precompute-go).SumWrappertracks two tiers:gosSinceCrossSum/Count(amount since last crossing, tested against T everyUpdate, zeroed on crossing) andgosReadySum/Count(captured-but-undrained, staged untilComputeDeltaAgainstdrains them, ignoringprev). Wire payload shape unchanged (16-byte{sum,count}); GOS just ships the drained amount directly rather than diffing two totals.Subtlety handled:
Snapshot()subtracts the stagedgosReadySumwhen GOS is active, so theSnapshotCacheprev==nilfirst-emit fallback (which callsSnapshot()directly, bypassingComputeDeltaAgainst) doesn't double-report anything already captured. No-op / byte-identical when GOS off.w.sumitself is not reduced by captures, so N stays a real running total.AggKindSumGOS-bypass cases insubWindowShouldEmit/subWindowMarkEmitted; factory primesSetGosMode;gos_delta_epsilonaccepted forfamily=sum. Sum has no sampling path, so no #518 interaction.Test plan
TestGOSSumInsertWakesFlush(SubWindowInterval unset, WindowDuration 1h, ConsumeMetrics-only flush).Note (documented, deferred):
monitor.Engine'sFunctionalSumalerting readsSum()directly, which under GOS returns a residual — the same "local read corrupted under GOS" trade-off the design doc's Retirements section defers to the Discipline-B split PR.🤖 Generated with Claude Code