131 lines
3.5 KiB
Python
131 lines
3.5 KiB
Python
from abc import ABC, abstractmethod
|
|
from typing import Optional, Union
|
|
|
|
import redis
|
|
from redis import RedisCluster
|
|
from redis.data_structure import WeightedList
|
|
from redis.multidb.circuit import CircuitBreaker
|
|
from redis.typing import Number
|
|
|
|
|
|
class AbstractDatabase(ABC):
|
|
@property
|
|
@abstractmethod
|
|
def weight(self) -> float:
|
|
"""The weight of this database in compare to others. Used to determine the database failover to."""
|
|
pass
|
|
|
|
@weight.setter
|
|
@abstractmethod
|
|
def weight(self, weight: float):
|
|
"""Set the weight of this database in compare to others."""
|
|
pass
|
|
|
|
@property
|
|
@abstractmethod
|
|
def health_check_url(self) -> Optional[str]:
|
|
"""Health check URL associated with the current database."""
|
|
pass
|
|
|
|
@health_check_url.setter
|
|
@abstractmethod
|
|
def health_check_url(self, health_check_url: Optional[str]):
|
|
"""Set the health check URL associated with the current database."""
|
|
pass
|
|
|
|
|
|
class BaseDatabase(AbstractDatabase):
|
|
def __init__(
|
|
self,
|
|
weight: float,
|
|
health_check_url: Optional[str] = None,
|
|
):
|
|
self._weight = weight
|
|
self._health_check_url = health_check_url
|
|
|
|
@property
|
|
def weight(self) -> float:
|
|
return self._weight
|
|
|
|
@weight.setter
|
|
def weight(self, weight: float):
|
|
self._weight = weight
|
|
|
|
@property
|
|
def health_check_url(self) -> Optional[str]:
|
|
return self._health_check_url
|
|
|
|
@health_check_url.setter
|
|
def health_check_url(self, health_check_url: Optional[str]):
|
|
self._health_check_url = health_check_url
|
|
|
|
|
|
class SyncDatabase(AbstractDatabase):
|
|
"""Database with an underlying synchronous redis client."""
|
|
|
|
@property
|
|
@abstractmethod
|
|
def client(self) -> Union[redis.Redis, RedisCluster]:
|
|
"""The underlying redis client."""
|
|
pass
|
|
|
|
@client.setter
|
|
@abstractmethod
|
|
def client(self, client: Union[redis.Redis, RedisCluster]):
|
|
"""Set the underlying redis client."""
|
|
pass
|
|
|
|
@property
|
|
@abstractmethod
|
|
def circuit(self) -> CircuitBreaker:
|
|
"""Circuit breaker for the current database."""
|
|
pass
|
|
|
|
@circuit.setter
|
|
@abstractmethod
|
|
def circuit(self, circuit: CircuitBreaker):
|
|
"""Set the circuit breaker for the current database."""
|
|
pass
|
|
|
|
|
|
Databases = WeightedList[tuple[SyncDatabase, Number]]
|
|
|
|
|
|
class Database(BaseDatabase, SyncDatabase):
|
|
def __init__(
|
|
self,
|
|
client: Union[redis.Redis, RedisCluster],
|
|
circuit: CircuitBreaker,
|
|
weight: float,
|
|
health_check_url: Optional[str] = None,
|
|
):
|
|
"""
|
|
Initialize a new Database instance.
|
|
|
|
Args:
|
|
client: Underlying Redis client instance for database operations
|
|
circuit: Circuit breaker for handling database failures
|
|
weight: Weight value used for database failover prioritization
|
|
health_check_url: Health check URL associated with the current database
|
|
"""
|
|
self._client = client
|
|
self._cb = circuit
|
|
self._cb.database = self
|
|
super().__init__(weight, health_check_url)
|
|
|
|
@property
|
|
def client(self) -> Union[redis.Redis, RedisCluster]:
|
|
return self._client
|
|
|
|
@client.setter
|
|
def client(self, client: Union[redis.Redis, RedisCluster]):
|
|
self._client = client
|
|
|
|
@property
|
|
def circuit(self) -> CircuitBreaker:
|
|
return self._cb
|
|
|
|
@circuit.setter
|
|
def circuit(self, circuit: CircuitBreaker):
|
|
self._cb = circuit
|