R7.03.1 · dive 1
Cardinality at a fixed 12KB.
HyperLogLog estimates how many distinct items a set holds, at a fixed 12KB regardless of how many you add, with about 0.81% standard error. The probabilistic trade in its clearest form.
A plain Set is exact — one billion unique ids cost roughly 12GB. A HyperLogLog is fixed: 12KB for that billion, and for a thousand, and for a trillion. Select a representation; the readout reports its memory and its error. It answers how many, never which — so it cannot stand in for EchoMQ's exact dedup.
The memory cliff
probabilistic-data-structures.md.txt · cardinalityA plain Set is exact: to count one billion unique ids it stores all one billion, roughly 12GB of RAM. A HyperLogLog is fixed: 12KB for that billion, and 12KB for a thousand, and 12KB for a trillion. The cost does not grow with the cardinality — it is paid once.
The structure hashes each element and watches the maximum run of leading zeros. The chance of an N-zero run is 1/2N, so a long run is evidence of many distinct elements. A single estimator would be noisy, so HLL keeps many registers and combines them with a harmonic mean. The result is an estimate with about 0.81% standard error — for one million actual uniques, typically between 991,900 and 1,008,100.
The trade is exactness for memory. You cannot ask an HLL “is user:123 in here?”
— it stores no members, only the evidence of how many there were. What comes back is a count, close
but not exact.
A 12GB exact count becomes a 12KB estimate with a 0.81% error. The cost is fixed for any cardinality, and the price is that an HLL answers how many, never which.
The commands — core Valkey
probabilistic-data-structures.md.txt · PFADD / PFCOUNT / PFMERGEHyperLogLog is core Valkey: PFADD, PFCOUNT, and PFMERGE ship
in the engine, no module needed. The PF prefix is a nod to Philippe Flajolet, who co-invented
the algorithm.
PFADD visitors:2024-01-30 "user:123" "user:456" "user:789" # add elements (creates the key) PFCOUNT visitors:2024-01-30 # the estimated cardinality PFMERGE visitors:week visitors:2024-01-28 visitors:2024-01-29 # union of HLLs — no double-count
PFADD returns 1 if the internal registers changed (a likely-new element) and 0 if not.
PFMERGE computes the union — the count of distinct elements across several HLLs —
which is how a daily uniques HLL rolls up into a weekly one without double-counting a visitor who returned.
Hover the steps below to follow an add and a count through the registers.
Notes on ValkeyPFADD returns 1 when the registers changed
and 0 otherwise; an HLL is stored in a string and starts in a compact sparse encoding, converting to the
fixed-size dense encoding as the cardinality grows — which is why a small HLL is under 12KB and a large
one settles at 12KB —
valkey.io/commands/pfadd.
Three core commands — add, count, merge — over a string of registers. The count is derived from the registers, not from stored members, which is why the footprint is fixed and the answer is an estimate.
The EchoMQ contrast — exact, by choice
echo_mq/metrics.ex · the road not takenEchoMQ counts and dedups exactly, and uses no HyperLogLog anywhere. Its dedup parks each producer idempotency key at a real string key holding the actual branded job id:
def get_deduplication_job_id(conn, queue, dedup_id) when is_binary(dedup_id) do key = Keyspace.queue_key(queue, "de:" <> dedup_id) case Connector.command(conn, ["GET", key]) do {:ok, nil} -> :absent {:ok, id} when is_binary(id) -> {:ok, id} other -> other end end
The difference is the question each answers. An HLL answers “how many distinct ids”
approximately. EchoMQ's de: key answers “which id, exactly” — it must,
because the dedup decides whether to admit or drop a job, and a wrong “already seen” silently
loses work. HyperLogLog cannot answer the membership question at all (it stores no members), and even if it
could, its error rate is the wrong property for idempotency. So the road EchoMQ took is the exact key;
HyperLogLog is the road for the other problem — counting a huge population cheaply.
HyperLogLog: cardinality of a huge set at a fixed 12KB, ~0.81% error. Exactness traded for a flat, tiny footprint; no members stored.
The emq:{q}:de:<dedupId> key is exact membership — O(n) memory, zero false
positives. The dedup must be exact, so EchoMQ pays the memory rather than estimate.
HyperLogLog is the trade in its clearest form: a 12GB exact count becomes a 12KB estimate.
Use it when you need how many across a huge population and a sub-percent error is fine — and
notice it cannot answer which, which is exactly why EchoMQ's idempotency keeps an exact key. A
codemojex note, forward-tense: counting distinct players per day —
PFADD cm:uniques:2024-01-30 <player> on each guess, PFCOUNT for the dashboard
— is the natural HLL fit, but codemojex ships no HLL surface today, so it is a possibility for the
workshop's “uniques” view, not a claim about the live code.
The exact-dedup home
The emq:{q}:de:<dedupId> key and the @enqueue EXISTS refusal
are taught in full in R7.1, the
system of record: the job HASH is the record of truth, and its dedup is exact by the same logic.
The architecture
The exact dedup follows from the Branded Component System discipline — the branded id is the
identity the de: key is built from. The Branded Component System is the
architecture EchoMQ is built to.
References
sources · relatedSources
- Valkey — PFADD — add elements to a HyperLogLog; core Valkey, no module.
- Valkey — PFCOUNT — the estimated cardinality of one or more HyperLogLogs.
- Valkey — PFMERGE — the union of several HyperLogLogs into one.
- Redis — HyperLogLog — how the registers and the harmonic mean give a 0.81%-error cardinality.
Related in this course
- R7.03 · Probabilistic data structuresthe module hub and the exact-vs-probabilistic frame
- R7.03.2 · Bloom & Cuckoomembership, where HLL cannot help
- R7.01.1 · System of recordthe exact de: dedup key in context
- /bcs · The Branded Component Systemthe architecture EchoMQ is built to