Skip to content

Deduplicate in-flight Prometheus polls and add interval jitter#16747

Open
alimobrem wants to merge 2 commits into
openshift:mainfrom
alimobrem:perf/prometheus-poll-dedup
Open

Deduplicate in-flight Prometheus polls and add interval jitter#16747
alimobrem wants to merge 2 commits into
openshift:mainfrom
alimobrem:perf/prometheus-poll-dedup

Conversation

@alimobrem

@alimobrem alimobrem commented Jul 10, 2026

Copy link
Copy Markdown

Summary

  • Add in-flight request deduplication to useURLPoll — when multiple hooks poll the same URL simultaneously, they share one HTTP request instead of issuing duplicates
  • Add per-instance jitter (0-20% of interval) to usePoll — spreads ~11 simultaneous dashboard requests across a 0-3 second window instead of thundering-herding at t=0
  • No changes to usePrometheusPoll or useURLPoll signatures — SDK compatibility preserved, external plugins benefit automatically

Details

Part A — In-flight dedup (url-fetch-cache.ts):
A module-level Map<string, Promise> keyed by URL. On cache hit, returns the existing in-flight Promise. On settlement (success or error), the entry is cleared so the next poll cycle fetches fresh data. 16 lines, zero dependencies.

Part B — Poll jitter (usePoll.ts):
Each hook instance gets a stable random offset of 0 to delay * 0.2 (0-3 seconds for the default 15-second interval). The first tick still fires immediately for fast initial data load. After the jittered start, polls run at consistent intervals. The jitter value is stored in a ref so it's stable across re-renders.

Test plan

  • New url-fetch-cache tests passing (5 tests: same-URL dedup, different-URL separation, post-settlement new fetch, error propagation, cache cleanup)
  • New usePoll tests passing (4 tests: immediate fire, interval ticks, no interval when delay=0, cleanup)
  • Existing graph tests passing (area, bar, gauge, prometheus-graph, helpers, utils — 26 tests)
  • Existing adaptive-polling tests passing (13 tests)
  • Open Cluster Dashboard → DevTools Network → filter /api/prometheus/ → verify requests are staggered
  • Navigate away and back — no leaked intervals or zombie requests

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Improvements
    • Improved URL polling by preventing duplicate concurrent requests for the same URL.
    • Polling intervals now vary slightly between instances, helping distribute request traffic and reduce synchronized polling.
    • Requests can be started again after earlier requests complete or fail, ensuring fresh results.
  • Tests
    • Added coverage for polling behavior, request deduplication, error handling, cache cleanup, and interval cancellation.

Problem:
Each usePrometheusPoll hook creates an independent HTTP request every
15 seconds via useURLPoll. The cluster dashboard fires ~11 simultaneous
Prometheus requests per cycle, all at the same instant. When multiple
components poll the same query URL, duplicate requests are issued.

Solution:
Part A — In-flight request deduplication:
Add a module-level cache (url-fetch-cache.ts) keyed by URL. When a
fetch for a URL is already in-flight, return the existing Promise
instead of issuing a duplicate request. The cache entry is cleared
when the Promise settles, so the next poll cycle gets fresh data.

Part B — Poll interval jitter:
Add a per-instance random offset of 0-20% to each usePoll interval.
For the default 15-second delay, this spreads requests across a 0-3
second window, preventing the thundering herd where all dashboard
cards fire simultaneously.

SDK compatibility:
usePrometheusPoll (SDK-exported) and useURLPoll (internal SDK) have
unchanged signatures. The dedup and jitter are internal implementation
details. External plugins benefit automatically.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: LGTM mode

@openshift-ci openshift-ci Bot requested review from jhadvig and stefanonardo July 10, 2026 19:17
@openshift-ci openshift-ci Bot added the component/core Related to console core functionality label Jul 10, 2026
@openshift-ci

openshift-ci Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: alimobrem
Once this PR has been reviewed and has the lgtm label, please assign rhamilto for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the component/shared Related to console-shared label Jul 10, 2026
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

useURLPoll now routes requests through an in-flight URL cache and applies per-instance polling jitter. New Jest suites verify request deduplication, cache cleanup, rejection handling, polling intervals, and unmount cleanup.

Changes

URL polling behavior

Layer / File(s) Summary
In-flight request deduplication
frontend/public/components/utils/url-fetch-cache.ts, frontend/public/components/utils/url-poll-hook.ts, frontend/public/components/utils/__tests__/url-fetch-cache.spec.ts
dedupedFetch shares concurrent requests by URL, clears entries after settlement, and is used by useURLPoll; polling intervals include randomized jitter.
Polling lifecycle validation
frontend/packages/console-shared/src/hooks/__tests__/usePoll.spec.ts
Tests cover immediate polling, interval execution, zero-delay behavior, and interval cleanup on unmount.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant useURLPoll
  participant dedupedFetch
  participant safeFetch
  useURLPoll->>dedupedFetch: request URL
  dedupedFetch->>safeFetch: start one in-flight request
  safeFetch-->>dedupedFetch: resolve or reject
  dedupedFetch-->>useURLPoll: return shared promise
  dedupedFetch->>dedupedFetch: clear settled URL entry
