Academic Project Defense · Advanced Algorithms

Distributed Key-Value Engine: Consistent Hashing, Cuckoo Hashing & Bloom Filters

An algorithmic evaluation of deterministic routing, worst-case constant-time local storage, and probabilistic query acceleration under dynamic cluster membership churn.

Presented by: Arnav Angarkar 24bcs015 Amogh Itagi 24bcs014 · Department of Computer Science & Engineering
System Model

Storing Millions of Keys Across Multiple Machines

A single node runs out of physical memory and storage. We partition our key-value dataset across a cluster of independent storage nodes: Server A, Server B, and Server C.

RECORD 101
user:101 ➔ "Arnav"

User profile document containing authentication metadata and preferences.

RECORD 102
user:102 ➔ "Priya"

Session state token and encrypted account record.

RECORD 501
video:501 ➔ video_payload

High-throughput binary media chunk distributed across servers.

Interface Definition

The Core Client API: PUT, GET, and DELETE

Every client request must execute deterministically without global coordination bottlenecks:

01 · WRITE
PUT(key, value)

Locate responsible server, update the probabilistic filter, and insert into local table.

02 · READ
GET(key)

Route to responsible server, verify membership, and retrieve in bounded time.

03 · REMOVE
DELETE(key)

Locate the record and cleanly remove it without leaving corrupt tombstones.

Problem Formulation

Five Fundamental Problems in Distributed Storage

1. Cluster Partitioning

Which node owns a given key without a centralized metadata bottleneck? ➔ Consistent Hashing

2. Local Storage Engine

How to store keys in local RAM with strict worst-case lookup bounds? ➔ Cuckoo Hashing

3. Absent-Key Query Overhead

How to prevent disk/RAM access when a queried key does not exist? ➔ Bloom Filter

4. Elastic Cluster Churn

What happens when nodes join or crash? We must minimize data movement. ➔ Virtual Nodes

System Decomposition

Three Distinct Tiers Working in Harmony

These algorithms are not variations of each other; they operate at distinct structural tiers:

TIER 1 · CLUSTER ROUTING
Consistent Hashing

Selects the responsible physical server across the distributed network topology.

TIER 2 · GATEKEEPER
Bloom Filter

Probabilistically tests set membership in O(k) time with 0% False Negatives.

TIER 3 · LOCAL STORAGE
Cuckoo Hashing

Stores key-value entries in local memory with guaranteed worst-case O(1) lookup.

Mathematical Foundations

Mathematical Definition: Uniform Discrete Mapping

A hash function takes an arbitrary-length input key and maps it deterministically to a fixed integer domain:
h : Keys ➔ {0, 1, ..., m - 1}

Illustrative Arithmetic: h(x) = x mod 10

Key 17 ➔ 17 mod 10 = 7
Key 24 ➔ 24 mod 10 = 4
Key 37 ➔ 37 mod 10 = 7 (Collision!)
Key 42 ➔ 42 mod 10 = 2

Information Theory & Non-Invertibility

Hash functions are many-to-one functions. They are not encryption: information is intentionally compressed and cannot be algebraically inverted.

Non-cryptographic hashes (MurmurHash3, FNV-1a) optimize for uniform bit mixing and speed.

Live Visualizer

Bit Mixing, Avalanche & Why Hashing Cannot Be Inverted

Ready. Click "⚡ Mix & Hash" to trace bitwise avalanche, or "⚠️ Attempt De-Hash" to inspect pre-image collisions.
The Mathematical Trapdoor: Why We Cannot "De-Hash"

1. Surjection & Pigeonhole Principle: A hash function maps an infinite set of variable-length strings into a finite 32-bit domain ([0, 232 − 1]), and further reduces it via modulo (mod 10). Infinitely many inputs collide on the same bucket.

2. Permanent Information Destruction: In the reduction x = 10 · Q + R, the CPU permanently discards the quotient Q. Reconstructing the original integer requires guessing from an infinite domain.

3. Avalanche Non-Linearity: Bitwise shifts and XOR operations scramble entropy so thoroughly that gradient or inverse arithmetic does not exist.

Discrete Mathematics

Collisions Are Inevitable: Dirichlet's Principle

If the cardinality of the key space |Keys| exceeds the hash table capacity m, collisions must occur by the Pigeonhole Principle:

Mathematical Certainty

There must exist at least two distinct keys k1 ≠ k2 such that:

h(k1) = h(k2)

A collision is not a flaw in the hash function; it is a fundamental property of mapping large sets to smaller finite sets.

How Algorithms Handle Collisions
  • Separate Chaining: Stores collisions in linked lists (risk of O(N) degradation).
  • Open Addressing: Probes adjacent slots (suffers from clustering).
  • Cuckoo Hashing: Evicts conflicting elements to alternate slots (guarantees O(1)).
