0
# High Availability
1
2
Redis Sentinel provides high availability for Redis deployments through automatic failover, service discovery, and monitoring. Sentinel acts as a distributed system that monitors Redis master and replica instances and manages failover when needed.
3
4
## Capabilities
5
6
### Sentinel Client
7
8
Redis Sentinel client for connecting to Redis with automatic failover and service discovery.
9
10
```python { .api }
11
class Sentinel:
12
def __init__(
13
self,
14
sentinels: List[Tuple[str, int]],
15
min_other_sentinels: int = 0,
16
sentinel_kwargs: Optional[Dict[str, Any]] = None,
17
password: Optional[str] = None,
18
db: int = 0,
19
decode_responses: bool = False,
20
**kwargs
21
): ...
22
23
def discover_master(self, service_name: str) -> Tuple[str, int]: ...
24
25
def discover_slaves(self, service_name: str) -> List[Tuple[str, int]]: ...
26
27
def master_for(
28
self,
29
service_name: str,
30
redis_class: Type[Redis] = Redis,
31
connection_pool_class: Type[ConnectionPool] = SentinelConnectionPool,
32
**kwargs
33
) -> Redis: ...
34
35
def slave_for(
36
self,
37
service_name: str,
38
redis_class: Type[Redis] = Redis,
39
connection_pool_class: Type[ConnectionPool] = SentinelConnectionPool,
40
**kwargs
41
) -> Redis: ...
42
43
def execute_command(
44
self,
45
*args,
46
**kwargs
47
) -> Any: ...
48
```
49
50
### Sentinel Management Commands
51
52
Commands for managing and monitoring Sentinel instances and Redis services.
53
54
```python { .api }
55
def sentinel_masters(self) -> List[Dict[str, Any]]: ...
56
57
def sentinel_master(self, service_name: str) -> Dict[str, Any]: ...
58
59
def sentinel_slaves(self, service_name: str) -> List[Dict[str, Any]]: ...
60
61
def sentinel_sentinels(self, service_name: str) -> List[Dict[str, Any]]: ...
62
63
def sentinel_get_master_addr_by_name(self, service_name: str) -> Optional[Tuple[str, int]]: ...
64
65
def sentinel_reset(self, pattern: str) -> int: ...
66
67
def sentinel_failover(self, service_name: str) -> bool: ...
68
69
def sentinel_ckquorum(self, service_name: str) -> bool: ...
70
71
def sentinel_flushconfig(self) -> bool: ...
72
73
def sentinel_monitor(
74
self,
75
service_name: str,
76
ip: str,
77
port: int,
78
quorum: int
79
) -> bool: ...
80
81
def sentinel_remove(self, service_name: str) -> bool: ...
82
83
def sentinel_set(self, service_name: str, option: str, value: Any) -> bool: ...
84
```
85
86
### Sentinel Connection Classes
87
88
Specialized connection pool and connection classes for Sentinel-managed Redis instances.
89
90
```python { .api }
91
class SentinelConnectionPool(ConnectionPool):
92
def __init__(
93
self,
94
service_name: str,
95
sentinel_manager: SentinelManager,
96
**kwargs
97
): ...
98
99
def get_master_address(self) -> Tuple[str, int]: ...
100
101
def rotate_slaves(self) -> None: ...
102
103
class SentinelManagedConnection(Connection):
104
def __init__(
105
self,
106
**kwargs
107
): ...
108
109
class SentinelManagedSSLConnection(SSLConnection):
110
def __init__(
111
self,
112
**kwargs
113
): ...
114
```
115
116
## Usage Examples
117
118
### Basic Sentinel Setup
119
120
```python
121
import redis
122
from redis.sentinel import Sentinel
123
124
# Configure Sentinel connections
125
sentinels = [
126
('localhost', 26379),
127
('localhost', 26380),
128
('localhost', 26381)
129
]
130
131
# Create Sentinel instance
132
sentinel = Sentinel(sentinels, socket_timeout=0.1)
133
134
# Discover master and slaves
135
master_address = sentinel.discover_master('mymaster')
136
slave_addresses = sentinel.discover_slaves('mymaster')
137
138
print(f"Master: {master_address}")
139
print(f"Slaves: {slave_addresses}")
140
```
141
142
### Connecting to Redis via Sentinel
143
144
```python
145
from redis.sentinel import Sentinel
146
147
# Setup Sentinel
148
sentinels = [('localhost', 26379), ('localhost', 26380), ('localhost', 26381)]
149
sentinel = Sentinel(sentinels, socket_timeout=0.1)
150
151
# Get Redis master client
152
master = sentinel.master_for('mymaster', socket_timeout=0.1)
153
154
# Get Redis slave client for read operations
155
slave = sentinel.slave_for('mymaster', socket_timeout=0.1)
156
157
# Write to master
158
master.set('key', 'value')
159
160
# Read from slave
161
value = slave.get('key')
162
print(f"Value from slave: {value}")
163
```
164
165
### Sentinel with Authentication
166
167
```python
168
from redis.sentinel import Sentinel
169
170
# Sentinel with password for Redis instances
171
sentinels = [('localhost', 26379), ('localhost', 26380), ('localhost', 26381)]
172
173
sentinel = Sentinel(
174
sentinels,
175
socket_timeout=0.1,
176
password='redis_password', # Password for Redis instances
177
sentinel_kwargs={
178
'password': 'sentinel_password' # Password for Sentinel instances
179
}
180
)
181
182
# Connect to authenticated Redis
183
master = sentinel.master_for('mymaster', password='redis_password')
184
slave = sentinel.slave_for('mymaster', password='redis_password')
185
```
186
187
### Advanced Sentinel Configuration
188
189
```python
190
from redis.sentinel import Sentinel
191
from redis import Redis
192
193
# Custom Sentinel configuration
194
sentinels = [('localhost', 26379), ('localhost', 26380), ('localhost', 26381')]
195
196
sentinel = Sentinel(
197
sentinels,
198
min_other_sentinels=1, # Require at least 1 other sentinel
199
socket_timeout=0.1,
200
socket_connect_timeout=0.1,
201
socket_keepalive=True,
202
retry_on_timeout=True,
203
decode_responses=True
204
)
205
206
# Get Redis clients with custom settings
207
master = sentinel.master_for(
208
'mymaster',
209
redis_class=Redis,
210
socket_timeout=0.1,
211
socket_connect_timeout=0.1,
212
socket_keepalive=True,
213
retry_on_timeout=True,
214
max_connections=20,
215
health_check_interval=30
216
)
217
218
slave = sentinel.slave_for(
219
'mymaster',
220
redis_class=Redis,
221
socket_timeout=0.1,
222
socket_connect_timeout=0.1,
223
socket_keepalive=True,
224
retry_on_timeout=True,
225
max_connections=20,
226
health_check_interval=30
227
)
228
```
229
230
### Monitoring Sentinel State
231
232
```python
233
from redis.sentinel import Sentinel
234
235
sentinels = [('localhost', 26379), ('localhost', 26380), ('localhost', 26381)]
236
sentinel = Sentinel(sentinels)
237
238
# Get all masters managed by Sentinel
239
masters = sentinel.sentinel_masters()
240
for master in masters:
241
print(f"Master: {master['name']} at {master['ip']}:{master['port']}")
242
print(f"Status: {master['flags']}")
243
print(f"Slaves: {master['num-slaves']}")
244
print(f"Sentinels: {master['num-other-sentinels']}")
245
246
# Get specific master info
247
master_info = sentinel.sentinel_master('mymaster')
248
print(f"Master info: {master_info}")
249
250
# Get slaves for a master
251
slaves = sentinel.sentinel_slaves('mymaster')
252
for slave in slaves:
253
print(f"Slave: {slave['ip']}:{slave['port']} - {slave['flags']}")
254
255
# Get other sentinels monitoring this master
256
sentinels_info = sentinel.sentinel_sentinels('mymaster')
257
for sent in sentinels_info:
258
print(f"Sentinel: {sent['ip']}:{sent['port']} - {sent['flags']}")
259
```
260
261
### Handling Failover Events
262
263
```python
264
import time
265
from redis.sentinel import Sentinel
266
from redis.exceptions import ConnectionError, ResponseError
267
268
sentinels = [('localhost', 26379), ('localhost', 26380), ('localhost', 26381)]
269
sentinel = Sentinel(sentinels, socket_timeout=0.1)
270
271
# Get master with automatic failover
272
master = sentinel.master_for('mymaster', socket_timeout=0.1)
273
274
def robust_operation():
275
"""Perform Redis operations with failover handling"""
276
max_retries = 3
277
retry_count = 0
278
279
while retry_count < max_retries:
280
try:
281
# Try to perform operation
282
master.set('test_key', f'value_{int(time.time())}')
283
value = master.get('test_key')
284
print(f"Operation successful: {value}")
285
return
286
287
except ConnectionError as e:
288
retry_count += 1
289
print(f"Connection error (attempt {retry_count}): {e}")
290
291
if retry_count < max_retries:
292
print("Waiting for failover...")
293
time.sleep(2) # Wait for Sentinel failover
294
295
# Get new master connection after failover
296
master = sentinel.master_for('mymaster', socket_timeout=0.1)
297
else:
298
print("Max retries exceeded")
299
raise
300
301
# Demonstrate robust operation
302
robust_operation()
303
```
304
305
### Sentinel Configuration Management
306
307
```python
308
from redis.sentinel import Sentinel
309
310
sentinels = [('localhost', 26379)]
311
sentinel = Sentinel(sentinels)
312
313
# Add a new master to monitor
314
sentinel.sentinel_monitor('newmaster', '127.0.0.1', 6380, 2)
315
316
# Configure master settings
317
sentinel.sentinel_set('newmaster', 'down-after-milliseconds', 5000)
318
sentinel.sentinel_set('newmaster', 'failover-timeout', 60000)
319
sentinel.sentinel_set('newmaster', 'parallel-syncs', 1)
320
321
# Check quorum for failover
322
can_failover = sentinel.sentinel_ckquorum('newmaster')
323
print(f"Can perform failover: {can_failover}")
324
325
# Force manual failover
326
if can_failover:
327
result = sentinel.sentinel_failover('newmaster')
328
print(f"Failover initiated: {result}")
329
330
# Remove master from monitoring
331
# sentinel.sentinel_remove('newmaster')
332
333
# Reset sentinel state
334
reset_count = sentinel.sentinel_reset('*')
335
print(f"Reset {reset_count} masters")
336
337
# Save configuration
338
sentinel.sentinel_flushconfig()
339
```
340
341
### Pub/Sub with Sentinel
342
343
```python
344
import time
345
from redis.sentinel import Sentinel
346
347
sentinels = [('localhost', 26379), ('localhost', 26380), ('localhost', 26381)]
348
sentinel = Sentinel(sentinels)
349
350
# Get master for publishing
351
master = sentinel.master_for('mymaster')
352
353
# Get slave for subscribing (optional - can use master)
354
slave = sentinel.slave_for('mymaster')
355
356
# Publisher function
357
def publish_messages():
358
for i in range(10):
359
master.publish('news', f'Breaking news {i}')
360
time.sleep(1)
361
362
# Subscriber function
363
def subscribe_messages():
364
pubsub = master.pubsub() # Use master for pub/sub reliability
365
pubsub.subscribe('news')
366
367
for message in pubsub.listen():
368
if message['type'] == 'message':
369
print(f"Received: {message['data']}")
370
371
# Use threading or asyncio to run both
372
import threading
373
374
publisher_thread = threading.Thread(target=publish_messages)
375
subscriber_thread = threading.Thread(target=subscribe_messages)
376
377
subscriber_thread.start()
378
time.sleep(0.5) # Small delay to ensure subscription
379
publisher_thread.start()
380
381
publisher_thread.join()
382
```
383
384
### Error Handling with Sentinel
385
386
```python
387
from redis.sentinel import Sentinel
388
from redis.exceptions import (
389
ConnectionError,
390
ResponseError,
391
MasterNotFoundError,
392
SlaveNotFoundError
393
)
394
395
def setup_sentinel_client():
396
sentinels = [('localhost', 26379), ('localhost', 26380), ('localhost', 26381)]
397
398
try:
399
sentinel = Sentinel(sentinels, socket_timeout=1.0)
400
401
# Test master discovery
402
master_addr = sentinel.discover_master('mymaster')
403
print(f"Master found at: {master_addr}")
404
405
# Get master client
406
master = sentinel.master_for('mymaster', socket_timeout=1.0)
407
408
# Test connection
409
master.ping()
410
print("Successfully connected to Redis master via Sentinel")
411
412
return sentinel, master
413
414
except MasterNotFoundError as e:
415
print(f"Master not found: {e}")
416
raise
417
except ConnectionError as e:
418
print(f"Cannot connect to Sentinel: {e}")
419
raise
420
except ResponseError as e:
421
print(f"Sentinel response error: {e}")
422
raise
423
424
# Use the setup function
425
try:
426
sentinel, master = setup_sentinel_client()
427
428
# Perform operations
429
master.set('health_check', 'ok')
430
status = master.get('health_check')
431
print(f"Health check: {status}")
432
433
except Exception as e:
434
print(f"Failed to setup Sentinel client: {e}")
435
```