""" Cache configuration for thrillwiki project. This module configures Redis-based caching with connection pooling, session caching, and API response caching. Why python-decouple? - Already used in base.py for consistency - Simpler API than django-environ - Sufficient for our configuration needs - Better separation of config from code Cache Backends: - default: General purpose caching (queries, templates, etc.) - sessions: User session storage (separate for security) - api: API response caching (high concurrency) """ from decouple import config # ============================================================================= # Redis Configuration # ============================================================================= REDIS_URL = config("REDIS_URL", default="redis://127.0.0.1:6379/1") # Redis cache backend classes DJANGO_REDIS_CACHE_BACKEND = "django_redis.cache.RedisCache" DJANGO_REDIS_CLIENT_CLASS = "django_redis.client.DefaultClient" # ============================================================================= # Cache Configuration # ============================================================================= # Multiple cache backends for different purposes CACHES = { # Default cache for general purpose caching # Used for: database queries, computed values, template fragments "default": { "BACKEND": DJANGO_REDIS_CACHE_BACKEND, "LOCATION": config("REDIS_URL", default="redis://127.0.0.1:6379/1"), "OPTIONS": { "CLIENT_CLASS": DJANGO_REDIS_CLIENT_CLASS, # Use hiredis for faster C-based parsing "PARSER_CLASS": "redis.connection.HiredisParser", # Connection pooling for better performance "CONNECTION_POOL_CLASS": "redis.BlockingConnectionPool", "CONNECTION_POOL_CLASS_KWARGS": { "max_connections": config( "REDIS_MAX_CONNECTIONS", default=100, cast=int ), "timeout": config("REDIS_CONNECTION_TIMEOUT", default=20, cast=int), "socket_keepalive": True, "socket_keepalive_options": { 1: 1, # TCP_KEEPIDLE: Start keepalive after 1s idle 2: 1, # TCP_KEEPINTVL: Send probes every 1s 3: 3, # TCP_KEEPCNT: Close after 3 failed probes }, "retry_on_timeout": True, "health_check_interval": 30, }, # Compress cached data to save memory "COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor", # Graceful degradation if Redis is unavailable "IGNORE_EXCEPTIONS": config( "REDIS_IGNORE_EXCEPTIONS", default=True, cast=bool ), }, "KEY_PREFIX": config("CACHE_KEY_PREFIX", default="thrillwiki"), "VERSION": 1, }, # Session cache - separate for security isolation # Uses a different Redis database (db 2) "sessions": { "BACKEND": DJANGO_REDIS_CACHE_BACKEND, "LOCATION": config("REDIS_SESSIONS_URL", default="redis://127.0.0.1:6379/2"), "OPTIONS": { "CLIENT_CLASS": DJANGO_REDIS_CLIENT_CLASS, "PARSER_CLASS": "redis.connection.HiredisParser", "CONNECTION_POOL_CLASS": "redis.BlockingConnectionPool", "CONNECTION_POOL_CLASS_KWARGS": { "max_connections": config( "REDIS_SESSIONS_MAX_CONNECTIONS", default=50, cast=int ), "timeout": 10, "socket_keepalive": True, }, }, "KEY_PREFIX": "sessions", }, # API cache - high concurrency for API responses # Uses a different Redis database (db 3) "api": { "BACKEND": DJANGO_REDIS_CACHE_BACKEND, "LOCATION": config("REDIS_API_URL", default="redis://127.0.0.1:6379/3"), "OPTIONS": { "CLIENT_CLASS": DJANGO_REDIS_CLIENT_CLASS, "PARSER_CLASS": "redis.connection.HiredisParser", "CONNECTION_POOL_CLASS": "redis.BlockingConnectionPool", "CONNECTION_POOL_CLASS_KWARGS": { "max_connections": config( "REDIS_API_MAX_CONNECTIONS", default=100, cast=int ), "timeout": 15, "socket_keepalive": True, "retry_on_timeout": True, }, # Compress API responses to save bandwidth "COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor", }, "KEY_PREFIX": "api", }, } # ============================================================================= # Session Configuration # ============================================================================= # Use Redis for session storage for better performance and scalability SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_CACHE_ALIAS = "sessions" # Session timeout in seconds (1 hour) SESSION_COOKIE_AGE = config("SESSION_COOKIE_AGE", default=3600, cast=int) # Update session on each request (sliding expiry) SESSION_SAVE_EVERY_REQUEST = config( "SESSION_SAVE_EVERY_REQUEST", default=True, cast=bool ) # Session persists until cookie expires (not browser close) SESSION_EXPIRE_AT_BROWSER_CLOSE = config( "SESSION_EXPIRE_AT_BROWSER_CLOSE", default=False, cast=bool ) # ============================================================================= # Cache Middleware Settings # ============================================================================= # For Django's cache middleware (UpdateCacheMiddleware/FetchFromCacheMiddleware) CACHE_MIDDLEWARE_SECONDS = config("CACHE_MIDDLEWARE_SECONDS", default=300, cast=int) CACHE_MIDDLEWARE_KEY_PREFIX = config( "CACHE_MIDDLEWARE_KEY_PREFIX", default="thrillwiki" )