Compare commits

..

3 Commits

Author SHA1 Message Date
Laurent Nguyen
7c942cd3af fix: Update snapshots to include databasesRefreshed property 2026-03-14 08:49:32 +01:00
Laurent Nguyen
52a20b7360 fix: Fix unit test 2026-03-13 15:34:57 +01:00
Laurent Nguyen
6f6aae8ffd Remove duplicate database refresh logic. Add logging for collection fetching 2026-03-13 14:44:12 +01:00
8 changed files with 39 additions and 30 deletions

View File

@@ -1,5 +1,6 @@
import { ContainerResponse } from "@azure/cosmos";
import { Queries } from "Common/Constants";
import * as Logger from "Common/Logger";
import { CosmosDbArtifactType } from "Contracts/FabricMessagesContract";
import { isFabric, isFabricMirroredKey } from "Platform/Fabric/FabricUtil";
import { AuthType } from "../../AuthType";
@@ -61,7 +62,14 @@ export async function readCollections(databaseId: string): Promise<DataModels.Co
return await readCollectionsWithARM(databaseId);
}
Logger.logInfo(`readCollections: calling fetchAll for database ${databaseId}`, "readCollections");
const fetchAllStart = Date.now();
const sdkResponse = await client().database(databaseId).containers.readAll().fetchAll();
Logger.logInfo(
`readCollections: fetchAll completed for database ${databaseId}, count=${sdkResponse.resources
?.length}, durationMs=${Date.now() - fetchAllStart}`,
"readCollections",
);
return sdkResponse.resources as DataModels.Collection[];
} catch (error) {
handleError(error, "ReadCollections", `Error while querying containers for database ${databaseId}`);

View File

@@ -110,6 +110,7 @@ exports[`SettingsComponent renders 1`] = `
"conflictResolutionPolicy": [Function],
"container": Explorer {
"_isInitializingNotebooks": false,
"databasesRefreshed": Promise {},
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isTabsContentExpanded": [Function],
"onRefreshDatabasesKeyPress": [Function],
@@ -231,6 +232,7 @@ exports[`SettingsComponent renders 1`] = `
"conflictResolutionPolicy": [Function],
"container": Explorer {
"_isInitializingNotebooks": false,
"databasesRefreshed": Promise {},
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isTabsContentExpanded": [Function],
"onRefreshDatabasesKeyPress": [Function],
@@ -453,6 +455,7 @@ exports[`SettingsComponent renders 1`] = `
"conflictResolutionPolicy": [Function],
"container": Explorer {
"_isInitializingNotebooks": false,
"databasesRefreshed": Promise {},
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isTabsContentExpanded": [Function],
"onRefreshDatabasesKeyPress": [Function],
@@ -524,6 +527,7 @@ exports[`SettingsComponent renders 1`] = `
explorer={
Explorer {
"_isInitializingNotebooks": false,
"databasesRefreshed": Promise {},
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isTabsContentExpanded": [Function],
"onRefreshDatabasesKeyPress": [Function],
@@ -692,6 +696,7 @@ exports[`SettingsComponent renders 1`] = `
"conflictResolutionPolicy": [Function],
"container": Explorer {
"_isInitializingNotebooks": false,
"databasesRefreshed": Promise {},
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isTabsContentExpanded": [Function],
"onRefreshDatabasesKeyPress": [Function],
@@ -763,6 +768,7 @@ exports[`SettingsComponent renders 1`] = `
explorer={
Explorer {
"_isInitializingNotebooks": false,
"databasesRefreshed": Promise {},
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isTabsContentExpanded": [Function],
"onRefreshDatabasesKeyPress": [Function],

View File

@@ -107,6 +107,12 @@ export default class Explorer {
private static readonly MaxNbDatabasesToAutoExpand = 5;
public phoenixClient: PhoenixClient;
/**
* Resolves when the initial refreshAllDatabases (including collection loading) completes.
* Await this instead of calling refreshAllDatabases again to avoid duplicate concurrent loads.
*/
public databasesRefreshed: Promise<void> = Promise.resolve();
constructor() {
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
dataExplorerArea: Constants.Areas.ResourceTree,
@@ -1197,9 +1203,11 @@ export default class Explorer {
}
if (userContext.apiType !== "Postgres" && userContext.apiType !== "VCoreMongo") {
userContext.authType === AuthType.ResourceToken
? this.refreshDatabaseForResourceToken()
: await this.refreshAllDatabases(); // await: we rely on the databases to be loaded before restoring the tabs further in the flow
this.databasesRefreshed =
userContext.authType === AuthType.ResourceToken
? this.refreshDatabaseForResourceToken()
: this.refreshAllDatabases();
await this.databasesRefreshed; // await: we rely on the databases to be loaded before restoring the tabs further in the flow
}
if (!isFabricNative()) {

View File

@@ -146,6 +146,7 @@ exports[`AddGlobalSecondaryIndexPanel render default panel 1`] = `
explorer={
Explorer {
"_isInitializingNotebooks": false,
"databasesRefreshed": Promise {},
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isTabsContentExpanded": [Function],
"onRefreshDatabasesKeyPress": [Function],

View File

@@ -18,6 +18,7 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
{
"container": Explorer {
"_isInitializingNotebooks": false,
"databasesRefreshed": Promise {},
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isTabsContentExpanded": [Function],
"onRefreshDatabasesKeyPress": [Function],

View File

@@ -8,6 +8,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
explorer={
Explorer {
"_isInitializingNotebooks": false,
"databasesRefreshed": Promise {},
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isTabsContentExpanded": [Function],
"onRefreshDatabasesKeyPress": [Function],

View File

@@ -1,41 +1,25 @@
import React from "react";
import MetricScenario from "./MetricEvents";
import { ApplicationMetricPhase, CommonMetricPhase } from "./ScenarioConfig";
import { scenarioMonitor } from "./ScenarioMonitor";
import { ApplicationMetricPhase, CommonMetricPhase } from "./ScenarioConfig";
/**
* Completes the Interactive phase once the browser is ready to paint.
* Hook to automatically complete the Interactive phase when the component becomes interactive.
* Uses requestAnimationFrame to complete after the browser has painted.
*
* Uses requestAnimationFrame with a setTimeout fallback. In foreground tabs rAF fires
* first (~16 ms) giving an accurate "browser painted" signal. In background tabs browsers
* suspend rAF indefinitely, so the setTimeout fallback (1 s) completes the phase instead —
* well within the 10 s scenario timeout — preventing false-negative unhealthy reports.
* 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) {
React.useEffect(() => {
if (!enabled) {
return undefined;
}
let completed = false;
const complete = () => {
if (completed) {
return;
}
completed = true;
cancelAnimationFrame(rafId);
clearTimeout(timeoutId);
const id = requestAnimationFrame(() => {
scenarioMonitor.completePhase(scenario, CommonMetricPhase.Interactive);
};
const rafId = requestAnimationFrame(complete);
// Fallback for background tabs where rAF is suspended.
const timeoutId = setTimeout(complete, 1000);
return () => {
cancelAnimationFrame(rafId);
clearTimeout(timeoutId);
};
});
return () => cancelAnimationFrame(id);
}, [scenario, enabled]);
}

View File

@@ -165,7 +165,7 @@ async function configureFabric(): Promise<Explorer> {
explorer = createExplorerFabricLegacy(initializationMessage, data.version);
await scheduleRefreshFabricToken(true);
resolve(explorer);
await explorer.refreshAllDatabases();
await explorer.databasesRefreshed;
if (userContext.fabricContext.isVisible) {
firstContainerOpened = true;
openFirstContainer(explorer, userContext.fabricContext.databaseName);
@@ -189,7 +189,7 @@ async function configureFabric(): Promise<Explorer> {
}
resolve(explorer);
await explorer.refreshAllDatabases();
await explorer.databasesRefreshed;
const { databaseName } = userContext.fabricContext;
if (userContext.fabricContext.isVisible && databaseName) {