From 48b95aa0c733c2ecc97624851c50ce94bb292a9d Mon Sep 17 00:00:00 2001 From: xuan <118178092+yuee3@users.noreply.github.com> Date: Mon, 12 Oct 2026 13:45:04 +0800 Subject: [PATCH] fix(prometheus): give each proxy boot its own auto-created PROMETHEUS_MULTIPROC_DIR --- litellm/proxy/proxy_cli.py | 14 +++--- tests/unit/proxy/test_prometheus_cleanup.py | 47 +++++++++++++++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/litellm/proxy/proxy_cli.py b/litellm/proxy/proxy_cli.py index c7371f294f0..2e677857573 100644 --- a/litellm/proxy/proxy_cli.py +++ b/litellm/proxy/proxy_cli.py @@ -724,12 +724,16 @@ class ProxyInitializationHelpers: wipe_directory(configured_dir) return None - multiproc_dir: Final = configured_dir or os.path.join(tempfile.gettempdir(), "litellm_prometheus_multiproc") - os.environ["PROMETHEUS_MULTIPROC_DIR"] = multiproc_dir + if configured_dir: + # An operator-set directory is theirs to manage; keep the historical wipe-on-boot. + os.makedirs(configured_dir, exist_ok=True) + wipe_directory(configured_dir) + multiproc_dir, action = configured_dir, "Using existing" + else: + # Unique per boot: a fixed shared name lets a second proxy on this host wipe the first one's counters. + multiproc_dir, action = tempfile.mkdtemp(prefix="litellm_prometheus_multiproc_"), "Auto-created" - os.makedirs(multiproc_dir, exist_ok=True) - wipe_directory(multiproc_dir) - action: Final = "Using existing" if configured_dir else "Auto-created" + os.environ["PROMETHEUS_MULTIPROC_DIR"] = multiproc_dir print(f"LiteLLM: {action} PROMETHEUS_MULTIPROC_DIR={multiproc_dir}") return multiproc_dir diff --git a/tests/unit/proxy/test_prometheus_cleanup.py b/tests/unit/proxy/test_prometheus_cleanup.py index c774d376d33..95903540c47 100644 --- a/tests/unit/proxy/test_prometheus_cleanup.py +++ b/tests/unit/proxy/test_prometheus_cleanup.py @@ -6,8 +6,10 @@ ProxyInitializationHelpers._maybe_setup_prometheus_multiproc_dir. from __future__ import annotations import os +import shutil import subprocess import sys +import tempfile from pathlib import Path from typing import Final from unittest.mock import patch @@ -193,6 +195,8 @@ class TestMaybeSetupPrometheusMultiprocDir: result_dir = os.environ.get("PROMETHEUS_MULTIPROC_DIR") assert result_dir is not None assert os.path.isdir(result_dir) + legacy_shared = os.path.join(tempfile.gettempdir(), "litellm_prometheus_multiproc") + assert result_dir != legacy_shared # Cleanup os.environ.pop("PROMETHEUS_MULTIPROC_DIR", None) @@ -257,3 +261,46 @@ class TestMaybeSetupPrometheusMultiprocDir: assert os.environ["PROMETHEUS_MULTIPROC_DIR"] == str(tmp_path) assert not admitted.exists() assert not samples.exists() + + def test_auto_created_dir_is_unique_per_boot_and_keeps_sibling_counters(self): + """A fixed default dir made the second proxy boot wipe the first proxy's live counters (issue #42566).""" + with patch.dict(os.environ, {}, clear=False): + os.environ.pop("PROMETHEUS_MULTIPROC_DIR", None) + os.environ.pop("prometheus_multiproc_dir", None) + litellm_settings = {"callbacks": ["prometheus"]} + + first = ProxyInitializationHelpers._maybe_setup_prometheus_multiproc_dir( + num_workers=4, litellm_settings=litellm_settings + ) + assert first is not None and os.path.isdir(first) + + stale = os.path.join(first, "counter_proxy_one.db") + with open(stale, "w") as f: + f.write("live") + + os.environ.pop("PROMETHEUS_MULTIPROC_DIR", None) # simulate a second, independent boot + second = ProxyInitializationHelpers._maybe_setup_prometheus_multiproc_dir( + num_workers=4, litellm_settings=litellm_settings + ) + assert second is not None and os.path.isdir(second) + + assert first != second + assert os.path.exists(stale) + + os.environ.pop("PROMETHEUS_MULTIPROC_DIR", None) + shutil.rmtree(first, ignore_errors=True) + shutil.rmtree(second, ignore_errors=True) + + def test_configured_dir_is_still_wiped_on_boot(self, tmp_path): + """A directory the operator set keeps its historical wipe-on-boot lifecycle.""" + stale = tmp_path / "counter_stale.db" + stale.write_text("old") + + with patch.dict(os.environ, {"PROMETHEUS_MULTIPROC_DIR": str(tmp_path)}): + result_dir = ProxyInitializationHelpers._maybe_setup_prometheus_multiproc_dir( + num_workers=4, + litellm_settings={"callbacks": ["prometheus"]}, + ) + + assert result_dir == str(tmp_path) + assert not stale.exists()