1
1
import os
2
+ import asyncio
2
3
from redis import asyncio as aioredis
3
4
from dotenv import load_dotenv
4
5
@@ -15,30 +16,55 @@ class RedisClient:
15
16
"""Service for managing Redis connections with proper lifecycle management."""
16
17
17
18
_instance = None
19
+ _client = None
20
+ _lock = asyncio .Lock ()
18
21
19
22
@classmethod
20
23
async def get_instance (cls ) -> aioredis .Redis :
21
- """Get or create a Redis client instance."""
22
- if cls ._instance is None :
23
- cls ._instance = cls ()
24
- await cls ._instance .initialize ()
25
- return cls ._instance .client
24
+ """Get or create a Redis client instance with proper singleton behavior."""
25
+ if cls ._client is None :
26
+ async with cls ._lock :
27
+ # Double-check pattern to prevent race conditions
28
+ if cls ._client is None :
29
+ if cls ._instance is None :
30
+ cls ._instance = cls ()
31
+ await cls ._instance .initialize ()
32
+ return cls ._client
26
33
27
- def __init__ (self ):
28
- self .client = None
29
34
30
35
async def initialize (self ) -> None :
31
- """Initialize the Redis client."""
32
- self .client = aioredis .from_url (
33
- REDIS_URL ,
34
- password = REDIS_PASSWORD ,
35
- decode_responses = True ,
36
- health_check_interval = 30
37
- )
38
-
39
- async def close (self ) -> None :
40
- """Close the Redis client connection."""
41
- if self .client :
42
- await self .client .close ()
43
- self .client = None
44
- print ("Redis client closed." )
36
+ """Initialize the Redis client with connection pool limits."""
37
+ if RedisClient ._client is None :
38
+ try :
39
+ RedisClient ._client = aioredis .from_url (
40
+ REDIS_URL ,
41
+ password = REDIS_PASSWORD ,
42
+ decode_responses = True ,
43
+ health_check_interval = 30 ,
44
+ max_connections = 20 , # Limit connection pool size
45
+ retry_on_timeout = True ,
46
+ socket_keepalive = True ,
47
+ socket_keepalive_options = {}
48
+ )
49
+ print (f"Redis client initialized with connection pool (max 20 connections)" )
50
+
51
+ # Test the connection
52
+ await RedisClient ._client .ping ()
53
+
54
+ except Exception as e :
55
+ print (f"Failed to initialize Redis client: { e } " )
56
+ RedisClient ._client = None
57
+ raise
58
+
59
+ @classmethod
60
+ async def close (cls ) -> None :
61
+ """Close the Redis client connection and reset singleton state."""
62
+ if cls ._client :
63
+ try :
64
+ await cls ._client .close ()
65
+ print ("Redis client closed." )
66
+ except Exception as e :
67
+ print (f"Error closing Redis client: { e } " )
68
+ finally :
69
+ cls ._client = None
70
+ cls ._instance = None
0 commit comments