Back to Streaming Sketching

Streaming Statistics Toolkit

September 28, 2026 Wasil Zafar 21 min read

Ship foundational counters, samplers, filters, and summaries behind one API.

Table of Contents

  1. Define the Toolkit and Its Users
  2. Choose Interfaces, Guarantees and Backends
  3. Build Exact and Approximate Statistics Vertically
  4. Verify Every Contract and Failure Mode
  5. Package, Demonstrate and Extend the Toolkit

Define the Toolkit and Its Users

A streaming-statistics toolkit is like a dashboard at a busy railway station: it cannot store every passenger forever, but it must continuously answer useful questions—how many arrived, how many were distinct, whether a name has probably appeared, which destinations are busiest, and where the wait-time distribution lies. Producers send events, applications ask questions, and operators need to know whether an answer is exact, approximate, stale, or degraded.

Start with the stream A, B, A, C, A. An exact counter reports 5 events; a distinct set reports 3 keys; a frequency map reports A → 3; a membership structure answers that B was seen. An approximate toolkit may replace those structures with a Morris counter, HyperLogLog (HLL), Count-Min Sketch, and Bloom filter to use bounded memory. The output A ≈ 3 is only useful if it also states the configured error and confidence.

The common contract is update(event), query(request), merge(other), snapshot(), and restore(bytes). A parameter calculator turns an error budget into memory settings. An exact oracle processes the same replay with dictionaries, sets, or sorted arrays. Confidence metadata describes a probabilistic statement; it is not a promise that every answer is within the bound. Mergeability requires identical algorithm families, seed identities, schema versions, and compatible parameters—not merely two objects with the same method names.

The users are application engineers choosing a statistic, data-platform engineers deploying partitioned state, reliability engineers diagnosing lag or corruption, security reviewers limiting sensitive output, and analysts interpreting estimates. Acceptance means stable interfaces, deterministic seeded tests, bounded memory, merge-equivalent results where promised, versioned serialization, visible degradation, and exact-oracle evidence for every approximate backend.

From a station turnstile analogy to streaming statistics Events pass through a shared update interface to bounded-memory instruments for count, membership, distinct count, frequency, sampling, and quantiles. TurnstileA, B, A, C, A events Shared APIupdate • query • mergesnapshot • restore Labeled estimatevalue + method + errorconfidence + freshness + version Count: 5MembershipB: maybeDistinct≈ 3FrequencyA ≈ 3Samplek itemsQuantilep95 ≈ x Shape distinguishes instruments; labels state semantics—color is supplementary.
TermIntuitive meaningExact exampleNon-exampleAssumptionQuestion answered
Event countItems processedInteger incremented per accepted eventQueue lengthDefined deduplication policyHow many events arrived?
Reservoir sampleUniform sample of fixed sizeSeeded Algorithm R with k=100First 100 eventsStable RNG and one passWhat does a typical event look like?
Bloom filterCompact membership screenBits set by k hashesA key-value storeFalse positives allowed; no false negatives before deletion/corruptionHas this key possibly appeared?
HLLDistinct-cardinality estimateRegisters from leading-zero ranksSum of event countsUniform hash; compatible precisionRoughly how many unique keys?
Count-Min SketchOne-sided frequency estimateMinimum across hashed countersExact top-k listNonnegative updates for the standard boundHow frequent is this key, at most how overestimated?
Count SketchSigned frequency estimatorMedian of signed bucket valuesMembership filterPairwise-independent hash/sign functionsWhat is a frequency under signed updates?
Quantile summaryRank-oriented distribution synopsisGK summary with rank error εNArithmetic meanOrdered values and stated rank-error modelWhat value lies near p95?
Merge contractRule for combining partitionsAdd aligned CMS matricesConcatenating arbitrary serialized bytesMatching version, seeds, dimensions, semanticsCan these states be combined safely?
Warm-up

Define the Toolkit and Its Users

