Real-Time Matching Architecture Using Redis (Deep Dive)
Finding nearby restaurants is a search problem. Finding a nearby delivery agent is a real-time coordination problem.
This blog focuses on how large-scale systems match a restaurant or user with the nearest available delivery agent in near real time.
1️⃣ Problem Definition
Inputs
- Pickup location (restaurant or user)
- Thousands of moving agents
- Agent state: available, assigned, offline
Output
- Exactly one agent assigned
- Low ETA
- < 100ms decision time
2️⃣ Data Characteristics (Why Search Systems Fail Here)
| Attribute | Behavior |
|---|---|
| Location | Updates every 2–5 seconds |
| Availability | Highly volatile |
| Write throughput | Extremely high |
| Consistency | Near real-time |
➡️ This is not a search problem
➡️ This is a real-time in-memory coordination problem
3️⃣ High-Level Architecture
Agent App → Location Updates → Redis (Geo Index)
↓
Order Created → Matching Service → Availability Filter
↓
Assignment Lock
↓
Notification Service
Key idea: Location, availability, and assignment are separate concerns.
4️⃣ Core Redis Data Structures
1. Location Index (Geo / Geohash)
Stores only where agents are, nothing else.
GEOADD drivers:geo:blr 77.5946 12.9716 driver_123
This structure answers one question:
“Who is physically nearby?”
2. Availability Set (Separate)
Availability is volatile and must not be embedded in geo keys.
SADD drivers:available:blr driver_123
This answers:
“Who is eligible to receive an order?”
3. Assignment Lock (Separate)
Assignment is transient and must be atomic.
SET agent:123:lock order_789 NX EX 10
This ensures:
“No agent is assigned twice.”
5️⃣ Why Availability Must NOT Be Stored with GeoHash
Embedding availability inside geo keys causes serious problems.
| Geo + Availability Combined | Separated Structures |
|---|---|
| Frequent reindexing | Stable geo index |
| High write amplification | Cheap set updates |
| Complex cleanup | Simple TTL / set ops |
| Poor scalability | Horizontal scaling |
Rule of thumb:
📍 Geo index answers where
✅ Availability answers who
🔒 Lock answers who wins
6️⃣ Spatial Segmentation (Preprocessing)
To avoid hot keys and large scans, systems segment the map.
geo_cell = geohash(lat, lon, precision=7)
Data layout:
drivers:geo:cell:tdr9k
drivers:available:cell:tdr9k
Each cell is independent and scalable.
7️⃣ Matching Flow (Step-by-Step)
- Identify pickup geo cell
- Fetch nearby agent IDs from GEO index
- Intersect with availability set
- Sort by ETA / distance
- Attempt assignment via lock
Intersection example:
nearby_agents = GEORADIUS(...)
available_agents = SMEMBERS(...)
candidates = intersection(nearby_agents, available_agents)
8️⃣ Sequential Assignment (Preferred Strategy)
Most delivery platforms use sequential assignment.
for agent in candidates:
if acquire_lock(agent):
send_request(agent)
wait T seconds
if accepted:
assign
break
else:
release_lock(agent)
This minimizes agent spam and improves acceptance quality.
9️⃣ Handling Rejections & Timeouts
- Timeout → agent returned to availability set
- Reject → next best candidate
- No response → TTL auto-release
Redis TTLs prevent stuck assignments.
🔟 Write Amplification Control
Agents update location frequently. Mitigations include:
- Update only if moved > X meters
- Adaptive frequency (slow vs fast movement)
- Batch updates
Availability changes are cheap set operations, not geo rewrites.
1️⃣1️⃣ Failure Handling
| Failure | Mitigation |
|---|---|
| Agent crash | TTL expiry |
| Redis node down | Sharding + replicas |
| Network lag | Optimistic retries |
1️⃣2️⃣ Why Elasticsearch Is the Wrong Tool Here
| Elasticsearch | Redis |
|---|---|
| Index refresh latency | Instant mutation |
| Disk-based | In-memory |
| Search optimized | Coordination optimized |
Real-time assignment requires Redis.
1️⃣3️⃣ Key Tradeoffs
| Decision | Tradeoff |
|---|---|
| Separate availability | Extra lookup, massive scalability |
| Sequential assignment | Slight latency, better UX |
| In-memory only | Speed over durability |
Closing Thoughts
Real-time matching systems succeed because they:
- Separate location, availability, and assignment
- Avoid expensive reindexing
- Favor atomic operations
Great architectures don’t overload data structures — they compose them.
Top comments (0)