One call, five shapes

Every one of these is the same question, wearing different clothes.

"How often, how recently, first time this window?" shows up under a lot of names. Two of them are fully built packages on this site; the rest are the same three lines of code, aimed at a different key.

#01

The shape, once

ProblemKey"Frequent lately?" drivesStatus
Cache admissionobject keyadmit vs. rejectBuilt — admission
Rate limitingclient / tenantthrottle levelBuilt — SketchProxy
Hot-key detectionrow / IP / URLalarm / mitigationPattern below
Fraud velocitycard / devicerisk score inputPattern below
Telemetry samplinglog templatesuppression probabilityStudied — fleet-sampling
#02

Hot-key / trend detection

A database, CDN, or packet filter needs to spot keys that suddenly go hot — trending content, an abusive IP, a celebrity row — on the hottest path in the system. No package ships this; it's the same Observe call SketchProxy makes, aimed at a different key.

Go
trend := epochsketch.New(epochsketch.Config{
  NumBuckets:   1 << 18,
  TickDuration: 10 * time.Second,
})

func isTrending(key string) bool {
  estimate, first := trend.Observe(key)
  return !first && estimate >= 500 // climbing fast, not a one-off
}
#03

Fraud velocity checks

"Has this card or device been attempting things unusually often in the last few minutes?" Millions of entities, most seen once and never again — the churn case the fixed-memory guarantee exists for.

Go
velocity := epochsketch.New(epochsketch.Config{
  NumBuckets:   1 << 20,
  TickDuration: time.Minute,
})

func isSuspicious(cardFingerprint string) bool {
  estimate, _ := velocity.Observe(cardFingerprint)
  return estimate > 20 // attempts this minute
}

Note the different TickDuration in each example above — the decay window is the actual design decision here, not the threshold. A minute-long tick forgets a burst that ended five minutes ago; a second-long one wouldn't.

#04

Operational visibility

Every Sketch exposes Stats() — self-metrics for whatever's scraping your process, no separate instrumentation.

Go
s := sk.Stats()
fmt.Printf("evictions=%d chips=%d scrubbed=%d\n", s.Evictions, s.Chips, s.ScrubbedZombies)
FieldWhat it counts
CASRetriesExhaustedWrites dropped after 3 lost CAS races — fail-open, not corrupted
EvictionsChallengers that took over a fully-chipped slot
ChipsChallengers that weakened a victim by one tally, didn't take the slot
BucketOverflowPressureEviction attempts a challenger lost
ScrubbedZombiesSlots opportunistically cleared past the evaporation horizon

For the mechanism behind all of this — the word layout, the eviction contest, why decay needs no background process — see the design page. For the normative spec, see the docs.