Skip to content

OCPBUGS-98449: Replace global Redux timestamp dispatch with local useSyncExternalStore hook#16746

Open
alimobrem wants to merge 1 commit into
openshift:mainfrom
alimobrem:perf/timestamp-local-tick
Open

OCPBUGS-98449: Replace global Redux timestamp dispatch with local useSyncExternalStore hook#16746
alimobrem wants to merge 1 commit into
openshift:mainfrom
alimobrem:perf/timestamp-local-tick

Conversation

@alimobrem

@alimobrem alimobrem commented Jul 10, 2026

Copy link
Copy Markdown

Summary

  • Replace the global setInterval in app.tsx that dispatched UIActions.updateTimestamps(Date.now()) every 10s to the Redux store, causing all ~36 Timestamp components plus every other UI store subscriber to re-render simultaneously
  • Introduce a useTimestampTick hook using React 18's useSyncExternalStore with a shared interval that starts on first Timestamp mount and stops on last unmount
  • Remove dead Redux code: UpdateTimestamps enum member, updateTimestamps action creator, and lastTick reducer case (no remaining consumers confirmed via codebase-wide grep)
  • SDK compatibility preserved: Timestamp component props unchanged, updateTimestamps was never part of the public SDK API

Details

The old approach dispatched a Redux action every 10 seconds, which triggered:

  1. An Immutable.js state.set('lastTick', ...) in the UI reducer
  2. Selector comparisons in every component subscribed to the UI store slice — not just Timestamps
  3. Simultaneous re-renders of all ~36 Timestamp instances, including off-screen ones

The new useTimestampTick hook:

  • Uses useSyncExternalStore with a cached tick value that only changes when the interval fires (stable getSnapshot)
  • Shares a single setInterval across all consumers via a Set<listener> pattern
  • Starts/stops the interval based on component lifecycle — no wasted cycles when no Timestamps are mounted
  • Returns a number (epoch ms) directly, avoiding the Immutable.js serialization roundtrip

Test plan

  • Existing Timestamp tests updated and passing (5 tests)
  • New useTimestampTick hook tests passing (5 tests covering: value type, tick update, no early update, cleanup, shared interval)
  • Navigate to Pods list — timestamps update every ~10 seconds
  • Navigate to Events page (many timestamps) — verify updates
  • React DevTools Profiler — confirm Timestamp re-renders are local, not cascading through Redux
  • Verify no console errors or warnings

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved timestamp updates so relative times refresh consistently without requiring broader application updates.
    • Timestamp displays now remain accurate across multiple components and cleanly stop updating when no longer in use.
  • Tests

    • Added coverage for timestamp refresh timing, shared updates, cleanup, and common display formats.

…re hook

Problem:
Every 10 seconds, app.tsx dispatched UIActions.updateTimestamps(Date.now())
to the Redux store. This caused all ~36 Timestamp components to re-render
simultaneously via useConsoleSelector. Worse, the Redux dispatch triggered
selector comparisons in every component subscribed to the UI store slice —
not just Timestamps — adding unnecessary work across the entire app on
every tick. The Immutable.js state update (state.set('lastTick', ...)) also
added serialize/deserialize overhead on every dispatch.

Solution:
Replace with a self-contained useTimestampTick hook using React 18's
useSyncExternalStore. A single shared setInterval starts when the first
Timestamp mounts and stops when the last unmounts (no wasted cycles when
no Timestamps are visible). The cached tick value ensures getSnapshot
returns a stable reference between notifications, preventing spurious
re-renders. The Redux store is no longer involved in timestamp updates.

Changes:
- New hook: useTimestampTick (useSyncExternalStore + shared interval)
- Timestamp.tsx: swap useConsoleSelector for useTimestampTick
- app.tsx: remove global setInterval dispatch
- Remove dead Redux code: UpdateTimestamps enum, updateTimestamps action
  creator, lastTick reducer case (no remaining consumers confirmed via
  codebase-wide grep)

SDK compatibility:
The Timestamp component is exported in the dynamic plugin SDK
(core-api.ts:726). TimestampProps is unchanged — external plugins pass a
timestamp string and get rendered output. The tick mechanism is internal.
updateTimestamps was never part of the public SDK API.

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 rhamilto July 10, 2026 16:32
@openshift-ci openshift-ci Bot added component/core Related to console core functionality component/shared Related to console-shared labels Jul 10, 2026
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Timestamp now uses a shared useTimestampTick hook for current-time updates. The app-level Redux timestamp action, reducer state, and global interval dispatch were removed, with tests updated for the new behavior.

Changes

Timestamp tick migration

Layer / File(s) Summary
Shared timestamp hook and lifecycle tests
frontend/packages/console-shared/src/hooks/useTimestampTick.ts, frontend/packages/console-shared/src/hooks/__tests__/useTimestampTick.spec.ts
Adds a shared 10-second timestamp interval using useSyncExternalStore, with tests for updates, unchanged values, cleanup, and multiple consumers.
Timestamp component integration
frontend/packages/console-shared/src/components/datetime/Timestamp.tsx, frontend/packages/console-shared/src/components/datetime/__tests__/Timestamp.spec.tsx
Uses useTimestampTick for rendering and removes test setup that dispatched timestamp actions.
Redux timestamp plumbing removal
frontend/public/actions/common.ts, frontend/public/actions/ui.ts, frontend/public/reducers/ui.ts, frontend/public/components/app.tsx
Removes the timestamp action, reducer handling, and global application interval.

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

🚥 Pre-merge checks | ✅ 14 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (14 passed)
Check name Status Explanation
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 Changed tests use static Jest titles only; no dynamic pod/date/UUID/node/namespace/IP values or Ginkgo-style unstable names were introduced.
Test Structure And Quality ✅ Passed No Ginkgo tests were modified; this PR only changes React/Jest frontend timestamp code, so the Ginkgo-specific checklist is not applicable.
Microshift Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the touched tests are Jest unit tests for Timestamp/useTimestampTick and use no MicroShift-unsupported OpenShift APIs.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the changed tests are Jest unit tests in frontend, with no multi-node/SNO assumptions.
Topology-Aware Scheduling Compatibility ✅ Passed Only frontend timestamp/store code changed; no manifests, controllers, or scheduling constraints were added.
Ote Binary Stdout Contract ✅ Passed PR only changes frontend TypeScript files; no main/init/TestMain/RunSpecs code or stdout writes were introduced in process-level code.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The PR adds only frontend unit tests and hook code; no Ginkgo e2e tests or network/IP assumptions were introduced.
No-Weak-Crypto ✅ Passed No crypto code was added or modified in the touched files; searches found no weak algorithms, custom crypto, or secret/token comparisons.
Container-Privileges ✅ Passed PR changes only frontend TS files; none contain privileged, hostPID/hostNetwork/hostIPC, allowPrivilegeEscalation, or SYS_ADMIN settings.
No-Sensitive-Data-In-Logs ✅ Passed No new logging was added in the touched diff; the changed files contain no password/token/PII/session logging.
Title check ✅ Passed The title is specific, concise, and accurately describes the main change in the PR.
Description check ✅ Passed The PR includes a solid summary, implementation details, and test plan, with only some template sections left unfilled.
✨ 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.

@alimobrem

Copy link
Copy Markdown
Author

/retest ci/prow/backend

@alimobrem

Copy link
Copy Markdown
Author

/test backend

@alimobrem

Copy link
Copy Markdown
Author

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

🤖 Generated with Claude Code

@spadgett spadgett left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

/lgtm
👍

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Jul 10, 2026
@openshift-ci

openshift-ci Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: alimobrem, spadgett

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

The pull request process is described 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-merge-bot

Copy link
Copy Markdown
Contributor

Scheduling tests matching the pipeline_run_if_changed or not excluded by pipeline_skip_if_only_changed parameters:
/test e2e-gcp-console
/test e2e-playwright

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jul 10, 2026
@alimobrem alimobrem changed the title Replace global Redux timestamp dispatch with local useSyncExternalStore hook OCPBUGS-98449: Replace global Redux timestamp dispatch with local useSyncExternalStore hook Jul 10, 2026
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jul 10, 2026
@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@alimobrem: This pull request references Jira Issue OCPBUGS-98449, which is invalid:

  • expected the bug to target the "5.0.0" version, but no target version was set

Comment /jira refresh to re-evaluate validity if changes to the Jira bug are made, or edit the title of this pull request to link to a different bug.

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

Summary

  • Replace the global setInterval in app.tsx that dispatched UIActions.updateTimestamps(Date.now()) every 10s to the Redux store, causing all ~36 Timestamp components plus every other UI store subscriber to re-render simultaneously
  • Introduce a useTimestampTick hook using React 18's useSyncExternalStore with a shared interval that starts on first Timestamp mount and stops on last unmount
  • Remove dead Redux code: UpdateTimestamps enum member, updateTimestamps action creator, and lastTick reducer case (no remaining consumers confirmed via codebase-wide grep)
  • SDK compatibility preserved: Timestamp component props unchanged, updateTimestamps was never part of the public SDK API