Process [(A,4), (B,9), (A,7), (C,2), (A,8)] on paper. Maintain an exact event count, exact distinct set, exact frequency map, and a size-2 reservoir using replacement draws —, —, 2, 4, 1 for arrivals 1–5 (replace only when the draw is at most 2, at position draw−1). Checkpoints: after three arrivals the count is 3, distinct keys are {A,B}, frequencies are {A:2,B:1}, and the reservoir becomes [A,B] because draw 2 replaces the second entry with the third event (A,7). After five arrivals expect count 5, three distinct keys, A:3, and reservoir [(A,8),(A,7)]. Reflection: which answers remain exact, and why must a sample never be presented as a frequency table?

Choose Interfaces, Guarantees and Backends

The primary workload is keyed events on many partitions, with millions of updates per second, point queries below 20 ms at p99, snapshots every five minutes, and bounded per-tenant memory. Producers submit {tenant, stream, event_id, event_time, key, value, schema_version}. The service validates size and type, applies an explicit duplicate policy, routes by tenant and stream, and returns a Result containing value, algorithm, exactness, error model, confidence, parameters, watermark, and state version.

Functional requirements include counts, uniform samples, membership, distinct counts, point frequencies, heavy-hitter candidates, and quantiles. SLOs include 99.9% query availability, no silent restore across incompatible versions, deterministic replay under fixed seeds, and resource enforcement before allocation. Privacy limits prohibit raw sampled payloads unless the tenant policy permits them; access control, retention, redaction, and query auditing are part of the contract. The threat model covers malformed events, hash-flooding, cross-tenant reads, snapshot tampering, replay duplication, and a compromised operator credential, but not a fully compromised process host.

Parameter calculators translate targets into configuration: Bloom uses m = ceil(-n ln p/(ln 2)^2) bits and k ≈ (m/n)ln 2; CMS uses width ceil(e/ε) and depth ceil(ln(1/δ)); HLL precision selects m=2^p registers with relative standard error near 1.04/sqrt(m). Values outside safe ranges are rejected. Exact backends remain available for small streams, audits, and automatic fallback when an approximation is unsupported—not when memory would become unsafe.

Streaming Statistics Toolkit: Choose Interfaces, Guarantees and Backends
flowchart TD
    P[Producers] --> V[Validate and route]
    V --> L[Partition log]
    L --> E[State engine]
    E --> X[Exact backends]
    E --> A[Approximate backends]
    X --> S[Versioned snapshots]
    A --> S
    Q[Query API] --> E
    E --> R[Result plus guarantee metadata]
    O[Operators] --> C[Config and rollout control]
    C --> V
    E --> M[Metrics traces audit]
    classDef navy fill:#17365D,color:#fff,stroke:#102844;
    classDef teal fill:#147D84,color:#fff,stroke:#0c5c61;
    classDef crimson fill:#C6284D,color:#fff,stroke:#8e1935;
    classDef purple fill:#6C3BAA,color:#fff,stroke:#482675;
    class P,Q,O navy;
    class V,L,E,C teal;
    class X,A,S purple;
    class R,M crimson;
