Skip to content

feat(gos): CountSketch insert-time isotropic delta gate - #524

Merged
zzylol merged 1 commit into
mainfrom
split/pr-gos-countsketch
Jul 17, 2026
Merged

zzylol merged 1 commit into
mainfrom
split/pr-gos-countsketch

Conversation

@zzylol

@zzylol zzylol commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Third PR in the split stack (docs #520 → transport #521this).
Converts CountSketch's per-cell GOS delta gating from a periodic
decode-prev-diff sub-window model to insert-time detect + reset, per
docs/design-gos-unified-edge-telemetry.md §11. Purpose-built for this
mechanism (no reuse of sketchlib-go's existing OctoSketch worker framework —
its AdaptiveTau/always-emit semantics differ from GOS's closed-form
ε-accuracy thresholds).

Depends on sketchlib-go PR #69 (feat/gos-per-cell-delta), which this PR's
commit f5a4735 (UpdateStringGOS) was added to.

What changed

  • sketchlib-go CountSketch.UpdateStringGOS: applies the normal hashed
    update, then immediately checks each touched cell against a threshold — a
    crossing resets the cell to 0 in place (L2 adjusted) and is reported. The
    cell's value right before reset already equals its delta-since-last-send,
    so no separate per-cell accumulator is needed.
  • CountSketchWrapper: gosEpsilon/gosSites + SetGosMode, a
    gosDirty list drained by ComputeDeltaAgainst's new GOS branch
    (drainGosDelta — never decodes prev), gosWake/ConsumeWakeSignal for
    the runtime's wake-signal interface, and GosDeltaThreshold now reads the
    sketch's own incrementally-tracked L2 (QuerySum2, O(rows)) instead of an
    O(rows·cols) full-matrix scan. Anisotropic mode is not reintroduced
    its Activity_j definition breaks under per-cell async reset (open item
    in the design doc), a follow-up, not this PR.
  • Runtime plumbing: Precompute.SetWakeHook + windowState.wakeHook,
    invoked from recordLocked after every observation via a narrow
    wakeSignaler interface — the out-of-cycle counterpart to the existing
    monitorHook. subWindowShouldEmit/subWindowMarkEmitted bypass Gate 1's
    divergence check for GOS-mode CountSketch (the dirty-list drain already
    decided what's dirty); Gate 1 itself stays for every other family until
    its own GOS PR lands.
  • sketchAggregator.subWindowEnabled() now decouples from
    SubWindowInterval when GOS is active (flagged as this PR's job in
    edge(asapedgeprocessor): wake-on-demand sub-window flush (generic plumbing) #521's description) — a GOS-only family with no sub-window ticker
    configured still runs EmitSubWindow, driven purely by the wake.
  • gos_delta_epsilon/gos_sites config knobs, validated (countsketch +
    emit_heap=false only, epsilon in (0,1)).

Test plan

  • sketchlib-go: disabled passthrough, deterministic crossing+reset (cell
    AND L2 verified zeroed), telescoping reconstruction (every reported
    crossing applied onto a fresh target matches a reference sketch fed
    identical inserts without ever resetting).
  • asap-precompute-go/sketches: empty-drain→nil, wake fires once per
    crossing batch and clears on read, wrapper-level telescoping
    reconstruction via ComputeDeltaAgainst+ApplyDelta.
  • asapedgeprocessor: TestGOSCountSketchInsertWakesFlush — full
    pipeline, SubWindowInterval unset and WindowDuration=1h
    (neither can fire on its own); feeding CountSketch observations
    through ConsumeMetrics alone produces a flush, with no manual
    wakeSubWindow() call anywhere in the test.
  • go build/go vet/go test clean on asapedgeprocessor,
    asap-precompute-go (all subpackages), and sketchlib-go's
    CountSketch package.

🤖 Generated with Claude Code

Third PR in the split stack (docs #520 -> transport #521 -> this).
Converts CountSketch's per-cell delta gating from the periodic
decode-prev-diff sub-window model to insert-time detect+reset, per
design-gos-unified-edge-telemetry.md §11 — no octosketch package reuse,
purpose-built for this mechanism's own semantics.

- sketchlib-go CountSketch.UpdateStringGOS (separate commit/PR #69):
  applies the hashed update then immediately checks each touched cell
  against threshold; a crossing resets it to 0 in place and is
  reported, so the delta-since-last-send IS the cell's value right
  before reset — no separate per-cell accumulator needed.
- CountSketchWrapper (countsketch.go): gosEpsilon/gosSites + SetGosMode,
  a gosDirty list drained by ComputeDeltaAgainst's new GOS branch
  (drainGosDelta — never decodes `prev`, unlike the fixed-threshold
  path it leaves untouched), gosWake/ConsumeWakeSignal for the runtime's
  narrow wake-signal interface, GosDeltaThreshold using the sketch's own
  incrementally-tracked L2 (QuerySum2 median-of-rows, O(rows)) instead
  of the O(rows*cols) full-matrix scan L2DivergenceSinceEmit did.
  Anisotropic mode is NOT reintroduced (see docs/design doc §11 open
  items — its Activity_j definition breaks under per-cell async reset;
  tracked as a follow-up, not this PR's job).
- gos_threshold.go: F2IsotropicThreshold closed form
  (T=eps*||C||/(2k*sqrt(dw))), isotropic only.
- Runtime plumbing (precompute.go/window.go): Precompute.SetWakeHook +
  windowState.wakeHook, invoked from recordLocked via a narrow
  wakeSignaler interface after every observation — the out-of-cycle
  counterpart to the existing monitorHook. applyGosMode re-stamps GOS
  config on every already-live series at both flush paths (boundary and
  sub-window) so a control-plane change takes effect promptly; the
  CountSketch factory (warm_sketch.go) also primes it at series
  creation so inserts before a brand-new series' first flush are gated
  too. subWindowShouldEmit/subWindowMarkEmitted bypass Gate 1's O(dw)
  divergence check for GOS-mode CountSketch series (the dirty-list
  drain already decided what's worth sending — an empty drain IS
  "nothing to send"); Gate 1 itself is NOT deleted here since every
  other family still depends on it until its own GOS PR lands.
- sketchAggregator.subWindowEnabled() decouples from SubWindowInterval
  when GOS is active (PR #521 flagged this as this PR's job): a
  GOS-only family with no sub-window ticker configured now still runs
  EmitSubWindow, driven purely by wakeSubWindow.
- config.go/config_validate.go/processor.go: gos_delta_epsilon/gos_sites
  knobs (validated: countsketch + emit_heap=false only, epsilon in
  (0,1)), and the SetWakeHook wiring at aggregator construction.

Test plan (all new, all passing):
- sketchlib-go: disabled-passthrough, deterministic single-row
  crossing+reset (cell AND L2 verified zeroed), and the key
  correctness property — telescoping reconstruction (applying every
  reported crossing onto a fresh target matches a reference sketch fed
  identical inserts without ever resetting).
- asap-precompute-go/sketches: empty-drain-returns-nil, wake-signal
  fires exactly once per crossing batch and clears on read, wrapper-level
  telescoping reconstruction via ComputeDeltaAgainst+ApplyDelta.
- asapedgeprocessor: TestGOSCountSketchInsertWakesFlush — full pipeline,
  SubWindowInterval UNSET and WindowDuration=1h (neither can fire on its
  own), feeding CountSketch observations through ConsumeMetrics alone
  produces a flushed envelope with NO manual wakeSubWindow() call
  anywhere in the test.
- go build/go vet/go test clean on asapedgeprocessor, asap-precompute-go
  (all subpackages), and sketchlib-go's CountSketch package.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@zzylol
zzylol merged commit 9771b26 into main Jul 17, 2026
@zzylol
zzylol deleted the split/pr-gos-countsketch branch July 17, 2026 15:52
zzylol added a commit that referenced this pull request Jul 17, 2026
…dOccurrence

ingest.go: decode the 3 reserved wire attributes an SDK running
AggregationRowSampledSketch stamps on an individually-admitted raw
occurrence (admitted-rows bitmask, row count, sample_p), strip them
before building series identity, and skip cold-archiving a row-sampled
point (it's a raw occurrence, not an aggregate sample).

warm_sketch.go: observe() takes rowSampled/admittedRows/sampleP and,
when set, tags the ObservationValue so CMS/CountSketch route through
Sketch.ApplyAdmittedOccurrence (the SDK's admission decision applied
verbatim) instead of the plain insert path. Families with no
*AtRows sketchlib primitive (DDSketch/KLL/HLL) drop rather than
silently misapply an unrelated observer — this can only happen if the
SDK's AggregationRouter and this collector's AggID disagreed about
which family a PolicyFingerprint targets.

Built fresh against current main's warm_sketch.go/ingest.go (not
merged wholesale from feat/gos-unified-monitoring, which independently
regressed the insert-time GOS wake/anisotropic-threshold work already
merged here via #524-#532) — only the row-sampled-specific decode/
route logic was ported; the isotropic GOS priming, wakeCh wiring, and
sub-window trigger logic already on main are unchanged.

Extracted from #518 (stale base); depends on PR-A's aggID/config
plumbing (split/pr-aggid-policyfingerprint) — based on that branch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant