()
| 1721 | def _start_cleanup_thread(self): |
| 1722 | """Start background thread to maintain ownership and clean up resources""" |
| 1723 | def cleanup_task(): |
| 1724 | while True: |
| 1725 | try: |
| 1726 | close_old_connections() |
| 1727 | # Send worker heartbeat first |
| 1728 | if self.redis_client: |
| 1729 | worker_heartbeat_key = f"live:worker:{self.worker_id}:heartbeat" |
| 1730 | self._execute_redis_command( |
| 1731 | lambda: self.redis_client.setex(worker_heartbeat_key, 30, str(time.time())) |
| 1732 | ) |
| 1733 | |
| 1734 | # Refresh channel registry |
| 1735 | self.refresh_channel_registry() |
| 1736 | |
| 1737 | # Recover channels whose stop_channel call never returned |
| 1738 | self._recover_stuck_channel_stops() |
| 1739 | |
| 1740 | # Create a unified list of all channels we have locally |
| 1741 | all_local_channels = ( |
| 1742 | set(self.stream_managers.keys()) |
| 1743 | | set(self.client_managers.keys()) |
| 1744 | | set(self._live_stream_managers.keys()) |
| 1745 | ) |
| 1746 | |
| 1747 | # Single loop through all channels - process each exactly once |
| 1748 | for channel_id in list(all_local_channels): |
| 1749 | if self.am_i_owner(channel_id): |
| 1750 | # === OWNER CHANNEL HANDLING === |
| 1751 | # Extend ownership lease |
| 1752 | self.extend_ownership(channel_id) |
| 1753 | |
| 1754 | # Get channel state from metadata hash |
| 1755 | channel_state = "unknown" |
| 1756 | if self.redis_client: |
| 1757 | metadata_key = RedisKeys.channel_metadata(channel_id) |
| 1758 | metadata = self.redis_client.hgetall(metadata_key) |
| 1759 | if metadata and 'state' in metadata: |
| 1760 | channel_state = metadata['state'] |
| 1761 | |
| 1762 | # Check if channel has any clients left |
| 1763 | total_clients = 0 |
| 1764 | if channel_id in self.client_managers: |
| 1765 | client_manager = self.client_managers[channel_id] |
| 1766 | total_clients = client_manager.get_total_client_count() |
| 1767 | else: |
| 1768 | # This can happen during reconnection attempts or crashes |
| 1769 | # Check Redis directly for any connected clients |
| 1770 | if self.redis_client: |
| 1771 | client_set_key = RedisKeys.clients(channel_id) |
| 1772 | total_clients = self.redis_client.scard(client_set_key) or 0 |
| 1773 | |
| 1774 | if total_clients == 0: |
| 1775 | logger.warning(f"Channel {channel_id} is missing client_manager but we're the owner with 0 clients - will trigger cleanup") |
| 1776 | |
| 1777 | # Log client count periodically |
| 1778 | if time.time() % 30 < 1: # Every ~30 seconds |
| 1779 | logger.info(f"Channel {channel_id} has {total_clients} clients, state: {channel_state}") |
| 1780 |
nothing calls this directly
no test coverage detected