This commit is contained in:
Andras Schmelczer 2025-03-28 22:19:24 +00:00
commit 226b4f1db9
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C

View file

@ -1,4 +1,8 @@
import type { Database, RelativePath } from "../persistence/database"; import type {
Database,
DocumentId,
RelativePath
} from "../persistence/database";
import type { SyncService } from "../services/sync-service"; import type { SyncService } from "../services/sync-service";
import type { Logger } from "../tracing/logger"; import type { Logger } from "../tracing/logger";
import PQueue from "p-queue"; import PQueue from "p-queue";
@ -11,8 +15,10 @@ import { findMatchingFile } from "../utils/find-matching-file";
import type { UnrestrictedSyncer } from "./unrestricted-syncer"; import type { UnrestrictedSyncer } from "./unrestricted-syncer";
import { createPromise } from "../utils/create-promise"; import { createPromise } from "../utils/create-promise";
import { SyncResetError } from "../services/sync-reset-error"; import { SyncResetError } from "../services/sync-reset-error";
import { Locks } from "../utils/locks";
export class Syncer { export class Syncer {
private readonly remoteDocumentsLock: Locks<DocumentId>;
private readonly remainingOperationsListeners: (( private readonly remainingOperationsListeners: ((
remainingOperations: number remainingOperations: number
) => void)[] = []; ) => void)[] = [];
@ -38,6 +44,8 @@ export class Syncer {
this.updateWebSocket(settings.getSettings()); this.updateWebSocket(settings.getSettings());
this.remoteDocumentsLock = new Locks<DocumentId>(this.logger);
settings.addOnSettingsChangeListener((newSettings, oldSettings) => { settings.addOnSettingsChangeListener((newSettings, oldSettings) => {
if ( if (
newSettings.remoteUri !== oldSettings.remoteUri || newSettings.remoteUri !== oldSettings.remoteUri ||
@ -64,7 +72,6 @@ export class Syncer {
public async reset(): Promise<void> { public async reset(): Promise<void> {
await this.waitUntilFinished(); await this.waitUntilFinished();
this.internalSyncer.reset();
this.setWebSocketRefreshInterval(); this.setWebSocketRefreshInterval();
this.updateWebSocket(this.settings.getSettings()); this.updateWebSocket(this.settings.getSettings());
} }
@ -255,6 +262,7 @@ export class Syncer {
typeof globalThis.WebSocket === "undefined" typeof globalThis.WebSocket === "undefined"
) { ) {
// polyfill for WebSocket in Node.js // polyfill for WebSocket in Node.js
// eslint-disable-next-line
globalThis.WebSocket = require("ws"); globalThis.WebSocket = require("ws");
} }
@ -300,8 +308,19 @@ export class Syncer {
remoteVersion.documentId remoteVersion.documentId
); );
const [promise, resolve, reject] = createPromise(); let hasLockToRelease = false;
if (document === undefined) {
// Let's avoid the same documents getting created in parallel multiple times
await this.remoteDocumentsLock.waitForLock(
remoteVersion.documentId
);
hasLockToRelease = true;
document = this.database.getDocumentByDocumentId(
remoteVersion.documentId
);
}
try {
if (document === undefined) { if (document === undefined) {
await this.syncQueue.add(async () => await this.syncQueue.add(async () =>
this.internalSyncer.unrestrictedSyncRemotelyUpdatedFile( this.internalSyncer.unrestrictedSyncRemotelyUpdatedFile(
@ -309,7 +328,10 @@ export class Syncer {
) )
); );
} else { } else {
document = await this.database.getResolvedDocumentByRelativePath( const [promise, resolve, reject] = createPromise();
document =
await this.database.getResolvedDocumentByRelativePath(
document.relativePath, document.relativePath,
promise promise
); );
@ -329,6 +351,11 @@ export class Syncer {
this.database.removeDocumentPromise(promise); this.database.removeDocumentPromise(promise);
} }
} }
} finally {
if (hasLockToRelease) {
this.remoteDocumentsLock.unlock(remoteVersion.documentId);
}
}
} }
private async internalScheduleSyncForOfflineChanges(): Promise<void> { private async internalScheduleSyncForOfflineChanges(): Promise<void> {