Added health metrics for application load and database load (#2257)

* Added health metrics for application load

* Added health metrics for application load

* Fix unit tests

* Added more metrics

* Added few comments

* Added DatabaseLoad Scenario and address comments

* Fix unit tests

* fix unit tests

* Fix unit tests

* fix unit tests

* fix the mock

* Fix unit tests
This commit is contained in:
sunghyunkang1111
2025-12-09 14:14:35 -06:00
committed by GitHub
parent 8c0e6da377
commit 5b7d1a74af
19 changed files with 701 additions and 2 deletions

View File

@@ -38,6 +38,9 @@ import { ContainerConnectionInfo, IPhoenixServiceInfo, IProvisionData, IResponse
import * as ViewModels from "../Contracts/ViewModels";
import { UploadDetailsRecord } from "../Contracts/ViewModels";
import { GitHubOAuthService } from "../GitHub/GitHubOAuthService";
import MetricScenario from "../Metrics/MetricEvents";
import { ApplicationMetricPhase } from "../Metrics/ScenarioConfig";
import { scenarioMonitor } from "../Metrics/ScenarioMonitor";
import { PhoenixClient } from "../Phoenix/PhoenixClient";
import * as ExplorerSettings from "../Shared/ExplorerSettings";
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
@@ -402,7 +405,9 @@ export default class Explorer {
updatedDatabases = [...updatedDatabases, ...deltaDatabases.toAdd].sort((db1, db2) =>
db1.id().localeCompare(db2.id()),
);
useDatabases.setState({ databases: updatedDatabases });
useDatabases.setState({ databases: updatedDatabases, databasesFetchedSuccessfully: true });
scenarioMonitor.completePhase(MetricScenario.DatabaseLoad, ApplicationMetricPhase.DatabasesFetched);
await this.refreshAndExpandNewDatabases(deltaDatabases.toAdd, updatedDatabases);
} catch (error) {
const errorMessage = getErrorMessage(error);
@@ -416,6 +421,8 @@ export default class Explorer {
startKey,
);
logConsoleError(`Error while refreshing databases: ${errorMessage}`);
useDatabases.setState({ databasesFetchedSuccessfully: false });
scenarioMonitor.failPhase(MetricScenario.DatabaseLoad, ApplicationMetricPhase.DatabasesFetched);
}
}
@@ -1183,6 +1190,11 @@ export default class Explorer {
}
public async refreshExplorer(): Promise<void> {
// Start DatabaseLoad scenario before fetching databases
if (userContext.apiType !== "Postgres" && userContext.apiType !== "VCoreMongo") {
scenarioMonitor.start(MetricScenario.DatabaseLoad);
}
if (userContext.apiType !== "Postgres" && userContext.apiType !== "VCoreMongo") {
userContext.authType === AuthType.ResourceToken
? this.refreshDatabaseForResourceToken()

View File

@@ -35,6 +35,15 @@ import * as ViewModels from "../../../Contracts/ViewModels";
import { updateUserContext } from "../../../UserContext";
import Explorer from "../../Explorer";
jest.mock("rx-jupyter", () => ({
sessions: {
create: jest.fn(),
},
contents: {
JupyterContentProvider: jest.fn().mockImplementation(() => ({})),
},
}));
jest.mock("Common/dataAccess/queryDocuments", () => ({
queryDocuments: jest.fn(() => ({
// Omit headers, because we can't mock a private field and we don't need to test it

View File

@@ -19,6 +19,15 @@ import { act } from "react-dom/test-utils";
import * as ViewModels from "../../../Contracts/ViewModels";
import Explorer from "../../Explorer";
jest.mock("rx-jupyter", () => ({
sessions: {
create: jest.fn(),
},
contents: {
JupyterContentProvider: jest.fn().mockImplementation(() => ({})),
},
}));
jest.requireActual("Explorer/Controls/Editor/EditorReact");
const PROPERTY_VALUE = "__SOME_PROPERTY_VALUE__";

View File

@@ -6,6 +6,15 @@ import { updateUserContext, userContext } from "../../UserContext";
import Explorer from "../Explorer";
import Database from "./Database";
jest.mock("rx-jupyter", () => ({
sessions: {
create: jest.fn(),
},
contents: {
JupyterContentProvider: jest.fn().mockImplementation(() => ({})),
},
}));
const createMockContainer = (): Explorer => {
const mockContainer = new Explorer();
return mockContainer;

View File

@@ -17,6 +17,7 @@ import { ReactTabKind, useTabs } from "hooks/useTabs";
import * as React from "react";
import { useEffect, useMemo } from "react";
import shallow from "zustand/shallow";
import { useDatabaseLoadScenario } from "../../Metrics/useMetricPhases";
import Explorer from "../Explorer";
import { useNotebook } from "../Notebook/useNotebook";
@@ -53,6 +54,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ explorer }: Resource
resourceTokenCollection: state.resourceTokenCollection,
sampleDataResourceTokenCollection: state.sampleDataResourceTokenCollection,
}));
const databasesFetchedSuccessfully = useDatabases((state) => state.databasesFetchedSuccessfully);
const { isCopilotEnabled, isCopilotSampleDBEnabled } = useQueryCopilot((state) => ({
isCopilotEnabled: state.copilotEnabled,
isCopilotSampleDBEnabled: state.copilotSampleDBEnabled,
@@ -114,6 +116,9 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ explorer }: Resource
}
}, [databaseTreeNodes, sampleDataNodes]);
// Track complete DatabaseLoad scenario (start, tree rendered, interactive)
useDatabaseLoadScenario(databaseTreeNodes, databasesFetchedSuccessfully);
useEffect(() => {
// Compute open items based on node.isExpanded
const updateOpenItems = (node: TreeNode, parentNodeId: string): void => {

View File

@@ -9,6 +9,7 @@ interface DatabasesState {
databases: ViewModels.Database[];
resourceTokenCollection: ViewModels.CollectionBase;
sampleDataResourceTokenCollection: ViewModels.CollectionBase;
databasesFetchedSuccessfully: boolean; // Track if last database fetch was successful
updateDatabase: (database: ViewModels.Database) => void;
addDatabases: (databases: ViewModels.Database[]) => void;
deleteDatabase: (database: ViewModels.Database) => void;
@@ -30,6 +31,7 @@ export const useDatabases: UseStore<DatabasesState> = create((set, get) => ({
databases: [],
resourceTokenCollection: undefined,
sampleDataResourceTokenCollection: undefined,
databasesFetchedSuccessfully: false,
updateDatabase: (updatedDatabase: ViewModels.Database) =>
set((state) => {
const updatedDatabases = state.databases.map((database: ViewModels.Database) => {