System context and trust boundariesProducers enter an authenticated boundary, partitions isolate failure, state snapshots cross into durable storage, and operators use a separate control boundary. Authenticated data plane Producers Gatewayauth + limits Partition Astate Partition Bstate Query and merge API Snapshot storeseparate failure domain Operatorscontrol boundary Dashed enclosure = trust boundary; separate boxes = independently failing or authorized domains.
CandidateGuaranteeScale / latencyMemoryOperational burdenPrivacyRecoveryDecision
Exact map/setExact under accepted-event semanticsBest for small cardinality; tail latency grows with stateO(distinct keys)Simple until compaction/shardingStores identifiers; highest exposureStraightforward snapshot/replayKeep as oracle and small-stream backend
Morris counterProbabilistic total countO(1) updateO(log log N) bits conceptuallyLow, but surprising varianceNo keys retainedMerge is nontrivial and must be specifiedOffer only when extreme count compression is required
Reservoir samplingUniform k-sampleO(1) expected updateO(k) payloadsRequires deterministic merge strategyMay retain raw sensitive eventsSnapshot RNG state and seen countSelect with redaction and access controls
Bloom filterNo false negatives; configured false-positive rateConstant hash workFixed m bitsCapacity must be watchedMembership inference riskBitwise OR only for compatible filtersSelect for membership screening
HLLRelative-error distinct estimateConstant update/query over registersFixed 2^p registersBias/version compatibilityHashed identifiers still sensitiveRegister-wise max when compatibleSelect for distinct counts
Count-Min SketchAdditive overestimate for nonnegative frequenciesO(depth) update/queryO(width×depth)Saturation and candidate discovery need careFrequency leakageCell-wise addition when compatibleSelect for point frequency; pair with candidate heap
Count SketchUnbiased signed estimator with variance boundO(depth)O(width×depth)More seed/sign managementFrequency leakageCell-wise addition when compatibleSelect where negative updates are required
GK / KLL summaryRank-error quantilesLogarithmic or amortized updateBounded by accuracyMerge/version rules varyDistribution leakageOnly documented compatible mergeSelect KLL for merge-heavy analytics; GK for deterministic rank bounds

The central invariant is that accepted events and compatible merges have one unambiguous semantic effect, while every query returns the evidence needed to interpret it. Launch is blocked by cross-tenant leakage, unbounded allocation, silent version coercion, or any estimate lacking algorithm and guarantee metadata.

Build Exact and Approximate Statistics Vertically

Build one statistic end to end before adding the next. Slice 1 implements the envelope, validation, exact count, snapshot checksum, and replay. Slice 2 adds Bloom membership with a calculator and exact-set oracle. Slice 3 adds reservoir sampling, then HLL, CMS/Count Sketch, and a quantile summary. Each backend conforms to a narrow protocol but may expose statistic-specific queries; forcing membership and quantiles into the same scalar method would hide semantics.

Configuration is immutable within a state generation. A snapshot records {format_version, algorithm, parameters, seed_id, schema_version, watermark, payload, checksum}. Restore first validates size, checksum, version, and parameter compatibility. Migrators are explicit functions between adjacent formats; otherwise the service replays the retained log or returns UNSUPPORTED_STATE. Memory guards check calculated dimensions before allocation. Counter saturation, late-event policy, and serialization limits are observable. A circuit breaker can route a small stream to an exact backend, reject optional statistics, or serve a stale labeled snapshot; it never silently changes guarantees.

from dataclasses import dataclass
from hashlib import blake2b
from random import Random
from typing import Any

def _hash(seed: int, value: str) -> int:
    return int.from_bytes(blake2b(f"{seed}:{value}".encode(), digest_size=8).digest(), "big")

@dataclass(frozen=True)
class Config:
    width: int = 128
    depth: int = 5
    reservoir_size: int = 8
    seed: int = 7
    max_cells: int = 1_000_000
    format_version: int = 1

    def validate(self) -> None:
        if not (1 <= self.width and 1 <= self.depth and self.width * self.depth <= self.max_cells):
            raise ValueError("unsafe Count-Min dimensions")
        if not 1 <= self.reservoir_size <= 100_000:
            raise ValueError("unsafe reservoir size")
        if self.format_version != 1:
            raise ValueError("unsupported format version")

