Skip to content

Commit e42fb98

Browse files
committed
Update README to document replica-shuffle read path and hot key load test
1 parent 6e3a0e1 commit e42fb98

1 file changed

Lines changed: 31 additions & 6 deletions

File tree

README.md

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,16 +167,22 @@ Write: aux3 ← "hello"="world" (primary)
167167

168168
All replica writes fire in parallel goroutines. The master returns 200 as long as at least one write succeeds. If one replica is temporarily down, the write still lands on the other.
169169

170-
**Read path**primary-first with fallback:
170+
**Read path**random replica selection with fallback:
171171

172-
The master tries the primary node first. If it is unreachable or returns a non-200, it falls through to the secondary. The client sees a 200 either way.
172+
The master shuffles the replica list for each GET before trying nodes in order. This spreads reads evenly across all replicas so no single node becomes a bottleneck when one key receives disproportionately high traffic (a hot key). If the chosen replica is unreachable or returns a non-200, the master falls through to the next one. The client sees a 200 either way.
173173

174174
```
175175
GET "hello":
176-
try aux3unreachable (node is down)
176+
replicas shuffled[aux1, aux3]
177177
try aux1 → 200 {"key":"hello","value":"world"}
178+
179+
GET "hello" (next request):
180+
replicas shuffled → [aux3, aux1]
181+
try aux3 → 200 {"key":"hello","value":"world"}
178182
```
179183

184+
Bulk GETs apply the same logic per key: each key is independently assigned to a random replica when batching outbound requests to aux nodes.
185+
180186
**Delete path** — remove from all replicas:
181187

182188
A DELETE is sent to all replica nodes. Returns 200 if at least one held the key. This prevents "ghost reads" where a deleted key re-appears from a surviving replica.
@@ -252,9 +258,13 @@ The primary pushes a `RingUpdate{action, aux}` event to the standby over `/ring-
252258
1. Client sends GET /data/x
253259
2. Nginx routes to either master (reads are load-balanced)
254260
3. Master calls GetNodes("x", replicationFactor) → [aux2, aux3]
255-
4. Master tries aux2 first:
256-
GET aux2/data/x → 200 {"key":"x","value":"y"}
257-
5. Returns response to client immediately (aux3 never contacted)
261+
4. Master shuffles the replica list → e.g. [aux3, aux2]
262+
5. Master tries aux3 first:
263+
GET aux3/data/x → 200 {"key":"x","value":"y"}
264+
6. Returns response to client immediately (aux2 not contacted this time)
265+
266+
On the next request for the same key the shuffle may produce [aux2, aux3],
267+
so aux2 serves it — reads are spread across replicas over time.
258268
```
259269

260270
### TTL expiry
@@ -852,6 +862,21 @@ locust -f locust.py --host http://localhost:8080 \
852862
| `ReadHeavyUser` | 70% | 4 GETs per PUT across a 500-key pool — simulates a typical cache consumer |
853863
| `WriteHeavyUser` | 20% | High write rate with unique keys + write-then-read consistency checks — simulates an ingestion pipeline |
854864
| `BulkUser` | 10% | Bulk PUT and bulk GET with batches of 10-50 keys — exercises the shard batch-locking path |
865+
| `HotKeyUser` | 10% | Hammers a single key (`hotkey:burn`) at high frequency to verify that replica-shuffle distributes hot key reads across all replicas rather than saturating one aux node |
866+
867+
**Verifying hot key distribution:**
868+
869+
After a run, query each aux node's Prometheus metrics to confirm reads were spread across the replicas for the hot key:
870+
871+
```bash
872+
for port in 9001 9002 9003 9004; do
873+
gets=$(curl -sf "http://localhost:$port/metrics" \
874+
| awk '/^auxiliary_request_total\{method="GET"/ {sum += $2} END {print sum+0}')
875+
echo "aux:$port GETs = $gets"
876+
done
877+
```
878+
879+
The two aux nodes that own the hot key's ring slot should show comparable GET counts. Nodes that don't own the slot will show significantly lower counts (they only serve other keys in the general pool).
855880
856881
**Observed results on 3x t3.medium EKS nodes (ap-southeast-2), 100 concurrent users:**
857882

0 commit comments

Comments
 (0)