perf(execution): prewarm tab session pools and report perceived wait
This commit is contained in:
17 files changed
+558
-29
No files matched your search
@@ -9,6 +9,7 @@ const mocks = vi.hoisted(() => ({
|
||||
activeConnectionId: "",
|
||||
getConfig: vi.fn((connectionId: string) => ({ id: connectionId, db_type: "postgres" })),
|
||||
ensureConnected: vi.fn(),
|
||||
warmConnection: vi.fn(),
|
||||
connectionIdentifierQuote: vi.fn(() => undefined),
|
||||
// 与真实 store 一致:setTableMeta 记录连接元数据代次
|
||||
metadataGenerationFor: vi.fn(() => 0),
|
||||
|
||||
@@ -15,6 +15,7 @@ const mocks = vi.hoisted(() => ({
|
||||
openDataTabsNextToActive: false,
|
||||
metadataGeneration: 0,
|
||||
ensureConnected: vi.fn(),
|
||||
warmConnection: vi.fn(),
|
||||
executeTabSql: vi.fn(),
|
||||
loadTableMetadata: vi.fn(),
|
||||
buildTableSelectSql: vi.fn(),
|
||||
@@ -26,6 +27,7 @@ vi.mock("@/stores/connectionStore", () => ({
|
||||
useConnectionStore: () => ({
|
||||
getConfig: () => ({ id: "connection-1", db_type: mocks.databaseType }),
|
||||
ensureConnected: mocks.ensureConnected,
|
||||
warmConnection: mocks.warmConnection,
|
||||
connectionIdentifierQuote: () => undefined,
|
||||
metadataGenerationFor: () => mocks.metadataGeneration,
|
||||
}),
|
||||
@@ -237,6 +239,19 @@ describe("useSidebarDataOpenRuntime", () => {
|
||||
expect(mocks.tabs).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("prewarms the tab session pool before the first page is fetched", async () => {
|
||||
await useSidebarDataOpenRuntime().openData(tableNode);
|
||||
|
||||
const createdTab = mocks.tabs[0]!;
|
||||
// The tab executes on its own session pool; warming it up front keeps the
|
||||
// connection setup off the critical path of the visible loading spinner.
|
||||
expect(mocks.warmConnection).toHaveBeenCalledWith("connection-1", {
|
||||
database: "app",
|
||||
catalog: undefined,
|
||||
clientSessionId: createdTab.id,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps adjacent placement for every tab created in always-new mode", async () => {
|
||||
mocks.dataTabReuseMode = "always-new";
|
||||
mocks.openDataTabsNextToActive = true;
|
||||
|
||||
@@ -17,6 +17,7 @@ import { tableOpenPageLimit } from "@/lib/table/tableOpenPageLimit";
|
||||
import { tableDataLargeValuePreviewOptions } from "@/lib/dataGrid/dataGridLargeValues";
|
||||
import { canActivateExistingDataTableTab } from "@/lib/tabs/dataTabActivation";
|
||||
import { beginDataTabNavigation, endDataTabNavigation, isCurrentDataTabNavigation } from "@/lib/tabs/dataTabNavigationGeneration";
|
||||
import { dataTabExecutionDatabase } from "@/lib/table/dataTabExecutionDatabase";
|
||||
|
||||
const DATA_TAB_METADATA_TTL_MS = TABLE_METADATA_CACHE_TTL_MS;
|
||||
|
||||
@@ -60,6 +61,21 @@ export function useSidebarDataOpenRuntime() {
|
||||
});
|
||||
lastPhaseAt = now;
|
||||
};
|
||||
// Each data tab executes on its own session-scoped pool
|
||||
// (`<connection>:<database>[:catalog:..]:session:<tabId>`), so its first Run
|
||||
// pays a fresh database connection. On a remote link that setup costs
|
||||
// hundreds of milliseconds of round trips and lands on the critical path of
|
||||
// the page fetch the user is waiting for. Start it in the background as soon
|
||||
// as the tab exists (and again once the connection is up) so it overlaps the
|
||||
// table-metadata load instead of the visible loading spinner.
|
||||
const warmTabSessionPool = (targetTabId: string) => {
|
||||
if (!config) return;
|
||||
connectionStore.warmConnection(node.connectionId, {
|
||||
database: dataTabExecutionDatabase(config, node.database, node.catalog),
|
||||
catalog: node.catalog,
|
||||
clientSessionId: targetTabId,
|
||||
});
|
||||
};
|
||||
openDataLog("info", "start", {
|
||||
traceId,
|
||||
type: node.type,
|
||||
@@ -215,6 +231,7 @@ export function useSidebarDataOpenRuntime() {
|
||||
})();
|
||||
openDataLog("info", "tab-created", { traceId, tabId, elapsed: elapsed() });
|
||||
logPhase("tab-created", { tabId });
|
||||
warmTabSessionPool(tabId);
|
||||
// 接管该 tab:登记本次导航代次,作废在途的 openTableTarget/openData 旧代次
|
||||
// ——旧导航即使目标身份相同(同表不同 whereInput)也不得再落地
|
||||
const navigationToken = beginDataTabNavigation(tabId);
|
||||
@@ -312,6 +329,7 @@ export function useSidebarDataOpenRuntime() {
|
||||
}
|
||||
openDataLog("info", "ensure-connected:done", { traceId, elapsed: elapsed() });
|
||||
logPhase("ensure-connected", { tabId });
|
||||
warmTabSessionPool(tabId);
|
||||
if (!config) throw new Error("Connection config not found");
|
||||
|
||||
const limit = tableOpenPageLimit(settingsStore.editorSettings.tableOpenPageSize);
|
||||
|
||||
Reference in new issue
Block a user