Skip to content

Commit 2c71558

Browse files
author
Andrzej Pijanowski
committed
chore: Move common Redis configs to RedisCommonSettings class
1 parent 154707c commit 2c71558

File tree

2 files changed

+72
-69
lines changed

2 files changed

+72
-69
lines changed

stac_fastapi/core/stac_fastapi/core/redis_utils.py

Lines changed: 29 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,24 @@
1717
logger = 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
163127
sentinel_settings = RedisSentinelSettings()
164128
standalone_settings = RedisSettings()
165-
retry_settings = RedisRetrySettings()
166129

167130

168131
def 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

stac_fastapi/tests/redis/test_redis_utils.py

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,22 @@ async def test_redis_utils_functions():
5353
@pytest.mark.asyncio
5454
async def test_redis_retry_retries_until_success(monkeypatch):
5555
monkeypatch.setattr(
56-
redis_utils.retry_settings, "redis_query_retries_num", 3, raising=False
56+
redis_utils.sentinel_settings, "REDIS_QUERY_RETRIES_NUM", 3, raising=False
5757
)
5858
monkeypatch.setattr(
59-
redis_utils.retry_settings, "redis_query_initial_delay", 0, raising=False
59+
redis_utils.sentinel_settings, "REDIS_QUERY_INITIAL_DELAY", 0, raising=False
6060
)
6161
monkeypatch.setattr(
62-
redis_utils.retry_settings, "redis_query_backoff", 2.0, raising=False
62+
redis_utils.sentinel_settings, "REDIS_QUERY_BACKOFF", 2.0, raising=False
63+
)
64+
monkeypatch.setattr(
65+
redis_utils.standalone_settings, "REDIS_QUERY_RETRIES_NUM", 3, raising=False
66+
)
67+
monkeypatch.setattr(
68+
redis_utils.standalone_settings, "REDIS_QUERY_INITIAL_DELAY", 0, raising=False
69+
)
70+
monkeypatch.setattr(
71+
redis_utils.standalone_settings, "REDIS_QUERY_BACKOFF", 2.0, raising=False
6372
)
6473

6574
captured_kwargs = {}
@@ -99,24 +108,49 @@ async def flaky() -> str:
99108
assert result == "success"
100109
assert call_counter["count"] == 3
101110
assert (
102-
captured_kwargs["tries"] == redis_utils.retry_settings.redis_query_retries_num
111+
captured_kwargs["tries"]
112+
== redis_utils.sentinel_settings.REDIS_QUERY_RETRIES_NUM
113+
)
114+
assert (
115+
captured_kwargs["delay"]
116+
== redis_utils.sentinel_settings.REDIS_QUERY_INITIAL_DELAY
117+
)
118+
assert (
119+
captured_kwargs["backoff"] == redis_utils.sentinel_settings.REDIS_QUERY_BACKOFF
103120
)
104121
assert (
105-
captured_kwargs["delay"] == redis_utils.retry_settings.redis_query_initial_delay
122+
captured_kwargs["tries"]
123+
== redis_utils.standalone_settings.REDIS_QUERY_RETRIES_NUM
124+
)
125+
assert (
126+
captured_kwargs["delay"]
127+
== redis_utils.standalone_settings.REDIS_QUERY_INITIAL_DELAY
128+
)
129+
assert (
130+
captured_kwargs["backoff"]
131+
== redis_utils.standalone_settings.REDIS_QUERY_BACKOFF
106132
)
107-
assert captured_kwargs["backoff"] == redis_utils.retry_settings.redis_query_backoff
108133

109134

110135
@pytest.mark.asyncio
111136
async def test_redis_retry_raises_after_exhaustion(monkeypatch):
112137
monkeypatch.setattr(
113-
redis_utils.retry_settings, "redis_query_retries_num", 3, raising=False
138+
redis_utils.sentinel_settings, "REDIS_QUERY_RETRIES_NUM", 3, raising=False
139+
)
140+
monkeypatch.setattr(
141+
redis_utils.sentinel_settings, "REDIS_QUERY_INITIAL_DELAY", 0, raising=False
142+
)
143+
monkeypatch.setattr(
144+
redis_utils.sentinel_settings, "REDIS_QUERY_BACKOFF", 2.0, raising=False
145+
)
146+
monkeypatch.setattr(
147+
redis_utils.standalone_settings, "REDIS_QUERY_RETRIES_NUM", 3, raising=False
114148
)
115149
monkeypatch.setattr(
116-
redis_utils.retry_settings, "redis_query_initial_delay", 0, raising=False
150+
redis_utils.standalone_settings, "REDIS_QUERY_INITIAL_DELAY", 0, raising=False
117151
)
118152
monkeypatch.setattr(
119-
redis_utils.retry_settings, "redis_query_backoff", 2.0, raising=False
153+
redis_utils.standalone_settings, "REDIS_QUERY_BACKOFF", 2.0, raising=False
120154
)
121155

122156
def fake_retry(**kwargs):

0 commit comments

Comments
 (0)