Details

The old approach dispatched a Redux action every 10 seconds, which triggered:

  1. An Immutable.js state.set('lastTick', ...) in the UI reducer
  2. Selector comparisons in every component subscribed to the UI store slice — not just Timestamps
  3. Simultaneous re-renders of all ~36 Timestamp instances, including off-screen ones

The new useTimestampTick hook:

  • Uses useSyncExternalStore with a cached tick value that only changes when the interval fires (stable getSnapshot)
  • Shares a single setInterval across all consumers via a Set<listener> pattern
  • Starts/stops the interval based on component lifecycle — no wasted cycles when no Timestamps are mounted
  • Returns a number (epoch ms) directly, avoiding the Immutable.js serialization roundtrip

Test plan

  • Existing Timestamp tests updated and passing (5 tests)
  • New useTimestampTick hook tests passing (5 tests covering: value type, tick update, no early update, cleanup, shared interval)
  • Navigate to Pods list — timestamps update every ~10 seconds
  • Navigate to Events page (many timestamps) — verify updates
  • React DevTools Profiler — confirm Timestamp re-renders are local, not cascading through Redux
  • Verify no console errors or warnings

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

  • Improved timestamp updates so relative times refresh consistently without requiring broader application updates.

  • Timestamp displays now remain accurate across multiple components and cleanly stop updating when no longer in use.

  • Tests

  • Added coverage for timestamp refresh timing, shared updates, cleanup, and common display formats.

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 openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot openshift-ci-robot added the jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. label Jul 10, 2026
@alimobrem

Copy link
Copy Markdown
Author

/jira refresh

@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@alimobrem: This pull request references Jira Issue OCPBUGS-98449, which is invalid:

  • expected the bug to be in one of the following states: NEW, ASSIGNED, POST, but it is MODIFIED instead

Comment /jira refresh to re-evaluate validity if changes to the Jira bug are made, or edit the title of this pull request to link to a different bug.

Details

In response to this:

/jira refresh

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 openshift-eng/jira-lifecycle-plugin repository.

@alimobrem

Copy link
Copy Markdown
Author

/jira refresh

@openshift-ci-robot openshift-ci-robot added jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. and removed jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Jul 10, 2026
@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@alimobrem: This pull request references Jira Issue OCPBUGS-98449, which is valid.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (5.0.0) matches configured target version for branch (5.0.0)
  • bug is in the state POST, which is one of the valid states (NEW, ASSIGNED, POST)
Details

In response to this:

/jira refresh

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 openshift-eng/jira-lifecycle-plugin repository.

@spadgett

Copy link
Copy Markdown
Member

/verified by @spadgett

@openshift-ci-robot openshift-ci-robot added the verified Signifies that the PR passed pre-merge verification criteria label Jul 10, 2026
@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@spadgett: This PR has been marked as verified by @spadgett.

Details

In response to this:

/verified by @spadgett

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 openshift-eng/jira-lifecycle-plugin repository.

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

/retest-required

Remaining retests: 0 against base HEAD bc58d07 and 2 for PR HEAD ff6a9f6 in total

@alimobrem

Copy link
Copy Markdown
Author

/test e2e-playwright

@alimobrem

Copy link
Copy Markdown
Author

/label docs-approved

@openshift-ci openshift-ci Bot added the docs-approved Signifies that Docs has signed off on this PR label Jul 10, 2026
@alimobrem

Copy link
Copy Markdown
Author

/retest-required

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

/retest-required

Remaining retests: 0 against base HEAD 1a670be and 1 for PR HEAD ff6a9f6 in total

@alimobrem

Copy link
Copy Markdown
Author

/test e2e-playwright

@openshift-ci

openshift-ci Bot commented Jul 13, 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/e2e-playwright ff6a9f6 link false /test e2e-playwright

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.

const formattedDate = dateTimeFormatter().format(new Date(timestamp));
expect(screen.getByText(formattedDate)).toBeDefined();
});
it('should show dash for null timestamp', () => {

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.

Suggested change
it('should show dash for null timestamp', () => {
it('should show dash when timestamp is undefined', () => {

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

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. component/core Related to console core functionality component/shared Related to console-shared docs-approved Signifies that Docs has signed off on this PR jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged. verified Signifies that the PR passed pre-merge verification criteria

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants