11"""Utilities for connecting to and managing Redis connections.""" 
22
3+ import  logging 
34from  typing  import  Optional 
45
56from  pydantic_settings  import  BaseSettings 
89
910redis_pool : Optional [aioredis .Redis ] =  None 
1011
12+ logger  =  logging .getLogger (__name__ )
13+ 
1114
1215class  RedisSentinelSettings (BaseSettings ):
1316    """Configuration for connecting to Redis Sentinel.""" 
@@ -25,7 +28,7 @@ class RedisSentinelSettings(BaseSettings):
2528
2629
2730class  RedisSettings (BaseSettings ):
28-     """Configuration for connecting Redis Sentinel .""" 
31+     """Configuration for connecting Redis.""" 
2932
3033    REDIS_HOST : str  =  "" 
3134    REDIS_PORT : int  =  6379 
@@ -38,75 +41,71 @@ class RedisSettings(BaseSettings):
3841    REDIS_HEALTH_CHECK_INTERVAL : int  =  30 
3942
4043
41- # Select the Redis or  Redis Sentinel  configuration 
42- redis_settings :  BaseSettings   =   RedisSettings ()
43- 
44+ # Configure only one  Redis configuration 
45+ sentinel_settings   =   RedisSentinelSettings ()
46+ standalone_settings   =   RedisSettings () 
4447
45- async  def  connect_redis (settings : Optional [RedisSettings ] =  None ) ->  aioredis .Redis :
46-     """Return a Redis connection.""" 
47-     global  redis_pool 
48-     settings  =  settings  or  redis_settings 
49- 
50-     if  not  settings .REDIS_HOST  or  not  settings .REDIS_PORT :
51-         return  None 
5248
53-     if  redis_pool  is  None :
54-         pool  =  aioredis .ConnectionPool (
55-             host = settings .REDIS_HOST ,
56-             port = settings .REDIS_PORT ,
57-             db = settings .REDIS_DB ,
58-             max_connections = settings .REDIS_MAX_CONNECTIONS ,
59-             decode_responses = settings .REDIS_DECODE_RESPONSES ,
60-             retry_on_timeout = settings .REDIS_RETRY_TIMEOUT ,
61-             health_check_interval = settings .REDIS_HEALTH_CHECK_INTERVAL ,
62-         )
63-         redis_pool  =  aioredis .Redis (
64-             connection_pool = pool , client_name = settings .REDIS_CLIENT_NAME 
65-         )
66-     return  redis_pool 
67- 
68- 
69- async  def  connect_redis_sentinel (
70-     settings : Optional [RedisSentinelSettings ] =  None ,
71- ) ->  Optional [aioredis .Redis ]:
72-     """Return a Redis Sentinel connection.""" 
49+ async  def  connect_redis () ->  Optional [aioredis .Redis ]:
50+     """Return a Redis connection Redis or Redis Sentinel.""" 
7351    global  redis_pool 
7452
75-     settings  =  settings  or  redis_settings 
53+     if  redis_pool  is  not None :
54+         return  redis_pool 
55+ 
56+     try :
57+         if  sentinel_settings .REDIS_SENTINEL_HOSTS :
58+             hosts  =  [
59+                 h .strip ()
60+                 for  h  in  sentinel_settings .REDIS_SENTINEL_HOSTS .split ("," )
61+                 if  h .strip ()
62+             ]
63+             ports  =  [
64+                 int (p .strip ())
65+                 for  p  in  sentinel_settings .REDIS_SENTINEL_PORTS .split ("," )
66+                 if  p .strip ()
67+             ]
7668
77-     if  (
78-         not  settings .REDIS_SENTINEL_HOSTS 
79-         or  not  settings .REDIS_SENTINEL_PORTS 
80-         or  not  settings .REDIS_SENTINEL_MASTER_NAME 
81-     ):
82-         return  None 
83- 
84-     hosts  =  [h .strip () for  h  in  settings .REDIS_SENTINEL_HOSTS .split ("," ) if  h .strip ()]
85-     ports  =  [
86-         int (p .strip ()) for  p  in  settings .REDIS_SENTINEL_PORTS .split ("," ) if  p .strip ()
87-     ]
88- 
89-     if  redis_pool  is  None :
90-         try :
9169            sentinel  =  Sentinel (
9270                [(h , p ) for  h , p  in  zip (hosts , ports )],
93-                 decode_responses = settings .REDIS_DECODE_RESPONSES ,
94-             )
95-             master  =  sentinel .master_for (
96-                 service_name = settings .REDIS_SENTINEL_MASTER_NAME ,
97-                 db = settings .REDIS_DB ,
98-                 decode_responses = settings .REDIS_DECODE_RESPONSES ,
99-                 retry_on_timeout = settings .REDIS_RETRY_TIMEOUT ,
100-                 client_name = settings .REDIS_CLIENT_NAME ,
101-                 max_connections = settings .REDIS_MAX_CONNECTIONS ,
102-                 health_check_interval = settings .REDIS_HEALTH_CHECK_INTERVAL ,
71+                 decode_responses = sentinel_settings .REDIS_DECODE_RESPONSES ,
10372            )
104-             redis_pool  =  master 
10573
106-         except  Exception :
74+             redis_pool  =  sentinel .master_for (
75+                 service_name = sentinel_settings .REDIS_SENTINEL_MASTER_NAME ,
76+                 db = sentinel_settings .REDIS_DB ,
77+                 decode_responses = sentinel_settings .REDIS_DECODE_RESPONSES ,
78+                 retry_on_timeout = sentinel_settings .REDIS_RETRY_TIMEOUT ,
79+                 client_name = sentinel_settings .REDIS_CLIENT_NAME ,
80+                 max_connections = sentinel_settings .REDIS_MAX_CONNECTIONS ,
81+                 health_check_interval = sentinel_settings .REDIS_HEALTH_CHECK_INTERVAL ,
82+             )
83+             logger .info ("Connected to Redis Sentinel" )
84+ 
85+         elif  standalone_settings .REDIS_HOST :
86+             pool  =  aioredis .ConnectionPool (
87+                 host = standalone_settings .REDIS_HOST ,
88+                 port = standalone_settings .REDIS_PORT ,
89+                 db = standalone_settings .REDIS_DB ,
90+                 max_connections = standalone_settings .REDIS_MAX_CONNECTIONS ,
91+                 decode_responses = standalone_settings .REDIS_DECODE_RESPONSES ,
92+                 retry_on_timeout = standalone_settings .REDIS_RETRY_TIMEOUT ,
93+                 health_check_interval = standalone_settings .REDIS_HEALTH_CHECK_INTERVAL ,
94+             )
95+             redis_pool  =  aioredis .Redis (
96+                 connection_pool = pool , client_name = standalone_settings .REDIS_CLIENT_NAME 
97+             )
98+             logger .info ("Connected to Redis" )
99+         else :
100+             logger .warning ("No Redis configuration found" )
107101            return  None 
108102
109-     return  redis_pool 
103+         return  redis_pool 
104+ 
105+     except  Exception  as  e :
106+         logger .error (f"Failed to connect to Redis: { e }  )
107+         redis_pool  =  None 
108+         return  None 
110109
111110
112111async  def  save_self_link (
@@ -122,3 +121,32 @@ async def get_prev_link(redis: aioredis.Redis, token: Optional[str]) -> Optional
122121    if  not  token :
123122        return  None 
124123    return  await  redis .get (f"nav:self:{ token }  )
124+ 
125+ 
126+ async  def  redis_pagination_links (
127+     current_url : str , token : str , next_token : str , links : list 
128+ ) ->  None :
129+     """Handle Redis pagination.""" 
130+     redis  =  None 
131+     try :
132+         redis  =  await  connect_redis ()
133+         logger .info ("Redis connection established successfully" )
134+     except  Exception  as  e :
135+         redis  =  None 
136+         logger .warning (f"Redis connection failed: { e }  )
137+ 
138+     if  redis :
139+         if  next_token :
140+             await  save_self_link (redis , next_token , current_url )
141+ 
142+         prev_link  =  await  get_prev_link (redis , token )
143+         if  prev_link :
144+             links .insert (
145+                 0 ,
146+                 {
147+                     "rel" : "prev" ,
148+                     "type" : "application/json" ,
149+                     "method" : "GET" ,
150+                     "href" : prev_link ,
151+                 },
152+             )
0 commit comments