A team wants to cut key bytes in an in-memory store by hashing each long key to a short fixed-width string. What does that trade?
answer
- price the saving before doing the work
- bytes removed times the key count
- a digest cannot be read back
- collisions here are silent, not errors
- hash the tail, keep the prefix readable
basics
~20 sIt trades legibility and a small chance of silent collision for a saving of exactly the bytes removed times the distinct-key count. Compute that product first: a collision here is one caller reading another's value, unreported.
solid answer
~50 sThe gain is arithmetic and easy to check: bytes removed from each key, multiplied by the **distinct-key count**. If values are kilobytes, that product is a rounding error and the work is not worth doing. The costs are two. First, **attribution**: a digest cannot be read back, so nobody on call can tell which team, feature or tenant an entry belongs to, and any grouping that rested on the key text is gone. Second, **collision**: two different original keys can produce one digest, and because the store has no schema it cannot know two things claimed one address, so the failure is a silently wrong value rather than an error. Try the cheaper move first - cut segments that are constant on every key - and if you do hash, keep a readable prefix in the clear and hash only the tail.
go deeper
Remember that a hashed key is shorter but unreadable, and that two different keys can produce the same short string. The store cannot notice that, because it has no idea what a key was supposed to mean.
Explain both sides: the saving is bytes removed times the distinct-key count, and the risk is a birthday-bound collision that surfaces as a wrong value rather than an error.
Show the order of operations: measure the saving, cut constant segments first, keep a readable prefix, size the digest against the eventual key count, and treat changing a live scheme as separate work.
The real question is what the organisation owes the person on call in two years. An unreadable keyspace on a shared tier cannot be attributed or reclaimed, and that cost outlives whoever saved the bytes.
Hashing keys is a real technique and a frequently premature one. The discipline is to price the gain before accepting the costs, because the gain is a simple product and the costs are not recoverable later. ## The gain is one multiplication Saving equals (old key bytes minus new key bytes) times the distinct-key count. Nothing else enters it. That immediately settles most cases: - ninety-byte keys cut to sixteen, over forty million entries: roughly three gigabytes recovered — worth a conversation; - the same cut over two hundred thousand entries: about fifteen megabytes — not worth the meeting; - the same cut where each value is four kilobytes: under half a percent of the tier — invisible. Do this subtraction first. A large fraction of proposals to hash keys die here, which is the cheapest place for them to die. ## Cost one: the keyspace stops being readable A digest is one-way by construction, so the mapping from key back to meaning exists only in the application that computed it. What that removes: - **Attribution.** On a shared tier, the key text is usually the only evidence of which team or feature created an entry. Without it, an operator looking at a tier that is full has no way to say whose growth filled it. - **Grouping.** Summing memory by prefix, or removing everything belonging to one tenant, rests entirely on the key text. Where the store offers no container above the key, the prefix is the only grouping mechanism there is, and hashing destroys it. - **Debugging.** An engineer holding an entry and asking what it is has nowhere to look. - **Placement, on some topologies.** Where a store derives node placement from part of the key, rewriting keys changes where they land; the mechanics of that belong to the subject of assigning keys to nodes, but it is a consequence to check before rewriting a live scheme. ## Cost two: collisions, and why they are silent here A fixed-width digest maps unlimited inputs onto a finite set of outputs, so two distinct original keys can produce the same address. The probability follows the birthday bound: for **n** keys and a **d**-bit digest, the chance that some pair collides is roughly n squared over 2 to the power of (d plus one). Two consequences follow. 1. **Count the keys the system will ever mint**, not today's. The probability rises with the square of the count, so a design that is safe at ten million is a hundred times more exposed at a hundred million. 2. **Nothing will tell you.** The store has no schema and no notion that two different things claimed one address; it will simply serve the second writer's value to the first writer's reader. On a multi-tenant tier that is a cross-tenant data leak arriving as a correctness bug, not as an alert. So the width is a design parameter, chosen so that the expected time to a first collision far exceeds the life of the system, and the digest must be computed over the *whole* original key so that nothing distinguishing was dropped before hashing. ## The order to try things in | Move | What it costs | Reversible? | |---|---|---| | remove segments constant on every key | nothing; they distinguish nothing | yes | | abbreviate long descriptive segments by convention | a little legibility | yes, if the mapping is written down | | mint fewer keys, where the server understands the value | traffic, on stores of opaque bytes | yes | | hash the tail, keeping a readable prefix in the clear | partial legibility, small collision risk | no, for the hashed part | | hash the whole key | attribution and grouping entirely | no | Work down that list and stop at the first row that gets you the bytes. The middle rows are usually enough, and they keep the property that matters most on a tier other teams share: that the keyspace can be read by whoever inherits it. ## One thing this is not None of this addresses moving a scheme that is already carrying traffic from the old shape to the new one — readers on both shapes, the window where each entry exists twice, the point where the old shape can be retired. That migration is its own piece of work and its own subject; plan it separately, and never start it on the strength of an unmeasured saving.
- How do you decide how wide the digest must be?From the birthday bound and the count the system will ever reach, not today's. With n keys and a d-bit digest the chance of some colliding pair is roughly n squared over 2 to the power of (d plus one), so the exposure grows with the square of the count. Pick a width whose expected time to a first collision is far longer than the life of the system, then check the saving still exists at that width.
- What is the cheaper move to try before hashing anything?Delete the segments that are identical on every key. They distinguish nothing, they are multiplied by the whole key count, and removing them cannot make two entries share an address. After that, abbreviate long descriptive segments against a written-down mapping, and consider whether fewer keys can address the same data. Hash only if those leave the bytes still unaffordable.
- What breaks operationally on a tier with no container above the key?Prefix grouping, which is then the only grouping mechanism available. Attributing memory to a team, removing one tenant's entries, and answering who filled the tier all rest on reading the key text. Hashing the whole key removes that, so the compromise is to keep the owning prefix in the clear and hash only the distinguishing tail.
saying these in an interview costs you the question
- Hashes keys without computing what the saving actually is
- Believes a collision would surface as an error from the store
- Sizes the digest against today's key count rather than the eventual one
- Truncates the distinguishing part of the key to save bytes
- Assumes the store can reverse a digest back to the original key
- Treats key legibility as having no operational value