mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-17 16:50:53 +00:00
perf(m3u): Enhance memory management and performance during XC stream processing
- Improved memory cleanup by invoking `gc.collect()` after batch processing in `process_m3u_batch_direct` and `collect_xc_streams`, ensuring timely release of resources. - Added tests to verify that garbage collection is triggered appropriately after batch operations.
This commit is contained in:
parent
fe8309fd45
commit
56199aef81
3 changed files with 32 additions and 2 deletions
|
|
@ -790,6 +790,7 @@ def collect_xc_streams(account_id, enabled_groups):
|
|||
"""Collect all XC streams in a single API call and filter by enabled groups."""
|
||||
account = M3UAccount.objects.get(id=account_id)
|
||||
all_streams = []
|
||||
filtered_count = 0
|
||||
|
||||
# Create a mapping from category_id to group info for filtering
|
||||
enabled_category_ids = {}
|
||||
|
|
@ -819,7 +820,6 @@ def collect_xc_streams(account_id, enabled_groups):
|
|||
logger.info(f"Retrieved {len(all_xc_streams)} total live streams from provider")
|
||||
|
||||
# Filter streams based on enabled categories
|
||||
filtered_count = 0
|
||||
for stream in all_xc_streams:
|
||||
# Fall back to a generated name if the provider returns null/empty
|
||||
stream_name = stream.get("name") or f"{account.name} - {stream.get('stream_id', 'Unknown')}"
|
||||
|
|
@ -862,11 +862,17 @@ def collect_xc_streams(account_id, enabled_groups):
|
|||
all_streams.append(stream_data)
|
||||
filtered_count += 1
|
||||
|
||||
# Drop the full provider catalog before returning; only filtered rows are needed.
|
||||
del all_xc_streams
|
||||
gc.collect()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to fetch XC streams: {str(e)}")
|
||||
return []
|
||||
|
||||
logger.info(f"Filtered {filtered_count} streams from {len(enabled_category_ids)} enabled categories")
|
||||
logger.info(
|
||||
f"Filtered {filtered_count} streams from {len(enabled_category_ids)} enabled categories"
|
||||
)
|
||||
return all_streams
|
||||
|
||||
def process_xc_category_direct(account_id, batch, groups, hash_keys):
|
||||
|
|
@ -1270,6 +1276,7 @@ def process_m3u_batch_direct(account_id, batch, groups, hash_keys):
|
|||
|
||||
# Free batch data structures (reference-counted deallocation)
|
||||
del streams_to_create, streams_to_update, stream_hashes, existing_streams
|
||||
gc.collect()
|
||||
|
||||
return retval
|
||||
|
||||
|
|
@ -3338,6 +3345,8 @@ def _refresh_single_m3u_account_impl(account_id):
|
|||
except Exception as e:
|
||||
logger.error(f"Error in thread batch {batch_idx}: {str(e)}")
|
||||
completed_batches += 1 # Still count it to avoid hanging
|
||||
finally:
|
||||
batches[batch_idx] = None
|
||||
|
||||
logger.info(f"Thread-based processing completed for account {account_id}")
|
||||
else:
|
||||
|
|
@ -3449,6 +3458,8 @@ def _refresh_single_m3u_account_impl(account_id):
|
|||
except Exception as e:
|
||||
logger.error(f"Error in XC thread batch {batch_idx}: {str(e)}")
|
||||
completed_batches += 1 # Still count it to avoid hanging
|
||||
finally:
|
||||
batches[batch_idx] = None
|
||||
|
||||
logger.info(f"XC thread-based processing completed for account {account_id}")
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,24 @@ class ProcessM3UBatchCleanupTests(SimpleTestCase):
|
|||
process_m3u_batch_direct(1, [], {}, ["name", "url"])
|
||||
mock_connections.close_all.assert_called()
|
||||
|
||||
@patch("apps.m3u.tasks.Stream")
|
||||
@patch("apps.m3u.tasks.M3UAccount")
|
||||
def test_batch_calls_gc_collect(self, mock_account_cls, mock_stream_cls):
|
||||
"""gc.collect() must run after each batch so XC refresh threads release promptly."""
|
||||
from apps.m3u.tasks import process_m3u_batch_direct
|
||||
|
||||
mock_account = MagicMock()
|
||||
mock_account.filters.order_by.return_value = []
|
||||
mock_account_cls.objects.get.return_value = mock_account
|
||||
mock_stream_cls.objects.filter.return_value.select_related.return_value.only.return_value = (
|
||||
[]
|
||||
)
|
||||
mock_stream_cls.generate_hash_key = MagicMock(return_value="hash123")
|
||||
|
||||
with patch("gc.collect") as mock_gc, patch("django.db.connections"):
|
||||
process_m3u_batch_direct(1, [], {}, ["name", "url"])
|
||||
mock_gc.assert_called()
|
||||
|
||||
|
||||
class LockReleaseTests(SimpleTestCase):
|
||||
"""Verify task lock is released on all exit paths."""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue