Intra-replica communication
When you scale a service to multiple replicas behind a load balancer, each replica is isolated. A request that arrives at Replica A cannot directly talk to Replica B. tinybroker bridges this gap without requiring shared databases, Redis, or sticky sessions.
The problem
A user connects to POST /cache/invalidate. The load balancer routes the request to Replica A. Replicas B and C still hold the stale cache entry. Without a broadcast mechanism, you need either:
- A shared external cache (Redis, Memcached) — adds a network hop for every read
- Sticky sessions — reduces load-balancing effectiveness and complicates failover
- A heavy message broker — disproportionate operational cost
The solution
All replicas subscribe to cache.invalidate.* on startup. Any replica that receives an invalidation request publishes the key to that topic. All replicas receive the message and drop their local copy.
Replica A (HTTP handler) tinybroker Replica B Replica C
│ │ │ │
POST /cache/invalidate │ │ │
│ │ │ │
│── Publish ────────────► │ │
│ "cache.invalidate.user:42" │ │
│ │ │ │
│ │─ Message ────► │
│ │─ Message ─────────────────►
│ │ │
200 OK │ (local caches cleared)
Startup pattern
Each replica subscribes on startup and holds the connection open for the lifetime of the process:
stream, _ := client.Subscribe(ctx)
stream.Send(&pb.SubscribeCommand{
Command: &pb.SubscribeCommand_Open{Open: &pb.OpenSubscription{
TopicPatterns: []string{"cache.invalidate.*", "config.reload"},
Mode: pb.DeliveryMode_DELIVERY_MODE_MULTI,
}},
})
go func() {
for {
ev, err := stream.Recv()
if err != nil { reconnect(); return }
handleInternalEvent(ev.GetMessage())
}
}()
Patterns for common use cases
| Scenario | Topic pattern | Mode |
|---|---|---|
| Cache invalidation | cache.invalidate.* | MULTI (all replicas) |
| Config hot-reload | config.reload | MULTI |
| Distributed rate-limit sync | rate.counter.* | MULTI |
| Job dispatch to one worker | jobs.* | BALANCED |
| Scheduled task, one leader | schedule.* | SINGLETON |
Reconnection
Always implement reconnect logic in the subscription goroutine/task. tinybroker is in-memory — a restart clears all state. A dropped connection (pod restart, rolling update) is expected and should be handled with an exponential-backoff reconnect loop.