fix(mysql): refresh stale snapshots and streamline table reloads

This commit is contained in:
t8y2 authored and GitHub committed 2026-09-17 19:29:09 +08:00
1 parent e00fbabb35
commit fac270fc8a
11 files changed
+911 -54

No files matched your search

@@ -55,6 +55,20 @@ describe("tableMetadataCache columns facet request counts", () => {
mocks.listIndexes.mockResolvedValue([]);
});
it("does not treat failed index discovery as a complete cacheable row identity", async () => {
mocks.getColumns.mockResolvedValue([{ ...column("id"), is_primary_key: false }]);
mocks.listIndexes.mockRejectedValueOnce(new Error("indexes unavailable"));
const failed = await loadTableMetadata(usersRequest);
expect(failed.metadata.rowIdentityResolved).toBe(false);
expect(failed.metadata.primaryKeys).toEqual([]);
expect(getCachedTableMetadata(usersRequest)).toBeUndefined();
const retried = await loadTableMetadata(usersRequest);
expect(retried.metadata.rowIdentityResolved).toBe(true);
expect(mocks.listIndexes).toHaveBeenCalledTimes(2);
expect(mocks.getColumns).toHaveBeenCalledOnce();
});
it("R1 — cold single-source columns load: 1 getColumns, 0 listIndexes", async () => {
const result = await loadTableColumns({ ...usersRequest });
expect(result.columns[0]?.name).toBe("users");
@@ -17,6 +17,7 @@ export interface TableMetadata {
columns: ColumnInfo[];
indexes: IndexInfo[];
primaryKeys: string[];
rowIdentityResolved?: boolean;
cachedAt: number;
}
@@ -331,11 +332,16 @@ export async function loadTableMetadata(request: TableMetadataRequest): Promise<
// discovery independently unless an agent-backed PostgreSQL-family
// relation must first report its visible schema for the index lookup.
const resolveReportedSchema = (request.databaseType === "vastbase" || request.databaseType === "kingbase") && !request.schema;
const indexesPromise = resolveReportedSchema ? undefined : loadTableIndexes(request).catch((): IndexInfo[] => []);
let rowIdentityResolved = true;
const indexFailure = (): IndexInfo[] => {
rowIdentityResolved = false;
return [];
};
const indexesPromise = resolveReportedSchema ? undefined : loadTableIndexes(request).catch(indexFailure);
const columnsResult = await columnsPromise;
const columns = columnsResult.columns;
const resolvedSchema = resolveReportedSchema ? columns.find((column) => column.resolved_schema)?.resolved_schema : request.schema;
const indexes = columns.length > 0 ? await (indexesPromise ?? loadTableIndexes({ ...request, schema: resolvedSchema }).catch((): IndexInfo[] => [])) : [];
const indexes = columns.length > 0 ? await (indexesPromise ?? loadTableIndexes({ ...request, schema: resolvedSchema }).catch(indexFailure)) : [];
const primaryKeys = editableRowIdentifierColumns(request.databaseType as DatabaseType, columns, indexes, request.tableType);
return {
schema: resolvedSchema || undefined,
@@ -346,6 +352,7 @@ export async function loadTableMetadata(request: TableMetadataRequest): Promise<
columns,
indexes,
primaryKeys,
rowIdentityResolved: rowIdentityResolved && columns.length > 0,
cachedAt: columnsResult.cachedAt,
};
},
@@ -353,7 +360,7 @@ export async function loadTableMetadata(request: TableMetadataRequest): Promise<
);
// 必须在 unregister 前比较:最后一个在途加载注销时会顺带清掉代数记录
if (invalidationStampAtStart === (tableMetadataInvalidationStamps.get(scopeKey) ?? 0)) {
if (metadata.rowIdentityResolved && invalidationStampAtStart === (tableMetadataInvalidationStamps.get(scopeKey) ?? 0)) {
tableMetadataCache.set(scope, metadata, { cachedAt: metadata.cachedAt });
}
} finally {