-
Notifications
You must be signed in to change notification settings - Fork 49
feat: support engine metrics from PROMETHEUS_MULTIPROC_DIR in grpc mode #1038
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -11,7 +11,9 @@ | |||||||||||||||||||||||||||||||
| import logging | ||||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||||
| import random | ||||||||||||||||||||||||||||||||
| import shutil | ||||||||||||||||||||||||||||||||
| import signal | ||||||||||||||||||||||||||||||||
| import tempfile | ||||||||||||||||||||||||||||||||
| import socket | ||||||||||||||||||||||||||||||||
| import subprocess | ||||||||||||||||||||||||||||||||
| import sys | ||||||||||||||||||||||||||||||||
|
|
@@ -550,6 +552,7 @@ def __init__(self, backend: str, args: argparse.Namespace, backend_args: list[st | |||||||||||||||||||||||||||||||
| self.launcher: WorkerLauncher = BACKEND_LAUNCHERS[backend]() | ||||||||||||||||||||||||||||||||
| self.workers: list[tuple[subprocess.Popen, int]] = [] | ||||||||||||||||||||||||||||||||
| self._shutting_down = False | ||||||||||||||||||||||||||||||||
| self._prometheus_dir: str | None = None | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| # -- public API --------------------------------------------------------- | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
@@ -571,6 +574,15 @@ def run(self) -> None: | |||||||||||||||||||||||||||||||
| def _launch_workers(self) -> None: | ||||||||||||||||||||||||||||||||
| ports = _find_available_ports(self.args.worker_base_port, self.args.data_parallel_size) | ||||||||||||||||||||||||||||||||
| host = self.args.worker_host | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if getattr(self.args, "connection_mode", "grpc") == "grpc": | ||||||||||||||||||||||||||||||||
| self._prometheus_dir = tempfile.mkdtemp(prefix="smg_prometheus_") | ||||||||||||||||||||||||||||||||
| os.environ["PROMETHEUS_MULTIPROC_DIR"] = self._prometheus_dir | ||||||||||||||||||||||||||||||||
| logger.info( | ||||||||||||||||||||||||||||||||
| "Set PROMETHEUS_MULTIPROC_DIR=%s for gRPC metrics collection", | ||||||||||||||||||||||||||||||||
| self._prometheus_dir, | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
|
Comment on lines
+578
to
+584
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To ensure the metrics collector uses the same Python environment as the orchestrator (which is critical when running in a virtual environment), consider passing
Suggested change
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| for dp_rank, port in enumerate(ports): | ||||||||||||||||||||||||||||||||
| env = self.launcher.gpu_env(self.args, dp_rank) | ||||||||||||||||||||||||||||||||
| proc = self.launcher.launch(self.args, self.backend_args, host, port, env) | ||||||||||||||||||||||||||||||||
|
|
@@ -641,6 +653,23 @@ def _cleanup_workers(self) -> None: | |||||||||||||||||||||||||||||||
| except (ProcessLookupError, OSError): | ||||||||||||||||||||||||||||||||
| pass | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| self._cleanup_prometheus_dir() | ||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In gRPC mode Useful? React with 👍 / 👎. |
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def _cleanup_prometheus_dir(self) -> None: | ||||||||||||||||||||||||||||||||
| """Remove the temporary prometheus multiprocess directory and its .db files.""" | ||||||||||||||||||||||||||||||||
| if self._prometheus_dir is None: | ||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||
| shutil.rmtree(self._prometheus_dir) | ||||||||||||||||||||||||||||||||
| logger.info("Cleaned up PROMETHEUS_MULTIPROC_DIR=%s", self._prometheus_dir) | ||||||||||||||||||||||||||||||||
| except OSError as e: | ||||||||||||||||||||||||||||||||
| logger.warning( | ||||||||||||||||||||||||||||||||
| "Failed to clean up PROMETHEUS_MULTIPROC_DIR=%s: %s", | ||||||||||||||||||||||||||||||||
| self._prometheus_dir, | ||||||||||||||||||||||||||||||||
| e, | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| self._prometheus_dir = None | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| # --------------------------------------------------------------------------- | ||||||||||||||||||||||||||||||||
| # Entry point | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -17,7 +17,7 @@ use tokio::{ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sync::{watch, Mutex}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| task::JoinHandle, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use tracing::{debug, info}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use tracing::{debug, info, warn}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| core::{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -85,6 +85,40 @@ impl IntoResponse for EngineMetricsResult { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Collect gRPC worker metrics by aggregating `PROMETHEUS_MULTIPROC_DIR` via a python3 subprocess. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async fn collect_prometheus_multiproc_metrics() -> Result<String, String> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let dir = std::env::var("PROMETHEUS_MULTIPROC_DIR").map_err(|_| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "PROMETHEUS_MULTIPROC_DIR not set; cannot collect metrics from gRPC workers".to_string() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let output = tokio::process::Command::new("python3") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of hardcoding
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .args([ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "-c", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "import sys\n\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from prometheus_client import CollectorRegistry, generate_latest\n\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from prometheus_client.multiprocess import MultiProcessCollector\n\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| registry = CollectorRegistry()\n\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MultiProcessCollector(registry)\n\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sys.stdout.buffer.write(generate_latest(registry))\n", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .env("PROMETHEUS_MULTIPROC_DIR", &dir) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .output() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .await | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map_err(|e| format!("failed to run python3 prometheus collector: {e}"))?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+94
to
+107
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Important: This subprocess call has no timeout. The HTTP fan-out path uses Consider wrapping with
Suggested change
Comment on lines
+94
to
+107
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n -C2 'python3|sys\.executable|SMG_PYTHON_EXECUTABLE|PROMETHEUS_MULTIPROC_DIR' \
bindings/python/src/smg model_gateway/srcRepository: lightseekorg/smg Length of output: 5084 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Get more context around serve.py to see how the router is started
cat -n bindings/python/src/smg/serve.py | head -100Repository: lightseekorg/smg Length of output: 4451 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Search for where the router/model_gateway is actually invoked in serve.py
rg -n 'subprocess|Popen|run\(|spawn|model_gateway' bindings/python/src/smg/serve.py | head -30Repository: lightseekorg/smg Length of output: 370 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Check if serve.py exports SMG_PYTHON_EXECUTABLE anywhere (not just in docstrings)
rg -n 'SMG_PYTHON_EXECUTABLE' bindings/python/src/smg/serve.pyRepository: lightseekorg/smg Length of output: 42 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Find and examine launch_router to see how the router process is spawned
fd -t f "launch_router.py" bindings/python/src/smg/Repository: lightseekorg/smg Length of output: 100 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Check the launch_router implementation - see how the router subprocess is created
rg -n -A 20 'def launch_router' bindings/python/src/smg/Repository: lightseekorg/smg Length of output: 1665 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Get the full launch_router function implementation
cat -n bindings/python/src/smg/launch_router.pyRepository: lightseekorg/smg Length of output: 4526 Don't hardcode Line 94 can bypass the interpreter that launched Export the interpreter from 🛠 Suggested direction- let output = tokio::process::Command::new("python3")
+ let python = std::env::var("SMG_PYTHON_EXECUTABLE")
+ .unwrap_or_else(|_| "python3".to_string());
+ let output = tokio::process::Command::new(&python)Add this to os.environ["SMG_PYTHON_EXECUTABLE"] = sys.executableThis mirrors the pattern already used for worker launchers, which correctly use 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !output.status.success() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let stderr = String::from_utf8_lossy(&output.stderr); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Err(format!("python3 prometheus collector failed: {stderr}")); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let text = String::from_utf8(output.stdout) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map_err(|e| format!("prometheus collector output is not valid UTF-8: {e}"))?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if text.trim().is_empty() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Err("no metrics available from gRPC workers yet".to_string()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(text) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub struct WorkerManager; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl WorkerManager { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -273,22 +307,52 @@ impl WorkerManager { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return EngineMetricsResult::Err("No available workers".to_string()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let responses = fan_out(&workers, client, "metrics", reqwest::Method::GET).await; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let mut metric_packs = Vec::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for resp in responses { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Ok(r) = resp.result { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if r.status().is_success() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Ok(text) = r.text().await { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metric_packs.push(MetricPack { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| labels: vec![("worker_addr".into(), resp.url)], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metrics_text: text, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let http_workers: Vec<_> = workers | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .iter() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .filter(|w| matches!(w.connection_mode(), ConnectionMode::Http)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .cloned() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .collect(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let has_grpc = workers | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .iter() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .any(|w| matches!(w.connection_mode(), ConnectionMode::Grpc)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !http_workers.is_empty() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let responses = fan_out(&http_workers, client, "metrics", reqwest::Method::GET).await; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for resp in responses { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Ok(r) = resp.result { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if r.status().is_success() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Ok(text) = r.text().await { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metric_packs.push(MetricPack { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| labels: vec![("worker_addr".into(), resp.url)], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metrics_text: text, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if has_grpc { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| match collect_prometheus_multiproc_metrics().await { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(text) if !text.trim().is_empty() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Nit: This Consider simplifying to just |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metric_packs.push(MetricPack { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| labels: vec![], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metrics_text: text, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+327
to
+342
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Build These literals do not match 🛠 Proposed fix- metric_packs.push(MetricPack {
- labels: vec![("worker_addr".into(), resp.url)],
- metrics_text: text,
- });
+ metric_packs.push(MetricPack::new(
+ HashMap::from([("worker_addr".to_string(), resp.url)]),
+ text,
+ ));
...
- metric_packs.push(MetricPack {
- labels: vec![],
- metrics_text: text,
- });
+ metric_packs.push(MetricPack::new(
+ HashMap::<String, String>::new(),
+ text,
+ ));📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(_) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // No metrics available yet from gRPC workers — skip silently | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Err(e) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| warn!( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Failed to collect gRPC worker metrics from PROMETHEUS_MULTIPROC_DIR: {e}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if metric_packs.is_empty() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return EngineMetricsResult::Err("All backend requests failed".to_string()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Nit:
getattr(self.args, "connection_mode", "grpc")defaults to"grpc"when the attribute is absent. If an older or HTTP-only configuration doesn't setconnection_modeat all, this will unnecessarily create a temp directory and setPROMETHEUS_MULTIPROC_DIRin the process environment — which could interfere with any otherprometheus_clientusage in the same process.Consider defaulting to
"http"(the safer no-op path), or checking for the attribute explicitly: