fix(mysql): reuse data tab client session

This commit is contained in:
miracle 2026-07-10 10:56:48 +08:00 committed by GitHub
parent 8a30bb26f9
commit 3ff4f470f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 56 additions and 7 deletions

View File

@ -2555,9 +2555,9 @@ export const useQueryStore = defineStore("query", () => {
executionPromise = api.executeInManualTransaction(tab.txnSessionId, sqlToExecute, tab.database, executionSchema, pageLimit);
} else {
console.info("[DBX][executeTabSql:execute-multi:start]", { traceId, elapsed: elapsed() });
// Data tabs should reuse the already-open pool; session pools are reserved
// for query tabs/background tasks that need connection-local state isolation.
const clientSessionId = tab.mode === "query" ? tabClientSessionId(tab) : undefined;
// Query and data tabs use a tab-scoped pool so repeated executions keep
// connection-local state and avoid MySQL pool resets on every refresh.
const clientSessionId = tab.mode === "query" || tab.mode === "data" ? tabClientSessionId(tab) : undefined;
const executionOptions = {
...(typeof pageLimit === "number"
? useAgentResultSession

View File

@ -3515,7 +3515,7 @@ test("query execution is scoped to the tab client session", async () => {
}
});
test("data tab execution reuses the shared connection pool", async () => {
test("data tab execution uses a tab-scoped client session", async () => {
const restoreStorage = installMemoryStorage();
setActivePinia(createPinia());
const connectionStore = useConnectionStore();
@ -3541,7 +3541,7 @@ test("data tab execution reuses the shared connection pool", async () => {
try {
await store.executeTabSql(tabId, "select * from users");
assert.equal(executeBody.clientSessionId, undefined);
assert.equal(executeBody.clientSessionId, tabId);
assert.equal(executeBody.timeoutSecs, 30);
} finally {
globalThis.fetch = originalFetch;
@ -3549,6 +3549,55 @@ test("data tab execution reuses the shared connection pool", async () => {
}
});
test("closing a data tab releases its tab-scoped client session", async () => {
const restoreStorage = installMemoryStorage();
setActivePinia(createPinia());
const connectionStore = useConnectionStore();
const store = useQueryStore();
const originalFetch = globalThis.fetch;
connectionStore.addEphemeralConnection(conn("conn-1"));
const tabId = store.createTab("conn-1", "db", "users", "data", "public");
let executeBody: any;
const closedSessions: any[] = [];
globalThis.fetch = withConnectionHealthMock(async (input, init) => {
const url = String(input);
if (url === "/api/query/execute-multi") {
executeBody = JSON.parse(String(init?.body ?? "{}"));
return new Response(JSON.stringify([{ columns: ["id"], rows: [[1]], affected_rows: 0, execution_time_ms: 1 }]), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
if (url === "/api/query/close-client-session") {
closedSessions.push(JSON.parse(String(init?.body ?? "{}")));
return new Response(JSON.stringify(true), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
return new Response("unexpected request", { status: 500 });
});
try {
await store.executeTabSql(tabId, "select * from users");
assert.equal(executeBody.clientSessionId, tabId);
store.closeTab(tabId, { force: true });
// closeClientConnectionSession is fire-and-forget; wait for the request to land.
await waitFor(() => closedSessions.some((body) => body.clientSessionId === tabId));
assert.ok(
closedSessions.some((body) => body.clientSessionId === tabId && body.connectionId === "conn-1"),
`expected close-client-session for tab session, got ${JSON.stringify(closedSessions)}`,
);
} finally {
globalThis.fetch = originalFetch;
restoreStorage();
}
});
test("query execution keeps automatically counting total rows in the background", async () => {
const restoreStorage = installMemoryStorage();
setActivePinia(createPinia());

View File

@ -97,8 +97,8 @@ test("data reload executes before slow metadata refresh completes", async () =>
const actions = useDataGridActions(computed(() => tab));
const reload = actions.onReloadData(undefined, undefined, undefined, undefined, 50, 0);
await waitFor(() => !!executeBody);
assert.equal(executeBody.clientSessionId, undefined);
await waitFor(() => !!executeBody, 5_000);
assert.equal(executeBody.clientSessionId, tabId);
assert.deepEqual(tab.result?.rows, [[1]]);
await waitFor(() => typeof resolveColumns === "function");