redis 7.0.0b1__py3-none-any.whl → 7.0.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- redis/__init__.py +1 -1
- redis/_parsers/base.py +36 -22
- redis/asyncio/client.py +6 -1
- redis/asyncio/cluster.py +6 -1
- redis/asyncio/connection.py +43 -1
- redis/client.py +19 -15
- redis/cluster.py +2 -0
- redis/connection.py +206 -151
- redis/{maintenance_events.py → maint_notifications.py} +154 -140
- {redis-7.0.0b1.dist-info → redis-7.0.0b2.dist-info}/METADATA +1 -1
- {redis-7.0.0b1.dist-info → redis-7.0.0b2.dist-info}/RECORD +13 -13
- {redis-7.0.0b1.dist-info → redis-7.0.0b2.dist-info}/WHEEL +0 -0
- {redis-7.0.0b1.dist-info → redis-7.0.0b2.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,7 +5,7 @@ import re
|
|
|
5
5
|
import threading
|
|
6
6
|
import time
|
|
7
7
|
from abc import ABC, abstractmethod
|
|
8
|
-
from typing import TYPE_CHECKING, Optional, Union
|
|
8
|
+
from typing import TYPE_CHECKING, Literal, Optional, Union
|
|
9
9
|
|
|
10
10
|
from redis.typing import Number
|
|
11
11
|
|
|
@@ -38,25 +38,25 @@ if TYPE_CHECKING:
|
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
class
|
|
41
|
+
class MaintenanceNotification(ABC):
|
|
42
42
|
"""
|
|
43
|
-
Base class for maintenance
|
|
43
|
+
Base class for maintenance notifications sent through push messages by Redis server.
|
|
44
44
|
|
|
45
|
-
This class provides common functionality for all maintenance
|
|
45
|
+
This class provides common functionality for all maintenance notifications including
|
|
46
46
|
unique identification and TTL (Time-To-Live) functionality.
|
|
47
47
|
|
|
48
48
|
Attributes:
|
|
49
|
-
id (int): Unique identifier for this
|
|
49
|
+
id (int): Unique identifier for this notification
|
|
50
50
|
ttl (int): Time-to-live in seconds for this notification
|
|
51
51
|
creation_time (float): Timestamp when the notification was created/read
|
|
52
52
|
"""
|
|
53
53
|
|
|
54
54
|
def __init__(self, id: int, ttl: int):
|
|
55
55
|
"""
|
|
56
|
-
Initialize a new
|
|
56
|
+
Initialize a new MaintenanceNotification with unique ID and TTL functionality.
|
|
57
57
|
|
|
58
58
|
Args:
|
|
59
|
-
id (int): Unique identifier for this
|
|
59
|
+
id (int): Unique identifier for this notification
|
|
60
60
|
ttl (int): Time-to-live in seconds for this notification
|
|
61
61
|
"""
|
|
62
62
|
self.id = id
|
|
@@ -66,60 +66,60 @@ class MaintenanceEvent(ABC):
|
|
|
66
66
|
|
|
67
67
|
def is_expired(self) -> bool:
|
|
68
68
|
"""
|
|
69
|
-
Check if this
|
|
69
|
+
Check if this notification has expired based on its TTL
|
|
70
70
|
and creation time.
|
|
71
71
|
|
|
72
72
|
Returns:
|
|
73
|
-
bool: True if the
|
|
73
|
+
bool: True if the notification has expired, False otherwise
|
|
74
74
|
"""
|
|
75
75
|
return time.monotonic() > (self.creation_time + self.ttl)
|
|
76
76
|
|
|
77
77
|
@abstractmethod
|
|
78
78
|
def __repr__(self) -> str:
|
|
79
79
|
"""
|
|
80
|
-
Return a string representation of the maintenance
|
|
80
|
+
Return a string representation of the maintenance notification.
|
|
81
81
|
|
|
82
82
|
This method must be implemented by all concrete subclasses.
|
|
83
83
|
|
|
84
84
|
Returns:
|
|
85
|
-
str: String representation of the
|
|
85
|
+
str: String representation of the notification
|
|
86
86
|
"""
|
|
87
87
|
pass
|
|
88
88
|
|
|
89
89
|
@abstractmethod
|
|
90
90
|
def __eq__(self, other) -> bool:
|
|
91
91
|
"""
|
|
92
|
-
Compare two maintenance
|
|
92
|
+
Compare two maintenance notifications for equality.
|
|
93
93
|
|
|
94
94
|
This method must be implemented by all concrete subclasses.
|
|
95
|
-
|
|
95
|
+
Notifications are typically considered equal if they have the same id
|
|
96
96
|
and are of the same type.
|
|
97
97
|
|
|
98
98
|
Args:
|
|
99
99
|
other: The other object to compare with
|
|
100
100
|
|
|
101
101
|
Returns:
|
|
102
|
-
bool: True if the
|
|
102
|
+
bool: True if the notifications are equal, False otherwise
|
|
103
103
|
"""
|
|
104
104
|
pass
|
|
105
105
|
|
|
106
106
|
@abstractmethod
|
|
107
107
|
def __hash__(self) -> int:
|
|
108
108
|
"""
|
|
109
|
-
Return a hash value for the maintenance
|
|
109
|
+
Return a hash value for the maintenance notification.
|
|
110
110
|
|
|
111
111
|
This method must be implemented by all concrete subclasses to allow
|
|
112
112
|
instances to be used in sets and as dictionary keys.
|
|
113
113
|
|
|
114
114
|
Returns:
|
|
115
|
-
int: Hash value for the
|
|
115
|
+
int: Hash value for the notification
|
|
116
116
|
"""
|
|
117
117
|
pass
|
|
118
118
|
|
|
119
119
|
|
|
120
|
-
class
|
|
120
|
+
class NodeMovingNotification(MaintenanceNotification):
|
|
121
121
|
"""
|
|
122
|
-
This
|
|
122
|
+
This notification is received when a node is replaced with a new node
|
|
123
123
|
during cluster rebalancing or maintenance operations.
|
|
124
124
|
"""
|
|
125
125
|
|
|
@@ -131,10 +131,10 @@ class NodeMovingEvent(MaintenanceEvent):
|
|
|
131
131
|
ttl: int,
|
|
132
132
|
):
|
|
133
133
|
"""
|
|
134
|
-
Initialize a new
|
|
134
|
+
Initialize a new NodeMovingNotification.
|
|
135
135
|
|
|
136
136
|
Args:
|
|
137
|
-
id (int): Unique identifier for this
|
|
137
|
+
id (int): Unique identifier for this notification
|
|
138
138
|
new_node_host (str): Hostname or IP address of the new replacement node
|
|
139
139
|
new_node_port (int): Port number of the new replacement node
|
|
140
140
|
ttl (int): Time-to-live in seconds for this notification
|
|
@@ -162,10 +162,10 @@ class NodeMovingEvent(MaintenanceEvent):
|
|
|
162
162
|
|
|
163
163
|
def __eq__(self, other) -> bool:
|
|
164
164
|
"""
|
|
165
|
-
Two
|
|
165
|
+
Two NodeMovingNotification notifications are considered equal if they have the same
|
|
166
166
|
id, new_node_host, and new_node_port.
|
|
167
167
|
"""
|
|
168
|
-
if not isinstance(other,
|
|
168
|
+
if not isinstance(other, NodeMovingNotification):
|
|
169
169
|
return False
|
|
170
170
|
return (
|
|
171
171
|
self.id == other.id
|
|
@@ -175,11 +175,11 @@ class NodeMovingEvent(MaintenanceEvent):
|
|
|
175
175
|
|
|
176
176
|
def __hash__(self) -> int:
|
|
177
177
|
"""
|
|
178
|
-
Return a hash value for the
|
|
178
|
+
Return a hash value for the notification to allow
|
|
179
179
|
instances to be used in sets and as dictionary keys.
|
|
180
180
|
|
|
181
181
|
Returns:
|
|
182
|
-
int: Hash value based on
|
|
182
|
+
int: Hash value based on notification type class name, id,
|
|
183
183
|
new_node_host and new_node_port
|
|
184
184
|
"""
|
|
185
185
|
try:
|
|
@@ -197,15 +197,15 @@ class NodeMovingEvent(MaintenanceEvent):
|
|
|
197
197
|
)
|
|
198
198
|
|
|
199
199
|
|
|
200
|
-
class
|
|
200
|
+
class NodeMigratingNotification(MaintenanceNotification):
|
|
201
201
|
"""
|
|
202
|
-
|
|
202
|
+
Notification for when a Redis cluster node is in the process of migrating slots.
|
|
203
203
|
|
|
204
|
-
This
|
|
204
|
+
This notification is received when a node starts migrating its slots to another node
|
|
205
205
|
during cluster rebalancing or maintenance operations.
|
|
206
206
|
|
|
207
207
|
Args:
|
|
208
|
-
id (int): Unique identifier for this
|
|
208
|
+
id (int): Unique identifier for this notification
|
|
209
209
|
ttl (int): Time-to-live in seconds for this notification
|
|
210
210
|
"""
|
|
211
211
|
|
|
@@ -228,39 +228,39 @@ class NodeMigratingEvent(MaintenanceEvent):
|
|
|
228
228
|
|
|
229
229
|
def __eq__(self, other) -> bool:
|
|
230
230
|
"""
|
|
231
|
-
Two
|
|
231
|
+
Two NodeMigratingNotification notifications are considered equal if they have the same
|
|
232
232
|
id and are of the same type.
|
|
233
233
|
"""
|
|
234
|
-
if not isinstance(other,
|
|
234
|
+
if not isinstance(other, NodeMigratingNotification):
|
|
235
235
|
return False
|
|
236
236
|
return self.id == other.id and type(self) is type(other)
|
|
237
237
|
|
|
238
238
|
def __hash__(self) -> int:
|
|
239
239
|
"""
|
|
240
|
-
Return a hash value for the
|
|
240
|
+
Return a hash value for the notification to allow
|
|
241
241
|
instances to be used in sets and as dictionary keys.
|
|
242
242
|
|
|
243
243
|
Returns:
|
|
244
|
-
int: Hash value based on
|
|
244
|
+
int: Hash value based on notification type and id
|
|
245
245
|
"""
|
|
246
246
|
return hash((self.__class__.__name__, int(self.id)))
|
|
247
247
|
|
|
248
248
|
|
|
249
|
-
class
|
|
249
|
+
class NodeMigratedNotification(MaintenanceNotification):
|
|
250
250
|
"""
|
|
251
|
-
|
|
251
|
+
Notification for when a Redis cluster node has completed migrating slots.
|
|
252
252
|
|
|
253
|
-
This
|
|
253
|
+
This notification is received when a node has finished migrating all its slots
|
|
254
254
|
to other nodes during cluster rebalancing or maintenance operations.
|
|
255
255
|
|
|
256
256
|
Args:
|
|
257
|
-
id (int): Unique identifier for this
|
|
257
|
+
id (int): Unique identifier for this notification
|
|
258
258
|
"""
|
|
259
259
|
|
|
260
260
|
DEFAULT_TTL = 5
|
|
261
261
|
|
|
262
262
|
def __init__(self, id: int):
|
|
263
|
-
super().__init__(id,
|
|
263
|
+
super().__init__(id, NodeMigratedNotification.DEFAULT_TTL)
|
|
264
264
|
|
|
265
265
|
def __repr__(self) -> str:
|
|
266
266
|
expiry_time = self.creation_time + self.ttl
|
|
@@ -278,33 +278,33 @@ class NodeMigratedEvent(MaintenanceEvent):
|
|
|
278
278
|
|
|
279
279
|
def __eq__(self, other) -> bool:
|
|
280
280
|
"""
|
|
281
|
-
Two
|
|
281
|
+
Two NodeMigratedNotification notifications are considered equal if they have the same
|
|
282
282
|
id and are of the same type.
|
|
283
283
|
"""
|
|
284
|
-
if not isinstance(other,
|
|
284
|
+
if not isinstance(other, NodeMigratedNotification):
|
|
285
285
|
return False
|
|
286
286
|
return self.id == other.id and type(self) is type(other)
|
|
287
287
|
|
|
288
288
|
def __hash__(self) -> int:
|
|
289
289
|
"""
|
|
290
|
-
Return a hash value for the
|
|
290
|
+
Return a hash value for the notification to allow
|
|
291
291
|
instances to be used in sets and as dictionary keys.
|
|
292
292
|
|
|
293
293
|
Returns:
|
|
294
|
-
int: Hash value based on
|
|
294
|
+
int: Hash value based on notification type and id
|
|
295
295
|
"""
|
|
296
296
|
return hash((self.__class__.__name__, int(self.id)))
|
|
297
297
|
|
|
298
298
|
|
|
299
|
-
class
|
|
299
|
+
class NodeFailingOverNotification(MaintenanceNotification):
|
|
300
300
|
"""
|
|
301
|
-
|
|
301
|
+
Notification for when a Redis cluster node is in the process of failing over.
|
|
302
302
|
|
|
303
|
-
This
|
|
303
|
+
This notification is received when a node starts a failover process during
|
|
304
304
|
cluster maintenance operations or when handling node failures.
|
|
305
305
|
|
|
306
306
|
Args:
|
|
307
|
-
id (int): Unique identifier for this
|
|
307
|
+
id (int): Unique identifier for this notification
|
|
308
308
|
ttl (int): Time-to-live in seconds for this notification
|
|
309
309
|
"""
|
|
310
310
|
|
|
@@ -327,39 +327,39 @@ class NodeFailingOverEvent(MaintenanceEvent):
|
|
|
327
327
|
|
|
328
328
|
def __eq__(self, other) -> bool:
|
|
329
329
|
"""
|
|
330
|
-
Two
|
|
330
|
+
Two NodeFailingOverNotification notifications are considered equal if they have the same
|
|
331
331
|
id and are of the same type.
|
|
332
332
|
"""
|
|
333
|
-
if not isinstance(other,
|
|
333
|
+
if not isinstance(other, NodeFailingOverNotification):
|
|
334
334
|
return False
|
|
335
335
|
return self.id == other.id and type(self) is type(other)
|
|
336
336
|
|
|
337
337
|
def __hash__(self) -> int:
|
|
338
338
|
"""
|
|
339
|
-
Return a hash value for the
|
|
339
|
+
Return a hash value for the notification to allow
|
|
340
340
|
instances to be used in sets and as dictionary keys.
|
|
341
341
|
|
|
342
342
|
Returns:
|
|
343
|
-
int: Hash value based on
|
|
343
|
+
int: Hash value based on notification type and id
|
|
344
344
|
"""
|
|
345
345
|
return hash((self.__class__.__name__, int(self.id)))
|
|
346
346
|
|
|
347
347
|
|
|
348
|
-
class
|
|
348
|
+
class NodeFailedOverNotification(MaintenanceNotification):
|
|
349
349
|
"""
|
|
350
|
-
|
|
350
|
+
Notification for when a Redis cluster node has completed a failover.
|
|
351
351
|
|
|
352
|
-
This
|
|
352
|
+
This notification is received when a node has finished the failover process
|
|
353
353
|
during cluster maintenance operations or after handling node failures.
|
|
354
354
|
|
|
355
355
|
Args:
|
|
356
|
-
id (int): Unique identifier for this
|
|
356
|
+
id (int): Unique identifier for this notification
|
|
357
357
|
"""
|
|
358
358
|
|
|
359
359
|
DEFAULT_TTL = 5
|
|
360
360
|
|
|
361
361
|
def __init__(self, id: int):
|
|
362
|
-
super().__init__(id,
|
|
362
|
+
super().__init__(id, NodeFailedOverNotification.DEFAULT_TTL)
|
|
363
363
|
|
|
364
364
|
def __repr__(self) -> str:
|
|
365
365
|
expiry_time = self.creation_time + self.ttl
|
|
@@ -377,20 +377,20 @@ class NodeFailedOverEvent(MaintenanceEvent):
|
|
|
377
377
|
|
|
378
378
|
def __eq__(self, other) -> bool:
|
|
379
379
|
"""
|
|
380
|
-
Two
|
|
380
|
+
Two NodeFailedOverNotification notifications are considered equal if they have the same
|
|
381
381
|
id and are of the same type.
|
|
382
382
|
"""
|
|
383
|
-
if not isinstance(other,
|
|
383
|
+
if not isinstance(other, NodeFailedOverNotification):
|
|
384
384
|
return False
|
|
385
385
|
return self.id == other.id and type(self) is type(other)
|
|
386
386
|
|
|
387
387
|
def __hash__(self) -> int:
|
|
388
388
|
"""
|
|
389
|
-
Return a hash value for the
|
|
389
|
+
Return a hash value for the notification to allow
|
|
390
390
|
instances to be used in sets and as dictionary keys.
|
|
391
391
|
|
|
392
392
|
Returns:
|
|
393
|
-
int: Hash value based on
|
|
393
|
+
int: Hash value based on notification type and id
|
|
394
394
|
"""
|
|
395
395
|
return hash((self.__class__.__name__, int(self.id)))
|
|
396
396
|
|
|
@@ -435,9 +435,9 @@ def _is_private_fqdn(host: str) -> bool:
|
|
|
435
435
|
return False
|
|
436
436
|
|
|
437
437
|
|
|
438
|
-
class
|
|
438
|
+
class MaintNotificationsConfig:
|
|
439
439
|
"""
|
|
440
|
-
Configuration class for maintenance
|
|
440
|
+
Configuration class for maintenance notifications handling behaviour. Notifications are received through
|
|
441
441
|
push notifications.
|
|
442
442
|
|
|
443
443
|
This class defines how the Redis client should react to different push notifications
|
|
@@ -447,21 +447,26 @@ class MaintenanceEventsConfig:
|
|
|
447
447
|
|
|
448
448
|
def __init__(
|
|
449
449
|
self,
|
|
450
|
-
enabled: bool =
|
|
450
|
+
enabled: Union[bool, Literal["auto"]] = "auto",
|
|
451
451
|
proactive_reconnect: bool = True,
|
|
452
|
-
|
|
452
|
+
relaxed_timeout: Optional[Number] = 10,
|
|
453
453
|
endpoint_type: Optional[EndpointType] = None,
|
|
454
454
|
):
|
|
455
455
|
"""
|
|
456
|
-
Initialize a new
|
|
456
|
+
Initialize a new MaintNotificationsConfig.
|
|
457
457
|
|
|
458
458
|
Args:
|
|
459
|
-
enabled (bool):
|
|
460
|
-
|
|
459
|
+
enabled (bool | "auto"): Controls maintenance notifications handling behavior.
|
|
460
|
+
- True: The CLIENT MAINT_NOTIFICATIONS command must succeed during connection setup,
|
|
461
|
+
otherwise a ResponseError is raised.
|
|
462
|
+
- "auto": The CLIENT MAINT_NOTIFICATIONS command is attempted but failures are
|
|
463
|
+
gracefully handled - a warning is logged and normal operation continues.
|
|
464
|
+
- False: Maintenance notifications are completely disabled.
|
|
465
|
+
Defaults to "auto".
|
|
461
466
|
proactive_reconnect (bool): Whether to proactively reconnect when a node is replaced.
|
|
462
467
|
Defaults to True.
|
|
463
|
-
|
|
464
|
-
If -1 is provided - the
|
|
468
|
+
relaxed_timeout (Number): The relaxed timeout to use for the connection during maintenance.
|
|
469
|
+
If -1 is provided - the relaxed timeout is disabled. Defaults to 20.
|
|
465
470
|
endpoint_type (Optional[EndpointType]): Override for the endpoint type to use in CLIENT MAINT_NOTIFICATIONS.
|
|
466
471
|
If None, the endpoint type will be automatically determined based on the host and TLS configuration.
|
|
467
472
|
Defaults to None.
|
|
@@ -470,7 +475,7 @@ class MaintenanceEventsConfig:
|
|
|
470
475
|
ValueError: If endpoint_type is provided but is not a valid endpoint type.
|
|
471
476
|
"""
|
|
472
477
|
self.enabled = enabled
|
|
473
|
-
self.
|
|
478
|
+
self.relaxed_timeout = relaxed_timeout
|
|
474
479
|
self.proactive_reconnect = proactive_reconnect
|
|
475
480
|
self.endpoint_type = endpoint_type
|
|
476
481
|
|
|
@@ -479,21 +484,21 @@ class MaintenanceEventsConfig:
|
|
|
479
484
|
f"{self.__class__.__name__}("
|
|
480
485
|
f"enabled={self.enabled}, "
|
|
481
486
|
f"proactive_reconnect={self.proactive_reconnect}, "
|
|
482
|
-
f"
|
|
487
|
+
f"relaxed_timeout={self.relaxed_timeout}, "
|
|
483
488
|
f"endpoint_type={self.endpoint_type!r}"
|
|
484
489
|
f")"
|
|
485
490
|
)
|
|
486
491
|
|
|
487
|
-
def
|
|
492
|
+
def is_relaxed_timeouts_enabled(self) -> bool:
|
|
488
493
|
"""
|
|
489
|
-
Check if the
|
|
490
|
-
If
|
|
494
|
+
Check if the relaxed_timeout is enabled. The '-1' value is used to disable the relaxed_timeout.
|
|
495
|
+
If relaxed_timeout is set to None, it will make the operation blocking
|
|
491
496
|
and waiting until any response is received.
|
|
492
497
|
|
|
493
498
|
Returns:
|
|
494
|
-
True if the
|
|
499
|
+
True if the relaxed_timeout is enabled, False otherwise.
|
|
495
500
|
"""
|
|
496
|
-
return self.
|
|
501
|
+
return self.relaxed_timeout != -1
|
|
497
502
|
|
|
498
503
|
def get_endpoint_type(
|
|
499
504
|
self, host: str, connection: "ConnectionInterface"
|
|
@@ -550,15 +555,15 @@ class MaintenanceEventsConfig:
|
|
|
550
555
|
return EndpointType.INTERNAL_FQDN if is_private else EndpointType.EXTERNAL_FQDN
|
|
551
556
|
|
|
552
557
|
|
|
553
|
-
class
|
|
558
|
+
class MaintNotificationsPoolHandler:
|
|
554
559
|
def __init__(
|
|
555
560
|
self,
|
|
556
561
|
pool: Union["ConnectionPool", "BlockingConnectionPool"],
|
|
557
|
-
config:
|
|
562
|
+
config: MaintNotificationsConfig,
|
|
558
563
|
) -> None:
|
|
559
564
|
self.pool = pool
|
|
560
565
|
self.config = config
|
|
561
|
-
self.
|
|
566
|
+
self._processed_notifications = set()
|
|
562
567
|
self._lock = threading.RLock()
|
|
563
568
|
self.connection = None
|
|
564
569
|
|
|
@@ -567,35 +572,35 @@ class MaintenanceEventPoolHandler:
|
|
|
567
572
|
|
|
568
573
|
def remove_expired_notifications(self):
|
|
569
574
|
with self._lock:
|
|
570
|
-
for notification in tuple(self.
|
|
575
|
+
for notification in tuple(self._processed_notifications):
|
|
571
576
|
if notification.is_expired():
|
|
572
|
-
self.
|
|
577
|
+
self._processed_notifications.remove(notification)
|
|
573
578
|
|
|
574
|
-
def
|
|
579
|
+
def handle_notification(self, notification: MaintenanceNotification):
|
|
575
580
|
self.remove_expired_notifications()
|
|
576
581
|
|
|
577
|
-
if isinstance(notification,
|
|
578
|
-
return self.
|
|
582
|
+
if isinstance(notification, NodeMovingNotification):
|
|
583
|
+
return self.handle_node_moving_notification(notification)
|
|
579
584
|
else:
|
|
580
585
|
logging.error(f"Unhandled notification type: {notification}")
|
|
581
586
|
|
|
582
|
-
def
|
|
587
|
+
def handle_node_moving_notification(self, notification: NodeMovingNotification):
|
|
583
588
|
if (
|
|
584
589
|
not self.config.proactive_reconnect
|
|
585
|
-
and not self.config.
|
|
590
|
+
and not self.config.is_relaxed_timeouts_enabled()
|
|
586
591
|
):
|
|
587
592
|
return
|
|
588
593
|
with self._lock:
|
|
589
|
-
if
|
|
594
|
+
if notification in self._processed_notifications:
|
|
590
595
|
# nothing to do in the connection pool handling
|
|
591
|
-
# the
|
|
596
|
+
# the notification has already been handled or is expired
|
|
592
597
|
# just return
|
|
593
598
|
return
|
|
594
599
|
|
|
595
600
|
with self.pool._lock:
|
|
596
601
|
if (
|
|
597
602
|
self.config.proactive_reconnect
|
|
598
|
-
or self.config.
|
|
603
|
+
or self.config.is_relaxed_timeouts_enabled()
|
|
599
604
|
):
|
|
600
605
|
# Get the current connected address - if any
|
|
601
606
|
# This is the address that is being moved
|
|
@@ -614,21 +619,21 @@ class MaintenanceEventPoolHandler:
|
|
|
614
619
|
# connection settings for matching connections
|
|
615
620
|
self.pool.update_connections_settings(
|
|
616
621
|
state=MaintenanceState.MOVING,
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
host_address=
|
|
622
|
+
maintenance_notification_hash=hash(notification),
|
|
623
|
+
relaxed_timeout=self.config.relaxed_timeout,
|
|
624
|
+
host_address=notification.new_node_host,
|
|
620
625
|
matching_address=moving_address_src,
|
|
621
626
|
matching_pattern="connected_address",
|
|
622
|
-
|
|
627
|
+
update_notification_hash=True,
|
|
623
628
|
include_free_connections=True,
|
|
624
629
|
)
|
|
625
630
|
|
|
626
631
|
if self.config.proactive_reconnect:
|
|
627
|
-
if
|
|
632
|
+
if notification.new_node_host is not None:
|
|
628
633
|
self.run_proactive_reconnect(moving_address_src)
|
|
629
634
|
else:
|
|
630
635
|
threading.Timer(
|
|
631
|
-
|
|
636
|
+
notification.ttl / 2,
|
|
632
637
|
self.run_proactive_reconnect,
|
|
633
638
|
args=(moving_address_src,),
|
|
634
639
|
).start()
|
|
@@ -639,22 +644,22 @@ class MaintenanceEventPoolHandler:
|
|
|
639
644
|
# if relax timeouts are enabled - update timeouts
|
|
640
645
|
kwargs: dict = {
|
|
641
646
|
"maintenance_state": MaintenanceState.MOVING,
|
|
642
|
-
"
|
|
647
|
+
"maintenance_notification_hash": hash(notification),
|
|
643
648
|
}
|
|
644
|
-
if
|
|
649
|
+
if notification.new_node_host is not None:
|
|
645
650
|
# the host is not updated if the new node host is None
|
|
646
651
|
# this happens when the MOVING push notification does not contain
|
|
647
652
|
# the new node host - in this case we only update the timeouts
|
|
648
653
|
kwargs.update(
|
|
649
654
|
{
|
|
650
|
-
"host":
|
|
655
|
+
"host": notification.new_node_host,
|
|
651
656
|
}
|
|
652
657
|
)
|
|
653
|
-
if self.config.
|
|
658
|
+
if self.config.is_relaxed_timeouts_enabled():
|
|
654
659
|
kwargs.update(
|
|
655
660
|
{
|
|
656
|
-
"socket_timeout": self.config.
|
|
657
|
-
"socket_connect_timeout": self.config.
|
|
661
|
+
"socket_timeout": self.config.relaxed_timeout,
|
|
662
|
+
"socket_connect_timeout": self.config.relaxed_timeout,
|
|
658
663
|
}
|
|
659
664
|
)
|
|
660
665
|
self.pool.update_connection_kwargs(**kwargs)
|
|
@@ -663,10 +668,12 @@ class MaintenanceEventPoolHandler:
|
|
|
663
668
|
self.pool.set_in_maintenance(False)
|
|
664
669
|
|
|
665
670
|
threading.Timer(
|
|
666
|
-
|
|
671
|
+
notification.ttl,
|
|
672
|
+
self.handle_node_moved_notification,
|
|
673
|
+
args=(notification,),
|
|
667
674
|
).start()
|
|
668
675
|
|
|
669
|
-
self.
|
|
676
|
+
self._processed_notifications.add(notification)
|
|
670
677
|
|
|
671
678
|
def run_proactive_reconnect(self, moving_address_src: Optional[str] = None):
|
|
672
679
|
"""
|
|
@@ -687,17 +694,20 @@ class MaintenanceEventPoolHandler:
|
|
|
687
694
|
moving_address_src=moving_address_src,
|
|
688
695
|
)
|
|
689
696
|
|
|
690
|
-
def
|
|
697
|
+
def handle_node_moved_notification(self, notification: NodeMovingNotification):
|
|
691
698
|
"""
|
|
692
|
-
Handle the cleanup after a node moving
|
|
699
|
+
Handle the cleanup after a node moving notification expires.
|
|
693
700
|
"""
|
|
694
|
-
|
|
701
|
+
notification_hash = hash(notification)
|
|
695
702
|
|
|
696
703
|
with self._lock:
|
|
697
|
-
# if the current
|
|
698
|
-
# it means there has been a new moving
|
|
704
|
+
# if the current maintenance_notification_hash in kwargs is not matching the notification
|
|
705
|
+
# it means there has been a new moving notification after this one
|
|
699
706
|
# and we don't need to revert the kwargs yet
|
|
700
|
-
if
|
|
707
|
+
if (
|
|
708
|
+
self.pool.connection_kwargs.get("maintenance_notification_hash")
|
|
709
|
+
== notification_hash
|
|
710
|
+
):
|
|
701
711
|
orig_host = self.pool.connection_kwargs.get("orig_host_address")
|
|
702
712
|
orig_socket_timeout = self.pool.connection_kwargs.get(
|
|
703
713
|
"orig_socket_timeout"
|
|
@@ -707,7 +717,7 @@ class MaintenanceEventPoolHandler:
|
|
|
707
717
|
)
|
|
708
718
|
kwargs: dict = {
|
|
709
719
|
"maintenance_state": MaintenanceState.NONE,
|
|
710
|
-
"
|
|
720
|
+
"maintenance_notification_hash": None,
|
|
711
721
|
"host": orig_host,
|
|
712
722
|
"socket_timeout": orig_socket_timeout,
|
|
713
723
|
"socket_connect_timeout": orig_connect_timeout,
|
|
@@ -715,71 +725,75 @@ class MaintenanceEventPoolHandler:
|
|
|
715
725
|
self.pool.update_connection_kwargs(**kwargs)
|
|
716
726
|
|
|
717
727
|
with self.pool._lock:
|
|
718
|
-
|
|
728
|
+
reset_relaxed_timeout = self.config.is_relaxed_timeouts_enabled()
|
|
719
729
|
reset_host_address = self.config.proactive_reconnect
|
|
720
730
|
|
|
721
731
|
self.pool.update_connections_settings(
|
|
722
|
-
|
|
732
|
+
relaxed_timeout=-1,
|
|
723
733
|
state=MaintenanceState.NONE,
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
matching_pattern="
|
|
727
|
-
|
|
728
|
-
|
|
734
|
+
maintenance_notification_hash=None,
|
|
735
|
+
matching_notification_hash=notification_hash,
|
|
736
|
+
matching_pattern="notification_hash",
|
|
737
|
+
update_notification_hash=True,
|
|
738
|
+
reset_relaxed_timeout=reset_relaxed_timeout,
|
|
729
739
|
reset_host_address=reset_host_address,
|
|
730
740
|
include_free_connections=True,
|
|
731
741
|
)
|
|
732
742
|
|
|
733
743
|
|
|
734
|
-
class
|
|
735
|
-
# 1 = "starting maintenance"
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
744
|
+
class MaintNotificationsConnectionHandler:
|
|
745
|
+
# 1 = "starting maintenance" notifications, 0 = "completed maintenance" notifications
|
|
746
|
+
_NOTIFICATION_TYPES: dict[type["MaintenanceNotification"], int] = {
|
|
747
|
+
NodeMigratingNotification: 1,
|
|
748
|
+
NodeFailingOverNotification: 1,
|
|
749
|
+
NodeMigratedNotification: 0,
|
|
750
|
+
NodeFailedOverNotification: 0,
|
|
741
751
|
}
|
|
742
752
|
|
|
743
753
|
def __init__(
|
|
744
|
-
self, connection: "ConnectionInterface", config:
|
|
754
|
+
self, connection: "ConnectionInterface", config: MaintNotificationsConfig
|
|
745
755
|
) -> None:
|
|
746
756
|
self.connection = connection
|
|
747
757
|
self.config = config
|
|
748
758
|
|
|
749
|
-
def
|
|
750
|
-
# get the
|
|
751
|
-
|
|
759
|
+
def handle_notification(self, notification: MaintenanceNotification):
|
|
760
|
+
# get the notification type by checking its class in the _NOTIFICATION_TYPES dict
|
|
761
|
+
notification_type = self._NOTIFICATION_TYPES.get(notification.__class__, None)
|
|
752
762
|
|
|
753
|
-
if
|
|
754
|
-
logging.error(f"Unhandled
|
|
763
|
+
if notification_type is None:
|
|
764
|
+
logging.error(f"Unhandled notification type: {notification}")
|
|
755
765
|
return
|
|
756
766
|
|
|
757
|
-
if
|
|
758
|
-
self.
|
|
767
|
+
if notification_type:
|
|
768
|
+
self.handle_maintenance_start_notification(MaintenanceState.MAINTENANCE)
|
|
759
769
|
else:
|
|
760
|
-
self.
|
|
770
|
+
self.handle_maintenance_completed_notification()
|
|
761
771
|
|
|
762
|
-
def
|
|
772
|
+
def handle_maintenance_start_notification(
|
|
773
|
+
self, maintenance_state: MaintenanceState
|
|
774
|
+
):
|
|
763
775
|
if (
|
|
764
776
|
self.connection.maintenance_state == MaintenanceState.MOVING
|
|
765
|
-
or not self.config.
|
|
777
|
+
or not self.config.is_relaxed_timeouts_enabled()
|
|
766
778
|
):
|
|
767
779
|
return
|
|
768
780
|
|
|
769
781
|
self.connection.maintenance_state = maintenance_state
|
|
770
|
-
self.connection.set_tmp_settings(
|
|
782
|
+
self.connection.set_tmp_settings(
|
|
783
|
+
tmp_relaxed_timeout=self.config.relaxed_timeout
|
|
784
|
+
)
|
|
771
785
|
# extend the timeout for all created connections
|
|
772
|
-
self.connection.update_current_socket_timeout(self.config.
|
|
786
|
+
self.connection.update_current_socket_timeout(self.config.relaxed_timeout)
|
|
773
787
|
|
|
774
|
-
def
|
|
775
|
-
# Only reset timeouts if state is not MOVING and
|
|
788
|
+
def handle_maintenance_completed_notification(self):
|
|
789
|
+
# Only reset timeouts if state is not MOVING and relaxed timeouts are enabled
|
|
776
790
|
if (
|
|
777
791
|
self.connection.maintenance_state == MaintenanceState.MOVING
|
|
778
|
-
or not self.config.
|
|
792
|
+
or not self.config.is_relaxed_timeouts_enabled()
|
|
779
793
|
):
|
|
780
794
|
return
|
|
781
|
-
self.connection.reset_tmp_settings(
|
|
795
|
+
self.connection.reset_tmp_settings(reset_relaxed_timeout=True)
|
|
782
796
|
# Maintenance completed - reset the connection
|
|
783
|
-
# timeouts by providing -1 as the
|
|
797
|
+
# timeouts by providing -1 as the relaxed timeout
|
|
784
798
|
self.connection.update_current_socket_timeout(-1)
|
|
785
799
|
self.connection.maintenance_state = MaintenanceState.NONE
|