class Toolkit:
    def __init__(self, config: Config):
        config.validate()
        self.config = config
        self.count = 0
        self.exact: dict[str, int] = {}
        self.cms = [[0] * config.width for _ in range(config.depth)]
        self.sample: list[Any] = []
        self.rng = Random(config.seed)

    def update(self, key: str, payload: Any = None) -> None:
        if not isinstance(key, str) or not key or len(key.encode()) > 256:
            raise ValueError("key must be 1..256 UTF-8 bytes")
        self.count += 1
        self.exact[key] = self.exact.get(key, 0) + 1
        for row in range(self.config.depth):
            col = _hash(self.config.seed + row, key) % self.config.width
            self.cms[row][col] += 1
        item = payload if payload is not None else key
        if len(self.sample) < self.config.reservoir_size:
            self.sample.append(item)
        else:
            slot = self.rng.randrange(self.count)
            if slot < self.config.reservoir_size:
                self.sample[slot] = item

    def frequency(self, key: str) -> dict[str, Any]:
        estimate = min(self.cms[row][_hash(self.config.seed + row, key) % self.config.width]
                       for row in range(self.config.depth))
        return {"value": estimate, "algorithm": "count_min", "exact": False,
                "epsilon_upper": 2.718281828 / self.config.width,
                "delta": 2.718281828 ** (-self.config.depth),
                "format_version": self.config.format_version}

    def merge(self, other: "Toolkit") -> "Toolkit":
        if self.config != other.config:
            raise ValueError("incompatible state: config or seed differs")
        merged = Toolkit(self.config)
        merged.count = self.count + other.count
        merged.exact = self.exact | other.exact
        for key in set(self.exact) | set(other.exact):
            merged.exact[key] = self.exact.get(key, 0) + other.exact.get(key, 0)
        merged.cms = [[a + b for a, b in zip(left, right)]
                      for left, right in zip(self.cms, other.cms)]
        # A production merge uses a priority reservoir; refusing here is safer
        # than claiming concatenation is uniform.
        merged.sample = []
        return merged

def self_test() -> None:
    cfg = Config(width=64, depth=4, reservoir_size=3, seed=11)
    a, b = Toolkit(cfg), Toolkit(cfg)
    for key in ["A", "B", "A"]: a.update(key)
    for key in ["C", "A"]: b.update(key)
    whole = Toolkit(cfg)
    for key in ["A", "B", "A", "C", "A"]: whole.update(key)
    merged = a.merge(b)
    assert whole.count == merged.count == 5
    assert whole.exact == merged.exact == {"A": 3, "B": 1, "C": 1}
    assert whole.cms == merged.cms
    assert whole.frequency("A")["value"] >= 3
    assert Toolkit(cfg).sample == []
    try:
        Toolkit(Config(width=10**6, depth=2))
    except ValueError:
        pass
    else:
        raise AssertionError("resource guard failed")
    try:
        a.merge(Toolkit(Config(width=64, depth=4, reservoir_size=3, seed=12)))
    except ValueError:
        pass
    else:
        raise AssertionError("compatibility guard failed")
    print("all integration checks passed", whole.frequency("A"))

if __name__ == "__main__":
    self_test()
Streaming Statistics Toolkit: Build Exact and Approximate Statistics Vertically
flowchart TD
    C[Contract and envelope] --> E[Exact baseline]
    E --> B[Bloom and reservoir]
    B --> H[HLL and frequency sketches]
    H --> Q[Quantile summary]
    Q --> I[Integrated canary]
    I --> D[Progressive deployment]
    D -->|SLOs pass| P[Promote]
    D -->|Mismatch or saturation| R[Rollback generation]
    R --> E
    classDef navy fill:#17365D,color:#fff,stroke:#102844;
    classDef teal fill:#147D84,color:#fff,stroke:#0c5c61;
    classDef crimson fill:#C6284D,color:#fff,stroke:#8e1935;
    classDef purple fill:#6C3BAA,color:#fff,stroke:#482675;
    class C,E navy;
    class B,H,Q teal;
    class I,D purple;
    class P,R crimson;
Build

Build Exact and Approximate Statistics Vertically: Streaming Statistics Toolkit

Create contracts.py, exact.py, cms.py, snapshot.py, and test_replay.py. First replay A,B,A,C,A; checkpoint 1 passes when exact count is 5 and frequencies are {A:3,B:1,C:1}. Add CMS with width=64, depth=4, seed=11; checkpoint 2 passes when every point estimate is at least its exact nonnegative frequency and two identical replays produce identical matrices. Split the stream after three events, merge aligned CMS states, and compare with the single-pass matrix; they must be byte-for-byte equal. Attempt a merge with seed 12 and an allocation above max_cells; both must fail before mutation. Serialize, corrupt one byte, and verify checksum rejection. Definition of done: normal replay, compatible merge, deterministic snapshot, corruption rejection, and resource/compatibility guards all run in one command with zero unexplained warnings.

