Fix redis ssl issues 2
This commit is contained in:
@@ -53,6 +53,7 @@ class SchedulerLeaderMixin:
|
||||
self._redis_url = redis_url
|
||||
self._service_name = service_name
|
||||
self._leader_election = None
|
||||
self._redis_manager = None
|
||||
self._redis_client = None
|
||||
self.scheduler = None
|
||||
self._scheduler_started = False
|
||||
@@ -63,21 +64,16 @@ class SchedulerLeaderMixin:
|
||||
|
||||
Only the leader will start the scheduler.
|
||||
"""
|
||||
import ssl
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
from shared.leader_election.service import LeaderElectionService
|
||||
import redis.asyncio as redis
|
||||
from shared.redis_utils import RedisConnectionManager
|
||||
|
||||
try:
|
||||
# Create Redis connection with proper SSL handling for self-signed certificates
|
||||
connection_kwargs = {"decode_responses": False}
|
||||
|
||||
# Handle SSL/TLS for rediss:// URLs (self-signed certificates)
|
||||
if self._redis_url and self._redis_url.startswith("rediss://"):
|
||||
connection_kwargs["ssl_cert_reqs"] = ssl.CERT_NONE
|
||||
|
||||
self._redis_client = redis.from_url(self._redis_url, **connection_kwargs)
|
||||
await self._redis_client.ping()
|
||||
# Create Redis connection using shared manager (handles SSL, pooling, health checks)
|
||||
self._redis_manager = await RedisConnectionManager.create(
|
||||
self._redis_url, decode_responses=False
|
||||
)
|
||||
self._redis_client = self._redis_manager.get_client()
|
||||
|
||||
# Create scheduler (but don't start it yet)
|
||||
self.scheduler = AsyncIOScheduler()
|
||||
@@ -197,9 +193,9 @@ class SchedulerLeaderMixin:
|
||||
# Stop scheduler
|
||||
await self._stop_scheduler()
|
||||
|
||||
# Close Redis
|
||||
if self._redis_client:
|
||||
await self._redis_client.close()
|
||||
# Close Redis manager (handles client and pool cleanup)
|
||||
if self._redis_manager:
|
||||
await self._redis_manager.close()
|
||||
|
||||
logger.info("Scheduler service stopped",
|
||||
service=self._service_name)
|
||||
|
||||
@@ -13,17 +13,19 @@ from shared.redis_utils.client import (
|
||||
set_with_ttl,
|
||||
get_value,
|
||||
increment_counter,
|
||||
get_keys_pattern
|
||||
get_keys_pattern,
|
||||
get_ssl_kwargs_for_url,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
# Connection management
|
||||
# Connection management (RedisConnectionManager.create() is recommended)
|
||||
"RedisConnectionManager",
|
||||
"get_redis_manager",
|
||||
"initialize_redis",
|
||||
"get_redis_client",
|
||||
"close_redis",
|
||||
"redis_context",
|
||||
"get_ssl_kwargs_for_url",
|
||||
|
||||
# Convenience functions
|
||||
"set_with_ttl",
|
||||
|
||||
@@ -3,25 +3,111 @@ Redis client initialization and connection management
|
||||
Provides standardized Redis connection for all services
|
||||
"""
|
||||
|
||||
import ssl
|
||||
import redis.asyncio as redis
|
||||
from typing import Optional
|
||||
from typing import Optional, Dict, Any
|
||||
import structlog
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
logger = structlog.get_logger()
|
||||
|
||||
|
||||
def get_ssl_kwargs_for_url(redis_url: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Get SSL kwargs for redis.from_url() based on the URL scheme.
|
||||
|
||||
Handles self-signed certificates by disabling certificate verification
|
||||
when using rediss:// (TLS-enabled) URLs.
|
||||
|
||||
Args:
|
||||
redis_url: Redis connection URL (redis:// or rediss://)
|
||||
|
||||
Returns:
|
||||
Dict with SSL configuration kwargs
|
||||
"""
|
||||
if redis_url and redis_url.startswith("rediss://"):
|
||||
return {
|
||||
"ssl_cert_reqs": ssl.CERT_NONE, # Disable certificate verification
|
||||
"ssl_ca_certs": None, # Don't require CA certificates
|
||||
"ssl_certfile": None, # Don't require client cert
|
||||
"ssl_keyfile": None, # Don't require client key
|
||||
}
|
||||
return {}
|
||||
|
||||
|
||||
class RedisConnectionManager:
|
||||
"""
|
||||
Manages Redis connections with connection pooling and error handling
|
||||
Thread-safe singleton pattern for sharing connections across service
|
||||
Manages Redis connections with connection pooling and error handling.
|
||||
Thread-safe singleton pattern for sharing connections across service.
|
||||
|
||||
Usage:
|
||||
# Option 1: Using class method (recommended for new code)
|
||||
manager = await RedisConnectionManager.create(redis_url)
|
||||
client = manager.get_client()
|
||||
|
||||
# Option 2: Using instance method
|
||||
manager = RedisConnectionManager()
|
||||
await manager.initialize(redis_url)
|
||||
client = manager.get_client()
|
||||
|
||||
# Don't forget to close when done
|
||||
await manager.close()
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._client: Optional[redis.Redis] = None
|
||||
self._pool: Optional[redis.ConnectionPool] = None
|
||||
self._redis_url: Optional[str] = None
|
||||
self.logger = logger
|
||||
|
||||
@classmethod
|
||||
async def create(
|
||||
cls,
|
||||
redis_url: str,
|
||||
db: int = 0,
|
||||
max_connections: int = 50,
|
||||
decode_responses: bool = False,
|
||||
retry_on_timeout: bool = True,
|
||||
socket_keepalive: bool = True,
|
||||
health_check_interval: int = 30
|
||||
) -> "RedisConnectionManager":
|
||||
"""
|
||||
Factory method to create and initialize a RedisConnectionManager.
|
||||
|
||||
This is the recommended way to create Redis connections across all services.
|
||||
Handles SSL/TLS configuration automatically for self-signed certificates.
|
||||
|
||||
Args:
|
||||
redis_url: Redis connection URL (redis:// or rediss://)
|
||||
db: Database number (0-15)
|
||||
max_connections: Maximum connections in pool
|
||||
decode_responses: Automatically decode responses to strings
|
||||
retry_on_timeout: Retry on timeout errors
|
||||
socket_keepalive: Enable TCP keepalive
|
||||
health_check_interval: Health check interval in seconds
|
||||
|
||||
Returns:
|
||||
Initialized RedisConnectionManager
|
||||
|
||||
Example:
|
||||
from shared.redis_utils import RedisConnectionManager
|
||||
|
||||
manager = await RedisConnectionManager.create(settings.REDIS_URL)
|
||||
client = manager.get_client()
|
||||
await client.ping()
|
||||
"""
|
||||
instance = cls()
|
||||
await instance.initialize(
|
||||
redis_url=redis_url,
|
||||
db=db,
|
||||
max_connections=max_connections,
|
||||
decode_responses=decode_responses,
|
||||
retry_on_timeout=retry_on_timeout,
|
||||
socket_keepalive=socket_keepalive,
|
||||
health_check_interval=health_check_interval,
|
||||
)
|
||||
return instance
|
||||
|
||||
async def initialize(
|
||||
self,
|
||||
redis_url: str,
|
||||
@@ -45,8 +131,9 @@ class RedisConnectionManager:
|
||||
health_check_interval: Health check interval in seconds
|
||||
"""
|
||||
try:
|
||||
# Create connection pool
|
||||
# Handle SSL parameters for self-signed certificates
|
||||
self._redis_url = redis_url
|
||||
|
||||
# Create connection pool with SSL handling for self-signed certificates
|
||||
connection_kwargs = {
|
||||
'db': db,
|
||||
'max_connections': max_connections,
|
||||
@@ -55,16 +142,10 @@ class RedisConnectionManager:
|
||||
'socket_keepalive': socket_keepalive,
|
||||
'health_check_interval': health_check_interval
|
||||
}
|
||||
|
||||
# If using SSL/TLS, add SSL parameters to handle self-signed certificates
|
||||
if redis_url.startswith('rediss://'):
|
||||
connection_kwargs.update({
|
||||
'ssl_cert_reqs': None, # Disable certificate verification
|
||||
'ssl_ca_certs': None, # Don't require CA certificates
|
||||
'ssl_certfile': None, # Don't require client cert
|
||||
'ssl_keyfile': None # Don't require client key
|
||||
})
|
||||
|
||||
|
||||
# Add SSL kwargs for self-signed certificates (using shared helper)
|
||||
connection_kwargs.update(get_ssl_kwargs_for_url(redis_url))
|
||||
|
||||
self._pool = redis.ConnectionPool.from_url(
|
||||
redis_url,
|
||||
**connection_kwargs
|
||||
|
||||
Reference in New Issue
Block a user