Cluster Partitioning

The Naive Approach: Modulo Partitioning

The simplest possible server assignment algorithm maps a key to N servers via:
server(k) = h(k) mod N

KeyHash ValueCalculation (N = 3)Target Server
user:1011717 mod 3 = 2Server 2
user:1022424 mod 3 = 0Server 0
user:1033131 mod 3 = 1Server 1
user:1044242 mod 3 = 0Server 0
user:1055656 mod 3 = 2Server 2
Scaling Failure

Cluster Scaling: The Modulo Resharding Catastrophe

When traffic grows and we add a 4th server, the divisor shifts from mod 3 to mod 4:

Key Assignment Under N = 4

user:101 (17) ➔ S2 ➔ S1 (Remapped!)
user:102 (24) ➔ S0 ➔ S0 (Stable)
user:103 (31) ➔ S1 ➔ S3 (Remapped!)
user:104 (42) ➔ S0 ➔ S2 (Remapped!)
user:105 (56) ➔ S2 ➔ S0 (Remapped!)
user:106 (67) ➔ S1 ➔ S3 (Remapped!)

75% Key Churn!

Under uniform hashing, adding 1 node remaps:
E[Δ] = N / (N + 1) = 3/4 = 75%

In a 10 TB cluster, 7.5 TB must migrate across network switches, causing cache stampedes and downtime.

Ring Topology

The Solution: The Circular Hash Ring

Wrap the hash domain [0, 232 − 1] (or illustrative [0, 99]) into a continuous circle where position 99 wraps directly to 0.

1. Hash the Servers

Hash physical server identifiers onto ring positions:

h(A) = 20 · h(B) = 50 · h(C) = 80

2. Hash the Keys

Hash user keys onto the exact same circular space:

k1=10 · k2=35 · k3=65 · k4=90

3. The Successor Rule

Walk clockwise from a key's position until encountering the first server token. That server owns the key!

Remapping Guarantee

Adding Server D at Position 40: Only 1 Key Moves

Interval Ownership Shifting

Previously, keys in interval (20, 50] belonged to Server B.

  • Keys in (20, 40] now transfer to Server D.
  • Keys in (40, 50] remain with Server B.
  • Servers A and C: Zero keys moved! Completely unaffected!
1 of 4 Keys Move!

Under Consistent Hashing, the expected remapped fraction is:
E[Δ] = 1 / (N + 1) = 1/4 = 25%

A 3x reduction in network data migration compared to modulo sharding.

Live Visualizer

Interactive Ring Simulator: Animated Clockwise Routing

40°
Servers: A @ 20°, B @ 50°, C @ 80°, D @ 40°.
k1 (10°): Server A (Stable)
k2 (35°): Server D (Transferred from B)
k3 (65°): Server C (Stable)
k4 (90°): Server A (Wraps past 99° ➔ 0° ➔ 20°)
Variance Challenge

The Non-Uniformity Problem of Simple Rings

A critical distinction for algorithm defense: Consistent hashing minimizes remapping, but it does NOT guarantee equal load distribution on its own!

Variance in Arc Lengths

If physical nodes A, B, and C are placed at 20, 50, and 80, Server A owns from 80 through 99, 0, to 20 ➔ 40% of the entire ring.

With few servers, random placement creates severe hotspots.

Algorithmic Trade-off

Fast, deterministic routing is not the same as optimal load balancing. How do we equalize interval sizes without expensive centralized coordination?

The solution: Virtual Nodes.

Variance Reduction

Virtual Nodes (vnodes): Variance Scales as O(1/√v)

Instead of hashing each physical server once, assign each server v distinct logical tokens across the ring:

Server A

A#vn0, A#vn1, ... A#vn50

50 independent positions scattered uniformly.

Server B

B#vn0, B#vn1, ... B#vn50

50 independent positions scattered uniformly.

Server C

C#vn0, C#vn1, ... C#vn50

50 independent positions scattered uniformly.

By the Law of Large Numbers, the standard deviation of load across physical nodes decays as O(1 / √v). Hotspots are eliminated!

Implementation

C++ Implementation: Logarithmic Binary Search

// Binary search via std::map::lower_bound in O(log V) std::string ConsistentHashRing::get_server(const std::string& key) const { if (ring_.empty()) return ""; uint32_t key_token = hash_fn(key); // Find first token >= key_token on the sorted ring auto it = ring_.lower_bound(key_token); // Cyclic wrap-around: past the end wraps to ring.begin() if (it == ring_.end()) { it = ring_.begin(); } return it->second; // Return responsible physical server ID }

With V = N × v total tokens, successor lookup is strictly O(log V) using red-black tree traversal.

Local Storage Tier

