1717logger = logging .getLogger (__name__ )
1818
1919
20- class RedisSentinelSettings (BaseSettings ):
21- """Configuration for connecting to Redis Sentinel ."""
20+ class RedisCommonSettings (BaseSettings ):
21+ """Common configuration for Redis Sentinel and Redis Standalone ."""
2222
23- REDIS_SENTINEL_HOSTS : str = ""
24- REDIS_SENTINEL_PORTS : str = "26379"
25- REDIS_SENTINEL_MASTER_NAME : str = "master"
2623 REDIS_DB : int = 15
27-
2824 REDIS_MAX_CONNECTIONS : Optional [int ] = None
2925 REDIS_RETRY_TIMEOUT : bool = True
3026 REDIS_DECODE_RESPONSES : bool = True
3127 REDIS_CLIENT_NAME : str = "stac-fastapi-app"
3228 REDIS_HEALTH_CHECK_INTERVAL : int = Field (default = 30 , gt = 0 )
3329 REDIS_SELF_LINK_TTL : int = 1800
3430
31+ REDIS_QUERY_RETRIES_NUM : int = Field (default = 3 , gt = 0 )
32+ REDIS_QUERY_INITIAL_DELAY : float = Field (default = 1.0 , gt = 0 )
33+ REDIS_QUERY_BACKOFF : float = Field (default = 2.0 , gt = 1 )
34+
3535 @field_validator ("REDIS_DB" )
3636 @classmethod
37- def validate_db_sentinel (cls , v : int ) -> int :
37+ def validate_db (cls , v : int ) -> int :
3838 """Validate REDIS_DB is not negative integer."""
3939 if v < 0 :
4040 raise ValueError ("REDIS_DB must be a positive integer" )
@@ -50,12 +50,20 @@ def validate_max_connections(cls, v):
5050
5151 @field_validator ("REDIS_SELF_LINK_TTL" )
5252 @classmethod
53- def validate_self_link_ttl_sentinel (cls , v : int ) -> int :
54- """Validate REDIS_SELF_LINK_TTL is not a negative integer ."""
53+ def validate_self_link_ttl (cls , v : int ) -> int :
54+ """Validate REDIS_SELF_LINK_TTL is negative."""
5555 if v < 0 :
5656 raise ValueError ("REDIS_SELF_LINK_TTL must be a positive integer" )
5757 return v
5858
59+
60+ class RedisSentinelSettings (RedisCommonSettings ):
61+ """Configuration for connecting to Redis Sentinel."""
62+
63+ REDIS_SENTINEL_HOSTS : str = ""
64+ REDIS_SENTINEL_PORTS : str = "26379"
65+ REDIS_SENTINEL_MASTER_NAME : str = "master"
66+
5967 def get_sentinel_hosts (self ) -> List [str ]:
6068 """Parse Redis Sentinel hosts from string to list."""
6169 if not self .REDIS_SENTINEL_HOSTS :
@@ -100,19 +108,11 @@ def get_sentinel_nodes(self) -> List[Tuple[str, int]]:
100108 return [(str (host ), int (port )) for host , port in zip (hosts , ports )]
101109
102110
103- class RedisSettings (BaseSettings ):
111+ class RedisSettings (RedisCommonSettings ):
104112 """Configuration for connecting Redis."""
105113
106114 REDIS_HOST : str = ""
107115 REDIS_PORT : int = 6379
108- REDIS_DB : int = 15
109-
110- REDIS_MAX_CONNECTIONS : Optional [int ] = None
111- REDIS_RETRY_TIMEOUT : bool = True
112- REDIS_DECODE_RESPONSES : bool = True
113- REDIS_CLIENT_NAME : str = "stac-fastapi-app"
114- REDIS_HEALTH_CHECK_INTERVAL : int = Field (default = 30 , gt = 0 )
115- REDIS_SELF_LINK_TTL : int = 1800
116116
117117 @field_validator ("REDIS_PORT" )
118118 @classmethod
@@ -122,58 +122,28 @@ def validate_port_standalone(cls, v: int) -> int:
122122 raise ValueError ("REDIS_PORT must be a positive integer" )
123123 return v
124124
125- @field_validator ("REDIS_DB" )
126- @classmethod
127- def validate_db_standalone (cls , v : int ) -> int :
128- """Validate REDIS_DB is not a negative integer."""
129- if v < 0 :
130- raise ValueError ("REDIS_DB must be a positive integer" )
131- return v
132-
133- @field_validator ("REDIS_MAX_CONNECTIONS" , mode = "before" )
134- @classmethod
135- def validate_max_connections (cls , v ):
136- """Handle empty/None values for REDIS_MAX_CONNECTIONS."""
137- if v in ["" , "null" , "Null" , "NULL" , "none" , "None" , "NONE" , None ]:
138- return None
139- return v
140-
141- @field_validator ("REDIS_SELF_LINK_TTL" )
142- @classmethod
143- def validate_self_link_ttl_standalone (cls , v : int ) -> int :
144- """Validate REDIS_SELF_LINK_TTL is negative."""
145- if v < 0 :
146- raise ValueError ("REDIS_SELF_LINK_TTL must be a positive integer" )
147- return v
148-
149-
150- class RedisRetrySettings (BaseSettings ):
151- """Configuration for Redis retry wrapper."""
152-
153- redis_query_retries_num : int = Field (
154- default = 3 , alias = "REDIS_QUERY_RETRIES_NUM" , gt = 0
155- )
156- redis_query_initial_delay : float = Field (
157- default = 1.0 , alias = "REDIS_QUERY_INITIAL_DELAY" , gt = 0
158- )
159- redis_query_backoff : float = Field (default = 2.0 , alias = "REDIS_QUERY_BACKOFF" , gt = 1 )
160-
161125
162126# Configure only one Redis configuration
163127sentinel_settings = RedisSentinelSettings ()
164128standalone_settings = RedisSettings ()
165- retry_settings = RedisRetrySettings ()
166129
167130
168131def redis_retry (func : Callable ) -> Callable :
169- """Wrap function in retry with back-off logic."""
132+ """Retry with back-off decorator for Redis connections."""
133+ _is_sentinel = True if sentinel_settings .REDIS_SENTINEL_HOSTS else False
170134
171135 @wraps (func )
172136 @retry (
173137 exceptions = (RedisConnectionError , RedisTimeoutError ),
174- tries = retry_settings .redis_query_retries_num ,
175- delay = retry_settings .redis_query_initial_delay ,
176- backoff = retry_settings .redis_query_backoff ,
138+ tries = sentinel_settings .REDIS_QUERY_RETRIES_NUM
139+ if _is_sentinel
140+ else standalone_settings .REDIS_QUERY_RETRIES_NUM ,
141+ delay = sentinel_settings .REDIS_QUERY_INITIAL_DELAY
142+ if _is_sentinel
143+ else standalone_settings .REDIS_QUERY_INITIAL_DELAY ,
144+ backoff = sentinel_settings .REDIS_QUERY_BACKOFF
145+ if _is_sentinel
146+ else standalone_settings .REDIS_QUERY_BACKOFF ,
177147 logger = logger ,
178148 )
179149 async def wrapper (* args , ** kwargs ):
@@ -242,7 +212,6 @@ async def connect_redis() -> Optional[aioredis.Redis]:
242212 except Exception as e :
243213 logger .error (f"Failed to connect to Redis: { e } " )
244214 return None
245-
246215 return None
247216
248217
0 commit comments