Loading

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
No-Sensitive-Data-In-Logs ❌ Error useURLPoll logs raw err via console.error; fetch errors can include full URLs (TimeoutError) and response/json bodies (HttpError). Remove the raw error log or sanitize it to a non-sensitive message/code-only form, avoiding URLs, response bodies, and request metadata.
Description check ⚠️ Warning The description has useful detail, but it does not follow the required template and is missing several required sections. Add the required sections from the template: Analysis/Root cause, Solution description, Screenshots, Test setup, Test cases, Browser conformance, Additional info, and Reviewers/assignees.
✅ Passed checks (13 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: request deduplication plus polling jitter for Prometheus/URL polling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed Both added specs use fixed literal titles only; no interpolated, generated, timestamped, or otherwise dynamic test names were found.
Test Structure And Quality ✅ Passed The PR only adds Jest frontend tests and hook code; there are no Ginkgo tests or cluster interactions in the touched files, so this check is not applicable.
Microshift Test Compatibility ✅ Passed Only frontend TypeScript/Jest files changed; no new Ginkgo e2e tests or MicroShift-relevant OpenShift API usage were added.
Single Node Openshift (Sno) Test Compatibility ✅ Passed Only Jest unit tests and hook code changed; no Ginkgo e2e tests or multi-node/SNO assumptions were added.
Topology-Aware Scheduling Compatibility ✅ Passed PASS: The PR only adds frontend polling/cache hooks and tests; no deployment manifests, operators, controllers, node selectors, affinities, or replica logic were changed.
Ote Binary Stdout Contract ✅ Passed PR only changes frontend TS/JS hooks/tests; no main/init/TestMain/suite bootstrap stdout writes were introduced. The only logging is console.error in a hook, which goes to stderr.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The PR only adds Jest/unit tests and hook code; no new Ginkgo e2e tests, IPv4-only assumptions, or external-network dependencies were found.
No-Weak-Crypto ✅ Passed No weak-crypto primitives, custom crypto, or timing-sensitive secret comparisons appear in the changed files; the new code is polling/deduping only.
Container-Privileges ✅ Passed Diff only changes TypeScript hook/test files; no container/K8s manifests or privilege-related settings appear in the PR.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@openshift-ci

openshift-ci Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

@alimobrem: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/frontend c996ff3 link true /test frontend

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

…onsumers

usePoll is used by 5 consumers (URL polling, console updates, helm
detection, catalog items, query browser). Jitter should only apply to
URL-based polling, not to all interval-based polling. Move the jitter
ref into useURLPoll where it's scoped correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/public/components/utils/url-poll-hook.ts`:
- Around line 19-22: The polling flow in the hook’s tick callback must not let
one unmount abort the shared dedupedFetch request. Update the AbortController
and cleanup handling used by dedupedFetch so cancellation is scoped to the
individual subscriber, or avoid aborting the shared request during unmount,
while preserving normal polling and AbortError handling for genuinely canceled
requests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 8c0d4709-b42e-4f8e-8030-af78178ee26e

📥 Commits

Reviewing files that changed from the base of the PR and between c996ff3 and 5cd9f79.

📒 Files selected for processing (2)
  • frontend/packages/console-shared/src/hooks/__tests__/usePoll.spec.ts
  • frontend/public/components/utils/url-poll-hook.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/packages/console-shared/src/hooks/tests/usePoll.spec.ts

Comment on lines +19 to +22
const jitterRef = useRef(delay ? Math.floor(Math.random() * delay * 0.2) : 0);
const tick = useCallback(() => {
if (url) {
safeFetch(url)
dedupedFetch(url, safeFetch)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect dedupedFetch implementation for abort handling
cat -n frontend/public/components/utils/url-fetch-cache.ts

# Inspect useSafeFetch abort lifecycle
cat -n frontend/public/components/utils/safe-fetch-hook.ts

Repository: openshift/console

Length of output: 1144


Avoid aborting the shared poll request on unmount. dedupedFetch reuses the first caller’s promise, so when that hook unmounts its AbortController cancels the request for every subscriber. The others then swallow AbortError and miss a poll cycle.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/public/components/utils/url-poll-hook.ts` around lines 19 - 22, The
polling flow in the hook’s tick callback must not let one unmount abort the
shared dedupedFetch request. Update the AbortController and cleanup handling
used by dedupedFetch so cancellation is scoped to the individual subscriber, or
avoid aborting the shared request during unmount, while preserving normal
polling and AbortError handling for genuinely canceled requests.

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

Labels

component/core Related to console core functionality component/shared Related to console-shared

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant