Get Redis client optimized for PubSub operations
(cls, max_retries=5, retry_interval=1)
| 225 | |
| 226 | @classmethod |
| 227 | def get_pubsub_client(cls, max_retries=5, retry_interval=1): |
| 228 | """Get Redis client optimized for PubSub operations""" |
| 229 | if cls._pubsub_client is None: |
| 230 | retry_count = 0 |
| 231 | while retry_count < max_retries: |
| 232 | try: |
| 233 | # Get connection parameters from settings or environment |
| 234 | redis_host = os.environ.get("REDIS_HOST", getattr(settings, 'REDIS_HOST', 'localhost')) |
| 235 | redis_port = int(os.environ.get("REDIS_PORT", getattr(settings, 'REDIS_PORT', 6379))) |
| 236 | redis_db = int(os.environ.get("REDIS_DB", getattr(settings, 'REDIS_DB', 0))) |
| 237 | redis_password = os.environ.get("REDIS_PASSWORD", getattr(settings, 'REDIS_PASSWORD', '')) |
| 238 | redis_user = os.environ.get("REDIS_USER", getattr(settings, 'REDIS_USER', '')) |
| 239 | |
| 240 | # Use standardized settings but without socket timeouts for PubSub |
| 241 | # Important: socket_timeout is None for PubSub operations |
| 242 | socket_connect_timeout = getattr(settings, 'REDIS_SOCKET_CONNECT_TIMEOUT', 5) |
| 243 | socket_keepalive = getattr(settings, 'REDIS_SOCKET_KEEPALIVE', True) |
| 244 | health_check_interval = getattr(settings, 'REDIS_HEALTH_CHECK_INTERVAL', 30) |
| 245 | retry_on_timeout = getattr(settings, 'REDIS_RETRY_ON_TIMEOUT', True) |
| 246 | |
| 247 | ssl_params = getattr(settings, 'REDIS_SSL_PARAMS', {}) |
| 248 | |
| 249 | # Create Redis client with PubSub-optimized settings - no timeout |
| 250 | client = redis.Redis( |
| 251 | host=redis_host, |
| 252 | port=redis_port, |
| 253 | db=redis_db, |
| 254 | password=redis_password if redis_password else None, |
| 255 | username=redis_user if redis_user else None, |
| 256 | socket_timeout=None, # Critical: No timeout for PubSub operations |
| 257 | socket_connect_timeout=socket_connect_timeout, |
| 258 | socket_keepalive=socket_keepalive, |
| 259 | health_check_interval=health_check_interval, |
| 260 | retry_on_timeout=retry_on_timeout, |
| 261 | decode_responses=True, |
| 262 | **ssl_params |
| 263 | ) |
| 264 | |
| 265 | # Validate connection with ping |
| 266 | client.ping() |
| 267 | logger.info(f"Connected to Redis for PubSub at {redis_host}:{redis_port}/{redis_db}") |
| 268 | |
| 269 | # We don't need the keepalive thread anymore since we're using proper PubSub handling |
| 270 | cls._pubsub_client = client |
| 271 | break |
| 272 | |
| 273 | except (ConnectionError, TimeoutError) as e: |
| 274 | retry_count += 1 |
| 275 | _tls_hint = _REDIS_TLS_HINT if ssl_params else "" |
| 276 | if retry_count >= max_retries: |
| 277 | logger.error(f"Failed to connect to Redis for PubSub after {max_retries} attempts: {e}{_tls_hint}") |
| 278 | return None |
| 279 | else: |
| 280 | # Use exponential backoff for retries |
| 281 | wait_time = retry_interval * (2 ** (retry_count - 1)) |
| 282 | logger.warning(f"Redis PubSub connection failed. Retrying in {wait_time}s... ({retry_count}/{max_retries})") |
| 283 | time.sleep(wait_time) |
| 284 |