Inside the Server: Why Cuckoo Hashing?

Once a request arrives at the designated server, how does that server look up the key in local memory without worst-case degradation?

Separate Chaining Degrades

Under hash collisions, buckets grow long. Worst-case lookup degenerates to O(N). Pointer chasing induces CPU L1/L2 cache misses.

The Cuckoo Hashing Alternative

Pagh and Rodler (2001): Inspired by the cuckoo bird that evicts other eggs from nests.

Guarantees bounded worst-case O(1) lookup with at most 2 candidate slot reads!

Cuckoo Principle

The Invariant: Exactly Two Candidate Slots

We maintain two independent hash functions: h1(k) and h2(k). Any key k can ONLY reside in slot h1(k) or slot h2(k).

Key A

Candidate 1: Slot 2
Candidate 2: Slot 7

Key B

Candidate 1: Slot 2
Candidate 2: Slot 5

Key C

Candidate 1: Slot 4
Candidate 2: Slot 7

A key is never permitted in any third slot. This single invariant is what bounds lookup time.

Eviction Mechanics

Insertion with Displacement: Key D Kicks Key A

Initial Insertion Sequence (Slots 0 to 9)

Insert A (2, 7) ➔ Placed in Slot 2.
Insert B (2, 5) ➔ Slot 2 occupied; placed in Slot 5.
Insert C (4, 7) ➔ Placed in Slot 4.
Now insert Key D (candidates 2 and 5): Both occupied!

The Displacement Sequence

1. D takes Slot 2, evicting resident Key A!

2. A computes its alternate slot: h2(A) = 7.

3. Slot 7 is empty! Key A docks in Slot 7.

Chain settles in 1 kick with zero data loss!

Live Visualizer

Interactive Cuckoo Table: Animated Step-by-Step Kicks

Ready. Click "Insert 5 Keys" to observe displacement chain resolution.
Table Slots (Capacity: 8)
Complexity Guarantee

Worst-Case O(1) Lookup: Strictly At Most 2 Probes

To retrieve a key, the algorithm inspects at most two deterministic memory positions:

PROBE 1
Read Slot h1(k)

If key matches, return the value immediately.

PROBE 2
Read Slot h2(k)

If key matches, return the value immediately.

DEFINITIVE
Max 2 Reads

If neither matches, the key is 100% absent. No linked-list traversal!

Tombstone-Free Deletions: Check both candidate slots, clear the occupied flag. No tombstone markers needed.

Implementation

C++ Implementation: Constant-Time Retrieval

// WORST-CASE O(1) LOOKUP: Checks at most 2 candidate slots std::optional<ValueType> CuckooHashTable::get(const std::string& key) const { size_t pos1 = h1(key); if (table_[pos1].occupied && table_[pos1].key == key) return table_[pos1].value; size_t pos2 = h2(key); if (table_[pos2].occupied && table_[pos2].key == key) return table_[pos2].value; return std::nullopt; // Definitive absence in <= 2 reads }

Zero loop overhead. Perfect CPU cache predictability.

Query Amplification

The Absent-Key Crisis: 90% of Queries are Misses

In real production distributed databases (Bigtable, Cassandra), up to 90% of read requests are for keys that do not exist.

I/O Amplification Penalty

Every miss forces the server to traverse RAM or query storage controllers only to conclude "Not Found", saturating hardware buses.

The Gatekeeper Solution

Maintain a compact in-memory filter that rejects absent keys in O(1) before touching the table.

This is the role of the Bloom Filter.

Probabilistic Structure

Bloom Filter: Bit Array of Size m and k Hash Functions

A bit array B[0 .. m−1] initialized to all 0s, with k independent uniform hash functions.

Insertion Algorithm (Key: "Arnav")

h1("Arnav") = 2
h2("Arnav") = 5
h3("Arnav") = 8

Set bits at indices 2, 5, and 8 to 1.

Compact Bitset Representation

[0, 0, 1, 0, 0, 1, 0, 0, 1, 0]

The key string itself is never stored! Only bit flags are set, providing extreme space efficiency.

Membership Guarantees

Query Invariant: Zero False Negatives Guaranteed

Query "Arnav": Indices 2, 5, 8

Bit 2 is 1? Yes.
Bit 5 is 1? Yes.
Bit 8 is 1? Yes.

➔ "Possibly Present." Query local table.

Query "Priya": Indices 1, 5, 7

Bit 1 is 1? NO (It is 0!)

➔ "DEFINITELY ABSENT!"

Stop immediately! Return 404 with 0 table lookups.

The Fundamental Property: If a key was inserted, its k bits were set to 1. If any bit is 0, the key was mathematically never inserted (P(False Negative) = 0).
Live Visualizer

