diff --git a/asap-precompute-go/monitor/engine.go b/asap-precompute-go/monitor/engine.go index a28b9cb7..7bb14f4a 100644 --- a/asap-precompute-go/monitor/engine.go +++ b/asap-precompute-go/monitor/engine.go @@ -31,7 +31,7 @@ type monitorState struct { // obsCount is the number of observations admitted for this monitor since // the current epoch began. It is the edge's observed items/window (rate) // reported to the coordinator so it can size this edge's sampling - // probability (AllocateSampleRates). Reset to 0 at each epoch boundary. + // probability via the whole-sketch ε-floor. Reset to 0 at each epoch boundary. obsCount uint64 // grantedSampleP is the coordinator-allocated distributed-NitroSketch // update-sampling probability for this monitor's agg. 0 (unset) ⇒ no @@ -147,9 +147,9 @@ func (e *Engine) Observe(aggID uint64, key []byte, value float64, windowStart ui // unreported-but-below-slack mass. func (e *Engine) OnGrant(g Grant) { e.mu.Lock() - defer e.mu.Unlock() st := e.states[mapKey{g.AggID, string(g.Key)}] if st == nil || st.windowStart != g.WindowStartMs { + e.mu.Unlock() return // unknown monitor or stale epoch } st.round = g.Round @@ -159,6 +159,7 @@ func (e *Engine) OnGrant(g Grant) { // current decision, so 0 means "no sampling this round" and is recorded as // such; the precompute treats <=0 as p=1 (unsampled). st.grantedSampleP = g.SampleP + e.mu.Unlock() } // OnPoll answers a poll with the current local value and advances the baseline diff --git a/asap-precompute-go/monitor/grpcclient/monitorpb/monitor.pb.go b/asap-precompute-go/monitor/grpcclient/monitorpb/monitor.pb.go index baa16367..19e6740d 100644 --- a/asap-precompute-go/monitor/grpcclient/monitorpb/monitor.pb.go +++ b/asap-precompute-go/monitor/grpcclient/monitorpb/monitor.pb.go @@ -136,7 +136,7 @@ type MonitorReport struct { LocalValue float64 `protobuf:"fixed64,5,opt,name=local_value,json=localValue,proto3" json:"local_value,omitempty"` // current additive local value at report time Round uint64 `protobuf:"varint,6,opt,name=round,proto3" json:"round,omitempty"` // round this report answers Seq uint64 `protobuf:"varint,7,opt,name=seq,proto3" json:"seq,omitempty"` // per-edge monotonic counter; idempotent-retransmit dedup - Rate float64 `protobuf:"fixed64,8,opt,name=rate,proto3" json:"rate,omitempty"` // edge's observed items/window for this agg+key — feeds the coordinator's sample-rate allocation (p_i ~ sqrt(f_i/rate_i)) + Rate float64 `protobuf:"fixed64,8,opt,name=rate,proto3" json:"rate,omitempty"` // edge's observed items/window for this agg+key — feeds the coordinator's whole-sketch sampling floor (p_i = 1/(1+eps^2*rate_i)) unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -237,7 +237,8 @@ type SlackGrant struct { LocalSlack float64 `protobuf:"fixed64,4,opt,name=local_slack,json=localSlack,proto3" json:"local_slack,omitempty"` WindowStartMs uint64 `protobuf:"varint,5,opt,name=window_start_ms,json=windowStartMs,proto3" json:"window_start_ms,omitempty"` // sample_p is the distributed-NitroSketch update-sampling probability the - // coordinator allocates this edge (AllocateSampleRates: p_i ~ sqrt(f_i/rate_i)). + // coordinator allocates this edge via the whole-sketch epsilon-floor + // (p_i = 1/(1+eps^2*rate_i); see data_plane allocate_p / epsilon_sample_floor). // 0 (unset) => no sampling grant (p=1). The edge applies it via WithSampleP on // sampling-capable sketch wrappers (CMS/CountSketch/DDSketch) at the next // EpochReset; other families ignore it. Orthogonal to local_slack (CPU vs diff --git a/asap-precompute-go/monitor/grpcclient/monitorpb/monitor.proto b/asap-precompute-go/monitor/grpcclient/monitorpb/monitor.proto index 82948b6a..068c1954 100644 --- a/asap-precompute-go/monitor/grpcclient/monitorpb/monitor.proto +++ b/asap-precompute-go/monitor/grpcclient/monitorpb/monitor.proto @@ -53,7 +53,7 @@ message MonitorReport { double local_value = 5; // current additive local value at report time uint64 round = 6; // round this report answers uint64 seq = 7; // per-edge monotonic counter; idempotent-retransmit dedup - double rate = 8; // edge's observed items/window for this agg+key — feeds the coordinator's sample-rate allocation (p_i ~ sqrt(f_i/rate_i)) + double rate = 8; // edge's observed items/window for this agg+key — feeds the coordinator's whole-sketch sampling floor (p_i = 1/(1+eps^2*rate_i)) } // coordinator → edge: this round's per-edge slack budget. The edge reports once @@ -65,7 +65,8 @@ message SlackGrant { double local_slack = 4; uint64 window_start_ms = 5; // sample_p is the distributed-NitroSketch update-sampling probability the - // coordinator allocates this edge (AllocateSampleRates: p_i ~ sqrt(f_i/rate_i)). + // coordinator allocates this edge via the whole-sketch epsilon-floor + // (p_i = 1/(1+eps^2*rate_i); see data_plane allocate_p / epsilon_sample_floor). // 0 (unset) => no sampling grant (p=1). The edge applies it via WithSampleP on // sampling-capable sketch wrappers (CMS/CountSketch/DDSketch) at the next // EpochReset; other families ignore it. Orthogonal to local_slack (CPU vs diff --git a/asap-precompute-go/monitor/types.go b/asap-precompute-go/monitor/types.go index dae843bb..9de5ea4d 100644 --- a/asap-precompute-go/monitor/types.go +++ b/asap-precompute-go/monitor/types.go @@ -121,8 +121,9 @@ type Report struct { Round uint64 Seq uint64 // Rate is the edge's observed item count for this monitor over the current - // epoch (items/window). The coordinator feeds it into AllocateSampleRates - // (p_i ∝ √(f_i/rate_i)) to size this edge's distributed-NitroSketch sampling + // epoch (items/window). The coordinator feeds it into the whole-sketch + // ε-floor (p_i = 1/(1+ε²·rate_i); see data_plane allocate_p / + // epsilon_sample_floor) to size this edge's distributed-NitroSketch sampling // probability. 0 (unset) ⇒ the coordinator falls back to an unsampled // allocation for this edge. Rate float64 @@ -136,7 +137,8 @@ type Grant struct { LocalSlack float64 WindowStartMs uint64 // SampleP is the distributed-NitroSketch update-sampling probability the - // coordinator allocates this edge (AllocateSampleRates: p_i ∝ √(f_i/rate_i)). + // coordinator allocates this edge via the whole-sketch ε-floor + // (p_i = 1/(1+ε²·rate_i); see data_plane allocate_p / epsilon_sample_floor). // 0 (unset) ⇒ no sampling grant (p=1). The edge applies it via WithSampleP on // the metric's sketch wrapper at the next EpochReset (never mid-window, so // both merge operands share one p). Orthogonal to LocalSlack (which governs