diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a1f493f..c4fd6ddf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Performance + +- Eliminated repeated DB queries in the `ts_proxy` hot path. `StreamManager`, `StreamGenerator`, and `ProxyServer` were each calling `Channel.objects.get(uuid=...)` on every retry, reconnect, failover, and buffering event solely to retrieve `channel.name` for log events. `StreamManager` and `StreamGenerator` now fetch the channel name once at construction via a lightweight `values_list` query and store it as `self.channel_name`. `ProxyServer` caches the name in a `_channel_names` dict keyed by channel ID at channel-start time and pops it at channel-stop time. (Fixes #1138) + ## [0.23.0] - 2026-04-17 ### Security diff --git a/apps/proxy/ts_proxy/server.py b/apps/proxy/ts_proxy/server.py index b5820ca7..42e456e8 100644 --- a/apps/proxy/ts_proxy/server.py +++ b/apps/proxy/ts_proxy/server.py @@ -66,6 +66,7 @@ class ProxyServer: self.stream_managers = {} self.stream_buffers = {} self.client_managers = {} + self._channel_names = {} # Generate a unique worker ID import socket @@ -672,7 +673,9 @@ class ProxyServer: # Log channel start event try: - channel_obj = Channel.objects.get(uuid=channel_id) + _name = Channel.objects.filter(uuid=channel_id).values_list('name', flat=True).first() + channel_name = _name if _name else str(channel_id) + self._channel_names[channel_id] = channel_name # Get stream name if stream_id is available stream_name = None @@ -686,7 +689,7 @@ class ProxyServer: log_system_event( 'channel_start', channel_id=channel_id, - channel_name=channel_obj.name, + channel_name=channel_name, stream_name=stream_name, stream_id=channel_stream_id ) @@ -941,7 +944,7 @@ class ProxyServer: # Log channel stop event (after cleanup, before releasing ownership section ends) try: - channel_obj = Channel.objects.get(uuid=channel_id) + channel_name = self._channel_names.pop(channel_id, None) or str(channel_id) # Calculate runtime and get total bytes from metadata runtime = None @@ -967,7 +970,7 @@ class ProxyServer: log_system_event( 'channel_stop', channel_id=channel_id, - channel_name=channel_obj.name, + channel_name=channel_name, runtime=runtime, total_bytes=total_bytes ) diff --git a/apps/proxy/ts_proxy/stream_generator.py b/apps/proxy/ts_proxy/stream_generator.py index 0036be5c..df74b8d1 100644 --- a/apps/proxy/ts_proxy/stream_generator.py +++ b/apps/proxy/ts_proxy/stream_generator.py @@ -43,6 +43,12 @@ class StreamGenerator: self.client_user_agent = client_user_agent self.channel_initializing = channel_initializing self.user = user + # Cache channel name once to avoid repeated DB queries for logging + try: + _name = Channel.objects.filter(uuid=channel_id).values_list('name', flat=True).first() + self.channel_name = _name if _name else str(channel_id) + except Exception: + self.channel_name = str(channel_id) # Performance and state tracking self.stream_start_time = time.time() @@ -107,11 +113,10 @@ class StreamGenerator: # Log client connect event try: - channel_obj = Channel.objects.get(uuid=self.channel_id) log_system_event( 'client_connect', channel_id=self.channel_id, - channel_name=channel_obj.name, + channel_name=self.channel_name, client_ip=self.client_ip, client_id=self.client_id, user_agent=self.client_user_agent[:100] if self.client_user_agent else None, @@ -587,11 +592,10 @@ class StreamGenerator: # Log client disconnect event try: - channel_obj = Channel.objects.get(uuid=self.channel_id) log_system_event( 'client_disconnect', channel_id=self.channel_id, - channel_name=channel_obj.name, + channel_name=self.channel_name, client_ip=self.client_ip, client_id=self.client_id, user_agent=self.client_user_agent[:100] if self.client_user_agent else None, diff --git a/apps/proxy/ts_proxy/stream_manager.py b/apps/proxy/ts_proxy/stream_manager.py index d22fc7d0..993b9cf3 100644 --- a/apps/proxy/ts_proxy/stream_manager.py +++ b/apps/proxy/ts_proxy/stream_manager.py @@ -32,6 +32,12 @@ class StreamManager: def __init__(self, channel_id, url, buffer, user_agent=None, transcode=False, stream_id=None, worker_id=None): # Basic properties self.channel_id = channel_id + # Cache channel name once to avoid repeated DB queries in hot retry/reconnect loops + try: + _name = Channel.objects.filter(uuid=channel_id).values_list('name', flat=True).first() + self.channel_name = _name if _name else str(channel_id) + except Exception: + self.channel_name = str(channel_id) self.url = url self.buffer = buffer self.running = True @@ -274,11 +280,10 @@ class StreamManager: # Log reconnection event if this is a retry (not first attempt) if self.retry_count > 0: try: - channel_obj = Channel.objects.get(uuid=self.channel_id) log_system_event( 'channel_reconnect', channel_id=self.channel_id, - channel_name=channel_obj.name, + channel_name=self.channel_name, attempt=self.retry_count + 1, max_attempts=self.max_retries ) @@ -317,11 +322,10 @@ class StreamManager: # Log connection error event try: - channel_obj = Channel.objects.get(uuid=self.channel_id) log_system_event( 'channel_error', channel_id=self.channel_id, - channel_name=channel_obj.name, + channel_name=self.channel_name, error_type='connection_failed', url=self.url[:100] if self.url else None, attempts=self.max_retries @@ -344,11 +348,10 @@ class StreamManager: # Log connection error event with exception details try: - channel_obj = Channel.objects.get(uuid=self.channel_id) log_system_event( 'channel_error', channel_id=self.channel_id, - channel_name=channel_obj.name, + channel_name=self.channel_name, error_type='connection_exception', error_message=str(e)[:200], url=self.url[:100] if self.url else None, @@ -826,11 +829,10 @@ class StreamManager: # Log failover event try: - channel_obj = Channel.objects.get(uuid=self.channel_id) log_system_event( 'channel_failover', channel_id=self.channel_id, - channel_name=channel_obj.name, + channel_name=self.channel_name, reason='buffering_timeout', duration=buffering_duration ) @@ -846,11 +848,10 @@ class StreamManager: # Log system event for buffering try: - channel_obj = Channel.objects.get(uuid=self.channel_id) log_system_event( 'channel_buffering', channel_id=self.channel_id, - channel_name=channel_obj.name, + channel_name=self.channel_name, speed=ffmpeg_speed ) except Exception as e: @@ -1172,11 +1173,10 @@ class StreamManager: # Log stream switch event try: - channel_obj = Channel.objects.get(uuid=self.channel_id) log_system_event( 'stream_switch', channel_id=self.channel_id, - channel_name=channel_obj.name, + channel_name=self.channel_name, new_url=new_url[:100] if new_url else None, stream_id=stream_id ) @@ -1304,11 +1304,10 @@ class StreamManager: # Log reconnection event try: - channel_obj = Channel.objects.get(uuid=self.channel_id) log_system_event( 'channel_reconnect', channel_id=self.channel_id, - channel_name=channel_obj.name, + channel_name=self.channel_name, reason='health_monitor' ) except Exception as e: