Client Cluster Mode
TopGun clients can connect to a multi-node cluster for high availability and horizontal scalability. The client automatically handles node discovery, partition-aware routing, and failover.
Overview
Multi-Node
Connect to multiple seed nodes for redundancy
Smart Routing
Operations route directly to partition owners
Auto Failover
Circuit breaker handles node failures
Quick Start
import { TopGunClient, IDBAdapter } from '@topgunbuild/client';
const client = new TopGunClient({
cluster: {
seeds: [
'ws://node1.example.com:8765',
'ws://node2.example.com:8765',
'ws://node3.example.com:8765',
],
smartRouting: true,
},
storage: new IDBAdapter(),
});
await client.start();
// Use normally - routing is automatic
const users = client.getMap('users');
users.set('alice', { name: 'Alice', role: 'admin' }); Configuration
Seed Nodes
Seed nodes are the initial entry points to the cluster. The client connects to these nodes to discover the full cluster topology and receive the partition map.
cluster: {
seeds: [
'ws://node1:8765', // Primary seed
'ws://node2:8765', // Backup seed
'ws://node3:8765', // Additional backup
],
} Best Practice: Provide at least 2-3 seed nodes. If the first seed is unavailable, the client will try the next one. You don’t need to list all nodes—the client discovers them automatically.
Smart Routing
When enabled, the client maintains a partition map and routes each operation directly to the node that owns the relevant partition.
cluster: {
seeds: ['ws://node1:8765', 'ws://node2:8765'],
smartRouting: true, // Default: true
} How it works:
- Client receives partition map from the cluster (271 partitions)
- Each key is hashed to a partition ID using xxHash64
- Operation is sent directly to the partition owner
- If owner is unavailable, falls back to any available node
With Smart Routing
- Lower latency (direct to owner)
- Reduced cluster traffic
- Better load distribution
Without Smart Routing
- Simpler setup
- Server handles forwarding
- Good for small clusters
Connection Pool
Configure how many connections to maintain per node:
cluster: {
seeds: ['ws://node1:8765'],
connectionsPerNode: 2, // Connections per node (default: 1)
connectionTimeoutMs: 5000, // Connection timeout (default: 10000)
} Partition Map Refresh
The client periodically refreshes its partition map to stay in sync with cluster topology changes:
cluster: {
seeds: ['ws://node1:8765'],
partitionMapRefreshMs: 30000, // Refresh every 30 seconds (default)
} The partition map is also updated immediately when:
- A node reports
NOT_OWNERfor an operation - A cluster topology change event is received
Failover Handling
Circuit Breaker States
| State | Behavior |
|---|---|
| Closed | Normal operation, requests flow through |
| Open | Node marked failed, requests reroute to other nodes |
| Half-Open | Testing if node recovered, limited traffic allowed |
Configuration
cluster: {
seeds: ['ws://node1:8765'],
// Circuit breaker is built-in with sensible defaults:
// - Opens after 5 consecutive failures
// - Resets after 30 seconds
// - Transitions to half-open to test recovery
} Behavior During Failover
// Your code doesn't need to handle failover explicitly
const users = client.getMap('users');
// This works even if the primary node is down
// The client automatically routes to available nodes
await users.set('key', { data: 'value' });
// Reads also work - served from any replica
const user = users.get('key'); Offline Support
If all nodes become unavailable, operations are queued locally in IndexedDB. When connectivity is restored, the client automatically syncs pending operations. This provides true offline-first capability even in cluster mode.
Monitoring
Connection State
// Listen for connection state changes
client.onConnectionChange((state) => {
switch (state) {
case 'connected':
console.log('Connected to cluster');
break;
case 'connecting':
console.log('Connecting to cluster...');
break;
case 'reconnecting':
console.log('Reconnecting after failure...');
break;
case 'disconnected':
console.log('Disconnected from cluster');
break;
}
}); Sync State
// Monitor synchronization progress
client.onSyncStateChange((state) => {
if (state === 'syncing') {
console.log('Syncing pending operations...');
} else if (state === 'synced') {
console.log('All operations synced');
}
}); Best Practices
Use Multiple Seeds
Always provide 2-3 seed nodes for redundancy. If one is down during startup, the client can still connect via others.
Enable Smart Routing
Keep smartRouting: true for
best performance. Only disable if you have specific requirements.
Handle Offline Gracefully
Your app should work offline. Use onSyncStateChange to
show sync status to users.
Use Secure Connections
In production, use wss:// URLs
for encrypted WebSocket connections.
Example: React Integration
// src/lib/topgun.ts
import { TopGunClient, IDBAdapter } from '@topgunbuild/client';
export const client = new TopGunClient({
cluster: {
seeds: [
process.env.NEXT_PUBLIC_NODE1_URL!,
process.env.NEXT_PUBLIC_NODE2_URL!,
process.env.NEXT_PUBLIC_NODE3_URL!,
],
smartRouting: true,
},
storage: new IDBAdapter(),
});
// Initialize on app startup
client.start(); // src/app/providers.tsx
import { TopGunProvider } from '@topgunbuild/react';
import { client } from '@/lib/topgun';
export function Providers({ children }) {
return (
<TopGunProvider client={client}>
{children}
</TopGunProvider>
);
}