carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1// Package analytics is the pure half of the Analytics tab (spec
2// 2026-09-07-errors-and-analytics-design.md §3.3): the visitor and top-key
3// sketches, the in-memory meter the edge feeds, the day-file wire shape
4// and its idempotent CAS merge, the daily salt, the bot list and the
5// cross-box range read. internal/edge observes, internal/host flushes,
6// internal/console reads — the applog/apperr split.
7package analytics
8
9import (
10 "encoding/base64"
11 "errors"
12 "math"
13 "math/bits"
14)
15
16// HLLPrecision is the register-index width: 2^11 registers, 2 KB, ~2.3%
17// standard error (spec §3.3). One sketch per app per day per box.
18const HLLPrecision = 11
19const hllRegisters = 1 << HLLPrecision
20
21// HLL is a HyperLogLog cardinality sketch over 64-bit hashes.
22type HLL struct{ reg [hllRegisters]uint8 }
23
24// mix64 is MurmurHash3's fmix64 finalizer. Add uses it to avalanche the
25// incoming hash before slicing out the register index and run bits: the
26// register index is the hash's top HLLPrecision bits, and callers' hash
27// functions (FNV-1a included — see h64 in hll_test.go) do not reliably
28// mix entropy into their high bits, especially for short, sequential
29// inputs sharing a common prefix ("v0".."v99999"), which otherwise
30// starves most registers and skews Count.
31func mix64(x uint64) uint64 {
32 x ^= x >> 33
33 x *= 0xff51afd7ed558ccd
34 x ^= x >> 33
35 x *= 0xc4ceb9fe1a85ec53
36 x ^= x >> 33
37 return x
38}
39
40// Add records one hashed element.
41func (h *HLL) Add(hash uint64) {
42 hash = mix64(hash)
43 idx := hash >> (64 - HLLPrecision)
44 rest := hash<<HLLPrecision | 1<<(HLLPrecision-1) // guard bit bounds the run
45 rank := uint8(bits.LeadingZeros64(rest)) + 1
46 if rank > h.reg[idx] {
47 h.reg[idx] = rank
48 }
49}
50
51// Merge folds o into h by register max — the union of the two sets.
52func (h *HLL) Merge(o *HLL) {
53 for i := range h.reg {
54 if o.reg[i] > h.reg[i] {
55 h.reg[i] = o.reg[i]
56 }
57 }
58}
59
60// Empty reports whether nothing was added.
61func (h *HLL) Empty() bool {
62 for _, r := range h.reg {
63 if r != 0 {
64 return false
65 }
66 }
67 return true
68}
69
70// Count estimates the cardinality (Flajolet et al. 2007 with the small-
71// range linear-counting correction).
72func (h *HLL) Count() float64 {
73 m := float64(hllRegisters)
74 alpha := 0.7213 / (1 + 1.079/m)
75 var sum float64
76 zeros := 0
77 for _, r := range h.reg {
78 sum += math.Pow(2, -float64(r))
79 if r == 0 {
80 zeros++
81 }
82 }
83 est := alpha * m * m / sum
84 if est <= 2.5*m && zeros > 0 {
85 est = m * math.Log(m/float64(zeros))
86 }
87 return est
88}
89
90const hllVersion = 1
91
92// Encode renders the sketch as base64 of a version byte plus the registers.
93func (h *HLL) Encode() string {
94 b := make([]byte, 1+hllRegisters)
95 b[0] = hllVersion
96 copy(b[1:], h.reg[:])
97 return base64.StdEncoding.EncodeToString(b)
98}
99
100// DecodeHLL parses Encode's output; "" is an empty sketch, an unknown
101// version is an error (a future shape this build cannot read).
102func DecodeHLL(s string) (*HLL, error) {
103 h := &HLL{}
104 if s == "" {
105 return h, nil
106 }
107 b, err := base64.StdEncoding.DecodeString(s)
108 if err != nil {
109 return nil, err
110 }
111 if len(b) != 1+hllRegisters || b[0] != hllVersion {
112 return nil, errors.New("analytics: unknown visitors sketch version or size")
113 }
114 copy(h.reg[:], b[1:])
115 return h, nil
116}
117