lablup / lablup/backend.ai

Unbounded concurrent Prometheus queries in DeploymentExecutor._fetch_prometheus_metrics

Open
#13,830 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
670
Forks
183
Avg merge
17h 7m
Merged PRs (30d)
358

Description

Description

DeploymentExecutor._fetch_prometheus_metrics() executes one Prometheus metric fetch for every matching autoscaling rule.

The method builds the complete set of _fetch_prometheus_metric() coroutines and passes them to a single asyncio.gather() call without a concurrency bound.

As a result, the number of simultaneously active Prometheus queries grows directly with the number of Prometheus-backed autoscaling rules processed in the reconciliation pass.

I reproduced this behavior with 512 controlled autoscaling rules.

Observed behavior:

Prometheus autoscaling rules: 512
Peak active metric queries: 512
Pending child tasks before release: 512
External service contacted: No

For large rule sets, this can produce a burst of concurrent requests against the configured Prometheus service rather than maintaining a bounded level of query concurrency.

Affected Version

Confirmed on:

backend.ai-manager==26.7.0
Python 3.13.14
ai/backend/manager/sokovan/deployment/executor.py
DeploymentExecutor._fetch_prometheus_metrics()

Other released versions have not been tested.

The current main branch also contains the same unbounded asyncio.gather() pattern. The reproduction itself was performed against version 26.7.0.

Reproduction

The attached PoC executes the released _fetch_prometheus_metrics() implementation with 512 controlled Prometheus autoscaling rules.

The individual metric fetch operation is replaced with a local asynchronous double that records active calls and waits before completing. This allows the number of metric queries admitted concurrently by _fetch_prometheus_metrics() to be measured without contacting a Prometheus server.

Observed result:

input rules: 512
peak active metric queries: 512
pending child tasks before release: 512
fan-out reproduced: yes

All 512 metric fetches become active before they are released.

No external service is contacted during reproduction.

Expected behavior

Prometheus metric collection should maintain a bounded number of concurrent queries rather than allowing query concurrency to grow directly with the number of autoscaling rules.

A semaphore, bounded worker pool, or batched execution strategy could preserve concurrent metric collection while limiting the number of simultaneous requests.

Attachment

[run.py](https://github.com/user-attachments/files/31248818/run.py)

[rules.json](https://github.com/user-attachments/files/31253175/rules.json)

Contributor guide

Open the contributing guide

Research direction

Start in ai/backend/manager/sokovan/deployment/executor.py at DeploymentExecutor._fetch_prometheus_metrics() and inspect its asyncio.gather() call and the _fetch_prometheus_metric() entry point. Run the attached run.py with rules.json to reproduce the fan-out. Done means Prometheus metric collection has a bounded concurrency level instead of activating one query per rule.

Written by the indexing model from the issue text.

Assessment

Tech stack
prometheus, python
Domain
backend, observability-sre
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.