mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-06-08 21:47:35 +01:00
@@ -105,6 +105,23 @@ class ScenarioMonitor {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ctx.timeoutId = window.setTimeout(() => {
|
ctx.timeoutId = window.setTimeout(() => {
|
||||||
|
const missingPhases = ctx.config.requiredPhases.filter((p) => !ctx.completed.has(p));
|
||||||
|
|
||||||
|
this.devLog(
|
||||||
|
`timeout: ${scenario} | missing=[${missingPhases.join(", ")}] | completed=[${Array.from(ctx.completed).join(
|
||||||
|
", ",
|
||||||
|
)}] | documentHidden=${document.hidden} | hasExpectedFailure=${ctx.hasExpectedFailure}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
traceMark(Action.MetricsScenario, {
|
||||||
|
event: "scenario_timeout",
|
||||||
|
scenario,
|
||||||
|
missingPhases: missingPhases.join(","),
|
||||||
|
completedPhases: Array.from(ctx.completed).join(","),
|
||||||
|
documentHidden: document.hidden,
|
||||||
|
hasExpectedFailure: ctx.hasExpectedFailure,
|
||||||
|
});
|
||||||
|
|
||||||
// If an expected failure occurred (auth, firewall, etc.), emit healthy instead of unhealthy
|
// If an expected failure occurred (auth, firewall, etc.), emit healthy instead of unhealthy
|
||||||
const healthy = ctx.hasExpectedFailure;
|
const healthy = ctx.hasExpectedFailure;
|
||||||
this.emit(ctx, healthy, true);
|
this.emit(ctx, healthy, true);
|
||||||
@@ -288,6 +305,7 @@ class ScenarioMonitor {
|
|||||||
scenario: ctx.scenario,
|
scenario: ctx.scenario,
|
||||||
healthy,
|
healthy,
|
||||||
timedOut,
|
timedOut,
|
||||||
|
documentHidden: document.hidden,
|
||||||
platform,
|
platform,
|
||||||
api,
|
api,
|
||||||
durationMs: finalSnapshot.durationMs,
|
durationMs: finalSnapshot.durationMs,
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import MetricScenario from "./MetricEvents";
|
import MetricScenario from "./MetricEvents";
|
||||||
import { useMetricScenario } from "./MetricScenarioProvider";
|
import { scenarioMonitor } from "./ScenarioMonitor";
|
||||||
import { ApplicationMetricPhase, CommonMetricPhase } from "./ScenarioConfig";
|
import { ApplicationMetricPhase, CommonMetricPhase } from "./ScenarioConfig";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook to automatically complete the Interactive phase when the component becomes interactive.
|
* Hook to automatically complete the Interactive phase when the component becomes interactive.
|
||||||
* Uses requestAnimationFrame to complete after the browser has painted.
|
* Uses requestAnimationFrame to complete after the browser has painted.
|
||||||
|
*
|
||||||
|
* Calls scenarioMonitor directly (not via React context) so that the effect dependencies
|
||||||
|
* are only [scenario, enabled] — both stable primitives. This prevents re-renders from
|
||||||
|
* cancelling the pending rAF due to an unstable context function reference.
|
||||||
*/
|
*/
|
||||||
export function useInteractive(scenario: MetricScenario, enabled = true) {
|
export function useInteractive(scenario: MetricScenario, enabled = true) {
|
||||||
const { completePhase } = useMetricScenario();
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const id = requestAnimationFrame(() => {
|
const id = requestAnimationFrame(() => {
|
||||||
completePhase(scenario, CommonMetricPhase.Interactive);
|
scenarioMonitor.completePhase(scenario, CommonMetricPhase.Interactive);
|
||||||
});
|
});
|
||||||
return () => cancelAnimationFrame(id);
|
return () => cancelAnimationFrame(id);
|
||||||
}, [scenario, completePhase, enabled]);
|
}, [scenario, enabled]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,18 +28,20 @@ export function useInteractive(scenario: MetricScenario, enabled = true) {
|
|||||||
* Tracks tree rendering and completes Interactive phase.
|
* Tracks tree rendering and completes Interactive phase.
|
||||||
* Only completes DatabaseTreeRendered if the database fetch was successful.
|
* Only completes DatabaseTreeRendered if the database fetch was successful.
|
||||||
* Note: Scenario must be started before databases are fetched (in refreshExplorer).
|
* Note: Scenario must be started before databases are fetched (in refreshExplorer).
|
||||||
|
*
|
||||||
|
* Calls scenarioMonitor directly (not via React context) for the same stability reason
|
||||||
|
* as useInteractive — avoids effect re-runs from unstable context function references.
|
||||||
*/
|
*/
|
||||||
export function useDatabaseLoadScenario(databaseTreeNodes: unknown[], fetchSucceeded: boolean) {
|
export function useDatabaseLoadScenario(databaseTreeNodes: unknown[], fetchSucceeded: boolean) {
|
||||||
const { completePhase } = useMetricScenario();
|
|
||||||
const hasCompletedTreeRenderRef = React.useRef(false);
|
const hasCompletedTreeRenderRef = React.useRef(false);
|
||||||
|
|
||||||
// Track DatabaseTreeRendered phase (only if fetch succeeded)
|
// Track DatabaseTreeRendered phase (only if fetch succeeded)
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!hasCompletedTreeRenderRef.current && fetchSucceeded) {
|
if (!hasCompletedTreeRenderRef.current && fetchSucceeded) {
|
||||||
hasCompletedTreeRenderRef.current = true;
|
hasCompletedTreeRenderRef.current = true;
|
||||||
completePhase(MetricScenario.DatabaseLoad, ApplicationMetricPhase.DatabaseTreeRendered);
|
scenarioMonitor.completePhase(MetricScenario.DatabaseLoad, ApplicationMetricPhase.DatabaseTreeRendered);
|
||||||
}
|
}
|
||||||
}, [databaseTreeNodes, fetchSucceeded, completePhase]);
|
}, [databaseTreeNodes, fetchSucceeded]);
|
||||||
|
|
||||||
// Track Interactive phase
|
// Track Interactive phase
|
||||||
useInteractive(MetricScenario.DatabaseLoad);
|
useInteractive(MetricScenario.DatabaseLoad);
|
||||||
|
|||||||
Reference in New Issue
Block a user