Extract library from plugin
This commit is contained in:
parent
8374c971ee
commit
ae3acb9e1e
37 changed files with 61 additions and 77 deletions
|
|
@ -0,0 +1,61 @@
|
|||
import type { Database } from "../database/database";
|
||||
import type { SyncService } from "src/services/sync-service";
|
||||
import { Logger } from "src/tracing/logger";
|
||||
import type { Syncer } from "./syncer";
|
||||
|
||||
let isRunning = false;
|
||||
|
||||
export async function applyRemoteChangesLocally({
|
||||
database,
|
||||
syncService,
|
||||
syncer
|
||||
}: {
|
||||
database: Database;
|
||||
syncService: SyncService;
|
||||
syncer: Syncer;
|
||||
}): Promise<void> {
|
||||
if (!database.getSettings().isSyncEnabled) {
|
||||
Logger.getInstance().debug(
|
||||
`Syncing is disabled, not fetching remote changes`
|
||||
);
|
||||
return;
|
||||
} else if (isRunning) {
|
||||
Logger.getInstance().debug(
|
||||
"Applying remote changes locally is already in progress, skipping invocation"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
isRunning = true;
|
||||
|
||||
try {
|
||||
const remote = await syncService.getAll(database.getLastSeenUpdateId());
|
||||
|
||||
if (remote.latestDocuments.length === 0) {
|
||||
Logger.getInstance().debug("No remote changes to apply");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.getInstance().info("Applying remote changes locally");
|
||||
|
||||
await Promise.all(
|
||||
remote.latestDocuments.map(async (remoteDocument) =>
|
||||
syncer.syncRemotelyUpdatedFile(remoteDocument)
|
||||
)
|
||||
);
|
||||
|
||||
const lastSeenUpdateId = database.getLastSeenUpdateId();
|
||||
if (
|
||||
lastSeenUpdateId === undefined ||
|
||||
remote.lastUpdateId > lastSeenUpdateId
|
||||
) {
|
||||
await database.setLastSeenUpdateId(remote.lastUpdateId);
|
||||
}
|
||||
} catch (e) {
|
||||
Logger.getInstance().error(
|
||||
`Failed to apply remote changes locally: ${e}`
|
||||
);
|
||||
} finally {
|
||||
isRunning = false;
|
||||
}
|
||||
}
|
||||
79
sync-client/src/sync-operations/document-lock.test.ts
Normal file
79
sync-client/src/sync-operations/document-lock.test.ts
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
import {
|
||||
tryLockDocument,
|
||||
waitForDocumentLock,
|
||||
unlockDocument
|
||||
} from "./document-lock";
|
||||
import type { RelativePath } from "src/database/document-metadata";
|
||||
|
||||
describe("Document Lock Operations", () => {
|
||||
const testPath: RelativePath = "test/document/path";
|
||||
|
||||
beforeEach(() => {
|
||||
// Reset the state before each test
|
||||
(global as any).locked = new Set<RelativePath>();
|
||||
(global as any).waiters = new Map<RelativePath, (() => void)[]>();
|
||||
});
|
||||
|
||||
test("should lock a document successfully", () => {
|
||||
const result = tryLockDocument(testPath);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("should not lock a document that is already locked", () => {
|
||||
tryLockDocument(testPath);
|
||||
const result = tryLockDocument(testPath);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test("should unlock a locked document", () => {
|
||||
tryLockDocument(testPath);
|
||||
unlockDocument(testPath);
|
||||
const result = tryLockDocument(testPath);
|
||||
expect(result).toBe(true);
|
||||
unlockDocument(testPath);
|
||||
});
|
||||
|
||||
test("should throw an error when unlocking a document that is not locked", () => {
|
||||
expect(() => {
|
||||
unlockDocument(testPath);
|
||||
}).toThrow(`Document ${testPath} is not locked, cannot unlock`);
|
||||
});
|
||||
|
||||
test("should wait for a document lock and resolve when unlocked", async () => {
|
||||
tryLockDocument(testPath);
|
||||
|
||||
let resolved = false;
|
||||
const waitPromise = waitForDocumentLock(testPath).then(() => {
|
||||
resolved = true;
|
||||
});
|
||||
|
||||
unlockDocument(testPath);
|
||||
await waitPromise;
|
||||
|
||||
expect(resolved).toBe(true);
|
||||
});
|
||||
|
||||
test("should resolve multiple waiters in FIFO order", async () => {
|
||||
tryLockDocument(testPath);
|
||||
|
||||
let firstResolved = false;
|
||||
let secondResolved = false;
|
||||
|
||||
const firstWaitPromise = waitForDocumentLock(testPath).then(() => {
|
||||
firstResolved = true;
|
||||
});
|
||||
|
||||
const secondWaitPromise = waitForDocumentLock(testPath).then(() => {
|
||||
secondResolved = true;
|
||||
});
|
||||
|
||||
unlockDocument(testPath);
|
||||
await firstWaitPromise;
|
||||
expect(firstResolved).toBe(true);
|
||||
expect(secondResolved).toBe(false);
|
||||
|
||||
unlockDocument(testPath);
|
||||
await secondWaitPromise;
|
||||
expect(secondResolved).toBe(true);
|
||||
});
|
||||
});
|
||||
48
sync-client/src/sync-operations/document-lock.ts
Normal file
48
sync-client/src/sync-operations/document-lock.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import type { RelativePath } from "src/database/document-metadata";
|
||||
|
||||
const locked = new Set<RelativePath>();
|
||||
const waiters = new Map<RelativePath, (() => void)[]>();
|
||||
|
||||
export function tryLockDocument(relativePath: RelativePath): boolean {
|
||||
if (locked.has(relativePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
locked.add(relativePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function waitForDocumentLock(
|
||||
relativePath: RelativePath
|
||||
): Promise<void> {
|
||||
if (tryLockDocument(relativePath)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
let waiting = waiters.get(relativePath);
|
||||
if (!waiting) {
|
||||
waiting = [];
|
||||
waiters.set(relativePath, waiting);
|
||||
}
|
||||
|
||||
waiting.push(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
export function unlockDocument(relativePath: RelativePath): void {
|
||||
if (!locked.has(relativePath)) {
|
||||
throw new Error(
|
||||
`Document ${relativePath} is not locked, cannot unlock`
|
||||
);
|
||||
}
|
||||
|
||||
// Remove the first element to ensure FIFO unblocking order
|
||||
const nextWaiting = waiters.get(relativePath)?.shift();
|
||||
|
||||
if (nextWaiting) {
|
||||
nextWaiting();
|
||||
} else {
|
||||
locked.delete(relativePath);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue