mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-13 01:54:20 +08:00
⚡️ perf(driver-manager): 优化驱动状态缓存与加载协调
- 抽离驱动状态快照注册、恢复和请求收尾逻辑 - 避免过期请求覆盖最新目录状态或持续占用加载态 - 补充缓存恢复与请求竞态回归测试
This commit is contained in:
@@ -1,35 +1,91 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const source = readFileSync(
|
||||
fileURLToPath(new globalThis.URL('./DriverManagerModal.tsx', import.meta.url)),
|
||||
'utf8',
|
||||
).replace(/\r\n/g, '\n');
|
||||
import {
|
||||
createDriverStatusSnapshotRegistry,
|
||||
restoreDriverNetworkSnapshot,
|
||||
restoreDriverStatusSnapshot,
|
||||
settleLatestDriverRequest,
|
||||
} from '../utils/driverManagerRequestState';
|
||||
|
||||
describe('DriverManagerModal request coordination boundary', () => {
|
||||
it('lets the latest request clear a loading state even when it is a silent refresh', () => {
|
||||
expect(source).toMatch(
|
||||
/finally \{\s*if \(requestGeneration === statusRequestGenerationRef\.current\) \{\s*setLoading\(false\);/s,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/finally \{\s*if \(requestGeneration === networkRequestGenerationRef\.current\) \{\s*setNetworkChecking\(false\);/s,
|
||||
);
|
||||
expect(source).not.toContain('showLoading && requestGeneration === statusRequestGenerationRef.current');
|
||||
expect(source).not.toContain('showLoading && requestGeneration === networkRequestGenerationRef.current');
|
||||
it('lets only the latest request clear loading, including silent refreshes', () => {
|
||||
const setStatusLoading = vi.fn();
|
||||
const setNetworkLoading = vi.fn();
|
||||
|
||||
expect(settleLatestDriverRequest(2, 3, setStatusLoading)).toBe(false);
|
||||
expect(setStatusLoading).not.toHaveBeenCalled();
|
||||
expect(settleLatestDriverRequest(3, 3, setStatusLoading)).toBe(true);
|
||||
expect(setStatusLoading).toHaveBeenCalledWith(false);
|
||||
|
||||
expect(settleLatestDriverRequest(7, 7, setNetworkLoading)).toBe(true);
|
||||
expect(setNetworkLoading).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('clears cold loading flags when another instance populated a fresh snapshot first', () => {
|
||||
expect(source).toMatch(/if \(cachedStatus\) \{\s*setRows\(cachedStatus\.rows\);\s*setLoading\(false\);/s);
|
||||
expect(source).toContain('downloadDirRef.current = cachedStatus.downloadDir;');
|
||||
expect(source).toMatch(/if \(cachedNetwork\) \{\s*setNetworkStatus\(cachedNetwork\.status\);\s*setNetworkChecking\(false\);/s);
|
||||
it('restores fresh snapshots and clears cold loading flags', () => {
|
||||
const setRows = vi.fn();
|
||||
const setStatusLoading = vi.fn();
|
||||
const setDownloadDir = vi.fn();
|
||||
const setNetworkStatus = vi.fn();
|
||||
const setNetworkLoading = vi.fn();
|
||||
const rows = [{ type: 'mysql' }];
|
||||
const networkStatus = { reachable: true };
|
||||
|
||||
expect(restoreDriverStatusSnapshot({
|
||||
rows,
|
||||
downloadDir: 'D:/drivers',
|
||||
cachedAt: 100,
|
||||
intentSequence: 4,
|
||||
}, {
|
||||
setRows,
|
||||
setLoading: setStatusLoading,
|
||||
setDownloadDir,
|
||||
})).toBe(true);
|
||||
expect(setRows).toHaveBeenCalledWith(rows);
|
||||
expect(setStatusLoading).toHaveBeenCalledWith(false);
|
||||
expect(setDownloadDir).toHaveBeenCalledWith('D:/drivers');
|
||||
|
||||
expect(restoreDriverNetworkSnapshot({ status: networkStatus, cachedAt: 100 }, {
|
||||
setStatus: setNetworkStatus,
|
||||
setLoading: setNetworkLoading,
|
||||
})).toBe(true);
|
||||
expect(setNetworkStatus).toHaveBeenCalledWith(networkStatus);
|
||||
expect(setNetworkLoading).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('keeps status snapshots keyed and rejects writes older than the latest intent for a directory', () => {
|
||||
expect(source).toContain('const driverStatusSnapshotCache = new Map<string, DriverStatusSnapshot>();');
|
||||
expect(source).toContain('const driverStatusSnapshotIntentByKey = new Map<string, number>();');
|
||||
expect(source).toContain('preferredDriverStatusSnapshotKey = requestKey;');
|
||||
expect(source).toContain('snapshot.intentSequence < latestIntentForKey');
|
||||
expect(source).toContain('writeDriverStatusSnapshot(resolvedRequestKey, snapshot);');
|
||||
it('keeps status snapshots keyed and rejects writes older than the latest intent', () => {
|
||||
const snapshots = createDriverStatusSnapshotRegistry<{ type: string }>();
|
||||
const mysqlKey = snapshots.beginRequest('D:/mysql', 2);
|
||||
|
||||
expect(snapshots.write(mysqlKey, {
|
||||
rows: [{ type: 'stale' }],
|
||||
downloadDir: 'D:/mysql',
|
||||
cachedAt: 100,
|
||||
intentSequence: 1,
|
||||
})).toBe(false);
|
||||
expect(snapshots.getPreferred()).toBeNull();
|
||||
|
||||
expect(snapshots.write(mysqlKey, {
|
||||
rows: [{ type: 'mysql' }],
|
||||
downloadDir: 'D:/mysql',
|
||||
cachedAt: 200,
|
||||
intentSequence: 2,
|
||||
})).toBe(true);
|
||||
expect(snapshots.getPreferred()?.rows).toEqual([{ type: 'mysql' }]);
|
||||
|
||||
const postgresKey = snapshots.beginRequest('D:/postgres', 3);
|
||||
expect(snapshots.write(postgresKey, {
|
||||
rows: [{ type: 'postgres' }],
|
||||
downloadDir: 'D:/postgres',
|
||||
cachedAt: 300,
|
||||
intentSequence: 3,
|
||||
})).toBe(true);
|
||||
expect(snapshots.getPreferred()?.rows).toEqual([{ type: 'postgres' }]);
|
||||
|
||||
expect(snapshots.write(mysqlKey, {
|
||||
rows: [{ type: 'older-mysql' }],
|
||||
downloadDir: 'D:/mysql',
|
||||
cachedAt: 150,
|
||||
intentSequence: 1,
|
||||
})).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,6 +16,14 @@ import {
|
||||
getDriverLocalImportDirectoryHelp,
|
||||
getDriverLocalImportSingleFileHelp,
|
||||
} from '../utils/driverImportGuidance';
|
||||
import {
|
||||
createDriverStatusSnapshotRegistry,
|
||||
normalizeDriverStatusRequestKey,
|
||||
restoreDriverNetworkSnapshot,
|
||||
restoreDriverStatusSnapshot,
|
||||
settleLatestDriverRequest,
|
||||
type DriverStatusSnapshot as DriverStatusSnapshotState,
|
||||
} from '../utils/driverManagerRequestState';
|
||||
import {
|
||||
CheckDriverNetworkStatus,
|
||||
DownloadDriverPackage,
|
||||
@@ -469,16 +477,7 @@ const createDriverBatchProgress = (total: number, currentMessage: string): Drive
|
||||
currentMessage,
|
||||
});
|
||||
|
||||
type DriverStatusSnapshot = {
|
||||
rows: DriverStatusRow[];
|
||||
downloadDir: string;
|
||||
cachedAt: number;
|
||||
intentSequence: number;
|
||||
};
|
||||
const DEFAULT_DRIVER_STATUS_REQUEST_KEY = '<default>';
|
||||
const driverStatusSnapshotCache = new Map<string, DriverStatusSnapshot>();
|
||||
const driverStatusSnapshotIntentByKey = new Map<string, number>();
|
||||
let preferredDriverStatusSnapshotKey = DEFAULT_DRIVER_STATUS_REQUEST_KEY;
|
||||
const driverStatusSnapshots = createDriverStatusSnapshotRegistry<DriverStatusRow>();
|
||||
let driverNetworkSnapshotCache: { status: DriverNetworkStatus; cachedAt: number } | null = null;
|
||||
let driverStatusSnapshotIntentSequence = 0;
|
||||
type DriverStatusBackendResult = Awaited<ReturnType<typeof GetDriverStatusList>>;
|
||||
@@ -486,23 +485,6 @@ type DriverNetworkBackendResult = Awaited<ReturnType<typeof CheckDriverNetworkSt
|
||||
const driverStatusInFlightRequests = new Map<string, Promise<DriverStatusBackendResult>>();
|
||||
let driverNetworkInFlightRequest: Promise<DriverNetworkBackendResult> | null = null;
|
||||
|
||||
const normalizeDriverStatusRequestKey = (downloadDir: string): string => (
|
||||
String(downloadDir || '').trim() || DEFAULT_DRIVER_STATUS_REQUEST_KEY
|
||||
);
|
||||
|
||||
const getPreferredDriverStatusSnapshot = (): DriverStatusSnapshot | null => (
|
||||
driverStatusSnapshotCache.get(preferredDriverStatusSnapshotKey) || null
|
||||
);
|
||||
|
||||
const writeDriverStatusSnapshot = (requestKey: string, snapshot: DriverStatusSnapshot): void => {
|
||||
const latestIntentForKey = driverStatusSnapshotIntentByKey.get(requestKey) || 0;
|
||||
const cachedSnapshot = driverStatusSnapshotCache.get(requestKey);
|
||||
if (snapshot.intentSequence < latestIntentForKey || snapshot.intentSequence < (cachedSnapshot?.intentSequence || 0)) {
|
||||
return;
|
||||
}
|
||||
driverStatusSnapshotCache.set(requestKey, snapshot);
|
||||
};
|
||||
|
||||
const requestDriverStatusShared = (downloadDir: string): Promise<DriverStatusBackendResult> => {
|
||||
const requestKey = normalizeDriverStatusRequestKey(downloadDir);
|
||||
const pendingRequest = driverStatusInFlightRequests.get(requestKey);
|
||||
@@ -601,12 +583,12 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onBack?
|
||||
() => buildDriverManagerWorkbenchTheme(darkMode, opacity),
|
||||
[darkMode, opacity, appearance.uiVersion],
|
||||
);
|
||||
const [loading, setLoading] = useState(() => open && !getPreferredDriverStatusSnapshot());
|
||||
const [downloadDir, setDownloadDir] = useState(() => getPreferredDriverStatusSnapshot()?.downloadDir || '');
|
||||
const [loading, setLoading] = useState(() => open && !driverStatusSnapshots.getPreferred());
|
||||
const [downloadDir, setDownloadDir] = useState(() => driverStatusSnapshots.getPreferred()?.downloadDir || '');
|
||||
const [networkChecking, setNetworkChecking] = useState(() => open && !driverNetworkSnapshotCache);
|
||||
const [networkStatus, setNetworkStatus] = useState<DriverNetworkStatus | null>(() => driverNetworkSnapshotCache?.status || null);
|
||||
const [searchKeyword, setSearchKeyword] = useState('');
|
||||
const [rows, setRows] = useState<DriverStatusRow[]>(() => getPreferredDriverStatusSnapshot()?.rows || []);
|
||||
const [rows, setRows] = useState<DriverStatusRow[]>(() => driverStatusSnapshots.getPreferred()?.rows || []);
|
||||
const [actionState, setActionState] = useState<{ driverType: string; kind: DriverActionKind }>({ driverType: '', kind: '' });
|
||||
const [batchAction, setBatchAction] = useState<DriverBatchActionKind>('');
|
||||
const [batchProgress, setBatchProgress] = useState<DriverBatchProgressState | null>(null);
|
||||
@@ -762,9 +744,7 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onBack?
|
||||
const snapshotIntentSequence = driverStatusSnapshotIntentSequence + 1;
|
||||
driverStatusSnapshotIntentSequence = snapshotIntentSequence;
|
||||
const requestedDownloadDir = downloadDirRef.current;
|
||||
const requestKey = normalizeDriverStatusRequestKey(requestedDownloadDir);
|
||||
preferredDriverStatusSnapshotKey = requestKey;
|
||||
driverStatusSnapshotIntentByKey.set(requestKey, snapshotIntentSequence);
|
||||
const requestKey = driverStatusSnapshots.beginRequest(requestedDownloadDir, snapshotIntentSequence);
|
||||
const showLoading = options?.showLoading ?? true;
|
||||
if (showLoading) {
|
||||
setLoading(true);
|
||||
@@ -816,25 +796,23 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onBack?
|
||||
message: String(item.message || '').trim() || undefined,
|
||||
}));
|
||||
setRows(nextRows);
|
||||
const snapshot: DriverStatusSnapshot = {
|
||||
const snapshot: DriverStatusSnapshotState<DriverStatusRow> = {
|
||||
rows: nextRows,
|
||||
downloadDir: effectiveDownloadDir,
|
||||
cachedAt: Date.now(),
|
||||
intentSequence: snapshotIntentSequence,
|
||||
};
|
||||
writeDriverStatusSnapshot(requestKey, snapshot);
|
||||
driverStatusSnapshots.write(requestKey, snapshot);
|
||||
const resolvedRequestKey = normalizeDriverStatusRequestKey(effectiveDownloadDir);
|
||||
if (resolvedRequestKey !== requestKey) {
|
||||
writeDriverStatusSnapshot(resolvedRequestKey, snapshot);
|
||||
driverStatusSnapshots.write(resolvedRequestKey, snapshot);
|
||||
}
|
||||
} catch (err: any) {
|
||||
if (requestGeneration === statusRequestGenerationRef.current && toastOnError) {
|
||||
message.error(t('driver.modal.error.statusFetchWithDetail', { detail: err?.message || String(err) }));
|
||||
}
|
||||
} finally {
|
||||
if (requestGeneration === statusRequestGenerationRef.current) {
|
||||
setLoading(false);
|
||||
}
|
||||
settleLatestDriverRequest(requestGeneration, statusRequestGenerationRef.current, setLoading);
|
||||
}
|
||||
}, [resolveDriverErrorMessage]);
|
||||
|
||||
@@ -901,9 +879,7 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onBack?
|
||||
message.error(t('driver.modal.error.networkCheckWithDetail', { detail: err?.message || String(err) }));
|
||||
}
|
||||
} finally {
|
||||
if (requestGeneration === networkRequestGenerationRef.current) {
|
||||
setNetworkChecking(false);
|
||||
}
|
||||
settleLatestDriverRequest(requestGeneration, networkRequestGenerationRef.current, setNetworkChecking);
|
||||
}
|
||||
}, [resolveDriverErrorMessage]);
|
||||
|
||||
@@ -1061,16 +1037,16 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onBack?
|
||||
return;
|
||||
}
|
||||
|
||||
const cachedStatus = getPreferredDriverStatusSnapshot();
|
||||
const cachedStatus = driverStatusSnapshots.getPreferred();
|
||||
const hasCachedStatus = !!cachedStatus;
|
||||
if (cachedStatus) {
|
||||
setRows(cachedStatus.rows);
|
||||
setLoading(false);
|
||||
if (cachedStatus.downloadDir) {
|
||||
downloadDirRef.current = cachedStatus.downloadDir;
|
||||
setDownloadDir(cachedStatus.downloadDir);
|
||||
}
|
||||
}
|
||||
restoreDriverStatusSnapshot(cachedStatus, {
|
||||
setRows,
|
||||
setLoading,
|
||||
setDownloadDir: (nextDownloadDir) => {
|
||||
downloadDirRef.current = nextDownloadDir;
|
||||
setDownloadDir(nextDownloadDir);
|
||||
},
|
||||
});
|
||||
const shouldRefreshStatus = !cachedStatus || !isFreshCache(cachedStatus.cachedAt, DRIVER_STATUS_CACHE_TTL_MS);
|
||||
if (shouldRefreshStatus) {
|
||||
void refreshStatus(false, { showLoading: !hasCachedStatus });
|
||||
@@ -1078,10 +1054,10 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onBack?
|
||||
|
||||
const cachedNetwork = driverNetworkSnapshotCache;
|
||||
const hasCachedNetwork = !!cachedNetwork;
|
||||
if (cachedNetwork) {
|
||||
setNetworkStatus(cachedNetwork.status);
|
||||
setNetworkChecking(false);
|
||||
}
|
||||
restoreDriverNetworkSnapshot(cachedNetwork, {
|
||||
setStatus: setNetworkStatus,
|
||||
setLoading: setNetworkChecking,
|
||||
});
|
||||
const shouldRefreshNetwork = !cachedNetwork || !isFreshCache(cachedNetwork.cachedAt, DRIVER_NETWORK_CACHE_TTL_MS);
|
||||
if (shouldRefreshNetwork) {
|
||||
void checkNetworkStatus(false, { showLoading: !hasCachedNetwork });
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { resolveDriverErrorMessageText } from '../components/DriverManagerModal';
|
||||
import { t as catalogT } from '../i18n/catalog';
|
||||
import { t } from '../i18n';
|
||||
import {
|
||||
@@ -68,8 +69,6 @@ describe('driver import guidance', () => {
|
||||
backendWrapperKeys,
|
||||
expected,
|
||||
}) => {
|
||||
const { resolveDriverErrorMessageText } = await import('../components/DriverManagerModal');
|
||||
|
||||
expect(resolveDriverErrorMessageText(
|
||||
rawMessage,
|
||||
fallbackMessage,
|
||||
|
||||
96
frontend/src/utils/driverManagerRequestState.ts
Normal file
96
frontend/src/utils/driverManagerRequestState.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
export type DriverStatusSnapshot<TRow> = {
|
||||
rows: TRow[];
|
||||
downloadDir: string;
|
||||
cachedAt: number;
|
||||
intentSequence: number;
|
||||
};
|
||||
|
||||
export type DriverNetworkSnapshot<TStatus> = {
|
||||
status: TStatus;
|
||||
cachedAt: number;
|
||||
};
|
||||
|
||||
const DEFAULT_DRIVER_STATUS_REQUEST_KEY = '<default>';
|
||||
|
||||
export const normalizeDriverStatusRequestKey = (downloadDir: string): string => (
|
||||
String(downloadDir || '').trim() || DEFAULT_DRIVER_STATUS_REQUEST_KEY
|
||||
);
|
||||
|
||||
export const createDriverStatusSnapshotRegistry = <TRow>() => {
|
||||
const snapshots = new Map<string, DriverStatusSnapshot<TRow>>();
|
||||
const latestIntentByKey = new Map<string, number>();
|
||||
let preferredRequestKey = DEFAULT_DRIVER_STATUS_REQUEST_KEY;
|
||||
|
||||
return {
|
||||
beginRequest(downloadDir: string, intentSequence: number): string {
|
||||
const requestKey = normalizeDriverStatusRequestKey(downloadDir);
|
||||
preferredRequestKey = requestKey;
|
||||
latestIntentByKey.set(requestKey, intentSequence);
|
||||
return requestKey;
|
||||
},
|
||||
|
||||
getPreferred(): DriverStatusSnapshot<TRow> | null {
|
||||
return snapshots.get(preferredRequestKey) || null;
|
||||
},
|
||||
|
||||
write(requestKey: string, snapshot: DriverStatusSnapshot<TRow>): boolean {
|
||||
const normalizedRequestKey = normalizeDriverStatusRequestKey(requestKey);
|
||||
const latestIntentForKey = latestIntentByKey.get(normalizedRequestKey) || 0;
|
||||
const cachedSnapshot = snapshots.get(normalizedRequestKey);
|
||||
if (
|
||||
snapshot.intentSequence < latestIntentForKey
|
||||
|| snapshot.intentSequence < (cachedSnapshot?.intentSequence || 0)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
snapshots.set(normalizedRequestKey, snapshot);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const settleLatestDriverRequest = (
|
||||
requestGeneration: number,
|
||||
latestRequestGeneration: number,
|
||||
setLoading: (loading: boolean) => void,
|
||||
): boolean => {
|
||||
if (requestGeneration !== latestRequestGeneration) {
|
||||
return false;
|
||||
}
|
||||
setLoading(false);
|
||||
return true;
|
||||
};
|
||||
|
||||
export const restoreDriverStatusSnapshot = <TRow>(
|
||||
snapshot: DriverStatusSnapshot<TRow> | null,
|
||||
callbacks: {
|
||||
setRows: (rows: TRow[]) => void;
|
||||
setLoading: (loading: boolean) => void;
|
||||
setDownloadDir: (downloadDir: string) => void;
|
||||
},
|
||||
): boolean => {
|
||||
if (!snapshot) {
|
||||
return false;
|
||||
}
|
||||
callbacks.setRows(snapshot.rows);
|
||||
callbacks.setLoading(false);
|
||||
if (snapshot.downloadDir) {
|
||||
callbacks.setDownloadDir(snapshot.downloadDir);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
export const restoreDriverNetworkSnapshot = <TStatus>(
|
||||
snapshot: DriverNetworkSnapshot<TStatus> | null,
|
||||
callbacks: {
|
||||
setStatus: (status: TStatus) => void;
|
||||
setLoading: (loading: boolean) => void;
|
||||
},
|
||||
): boolean => {
|
||||
if (!snapshot) {
|
||||
return false;
|
||||
}
|
||||
callbacks.setStatus(snapshot.status);
|
||||
callbacks.setLoading(false);
|
||||
return true;
|
||||
};
|
||||
Reference in New Issue
Block a user