diff --git a/docs/multi_database.rst b/docs/geographic_failover.rst similarity index 99% rename from docs/multi_database.rst rename to docs/geographic_failover.rst index 479fab0cd0..39573f4932 100644 --- a/docs/multi_database.rst +++ b/docs/geographic_failover.rst @@ -1,4 +1,4 @@ -Multi-database client (Active-Active) +Client-side geographic failover (Active-Active) ===================================== The multi-database client allows your application to connect to multiple Redis databases, which are typically replicas of each other. diff --git a/redis/asyncio/multidb/client.py b/redis/asyncio/multidb/client.py index f6385e46ea..c541a33e20 100644 --- a/redis/asyncio/multidb/client.py +++ b/redis/asyncio/multidb/client.py @@ -23,7 +23,7 @@ class MultiDBClient(AsyncRedisModuleCommands, AsyncCoreCommands): """ Client that operates on multiple logical Redis databases. - Should be used in Active-Active database setups. + Should be used in Client-side geographic failover database setups. """ def __init__(self, config: MultiDbConfig): @@ -299,7 +299,7 @@ async def _check_databases_health( unhealthy_db = result.database unhealthy_db.circuit.state = CBState.OPEN - logger.exception( + logger.debug( "Health check failed, due to exception", exc_info=result.original_exception, ) @@ -337,8 +337,14 @@ def _on_circuit_state_change_callback( return if old_state == CBState.CLOSED and new_state == CBState.OPEN: + logger.error( + f"Database {circuit.database} is unreachable. Failover has been initiated." + ) loop.call_later(DEFAULT_GRACE_PERIOD, _half_open_circuit, circuit) + if old_state != CBState.CLOSED and new_state == CBState.CLOSED: + logger.info(f"Database {circuit.database} is reachable again.") + async def aclose(self): if self.command_executor.active_database: await self.command_executor.active_database.client.aclose() diff --git a/redis/asyncio/multidb/database.py b/redis/asyncio/multidb/database.py index ecf7a1b972..a8993976fe 100644 --- a/redis/asyncio/multidb/database.py +++ b/redis/asyncio/multidb/database.py @@ -67,3 +67,6 @@ def circuit(self) -> CircuitBreaker: @circuit.setter def circuit(self, circuit: CircuitBreaker): self._cb = circuit + + def __repr__(self): + return f"Database(client={self.client}, weight={self.weight})" diff --git a/redis/multidb/client.py b/redis/multidb/client.py index d557aaa24b..3b91043396 100644 --- a/redis/multidb/client.py +++ b/redis/multidb/client.py @@ -24,7 +24,7 @@ class MultiDBClient(RedisModuleCommands, CoreCommands): """ Client that operates on multiple logical Redis databases. - Should be used in Active-Active database setups. + Should be used in Client-side geographic failover database setups. """ def __init__(self, config: MultiDbConfig): @@ -276,6 +276,11 @@ def _check_databases_health(self, on_error: Callable[[Exception], None] = None): unhealthy_db = e.database unhealthy_db.circuit.state = CBState.OPEN + logger.debug( + "Health check failed, due to exception", + exc_info=e.original_exception, + ) + if on_error: on_error(e.original_exception) except TimeoutError: @@ -291,10 +296,17 @@ def _on_circuit_state_change_callback( return if old_state == CBState.CLOSED and new_state == CBState.OPEN: + logger.error( + f"Database {circuit.database} is unreachable. Failover has been initiated." + ) + self._bg_scheduler.run_once( DEFAULT_GRACE_PERIOD, _half_open_circuit, circuit ) + if old_state != CBState.CLOSED and new_state == CBState.CLOSED: + logger.info(f"Database {circuit.database} is reachable again.") + def close(self): """ Closes the client and all its resources. diff --git a/redis/multidb/database.py b/redis/multidb/database.py index d46de99e2d..46a29ad35e 100644 --- a/redis/multidb/database.py +++ b/redis/multidb/database.py @@ -128,3 +128,6 @@ def circuit(self) -> CircuitBreaker: @circuit.setter def circuit(self, circuit: CircuitBreaker): self._cb = circuit + + def __repr__(self): + return f"Database(client={self.client}, weight={self.weight})"