mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-10-31 13:28:18 +00:00
test(syncguard): protect catch-up handoff while finite pull settles
This commit is contained in:
1 parent
101fe958f5
commit
b552c2a455
2 files changed
+30
-2
No files matched your search
@@ -102,9 +102,14 @@ export function useSyncGuardFeature(host: LiveSyncCore, createOverlay: SyncGuard
|
||||
guard.catchUpSucceeded();
|
||||
guard.catchUpActivityChanged(false, readPendingCount(), Date.now());
|
||||
} else if (guard.presentation.phase !== "idle" && terminalPhaseTimer === undefined) {
|
||||
// The one-shot PouchDB progress observer can report 'idle' before the
|
||||
// enclosing finite CouchDB pull has finished releasing its resources.
|
||||
// This counter is not used to identify the provider; CouchDB phase owns that.
|
||||
if (phase === "idle" && services.replicator.finiteReplicationActivityCount.value > 0) return;
|
||||
// PouchDB disposes its per-attempt progress observer with phase 'idle' just
|
||||
// before Commonlib publishes 'continuous'. Defer the terminal observation by
|
||||
// one event-loop turn so that this internal hand-off never looks like a failure.
|
||||
// before Commonlib publishes 'continuous'. When the enclosing finite
|
||||
// transfer settles, defer the terminal observation by one event-loop
|
||||
// turn so that this internal hand-off never looks like a failure.
|
||||
terminalPhaseTimer = compatGlobal.setTimeout(() => {
|
||||
terminalPhaseTimer = undefined;
|
||||
const current = services.replicator.replicationStatics.value.couchDB?.phase;
|
||||
@@ -119,6 +124,9 @@ export function useSyncGuardFeature(host: LiveSyncCore, createOverlay: SyncGuard
|
||||
else guard.updateThreshold(services.setting.currentSettings().syncGuardMinimumFiles);
|
||||
};
|
||||
const onPendingChanged = () => checkForUnlock();
|
||||
const onFiniteChanged = () => {
|
||||
if (services.replicator.finiteReplicationActivityCount.value === 0) onReplicationChanged();
|
||||
};
|
||||
const removeReflectionFailureHandler = services.context.events.onEvent(EVENT_VAULT_REFLECTION_FAILED, () => {
|
||||
guard.reflectionFailed();
|
||||
});
|
||||
@@ -127,6 +135,7 @@ export function useSyncGuardFeature(host: LiveSyncCore, createOverlay: SyncGuard
|
||||
});
|
||||
|
||||
const replication = services.replicator.replicationStatics;
|
||||
const finite = services.replicator.finiteReplicationActivityCount;
|
||||
const pendingSources = [
|
||||
services.replication.replicationResultCount,
|
||||
services.replication.databaseQueueCount,
|
||||
@@ -139,6 +148,7 @@ export function useSyncGuardFeature(host: LiveSyncCore, createOverlay: SyncGuard
|
||||
chunkFetchCounts,
|
||||
];
|
||||
replication.onChanged(onReplicationChanged);
|
||||
finite.onChanged(onFiniteChanged);
|
||||
for (const source of pendingSources) source.onChanged(onPendingChanged);
|
||||
|
||||
// Register before the ordinary replication scheduling handler so the first finite pull
|
||||
@@ -164,6 +174,7 @@ export function useSyncGuardFeature(host: LiveSyncCore, createOverlay: SyncGuard
|
||||
stopTimer();
|
||||
stopTerminalPhaseTimer();
|
||||
replication.offChanged(onReplicationChanged);
|
||||
finite.offChanged(onFiniteChanged);
|
||||
for (const source of pendingSources) source.offChanged(onPendingChanged);
|
||||
overlay.dispose();
|
||||
removeSettingSavedHandler();
|
||||
|
||||
@@ -159,6 +159,23 @@ describe("useSyncGuardFeature", () => {
|
||||
await h.onUnload[0]();
|
||||
});
|
||||
|
||||
it("waits for the finite pull to settle when progress temporarily reports idle", async () => {
|
||||
vi.useFakeTimers();
|
||||
const h = setup({ minimumFiles: 1 });
|
||||
await h.onResumed[0]();
|
||||
h.setCouchDbPhase("catch-up");
|
||||
h.finiteReplicationActivityCount.value = 1;
|
||||
await h.parseHandlers[0]([receivedNote("first")]);
|
||||
h.setCouchDbPhase("idle");
|
||||
await vi.advanceTimersByTimeAsync(5_000); // Resource disposal may be asynchronous.
|
||||
expect(h.updates[h.updates.length - 1]?.phase).toBe("transferring");
|
||||
h.finiteReplicationActivityCount.value = 0;
|
||||
h.setCouchDbPhase("continuous");
|
||||
await vi.advanceTimersByTimeAsync(1);
|
||||
expect(h.updates[h.updates.length - 1]?.phase).toBe("applying");
|
||||
await h.onUnload[0]();
|
||||
});
|
||||
|
||||
it("ignores large P2P finite transfers when CouchDB catch-up is not running", async () => {
|
||||
const h = setup({ minimumFiles: 2 });
|
||||
await h.onResumed[0]();
|
||||
|
||||
Reference in new issue
Block a user