Skip to content

prometheus: fix scrape duration growing unbounded (#13667)#13678

Closed
PrashantBhanage wants to merge 958 commits into
apache:ghi13586-prometheusDrainage-20from
PrashantBhanage:fix/13667-prometheus-scrape-executor
Closed

prometheus: fix scrape duration growing unbounded (#13667)#13678
PrashantBhanage wants to merge 958 commits into
apache:ghi13586-prometheusDrainage-20from
PrashantBhanage:fix/13667-prometheus-scrape-executor

Conversation

@PrashantBhanage

Copy link
Copy Markdown

Description

This PR fixes unbounded growth of Prometheus /metrics scrape duration
against the CloudStack management server (root cause tracked in #13586,
improvements requested in #13667).

Before: the exporter's HttpServer used the JDK default single-threaded
executor, so a slow scrape serialized/queued every other scrape behind it.
Additionally, updateMetrics() had no guard against concurrent or rapid
recomputation, so scrape frequency could multiply backend load
indefinitely. Only a full management server restart reset the growth.

After: three targeted, contained changes, all inside
plugins/integrations/prometheus/:

  1. Bounded HTTP executorPrometheusExporterServerImpl#start() now
    sets an explicit Executors.newFixedThreadPool(2) as the HttpServer's
    executor instead of relying on the JDK default, and shuts it down
    cleanly in stop().
  2. TTL / in-flight guardPrometheusExporterImpl#updateMetrics() is
    now synchronized and checks a lastMetricsUpdateTime timestamp
    against a new dynamic global setting,
    prometheus.exporter.metrics.min.refresh.interval (default 5 seconds).
    Scrapes arriving within that window reuse the previously computed
    metrics instead of triggering a fresh, expensive recomputation.
  3. Timing instrumentationupdateMetrics() now logs its wall-clock
    execution time in milliseconds at info level on every run (including
    on the exception path), so operators/CI can identify which
    sub-collector is slow and confirm the fix closes the growth.

This PR deliberately does not touch AlertManagerImpl or the
capacity-calculation executor in server/ — that code already creates a
fresh thread pool per invocation and reads capacity.calculate.workers
dynamically on main, and rewriting it was flagged in the issue comments
as unnecessary scope creep that introduced its own bugs (stale config,
swallowed shutdown exceptions, thread-pool leaks).

Fixes: #13667
Ref: #13586

Types of changes

  • Bug fix (non-breaking change which fixes an issue)

Feature/Enhancement Scale or Bug Severity

Bug Severity

  • Major

Screenshots (if appropriate):

N/A — backend-only change, no UI impact.

How Has This Been Tested?

  • Unit tests added to PrometheusExporterImplTest:
    • testUpdateMetricsTTLGuardSkipsSecondCall — two rapid calls to
      updateMetrics() within the TTL window result in only one
      recomputation.
    • testUpdateMetricsTTLGuardAllowsAfterInterval — after the configured
      interval elapses, a subsequent call triggers a fresh recomputation.
  • mvn -pl plugins/integrations/prometheus test → 6/6 tests pass (4
    existing + 2 new).
  • mvn -pl plugins/integrations/prometheus -am install → build success.
  • Manually verified repeated rapid scrapes against /metrics reuse cached
    output within the TTL window, and that logged updateMetrics() duration
    stays flat instead of climbing across successive scrapes.

How did you try to break this feature and the system with this change?

  • Fired concurrent scrape requests against /metrics to confirm the
    bounded executor and synchronized guard prevent overlapping
    recomputation instead of deadlocking or throwing.
  • Verified stop() shuts down the new executor cleanly with no resource
    leak warnings.
  • Confirmed the new prometheus.exporter.metrics.min.refresh.interval
    setting is dynamically adjustable at runtime without a restart, and
    that a value of 0 effectively disables the guard (falls back to
    original per-scrape recomputation behavior) for anyone who wants that.
  • Confirmed no other module (AlertManagerImpl, CapacityManagerImpl,
    etc.) was touched, keeping this change fully isolated to the exporter
    plugin.

sureshanaparti and others added 30 commits March 25, 2026 20:52
…e#12880)

* fixed database update on snapshot with multiple volumes and an api change

* changed overwritevolumecontent based on powerflex version and removed unnecessary comments

* Update plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java

Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anaparti@gmail.com>

* Update plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java

Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anaparti@gmail.com>

* Update plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java

Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anaparti@gmail.com>

---------

Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anaparti@gmail.com>
…pache#12805)

* Fix NPE in NASBackupProvider when no running KVM host is available

ResourceManager.findOneRandomRunningHostByHypervisor() can return null
when no KVM host in the zone has status=Up (e.g. during management
server startup, brief agent disconnections, or host state transitions).

NASBackupProvider.syncBackupStorageStats() and deleteBackup() call
host.getId() without a null check, causing a NullPointerException that
crashes the entire BackupSyncTask background job every sync interval.

This adds null checks in both methods:
- syncBackupStorageStats: log a warning and return early
- deleteBackup: throw CloudRuntimeException with a descriptive message
…ers (apache#12894)

* VM start error handling improvements, and config to expose error to user

* refactor
* Refactor Quota Summary API

* Fixes imports

* Fix QuotaServiceImplTest

* Update plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaSummaryCmd.java

Co-authored-by: Fabricio Duarte <fabricio.duarte.jr@gmail.com>

* Fix QuotaSummaryCmd

* Remove unnecessary imports

* Remove unused createQuotaSummaryResponse declarations

* Remove unnecessary imports

* Update plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaSummaryCmd.java

Co-authored-by: dahn <daan.hoogland@gmail.com>

* Fix QuotaSummaryCmd

* Fix QuotaResponseBuilderImplTest

* Refactor test

* Fix QuotaSummaryCmd

* Fix projectid behavior

* Simplify QuotaSummary and deprecate listall

* Fix createQuotaSummaryResponse

* Remove unused import

* Apply suggestions + some adjustments

* Remove duplicated check

* Fix checkstyle

* Adjust entity owner

* Remove unused method + fix tests

* Add missing @acl to some parameters

* Adjust how the parameters behave

* Allow domain admins and users to use keyword

* Address reviews

---------

Co-authored-by: Julien Hervot de Mattos Vaz <julien.vaz@scclouds.com.br>
Co-authored-by: Fabricio Duarte <fabricio.duarte.jr@gmail.com>
Co-authored-by: dahn <daan.hoogland@gmail.com>
* Fix NPE during VM setup for pvlan

* review comments
apache#12833)

Fixes an issue in NsxResource.executeRequest where Network.Service
comparison failed when DeleteNsxNatRuleCommand was executed in a
different process. Due to serialization/deserialization, the
deserialized Network.Service instance was not equal to the static
instances Network.Service.StaticNat and Network.Service.PortForwarding,
causing the comparison to always return false.

Co-authored-by: Andrey Volchkov <avolchkov@playtika.com>
…n user_statistics table in account_view for netstats (apache#12631)" (apache#12965)

This reverts commit 58916eb.
… in progress (apache#12792)

* Block backup deletion while create-VM-from-backup or restore jobs are in progress

* Add tests

* Fix exception message

* Update test

Co-authored-by: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com>
@PrashantBhanage

Copy link
Copy Markdown
Author

@PrashantBhanage , did you base your changes on my branch from #13311 (ghi13586-prometheusDrainage-20)? If so, can you redirect your PR to that branch (so we can merge them together and handle conflicts early)?

@DaanHoogland Done! I have switched the base branch of this PR to your ghi13586-prometheusDrainage-20 branch as requested.

@DaanHoogland

Copy link
Copy Markdown
Contributor

@PrashantBhanage , did you base your changes on my branch from #13311 (ghi13586-prometheusDrainage-20)? If so, can you redirect your PR to that branch (so we can merge them together and handle conflicts early)?

@DaanHoogland Done! I have switched the base branch of this PR to your ghi13586-prometheusDrainage-20 branch as requested.

quote, that went wrong @PrashantBhanage , look at the commit list in this pull. It contains a lot of undesirable extras. also 3000+ files changesd can’t be good. Can you have another look?

@PrashantBhanage

Copy link
Copy Markdown
Author

@DaanHoogland Thanks for catching that. I recreated the branch from ghi13586-prometheusDrainage-20 and opened a clean PR containing only the Prometheus fix (1 commit / 4 files changed). I'll close this PR to avoid the unrelated commits. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment