From 0c7291c8dd3a8f2f6eb628b72a3145b4f96ebe4f Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 5 Aug 2025 14:21:25 +0200 Subject: [PATCH] Dashboard: Cancel performance profile when tab becomes inactive. (#109184) * Bump scenes * Update docs * Bump scenes * Bump scenes to stable --- package.json | 4 +-- .../dashboard-render-performance-profiling.md | 33 +++++++++++++++++++ yarn.lock | 22 ++++++------- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 5a2b36d0472..90cc3ff2a46 100644 --- a/package.json +++ b/package.json @@ -290,8 +290,8 @@ "@grafana/plugin-ui": "0.10.8", "@grafana/prometheus": "workspace:*", "@grafana/runtime": "workspace:*", - "@grafana/scenes": "6.29.1", - "@grafana/scenes-react": "6.29.1", + "@grafana/scenes": "6.29.2", + "@grafana/scenes-react": "6.29.2", "@grafana/schema": "workspace:*", "@grafana/sql": "workspace:*", "@grafana/ui": "workspace:*", diff --git a/public/app/features/dashboard/services/dashboard-render-performance-profiling.md b/public/app/features/dashboard/services/dashboard-render-performance-profiling.md index 574e4f7eb90..6d1d292f691 100644 --- a/public/app/features/dashboard/services/dashboard-render-performance-profiling.md +++ b/public/app/features/dashboard/services/dashboard-render-performance-profiling.md @@ -189,6 +189,38 @@ The system reports the following data for each interaction: The profiler is integrated into dashboard creation paths and uses a singleton pattern to share profiler instances across dashboard reloads. The performance tracking is implemented using the `SceneRenderProfiler` from the `@grafana/scenes` library. +### Tab Inactivity Handling + +To prevent meaningless profiling data when users switch browser tabs, the `SceneRenderProfiler` implements dual protection mechanisms: + +#### Primary Protection: Page Visibility API + +The profiler automatically cancels active profiling sessions when the browser tab becomes inactive: + +```javascript +document.addEventListener('visibilitychange', () => { + if (document.hidden && this.#profileInProgress) { + this.cancelProfile(); + } +}); +``` + +This provides immediate response to tab switches using the browser's native visibility change events. + +#### Fallback Protection: Frame Length Detection + +As a backup mechanism, the profiler detects tab inactivity by monitoring frame duration: + +```javascript +if (frameLength > TAB_INACTIVE_THRESHOLD) { + // 1000ms + this.cancelProfile(); + return; +} +``` + +This fallback catches cases where visibility events might be missed and prevents recording of artificially long frame times (hours instead of milliseconds) that occur when `requestAnimationFrame` callbacks resume after tab reactivation. + ## Related Documentation - [PR #858 - Add SceneRenderProfiler to scenes](https://github.com/grafana/scenes/pull/858) @@ -197,3 +229,4 @@ The profiler is integrated into dashboard creation paths and uses a singleton pa - [PR #1195 - Enhance SceneRenderProfiler with additional interaction tracking](https://github.com/grafana/scenes/pull/1195) - [PR #1198 - Make SceneRenderProfiler optional and injectable](https://github.com/grafana/scenes/pull/1198) - [PR #1199 - SceneRenderProfiler: add start and end timestamps to profile events](https://github.com/grafana/scenes/pull/1199) +- [PR #1205 - SceneRenderProfiler: Handle tab inactivity](https://github.com/grafana/scenes/pull/1205) diff --git a/yarn.lock b/yarn.lock index 69e46ba911c..616db850155 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3592,11 +3592,11 @@ __metadata: languageName: unknown linkType: soft -"@grafana/scenes-react@npm:6.29.1": - version: 6.29.1 - resolution: "@grafana/scenes-react@npm:6.29.1" +"@grafana/scenes-react@npm:6.29.2": + version: 6.29.2 + resolution: "@grafana/scenes-react@npm:6.29.2" dependencies: - "@grafana/scenes": "npm:6.29.1" + "@grafana/scenes": "npm:6.29.2" lru-cache: "npm:^10.2.2" react-use: "npm:^17.4.0" peerDependencies: @@ -3608,13 +3608,13 @@ __metadata: react: ^18.0.0 react-dom: ^18.0.0 react-router-dom: ^6.28.0 - checksum: 10/0f1d77588b49e3e8e265813b1d9217d6de9501858d0a917e04d55fec070a0c3a0d28a848ccdb17c47badac5e5daa6ae64326387a1822ec1b2163c049402dcb0f + checksum: 10/41a232eb4b003f7cc6056e6d9eb88882b385b84f84ef71aa4cf8e222e3451af68d72367508e00f33dc7de156a602dae8b6129a7ec6fd2ab29b3d43b22e3bc023 languageName: node linkType: hard -"@grafana/scenes@npm:6.29.1": - version: 6.29.1 - resolution: "@grafana/scenes@npm:6.29.1" +"@grafana/scenes@npm:6.29.2": + version: 6.29.2 + resolution: "@grafana/scenes@npm:6.29.2" dependencies: "@floating-ui/react": "npm:^0.26.16" "@leeoniya/ufuzzy": "npm:^1.0.16" @@ -3634,7 +3634,7 @@ __metadata: react: ^18.0.0 react-dom: ^18.0.0 react-router-dom: ^6.28.0 - checksum: 10/c93f95929780ec7263193d49d82ea8cae0409b208c598fd1dbae983119d80ffad1b103e01bc0a2801d841b014bd66a458c2852cb60168cdfbd253c5305f61a92 + checksum: 10/f8c578b4542f3e393acf4493624067c0bc0dd40e48a41aaaaec57d768ae1ef443feb71114781e38bb3aac3e66f5702427a03c0bd19525bab379bf6e0471dd484 languageName: node linkType: hard @@ -18222,8 +18222,8 @@ __metadata: "@grafana/plugin-ui": "npm:0.10.8" "@grafana/prometheus": "workspace:*" "@grafana/runtime": "workspace:*" - "@grafana/scenes": "npm:6.29.1" - "@grafana/scenes-react": "npm:6.29.1" + "@grafana/scenes": "npm:6.29.2" + "@grafana/scenes-react": "npm:6.29.2" "@grafana/schema": "workspace:*" "@grafana/sql": "workspace:*" "@grafana/test-utils": "workspace:*"