Verify Every Contract and Failure Mode

Verification layers reinforce one another. Unit tests cover parameter formulas and serialization. Property tests generate streams and assert exact identities, Bloom no-false-negative behavior, CMS non-underestimation for nonnegative updates, merge equivalence, and bounded state. Differential replay compares approximate answers with exact oracles across seeds and skew levels. Load tests measure update throughput, query p99, memory per stream, snapshot duration, and recovery time. Adversarial tests target hot keys, empty streams, Unicode, maximum counters, late events, duplicate IDs, incompatible seeds, corrupt snapshots, storage stalls, and partition loss.

An approachable end-to-end case replays 10,000 café orders and checks count, distinct menu items, frequent products, a uniform audit sample, and p95 preparation time. The professional case is a multi-tenant telemetry service at 500,000 events/s, 5,000 active streams, a 4 GiB state budget per worker, query p99 under 20 ms, and recovery under 10 minutes. Canary traffic shadows exact results for small tenants. Dashboards separate algorithm error from systems error: lag, rejected events, saturation, incompatible merges, checksum failures, and estimate/oracle deltas. An incident commander owns rollback; the correctness lead signs the dossier; security reviews sampled data and membership-inference exposure.

Verification and observability surfaceOffline tests and online canaries feed evidence; metrics, traces, alerts, and audit logs lead to operator decisions. Exact + property testsreplay • merge • bounds Load + fault testslatency • loss • corruption Canary shadowingestimate vs oracle Metrics + traces Audit evidenceconfig • query • change AlertsSLO or invariant Evidence bundledossier + timeline Operatorhold • rollbackpromote Rectangles produce evidence; the decision diamond requires named ownership and a recorded outcome.
Requirement / guaranteeOracleWorkloadMetric and thresholdFailure injectionEvidenceOwner
Accepted-event count is exactInteger replay counterCafé trace + duplicate IDsEquality after dedup policy; zero mismatchDuplicate and out-of-order replaySeeded replay reportCorrectness lead
Bloom has no false negativesExact setCapacity and 2× capacity streams0 false negatives; measured FP ≤ configured p plus statistical toleranceBit-flip snapshotProperty-test log, checksum alertBackend owner
HLL distinct estimate is calibratedExact set cardinalityUniform and Zipf keys over 30 seedsRMSE within documented envelope; no systematic regression >10%Incompatible precision mergeAccuracy notebook and seed manifestAlgorithms reviewer
CMS respects one-sided boundExact frequency mapUniform, Zipf, adversarial collisionsNo underestimates; violation rate of additive bound ≤ δ envelopeCounter saturationDifferential report and saturation metricBackend owner
Reservoir is uniformExact inclusion probabilitiesRepeated seeded runs, N=100, k=10Chi-square test not persistently rejected across seed suiteRNG-state loss on restoreStatistical test reportCorrectness lead
Quantile rank error is boundedSorted exact arrayBimodal, repeated values, heavy tailsObserved rank error ≤ configured contractTruncated snapshotRank-error plots and restore logsBackend owner
Query and update SLOsLoad generator and trace clock500k updates/s, 5k streamsQuery p99 <20 ms; availability ≥99.9%200 ms storage stallDashboard export and trace exemplarsSRE
Memory is boundedAllocator telemetryCardinality ramp to limitWorker state ≤4 GiB; rejection before unsafe allocationMalicious dimensionsHeap profile and rejection auditPlatform lead
Recovery is safeSnapshot + retained logWorker loss during snapshotRTO <10 min; checksum/version errors never bypassedKill worker, corrupt byteIncident timeline and replay hashIncident commander
Tenant/privacy isolationAuthorization oracleCross-tenant queries and samples0 unauthorized reads; all sensitive queries auditedForged tenant tokenSecurity test and audit exportSecurity owner
Production

Verify Every Contract and Failure Mode: Streaming Statistics Toolkit

Run a 30-minute readiness trial: 10 minutes at 250k events/s, 10 at 500k, and 10 at 650k with Zipf-skewed keys across 5,000 streams. Require p99 query latency below 20 ms at target load, update acceptance above 99.99%, memory below 4 GiB, zero cross-tenant results, and estimate/oracle metrics inside each backend's documented envelope. At minute 12 kill one worker; expect reassignment, snapshot restore, replay, and recovery under 10 minutes. At minute 20 corrupt a snapshot and change one CMS seed; expect checksum and compatibility alerts, quarantine, and no merge. Roll back if p99 exceeds 30 ms for five minutes, memory exceeds 90% of budget, an invariant fails once, or recovery breaches 10 minutes. The written decision must be GO, CONDITIONAL GO with dated owners, or NO-GO, citing dashboard captures, replay hashes, alert timelines, cost per million events, and unresolved risks.

Package, Demonstrate and Extend the Toolkit

The release bundle contains an API reference with semantic examples, configuration calculators, a pinned reproducible environment, seeded datasets, benchmark commands, raw results, a correctness dossier, schema and migration notes, an operational runbook, security/privacy review, limitations, and a roadmap. The runbook covers saturation, lag, snapshot corruption, incompatible state, elevated false positives, unexpected oracle drift, tenant isolation, rollback, and evidence preservation. Every artifact names its version, owner, generation command, and review date.

The demonstration starts with the five-event example, shows exact and approximate result envelopes, partitions and merges a replay, rejects an incompatible seed, restores a valid snapshot, rejects a corrupt one, and finishes with the professional workload dashboard. Peer reviewers should ask: Can a caller distinguish every guarantee? Are calculators tested at boundary values? Is merge equivalence proved or explicitly unsupported? Can operators see degradation before users do? Are samples and membership queries appropriately protected? Could a new backend be added without weakening old contracts?

Handoff requires tagged source, checksums, a clean one-command test run, benchmark provenance, reviewed dashboards and alerts, rehearsed rollback, ownership rotation, and no undocumented exceptions. The roadmap prioritizes risk reduction before feature breadth: priority-reservoir merge correctness, production HLL bias tables, KLL quantiles, differential-privacy wrappers with explicit budgets, hardware-aware vectorization, and cross-language conformance fixtures.

Deliverable

Package, Demonstrate and Extend the Toolkit: Streaming Statistics Toolkit

Final checklist: (1) build from a clean checkout; (2) run unit, property, replay, load, security, and fault suites; (3) publish API docs and result-envelope examples; (4) attach parameter-calculator proofs and boundary tests; (5) archive benchmark data, environment lock, seeds, and commands; (6) sign the correctness dossier; (7) rehearse restore and rollback; (8) record privacy review and on-call ownership. Demo script: ingest the five-event stream, inspect labeled results, replay two partitions, show compatible CMS merge equality, trigger incompatible-seed rejection, corrupt a snapshot, then open the SLO dashboard from the production trial. Score 0–2 each for correctness, contract clarity, reproducibility, bounded resources, observability, recovery, security/privacy, and documentation; require at least 14/16 and no zero. Stretch goals are HLL, KLL, signed Count Sketch, and language-neutral fixtures. Milestones: merge-safe reservoir, calibrated HLL, then privacy-budgeted release. Reviewers sign only when every acceptance criterion links to evidence and an owner.

Launch only with passing contract tests, bounded resources, calibrated error evidence, rehearsed recovery, privacy controls, and explicit owners. Known risks include hash assumptions, distribution shift, membership and sample leakage, and operators misreading probabilistic bounds; ask which failure would remain invisible, then make the next milestone a merge-safe reservoir with statistical and recovery evidence.