Add persistence load and commit attribution diagnostics

This commit is contained in:
Youzini-afk
2026-04-22 17:11:59 +08:00
parent ad92f92afc
commit b849117646
5 changed files with 311 additions and 4 deletions

105
index.js
View File

@@ -1666,6 +1666,10 @@ function normalizeLoadDiagnosticsMs(value = 0) {
return Math.round((Number(value) || 0) * 10) / 10;
}
function normalizePersistDeltaDiagnosticsMs(value = 0) {
return Math.round((Number(value) || 0) * 10) / 10;
}
function updatePersistDeltaDiagnostics(snapshot = null) {
const nextSnapshot =
snapshot && typeof snapshot === "object" && !Array.isArray(snapshot)
@@ -9418,6 +9422,7 @@ async function loadGraphFromIndexedDb(
totalMs: normalizeLoadDiagnosticsMs(readLoadDiagnosticsNow() - loadStartedAt),
});
let exportSnapshotMs = 0;
let preApplyMs = 0;
let exportSnapshotSource = "";
if (!normalizedChatId) {
const result = {
@@ -9801,6 +9806,7 @@ async function loadGraphFromIndexedDb(
};
}
preApplyMs = readLoadDiagnosticsNow() - loadStartedAt;
const applyInvokeStartedAt = readLoadDiagnosticsNow();
const loadResult = applyIndexedDbSnapshotToRuntime(normalizedChatId, snapshot, {
source,
@@ -9811,6 +9817,8 @@ async function loadGraphFromIndexedDb(
reasonPrefix: snapshotStore.reasonPrefix,
});
const applyInvokeMs = readLoadDiagnosticsNow() - applyInvokeStartedAt;
const totalLoadMs = readLoadDiagnosticsNow() - loadStartedAt;
const loadAccountedMs = preApplyMs + applyInvokeMs;
if (commitMarkerDiagnostic?.reason && loadResult?.loaded) {
updateGraphPersistenceState({
persistMismatchReason: commitMarkerDiagnostic.reason,
@@ -9828,7 +9836,14 @@ async function loadGraphFromIndexedDb(
commitMarkerMismatched: commitMarkerMismatch.mismatched === true,
exportSnapshotSource: exportSnapshotSource || "snapshot-prepared",
exportSnapshotMs: normalizeLoadDiagnosticsMs(exportSnapshotMs),
preApplyMs: normalizeLoadDiagnosticsMs(preApplyMs),
preApplyOtherMs: normalizeLoadDiagnosticsMs(
Math.max(0, preApplyMs - exportSnapshotMs),
),
applyInvokeMs: normalizeLoadDiagnosticsMs(applyInvokeMs),
untrackedMs: normalizeLoadDiagnosticsMs(
Math.max(0, totalLoadMs - loadAccountedMs),
),
});
return loadResult;
} catch (error) {
@@ -9862,6 +9877,16 @@ async function loadGraphFromIndexedDb(
error: error?.message || String(error),
exportSnapshotSource: exportSnapshotSource || "unknown",
exportSnapshotMs: normalizeLoadDiagnosticsMs(exportSnapshotMs),
preApplyMs: normalizeLoadDiagnosticsMs(
preApplyMs || (readLoadDiagnosticsNow() - loadStartedAt),
),
preApplyOtherMs: normalizeLoadDiagnosticsMs(
Math.max(
0,
(preApplyMs || (readLoadDiagnosticsNow() - loadStartedAt)) -
exportSnapshotMs,
),
),
});
return result;
}
@@ -13363,12 +13388,19 @@ async function saveGraphToIndexedDb(
let nativePersistPreloadStatus = "not-requested";
let nativePersistPreloadError = "";
let nativePersistPreloadMs = 0;
let baseSnapshotReadMs = 0;
let graphSnapshotBuildMs = 0;
const persistDeltaStartedAt = readPersistDeltaDiagnosticsNow();
if (!delta) {
baseSnapshot =
readCachedIndexedDbSnapshot(normalizedChatId, localStore) ||
(await db.exportSnapshot());
const baseSnapshotReadStartedAt = readPersistDeltaDiagnosticsNow();
baseSnapshot = readCachedIndexedDbSnapshot(normalizedChatId, localStore);
if (!baseSnapshot) {
baseSnapshot = await db.exportSnapshot();
}
baseSnapshotReadMs =
readPersistDeltaDiagnosticsNow() - baseSnapshotReadStartedAt;
const graphSnapshotBuildStartedAt = readPersistDeltaDiagnosticsNow();
snapshot = buildSnapshotFromGraph(graph, {
chatId: normalizedChatId,
revision: requestedRevision,
@@ -13383,6 +13415,8 @@ async function saveGraphToIndexedDb(
hostChatId: currentIdentity.hostChatId || "",
},
});
graphSnapshotBuildMs =
readPersistDeltaDiagnosticsNow() - graphSnapshotBuildStartedAt;
}
const nativePersistBridgeMode = String(
currentSettings.persistNativeDeltaBridgeMode || "json",
@@ -13555,6 +13589,12 @@ async function saveGraphToIndexedDb(
requestedRevision,
markSyncDirty: true,
});
const commitDiagnostics =
commitResult?.diagnostics &&
typeof commitResult.diagnostics === "object" &&
!Array.isArray(commitResult.diagnostics)
? cloneRuntimeDebugValue(commitResult.diagnostics, {})
: null;
const committedRevision = normalizeIndexedDbRevision(
commitResult?.revision,
requestedRevision,
@@ -13564,6 +13604,7 @@ async function saveGraphToIndexedDb(
let scheduleUploadWarning = "";
if (graph) {
if (!snapshot) {
const graphSnapshotBuildStartedAt = readPersistDeltaDiagnosticsNow();
snapshot = buildSnapshotFromGraph(graph, {
chatId: normalizedChatId,
revision: committedRevision,
@@ -13578,6 +13619,8 @@ async function saveGraphToIndexedDb(
hostChatId: currentIdentity.hostChatId || "",
},
});
graphSnapshotBuildMs +=
readPersistDeltaDiagnosticsNow() - graphSnapshotBuildStartedAt;
}
if (!snapshot.meta || typeof snapshot.meta !== "object" || Array.isArray(snapshot.meta)) {
snapshot.meta = {};
@@ -13620,6 +13663,14 @@ async function saveGraphToIndexedDb(
}
}
const persistTotalMs = readPersistDeltaDiagnosticsNow() - persistDeltaStartedAt;
const persistAccountedMs =
Number(nativePersistPreloadMs || 0) +
Number(baseSnapshotReadMs || 0) +
Number(graphSnapshotBuildMs || 0) +
Number(persistDeltaBuildDiagnostics?.buildMs || 0) +
Number(commitDiagnostics?.queueWaitMs || 0) +
Number(commitDiagnostics?.commitMs || 0);
const persistDeltaDiagnostics = {
...cloneRuntimeDebugValue(persistDeltaBuildDiagnostics, {}),
chatId: normalizedChatId,
@@ -13669,13 +13720,59 @@ async function saveGraphToIndexedDb(
moduleError: String(
nativePersistModuleStatus?.error || nativePersistPreloadError || "",
),
baseSnapshotReadMs: normalizePersistDeltaDiagnosticsMs(baseSnapshotReadMs),
snapshotBuildMs: normalizePersistDeltaDiagnosticsMs(graphSnapshotBuildMs),
commitStorageKind: String(
commitDiagnostics?.storageKind || localStore.storagePrimary || "",
),
commitStoreMode: String(
commitDiagnostics?.storeMode || localStore.storageMode || "",
),
commitQueueWaitMs: normalizePersistDeltaDiagnosticsMs(
commitDiagnostics?.queueWaitMs,
),
commitMs: normalizePersistDeltaDiagnosticsMs(commitDiagnostics?.commitMs),
commitTxMs: normalizePersistDeltaDiagnosticsMs(commitDiagnostics?.txMs),
commitSnapshotReadMs: normalizePersistDeltaDiagnosticsMs(
commitDiagnostics?.snapshotReadMs,
),
commitSnapshotWriteMs: normalizePersistDeltaDiagnosticsMs(
commitDiagnostics?.snapshotWriteMs,
),
commitManifestReadMs: normalizePersistDeltaDiagnosticsMs(
commitDiagnostics?.manifestReadMs,
),
commitWalWriteMs: normalizePersistDeltaDiagnosticsMs(
commitDiagnostics?.walWriteMs,
),
commitManifestWriteMs: normalizePersistDeltaDiagnosticsMs(
commitDiagnostics?.manifestWriteMs,
),
commitCacheApplyMs: normalizePersistDeltaDiagnosticsMs(
commitDiagnostics?.cacheApplyMs,
),
commitPayloadBytes: Math.max(
0,
Math.floor(Number(commitDiagnostics?.payloadBytes || 0)),
),
commitWalBytes: Math.max(
0,
Math.floor(Number(commitDiagnostics?.walBytes || 0)),
),
commitRuntimeMetaKeyCount: Math.max(
0,
Math.floor(Number(commitDiagnostics?.runtimeMetaKeyCount || 0)),
),
status: "committed",
commitRevision: normalizeIndexedDbRevision(
commitResult?.revision,
requestedRevision,
),
commitDelta: cloneRuntimeDebugValue(commitResult?.delta, null),
totalMs: readPersistDeltaDiagnosticsNow() - persistDeltaStartedAt,
totalMs: normalizePersistDeltaDiagnosticsMs(persistTotalMs),
untrackedMs: normalizePersistDeltaDiagnosticsMs(
Math.max(0, persistTotalMs - persistAccountedMs),
),
};
persistDeltaDiagnostics.fallbackReason =
persistDeltaDiagnostics.requestedNative && !persistDeltaDiagnostics.usedNative

View File

@@ -109,6 +109,26 @@ function normalizeNonNegativeInteger(value, fallback = 0) {
return Math.max(0, Math.floor(parsed));
}
function readPersistCommitNow() {
if (typeof performance === "object" && typeof performance.now === "function") {
return performance.now();
}
return Date.now();
}
function normalizePersistCommitMs(value = 0) {
return Math.round((Number(value) || 0) * 10) / 10;
}
function estimatePersistPayloadBytes(value = null) {
if (value == null) return 0;
try {
return JSON.stringify(value).length;
} catch {
return 0;
}
}
function toPlainData(value, fallbackValue = null) {
if (value == null) {
return fallbackValue;
@@ -2530,6 +2550,7 @@ export class BmeDatabase {
async commitDelta(delta = {}, options = {}) {
const db = await this.open();
const commitRequestedAt = readPersistCommitNow();
const nowMs = Date.now();
const normalizedDelta =
delta && typeof delta === "object" && !Array.isArray(delta) ? delta : {};
@@ -2554,6 +2575,7 @@ export class BmeDatabase {
const reason = String(options.reason || "commitDelta");
const requestedRevision = normalizeRevision(options.requestedRevision);
const shouldMarkSyncDirty = options.markSyncDirty !== false;
const payloadBytes = estimatePersistPayloadBytes(normalizedDelta);
const normalizedCountDelta =
normalizedDelta.countDelta &&
typeof normalizedDelta.countDelta === "object" &&
@@ -2567,7 +2589,9 @@ export class BmeDatabase {
edges: 0,
tombstones: 0,
};
let transactionMs = 0;
const transactionStartedAt = readPersistCommitNow();
await db.transaction(
"rw",
db.table("nodes"),
@@ -2614,6 +2638,7 @@ export class BmeDatabase {
);
},
);
transactionMs = readPersistCommitNow() - transactionStartedAt;
return {
revision: nextRevision,
@@ -2630,6 +2655,17 @@ export class BmeDatabase {
deleteEdgeIds: deleteEdgeIds.length,
tombstones: tombstones.length,
},
diagnostics: {
storageKind: "indexeddb",
storeMode: "indexeddb",
queueWaitMs: 0,
commitMs: normalizePersistCommitMs(
readPersistCommitNow() - commitRequestedAt,
),
txMs: normalizePersistCommitMs(transactionMs),
payloadBytes,
runtimeMetaKeyCount: Object.keys(runtimeMetaPatch).length,
},
};
}

View File

@@ -112,6 +112,26 @@ function normalizeNonNegativeInteger(value, fallback = 0) {
return Math.floor(parsed);
}
function readPersistCommitNow() {
if (typeof performance === "object" && typeof performance.now === "function") {
return performance.now();
}
return Date.now();
}
function normalizePersistCommitMs(value = 0) {
return Math.round((Number(value) || 0) * 10) / 10;
}
function estimatePersistPayloadBytes(value = null) {
if (value == null) return 0;
try {
return JSON.stringify(value).length;
} catch {
return 0;
}
}
function deriveNodeSourceFloor(node = {}) {
const directSourceFloor = normalizeSourceFloor(node?.sourceFloor);
if (directSourceFloor != null) return directSourceFloor;
@@ -970,13 +990,19 @@ class LegacyOpfsGraphStore {
}
async commitDelta(delta = {}, options = {}) {
const commitRequestedAt = readPersistCommitNow();
return await this._runSerializedWrite(
String(options?.reason || "commitDelta"),
async () => {
const commitStartedAt = readPersistCommitNow();
const queueWaitMs = commitStartedAt - commitRequestedAt;
const nowMs = Date.now();
const normalizedDelta =
delta && typeof delta === "object" && !Array.isArray(delta) ? delta : {};
const payloadBytes = estimatePersistPayloadBytes(normalizedDelta);
const snapshotReadStartedAt = readPersistCommitNow();
const currentSnapshot = await this._loadSnapshot({ awaitWrites: false });
const snapshotReadMs = readPersistCommitNow() - snapshotReadStartedAt;
const nodeMap = new Map();
const edgeMap = new Map();
const tombstoneMap = new Map();
@@ -1093,7 +1119,9 @@ class LegacyOpfsGraphStore {
edges: Array.from(edgeMap.values()),
tombstones: Array.from(tombstoneMap.values()),
};
const snapshotWriteStartedAt = readPersistCommitNow();
await this._writeResolvedSnapshot(nextSnapshot);
const snapshotWriteMs = readPersistCommitNow() - snapshotWriteStartedAt;
return {
revision: nextRevision,
@@ -1110,6 +1138,18 @@ class LegacyOpfsGraphStore {
deleteEdgeIds: deleteEdgeIds.length,
tombstones: tombstones.length,
},
diagnostics: {
storageKind: OPFS_STORE_KIND,
storeMode: this.storeMode,
queueWaitMs: normalizePersistCommitMs(queueWaitMs),
commitMs: normalizePersistCommitMs(
readPersistCommitNow() - commitStartedAt,
),
snapshotReadMs: normalizePersistCommitMs(snapshotReadMs),
snapshotWriteMs: normalizePersistCommitMs(snapshotWriteMs),
payloadBytes,
runtimeMetaKeyCount: Object.keys(runtimeMetaPatch).length,
},
};
},
);
@@ -2326,12 +2366,18 @@ export class OpfsGraphStore {
}
async commitDelta(delta = {}, options = {}) {
const commitRequestedAt = readPersistCommitNow();
return await this._runSerializedWrite(
String(options?.reason || "commitDelta"),
async () => {
const commitStartedAt = readPersistCommitNow();
const queueWaitMs = commitStartedAt - commitRequestedAt;
const manifestReadStartedAt = readPersistCommitNow();
const manifest = await this._ensureV2Ready({ awaitWrites: false });
const manifestReadMs = readPersistCommitNow() - manifestReadStartedAt;
const nowMs = Date.now();
const normalizedDelta = sanitizeOpfsV2Delta(delta, nowMs);
const payloadBytes = estimatePersistPayloadBytes(normalizedDelta);
const requestedRevision = normalizeRevision(options.requestedRevision);
const shouldMarkSyncDirty = options.markSyncDirty !== false;
const reason = String(options.reason || "commitDelta");
@@ -2370,10 +2416,12 @@ export class OpfsGraphStore {
runtimeMetaPatch: normalizedDelta.runtimeMetaPatch,
countDelta: nextCountDelta,
};
const walWriteStartedAt = readPersistCommitNow();
const walDirectory = await this._getWalDirectory();
const walFilename = buildOpfsV2WalFilename(nextRevision);
await writeJsonFile(walDirectory, walFilename, walRecord);
const walByteLength = JSON.stringify(walRecord).length;
const walWriteMs = readPersistCommitNow() - walWriteStartedAt;
const hadPendingWal =
normalizeRevision(manifest?.pendingLogFromRevision) <= currentHeadRevision;
@@ -2400,9 +2448,13 @@ export class OpfsGraphStore {
lastReason: reason,
},
};
const manifestWriteStartedAt = readPersistCommitNow();
await this._writeManifest(nextManifest);
const manifestWriteMs = readPersistCommitNow() - manifestWriteStartedAt;
let cacheApplyMs = 0;
if (this._snapshotCache) {
const cacheApplyStartedAt = readPersistCommitNow();
const nextSnapshot = applyOpfsV2DeltaToSnapshot(
this._snapshotCache,
normalizedDelta,
@@ -2414,6 +2466,7 @@ export class OpfsGraphStore {
};
nextSnapshot.state = normalizeSnapshotState(nextSnapshot);
this._snapshotCache = nextSnapshot;
cacheApplyMs = readPersistCommitNow() - cacheApplyStartedAt;
}
this._maybeScheduleCompaction(nextManifest, reason);
@@ -2433,6 +2486,23 @@ export class OpfsGraphStore {
deleteEdgeIds: normalizedDelta.deleteEdgeIds.length,
tombstones: normalizedDelta.tombstones.length,
},
diagnostics: {
storageKind: OPFS_STORE_KIND,
storeMode: this.storeMode,
queueWaitMs: normalizePersistCommitMs(queueWaitMs),
commitMs: normalizePersistCommitMs(
readPersistCommitNow() - commitStartedAt,
),
manifestReadMs: normalizePersistCommitMs(manifestReadMs),
walWriteMs: normalizePersistCommitMs(walWriteMs),
manifestWriteMs: normalizePersistCommitMs(manifestWriteMs),
cacheApplyMs: normalizePersistCommitMs(cacheApplyMs),
payloadBytes,
walBytes: walByteLength,
runtimeMetaKeyCount: Object.keys(
normalizedDelta.runtimeMetaPatch || {},
).length,
},
};
},
);

View File

@@ -123,6 +123,7 @@ function evaluatePersistNativeDeltaGate() {
};
}
function readPersistDeltaDiagnosticsNow() { return Date.now(); }
function normalizePersistDeltaDiagnosticsMs(value = 0) { return Math.round((Number(value) || 0) * 10) / 10; }
function updatePersistDeltaDiagnostics() {}
function buildPersistDelta() {
return {

View File

@@ -1532,13 +1532,64 @@ function _formatPersistencePersistDeltaSummary(persistDelta = null) {
const pathText = String(diagnostics.path || "").trim() || "—";
const totalText = _formatDurationMs(diagnostics.totalMs || diagnostics.buildMs);
const commitText = _formatDurationMs(diagnostics.commitMs);
const gateText = String(_formatPersistDeltaGateText(diagnostics) || "").trim();
const parts = [pathText];
if (totalText !== "—") parts.push(totalText);
if (commitText !== "—") parts.push(`commit ${commitText}`);
if (gateText) parts.push(`native ${gateText}`);
return parts.join(" · ");
}
function _formatPersistCommitPhaseText(diagnostics = null) {
const snapshot = _readPersistenceDiagnosticObject(diagnostics);
if (!snapshot) return "—";
const queueText = _formatDurationMs(snapshot.commitQueueWaitMs);
const commitText = _formatDurationMs(snapshot.commitMs);
if (queueText === "—" && commitText === "—") return "—";
return `${queueText} / ${commitText}`;
}
function _formatPersistCommitBreakdownText(diagnostics = null) {
const snapshot = _readPersistenceDiagnosticObject(diagnostics);
if (!snapshot) return "—";
const parts = [
snapshot.commitTxMs ? `tx ${_formatDurationMs(snapshot.commitTxMs)}` : "",
snapshot.commitSnapshotReadMs
? `snapshot-read ${_formatDurationMs(snapshot.commitSnapshotReadMs)}`
: "",
snapshot.commitSnapshotWriteMs
? `snapshot-write ${_formatDurationMs(snapshot.commitSnapshotWriteMs)}`
: "",
snapshot.commitManifestReadMs
? `manifest-read ${_formatDurationMs(snapshot.commitManifestReadMs)}`
: "",
snapshot.commitWalWriteMs
? `wal ${_formatDurationMs(snapshot.commitWalWriteMs)}`
: "",
snapshot.commitManifestWriteMs
? `manifest-write ${_formatDurationMs(snapshot.commitManifestWriteMs)}`
: "",
snapshot.commitCacheApplyMs
? `cache ${_formatDurationMs(snapshot.commitCacheApplyMs)}`
: "",
].filter(Boolean);
return parts.join(" · ") || "—";
}
function _formatPersistCommitBytesText(diagnostics = null) {
const snapshot = _readPersistenceDiagnosticObject(diagnostics);
if (!snapshot) return "—";
const parts = [];
const payloadText = _formatDataSizeBytes(snapshot.commitPayloadBytes);
const walText = _formatDataSizeBytes(snapshot.commitWalBytes);
const metaKeyCount = Number(snapshot.commitRuntimeMetaKeyCount || 0);
if (payloadText !== "—") parts.push(`payload ${payloadText}`);
if (walText !== "—") parts.push(`wal ${walText}`);
if (metaKeyCount > 0) parts.push(`meta ${metaKeyCount} keys`);
return parts.join(" · ") || "—";
}
function _buildLoadDiagnosticRows(loadDiagnostics = null) {
const diagnostics = _readPersistenceDiagnosticObject(loadDiagnostics);
if (!diagnostics) {
@@ -1561,10 +1612,13 @@ function _buildLoadDiagnosticRows(loadDiagnostics = null) {
["Load 状态", statusText],
["Load 原因", String(diagnostics.reason || "—")],
["Load 总耗时", _formatDurationMs(diagnostics.totalMs)],
["Load 前置", _formatDurationMs(diagnostics.preApplyMs)],
["导出快照", _formatDurationMs(diagnostics.exportSnapshotMs)],
["前置(除导出)", _formatDurationMs(diagnostics.preApplyOtherMs)],
["Hydrate", _formatDurationMs(diagnostics.hydrateMs)],
["Apply 调用", _formatDurationMs(diagnostics.applyInvokeMs)],
["Apply 运行", _formatDurationMs(diagnostics.applyRuntimeMs)],
["Load 未归因", _formatDurationMs(diagnostics.untrackedMs)],
["Load 更新时间", updatedAtText],
];
}
@@ -1586,6 +1640,12 @@ function _buildPersistDeltaDiagnosticRows(persistDelta = null) {
)}E / ${Number(diagnostics.deleteNodeCount || 0)}DN / ${Number(
diagnostics.deleteEdgeCount || 0,
)}DE`;
const commitStoreText = `${String(diagnostics.commitStorageKind || "—")} / ${String(
diagnostics.commitStoreMode || "—",
)}`;
const commitPhaseText = _formatPersistCommitPhaseText(diagnostics);
const commitBreakdownText = _formatPersistCommitBreakdownText(diagnostics);
const commitBytesText = _formatPersistCommitBytesText(diagnostics);
const updatedAtText = diagnostics.updatedAt
? _formatTaskProfileTime(diagnostics.updatedAt)
: "—";
@@ -1594,8 +1654,11 @@ function _buildPersistDeltaDiagnosticRows(persistDelta = null) {
["Persist 路径", String(diagnostics.path || "—")],
["Native Gate", _formatPersistDeltaGateText(diagnostics)],
["Bridge 模式", bridgeText],
["Commit 存储", commitStoreText],
["Persist 总耗时", _formatDurationMs(diagnostics.totalMs || diagnostics.buildMs)],
["构建耗时", _formatDurationMs(diagnostics.buildMs)],
["Base 快照读取", _formatDurationMs(diagnostics.baseSnapshotReadMs)],
["图谱快照构建", _formatDurationMs(diagnostics.snapshotBuildMs)],
[
"Prepare / Native",
`${_formatDurationMs(diagnostics.prepareMs)} / ${_formatDurationMs(diagnostics.nativeAttemptMs)}`,
@@ -1605,11 +1668,15 @@ function _buildPersistDeltaDiagnosticRows(persistDelta = null) {
`${_formatDurationMs(diagnostics.lookupMs)} / ${_formatDurationMs(diagnostics.jsDiffMs)}`,
],
["Hydrate", _formatDurationMs(diagnostics.hydrateMs)],
["Commit 排队 / 提交", commitPhaseText],
["Commit 细分", commitBreakdownText],
["Commit Payload", commitBytesText],
["Preload", String(diagnostics.preloadStatus || "—")],
["Native 来源", String(diagnostics.moduleSource || "—")],
["Fallback 原因", String(diagnostics.fallbackReason || "—")],
["Preload / Native 错误", errorText || "—"],
["增量规模", deltaSizeText],
["Persist 未归因", _formatDurationMs(diagnostics.untrackedMs)],
["Persist 更新时间", updatedAtText],
];
}
@@ -8293,6 +8360,16 @@ function _formatDurationMs(durationMs) {
return `${(normalized / 1000).toFixed(normalized >= 10000 ? 0 : 1)}s`;
}
function _formatDataSizeBytes(byteCount) {
const normalized = Number(byteCount);
if (!Number.isFinite(normalized) || normalized <= 0) return "—";
if (normalized < 1024) return `${Math.round(normalized)} B`;
if (normalized < 1024 * 1024) {
return `${(normalized / 1024).toFixed(normalized >= 10 * 1024 ? 0 : 1)} KB`;
}
return `${(normalized / (1024 * 1024)).toFixed(normalized >= 10 * 1024 * 1024 ? 0 : 1)} MB`;
}
function _getMonitorTaskTypeLabel(taskType = "") {
const normalized = String(taskType || "").trim().toLowerCase();
const labels = {
@@ -8767,6 +8844,12 @@ function _renderPersistDeltaTraceCard(state) {
const payloadCharsText = diagnostics.combinedSerializedChars
? `${Number(diagnostics.combinedSerializedChars || 0)} / ${Number(diagnostics.minCombinedSerializedChars || 0)}`
: "—";
const snapshotBuildText = `${_formatDurationMs(diagnostics.baseSnapshotReadMs)} / ${_formatDurationMs(
diagnostics.snapshotBuildMs,
)}`;
const commitPhaseText = _formatPersistCommitPhaseText(diagnostics);
const commitBreakdownText = _formatPersistCommitBreakdownText(diagnostics);
const commitBytesText = _formatPersistCommitBytesText(diagnostics);
const cacheText = `${Number(diagnostics.serializationCacheHits || 0)}H / ${Number(
diagnostics.serializationCacheMisses || 0,
)}M`;
@@ -8819,6 +8902,10 @@ function _renderPersistDeltaTraceCard(state) {
<span>构建耗时</span>
<strong>${_escHtml(_formatDurationMs(diagnostics.buildMs))}</strong>
</div>
<div class="bme-ai-monitor-kv__row">
<span>Base / Snapshot</span>
<strong>${_escHtml(snapshotBuildText)}</strong>
</div>
<div class="bme-ai-monitor-kv__row">
<span>Prepare / Native</span>
<strong>${_escHtml(
@@ -8837,6 +8924,18 @@ function _renderPersistDeltaTraceCard(state) {
`${_formatDurationMs(diagnostics.hydrateMs)} / ${cacheText}`,
)}</strong>
</div>
<div class="bme-ai-monitor-kv__row">
<span>Commit 排队 / 提交</span>
<strong>${_escHtml(commitPhaseText)}</strong>
</div>
<div class="bme-ai-monitor-kv__row">
<span>Commit 细分</span>
<strong>${_escHtml(commitBreakdownText)}</strong>
</div>
<div class="bme-ai-monitor-kv__row">
<span>Commit Payload</span>
<strong>${_escHtml(commitBytesText)}</strong>
</div>
<div class="bme-ai-monitor-kv__row">
<span>PreparedSet Cache</span>
<strong>${_escHtml(preparedSetCacheText)}</strong>
@@ -8855,6 +8954,10 @@ function _renderPersistDeltaTraceCard(state) {
`${Number(diagnostics.upsertNodeCount || 0)}N / ${Number(diagnostics.upsertEdgeCount || 0)}E / ${Number(diagnostics.deleteNodeCount || 0)}DN / ${Number(diagnostics.deleteEdgeCount || 0)}DE`,
)}</strong>
</div>
<div class="bme-ai-monitor-kv__row">
<span>未归因</span>
<strong>${_escHtml(_formatDurationMs(diagnostics.untrackedMs))}</strong>
</div>
</div>
${_renderMessageTraceTextBlock(
"Fallback reason",