Skip to content

Commit e578e20

Browse files
authored
Modify stopSync to block if sync is currently active (#889)
- Add a missing changset for #887: `stopSync` now blocks if a current sync is in progress before clearing the interval. An optional timeout can be defined, the default is 2 seconds. After this timeout it will throw. TestHarness has been updated to stop sync before clearing storage, previously this caused an issue where an ongoing sync would attempt to sign messages for DID that no longer had keys after clearing storage. #890 has been created to better address this by creating a signal to gracefully stop sync immediately.
1 parent da3630a commit e578e20

10 files changed

+310
-21
lines changed

.changeset/afraid-geese-knock.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@web5/agent": patch
3+
"@web5/identity-agent": patch
4+
"@web5/proxy-agent": patch
5+
"@web5/user-agent": patch
6+
---
7+
8+
Fix sync race condition issue

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"@changesets/cli": "^2.27.5",
3232
"@npmcli/package-json": "5.0.0",
3333
"@typescript-eslint/eslint-plugin": "7.9.0",
34-
"@web5/dwn-server": "0.4.8",
34+
"@web5/dwn-server": "0.4.9",
3535
"audit-ci": "^7.0.1",
3636
"eslint-plugin-mocha": "10.4.3",
3737
"globals": "^13.24.0",

packages/agent/src/sync-api.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export class AgentSyncApi implements SyncEngine {
5353
return this._syncEngine.startSync(params);
5454
}
5555

56-
public stopSync(): void {
57-
this._syncEngine.stopSync();
56+
public stopSync(timeout?: number): Promise<void> {
57+
return this._syncEngine.stopSync(timeout);
5858
}
5959
}

packages/agent/src/sync-engine-level.ts

+16-1
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,22 @@ export class SyncEngineLevel implements SyncEngine {
315315
}
316316
}
317317

318-
public stopSync(): void {
318+
/**
319+
* stopSync currently awaits the completion of the current sync operation before stopping the sync interval.
320+
* TODO: implement a signal to gracefully stop sync immediately https://github.com/TBD54566975/web5-js/issues/890
321+
*/
322+
public async stopSync(timeout: number = 2000): Promise<void> {
323+
let elapsedTimeout = 0;
324+
325+
while(this._syncLock) {
326+
if (elapsedTimeout >= timeout) {
327+
throw new Error(`SyncEngineLevel: Existing sync operation did not complete within ${timeout} milliseconds.`);
328+
}
329+
330+
elapsedTimeout += 100;
331+
await new Promise((resolve) => setTimeout(resolve, timeout < 100 ? timeout : 100));
332+
}
333+
319334
if (this._syncIntervalId) {
320335
clearInterval(this._syncIntervalId);
321336
this._syncIntervalId = undefined;

packages/agent/src/test-harness.ts

+3
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ export class PlatformAgentTestHarness {
8585
}
8686

8787
public async clearStorage(): Promise<void> {
88+
// first stop any ongoing sync operations
89+
await this.agent.sync.stopSync();
90+
8891
// @ts-expect-error since normally this property shouldn't be set to undefined.
8992
this.agent.agentDid = undefined;
9093
await this.didResolverCache.clear();

packages/agent/src/types/sync.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ export interface SyncEngine {
3838
startSync(params: { interval: string }): Promise<void>;
3939
/**
4040
* Stops the periodic sync operation, will complete the current sync operation if one is already in progress.
41+
*
42+
* @param timeout the maximum amount of time, in milliseconds, to wait for the current sync operation to complete. Default is 2000 (2 seconds).
43+
* @throws {Error} if the sync operation fails to stop before the timeout.
4144
*/
42-
stopSync(): void;
45+
stopSync(timeout?: number): Promise<void>;
4346
}

0 commit comments

Comments
 (0)