Skip to content

Commit f5011a4

Browse files
committed
[Bugfix] Fix PrometheusStatsLogger crash on re-instantiation and non-daemon thread
Two bugs in observability.py: 1. When _metric_mappings is already populated (e.g. multi-engine or hot-reload), __init__ returns early without initializing instance attributes (is_running, thread, config, etc.). Any subsequent call to shutdown() or __del__() raises AttributeError. Fix: always initialize core instance attributes before the early-return guard. 2. The update_stats_loop thread is not created as a daemon thread. If shutdown() is never explicitly called, this thread prevents the Python process from exiting cleanly. Fix: set daemon=True, and guard shutdown() against thread being None. Signed-off-by: supermario_leo <leo.stack@outlook.com>
1 parent e6ca062 commit f5011a4

1 file changed

Lines changed: 12 additions & 6 deletions

File tree

ucm/observability.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,16 @@ def __init__(self, model_name, worker_id, config_path):
6666
Load metrics config from YAML file (config_path),
6767
register metrics using prometheus_client, and start a thread to get updated metrics.
6868
"""
69+
# Always initialize instance attributes so that shutdown() and
70+
# __del__() work even when we skip metric registration below.
71+
self.config = self._load_config(config_path)
72+
self.log_interval = self.config.get("log_interval", 10)
73+
self.is_running = False
74+
self.thread = None
75+
6976
if _metric_mappings:
7077
logger.warning("Metrics are already registered, skipping re-registration.")
7178
return
72-
# Load metrics config
73-
self.config = self._load_config(config_path)
74-
self.log_interval = self.config.get("log_interval", 10)
7579

7680
# Set up histogram max length
7781
histogram_max_length = self.config.get("histogram_max_length", 10000)
@@ -97,9 +101,10 @@ def __init__(self, model_name, worker_id, config_path):
97101
# Initialize metrics based on config
98102
self._init_metrics_from_config()
99103

100-
# Start thread to update metrics
104+
# Start daemon thread to update metrics so it won't block
105+
# process exit if shutdown() is not explicitly called.
101106
self.is_running = True
102-
self.thread = threading.Thread(target=self.update_stats_loop)
107+
self.thread = threading.Thread(target=self.update_stats_loop, daemon=True)
103108
self.thread.start()
104109

105110
def _register_metrics_by_type(self, metric_type):
@@ -190,7 +195,8 @@ def update_stats_loop(self):
190195

191196
def shutdown(self):
192197
self.is_running = False
193-
self.thread.join()
198+
if self.thread is not None:
199+
self.thread.join()
194200

195201
def __del__(self):
196202
try:

0 commit comments

Comments
 (0)