1
1
from abc import ABC , abstractmethod
2
2
from datetime import datetime , timedelta
3
- from typing import List , Union , Optional , Callable
3
+ from typing import List , Optional , Callable
4
4
5
- from redis .client import Pipeline
5
+ from redis .client import Pipeline , PubSub , PubSubWorkerThread
6
6
from redis .event import EventDispatcherInterface , OnCommandsFailEvent
7
7
from redis .multidb .config import DEFAULT_AUTO_FALLBACK_INTERVAL
8
8
from redis .multidb .database import Database , AbstractDatabase , Databases
9
9
from redis .multidb .circuit import State as CBState
10
- from redis .multidb .event import RegisterCommandFailure
10
+ from redis .multidb .event import RegisterCommandFailure , ActiveDatabaseChanged , ResubscribeOnActiveDatabaseChanged
11
11
from redis .multidb .failover import FailoverStrategy
12
12
from redis .multidb .failure_detector import FailureDetector
13
13
from redis .retry import Retry
@@ -34,7 +34,7 @@ def databases(self) -> Databases:
34
34
35
35
@property
36
36
@abstractmethod
37
- def active_database (self ) -> Union [Database , None ]:
37
+ def active_database (self ) -> Optional [Database ]:
38
38
"""Returns currently active database."""
39
39
pass
40
40
@@ -44,6 +44,23 @@ def active_database(self, database: AbstractDatabase) -> None:
44
44
"""Sets currently active database."""
45
45
pass
46
46
47
+ @abstractmethod
48
+ def pubsub (self , ** kwargs ):
49
+ """Initializes a PubSub object on a currently active database"""
50
+ pass
51
+
52
+ @property
53
+ @abstractmethod
54
+ def active_pubsub (self ) -> Optional [PubSub ]:
55
+ """Returns currently active pubsub."""
56
+ pass
57
+
58
+ @active_pubsub .setter
59
+ @abstractmethod
60
+ def active_pubsub (self , pubsub : PubSub ) -> None :
61
+ """Sets currently active pubsub."""
62
+ pass
63
+
47
64
@property
48
65
@abstractmethod
49
66
def failover_strategy (self ) -> FailoverStrategy :
@@ -103,7 +120,9 @@ def __init__(
103
120
self ._event_dispatcher = event_dispatcher
104
121
self ._auto_fallback_interval = auto_fallback_interval
105
122
self ._next_fallback_attempt : datetime
106
- self ._active_database : Union [Database , None ] = None
123
+ self ._active_database : Optional [Database ] = None
124
+ self ._active_pubsub : Optional [PubSub ] = None
125
+ self ._active_pubsub_kwargs = {}
107
126
self ._setup_event_dispatcher ()
108
127
self ._schedule_next_fallback ()
109
128
@@ -128,8 +147,22 @@ def active_database(self) -> Optional[AbstractDatabase]:
128
147
129
148
@active_database .setter
130
149
def active_database (self , database : AbstractDatabase ) -> None :
150
+ old_active = self ._active_database
131
151
self ._active_database = database
132
152
153
+ if old_active is not None and old_active is not database :
154
+ self ._event_dispatcher .dispatch (
155
+ ActiveDatabaseChanged (old_active , self ._active_database , self , ** self ._active_pubsub_kwargs )
156
+ )
157
+
158
+ @property
159
+ def active_pubsub (self ) -> Optional [PubSub ]:
160
+ return self ._active_pubsub
161
+
162
+ @active_pubsub .setter
163
+ def active_pubsub (self , pubsub : PubSub ) -> None :
164
+ self ._active_pubsub = pubsub
165
+
133
166
@property
134
167
def failover_strategy (self ) -> FailoverStrategy :
135
168
return self ._failover_strategy
@@ -143,6 +176,7 @@ def auto_fallback_interval(self, auto_fallback_interval: int) -> None:
143
176
self ._auto_fallback_interval = auto_fallback_interval
144
177
145
178
def execute_command (self , * args , ** options ):
179
+ """Executes a command and returns the result."""
146
180
def callback ():
147
181
return self ._active_database .client .execute_command (* args , ** options )
148
182
@@ -170,6 +204,39 @@ def callback():
170
204
171
205
return self ._execute_with_failure_detection (callback )
172
206
207
+ def pubsub (self , ** kwargs ):
208
+ def callback ():
209
+ if self ._active_pubsub is None :
210
+ self ._active_pubsub = self ._active_database .client .pubsub (** kwargs )
211
+ self ._active_pubsub_kwargs = kwargs
212
+ return None
213
+
214
+ return self ._execute_with_failure_detection (callback )
215
+
216
+ def execute_pubsub_method (self , method_name : str , * args , ** kwargs ):
217
+ """
218
+ Executes given method on active pub/sub.
219
+ """
220
+ def callback ():
221
+ method = getattr (self .active_pubsub , method_name )
222
+ return method (* args , ** kwargs )
223
+
224
+ return self ._execute_with_failure_detection (callback , * args )
225
+
226
+ def execute_pubsub_run_in_thread (
227
+ self ,
228
+ pubsub ,
229
+ sleep_time : float = 0.0 ,
230
+ daemon : bool = False ,
231
+ exception_handler : Optional [Callable ] = None ,
232
+ ) -> "PubSubWorkerThread" :
233
+ def callback ():
234
+ return self ._active_pubsub .run_in_thread (
235
+ sleep_time , daemon = daemon , exception_handler = exception_handler , pubsub = pubsub
236
+ )
237
+
238
+ return self ._execute_with_failure_detection (callback )
239
+
173
240
def _execute_with_failure_detection (self , callback : Callable , cmds : tuple = ()):
174
241
"""
175
242
Execute a commands execution callback with failure detection.
@@ -199,7 +266,7 @@ def _check_active_database(self):
199
266
and self ._next_fallback_attempt <= datetime .now ()
200
267
)
201
268
):
202
- self ._active_database = self ._failover_strategy .database
269
+ self .active_database = self ._failover_strategy .database
203
270
self ._schedule_next_fallback ()
204
271
205
272
def _schedule_next_fallback (self ) -> None :
@@ -210,9 +277,11 @@ def _schedule_next_fallback(self) -> None:
210
277
211
278
def _setup_event_dispatcher (self ):
212
279
"""
213
- Registers command failure event listener .
280
+ Registers necessary listeners .
214
281
"""
215
- event_listener = RegisterCommandFailure (self ._failure_detectors )
282
+ failure_listener = RegisterCommandFailure (self ._failure_detectors )
283
+ resubscribe_listener = ResubscribeOnActiveDatabaseChanged ()
216
284
self ._event_dispatcher .register_listeners ({
217
- OnCommandsFailEvent : [event_listener ],
285
+ OnCommandsFailEvent : [failure_listener ],
286
+ ActiveDatabaseChanged : [resubscribe_listener ],
218
287
})
0 commit comments