Interactive 16-Bit Array: Bit Ray Projections

Ready. Insert "Arnav", then query "Priya" to see instant short-circuiting!
16-Bit Array (k = 3 Hashes)
Probabilistic Analysis

Mathematical Derivation: Optimal Hash Count

False Positive Rate Formula

p ≈ (1 - e-kn/m)k

For m = 10,000 bits and n = 1,000 keys (10 bits/key):

Optimal k = (m/n) ln 2 ≈ 7 hashes ➔ p ≈ 0.82% FPR!

Why Deletion is Unsafe (Oral Exam Trap!)

Standard Bloom filters do not support deletions.

If Key A and Key B both set bit 5, clearing bit 5 to delete Key A inadvertently corrupts Key B, inducing illegal false negatives.

Counting Bloom filters (4-bit counters) are required for deletion.

Implementation

C++ Implementation: Double Hashing Optimization

// Kirsch-Mitzenmacher: Generate k hashes from only 2 base hashes bool BloomFilter::possibly_contains(const std::string& key) const { uint64_t h1 = fnv1a(key); uint64_t h2 = mix_hash(key); for (size_t i = 0; i < k_hashes; ++i) { size_t idx = (h1 + i * h2 + i * i) % m_bits; if (!bitset[idx]) return false; // Found a 0-bit -> DEFINITELY ABSENT! } return true; // All bits 1 -> Possibly present }

Kirsch-Mitzenmacher theorem: 2 base hashes simulate k independent hashes with zero asymptotic loss in accuracy.

Integrated Architecture

The Grand Synthesis: Tracing a GET Request

Case A: Key Present

1. Ring routes key to Server Beta.
2. Bloom filter says "Possibly present".
3. Cuckoo table retrieves value in slot 2.
Result: 200 OK

Case B: Key Absent (90% Cases)

1. Ring routes key to Server Beta.
2. Bloom filter detects 0-bit!
3. SHORT-CIRCUITED! Zero table lookups!
Result: 404 NOT FOUND

Case C: False Positive

1. Ring routes key to Server Beta.
2. Bloom says "Possibly present".
3. Cuckoo checks slots 1 & 2: not there.
Result: 404 in 2 reads!

Live Visualizer

Interactive Cluster Pipeline: Watch Request Traversal

Pipeline ready. Click "Execute Absent GET" to watch the Bloom filter halt execution!
🌐
Hash Ring
➔
🛡️
Bloom Filter
➔
⚡
Cuckoo Table
Lookups Saved by Bloom Filter: 0
Empirical Rigor

Empirical Benchmark: All 5 Configurations Evaluated

Benchmarked across 5,000 queries using our compiled C++17 engine (code/kv_bench):

Configuration 0% Miss Latency 90% Miss Latency Table Probes (90%) Probes Avoided
A: Ring + Baseline Hash0.12 μs0.11 μs5,0000
B: Ring + Bloom + Base0.11 μs0.16 μs5014,499
C: Ring + Cuckoo0.09 μs0.13 μs5,0000
D: Cuckoo Alone (Control)0.03 μs0.07 μs5,000N/A
E: Ring + Bloom + Cuckoo 0.11 μs 0.12 μs 501 4,499 (90% Avoided!)
Theoretical Mastery

Algorithmic Complexity & Connection to P vs NP

Theoretical Guarantees

Ring Successor Search ➔ O(log V)
Bloom Filter Membership ➔ O(k)
Cuckoo Table Lookup ➔ O(1) worst-case!
Cuckoo Table Insertion ➔ O(1) amortized

Connection to NP-Completeness

Optimal load balancing across machines (Minimum Makespan Scheduling) is NP-complete.

Consistent Hashing does not solve that offline NP-complete problem. It is a fast online randomized heuristic providing dynamic stability.

Oral Exam Preparation

Top 10 High-Yield Professor Defense Questions

1. Why not use hash(key) % N?
2. Does Consistent Hashing guarantee equal load?
3. Why can't Bloom filter replace a hash table?
4. Can a Bloom filter have false negatives?
5. Why is Cuckoo lookup worst-case O(1)?
6. Is Cuckoo insertion always O(1)?
Conclusion & Artifacts

System Defended: Reproducible C++ Engine

Verified C++ Implementation C++17 Benchmark Suite
💻 Browse Full Code on GitHub → Terminal: cd code && make run
Key Oral Defense Takeaway

We demonstrated that the synthesis of Consistent Hashing (routing tier), Bloom Filters (short-circuiting tier), and Cuckoo Hashing (storage tier) eliminates worst-case latency cliffs, guarantees strictly O(1) table reads, and minimizes cluster migration churn to 1 / (N + 1).

💡 "Theoretical bounds mathematically proven; empirical performance benchmarked and defended."