From ed065f718deccf2af5a166f066fcf500751a0441 Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Tue, 14 Oct 2025 13:44:28 -0500 Subject: [PATCH] Enhancement: Implement caching for proxy settings to improve performance and reduce database load. Also, ensure database connections are closed after use in both config and stream manager. --- apps/proxy/config.py | 28 +++++++++++++++++++++++++-- apps/proxy/ts_proxy/stream_manager.py | 7 +++++++ dispatcharr/settings.py | 1 + 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/apps/proxy/config.py b/apps/proxy/config.py index 9ce5b66c..c3d0cc27 100644 --- a/apps/proxy/config.py +++ b/apps/proxy/config.py @@ -1,4 +1,6 @@ """Shared configuration between proxy types""" +import time +from django.db import connection class BaseConfig: DEFAULT_USER_AGENT = 'VLC/3.0.20 LibVLC/3.0.20' # Will only be used if connection to settings fail @@ -12,13 +14,35 @@ class BaseConfig: BUFFERING_TIMEOUT = 15 # Seconds to wait for buffering before switching streams BUFFER_SPEED = 1 # What speed to condsider the stream buffering, 1x is normal speed, 2x is double speed, etc. + # Cache for proxy settings (class-level, shared across all instances) + _proxy_settings_cache = None + _proxy_settings_cache_time = 0 + _proxy_settings_cache_ttl = 10 # Cache for 10 seconds + @classmethod def get_proxy_settings(cls): - """Get proxy settings from CoreSettings JSON data with fallback to defaults""" + """Get proxy settings from CoreSettings JSON data with fallback to defaults (cached)""" + # Check if cache is still valid + now = time.time() + if cls._proxy_settings_cache is not None and (now - cls._proxy_settings_cache_time) < cls._proxy_settings_cache_ttl: + return cls._proxy_settings_cache + + # Cache miss or expired - fetch from database try: from core.models import CoreSettings - return CoreSettings.get_proxy_settings() + settings = CoreSettings.get_proxy_settings() + cls._proxy_settings_cache = settings + cls._proxy_settings_cache_time = now + + # Close the connection after reading settings to avoid keeping it open + try: + connection.close() + except Exception: + pass + + return settings except Exception: + # Return defaults if database query fails return { "buffering_timeout": 15, "buffering_speed": 1.0, diff --git a/apps/proxy/ts_proxy/stream_manager.py b/apps/proxy/ts_proxy/stream_manager.py index d1f4ded6..e3620886 100644 --- a/apps/proxy/ts_proxy/stream_manager.py +++ b/apps/proxy/ts_proxy/stream_manager.py @@ -9,6 +9,7 @@ import subprocess import gevent import re from typing import Optional, List +from django.db import connection from django.shortcuts import get_object_or_404 from urllib3.exceptions import ReadTimeoutError from apps.proxy.config import TSConfig as Config @@ -384,6 +385,12 @@ class StreamManager: except Exception as e: logger.error(f"Failed to update channel state in Redis: {e} for channel {self.channel_id}", exc_info=True) + # Close database connection for this thread + try: + connection.close() + except Exception: + pass + logger.info(f"Stream manager stopped for channel {self.channel_id}") def _establish_transcode_connection(self): diff --git a/dispatcharr/settings.py b/dispatcharr/settings.py index 057780de..a0c4fc84 100644 --- a/dispatcharr/settings.py +++ b/dispatcharr/settings.py @@ -134,6 +134,7 @@ else: "PASSWORD": os.environ.get("POSTGRES_PASSWORD", "secret"), "HOST": os.environ.get("POSTGRES_HOST", "localhost"), "PORT": int(os.environ.get("POSTGRES_PORT", 5432)), + "CONN_MAX_AGE": DATABASE_CONN_MAX_AGE, } }