fix: IndexedDB probe 失败后不再永久卡在 loading,重试耗尽后回退到 blocked

- index.js: 新增 reconcileIndexedDbProbeFailureState,后台 probe 失败时先有限重试,耗尽后切到 blocked
- index.js: scheduleIndexedDbGraphProbe 的 .then/.catch 均接入 reconcile 逻辑
- index.js: createGraphLoadUiStatus blocked 文案更新
- ui/panel.js: _getGraphLoadLabel blocked 文案更新,不再误导为元数据未就绪
- tests/graph-persistence.mjs: 新增 manager-unavailable / read-failed 回归
- tests/graph-persistence.mjs: harness 支持 __indexedDbExportSnapshotShouldThrow / __indexedDbGetCurrentDbShouldThrow
This commit is contained in:
Youzini-afk
2026-04-12 16:49:48 +08:00
parent f192a7d23a
commit b31088cc35
3 changed files with 152 additions and 4 deletions

View File

@@ -912,6 +912,9 @@ async function createGraphPersistenceHarness({
_createDb(dbChatId = "") {
return {
async exportSnapshot() {
if (runtimeContext.__indexedDbExportSnapshotShouldThrow) {
throw new Error("indexeddb-export-failed");
}
return getIndexedDbSnapshotForChat(dbChatId);
},
async commitDelta(delta, options = {}) {
@@ -993,6 +996,9 @@ async function createGraphPersistenceHarness({
runtimeContext.__indexedDbSnapshot = getIndexedDbSnapshotForChat(
this._currentChatId,
);
if (runtimeContext.__indexedDbGetCurrentDbShouldThrow) {
throw new Error("indexeddb-get-current-db-failed");
}
return this._createDb(this._currentChatId);
}
async switchChat(dbChatId = "") {
@@ -1224,7 +1230,6 @@ result = {
harness.api.setChatContext({
chatId: "chat-late",
chatMetadata: {
integrity: "chat-late-ready",
st_bme_graph: lateGraph,
},
characterId: "char-late",
@@ -1258,7 +1263,7 @@ result = {
assert.equal(result.loadState, "loading");
assert.equal(
harness.api.getCurrentGraph().historyState.chatId,
"chat-late-ready",
"chat-late",
);
assert.equal(harness.api.getGraphPersistenceState().dbReady, true);
assert.equal(
@@ -2096,6 +2101,62 @@ result = {
);
}
{
const harness = await createGraphPersistenceHarness({
chatId: "chat-manager-unavailable-fallback",
globalChatId: "chat-manager-unavailable-fallback",
chatMetadata: {
integrity: "meta-manager-unavailable-fallback",
},
});
harness.runtimeContext.BmeChatManager = null;
const result = harness.api.loadGraphFromChat({
attemptIndex: harness.api.GRAPH_LOAD_RETRY_DELAYS_MS.length,
source: "manager-unavailable-fallback",
});
await new Promise((resolve) => setTimeout(resolve, 0));
assert.equal(result.loadState, "loading");
assert.equal(
harness.api.getGraphPersistenceState().loadState,
"blocked",
"IndexedDB manager 不可用时,重试耗尽后不应永久停留在 loading",
);
assert.equal(
harness.api.getGraphPersistenceState().reason,
"indexeddb-manager-unavailable",
);
}
{
const harness = await createGraphPersistenceHarness({
chatId: "chat-indexeddb-read-failed-fallback",
globalChatId: "chat-indexeddb-read-failed-fallback",
chatMetadata: {
integrity: "meta-indexeddb-read-failed-fallback",
},
});
harness.runtimeContext.__indexedDbExportSnapshotShouldThrow = true;
const result = harness.api.loadGraphFromChat({
attemptIndex: harness.api.GRAPH_LOAD_RETRY_DELAYS_MS.length,
source: "indexeddb-read-failed-fallback",
});
await new Promise((resolve) => setTimeout(resolve, 0));
assert.equal(result.loadState, "loading");
assert.equal(
harness.api.getGraphPersistenceState().loadState,
"blocked",
"IndexedDB 读取失败时,重试耗尽后不应永久停留在 loading",
);
assert.equal(
harness.api.getGraphPersistenceState().reason,
"indexeddb-read-failed",
);
}
{
const harness = await createGraphPersistenceHarness({
chatId: "chat-create-first-graph",