Avoid duplication from initial sync
This commit is contained in:
parent
8723c8499b
commit
6906bc4f5e
2 changed files with 97 additions and 18 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
import type { Logger } from "../tracing/logger";
|
import type { Logger } from "../tracing/logger";
|
||||||
|
import { EMPTY_HASH } from "../utils/hash";
|
||||||
|
|
||||||
export type VaultUpdateId = number;
|
export type VaultUpdateId = number;
|
||||||
export type DocumentId = string;
|
export type DocumentId = string;
|
||||||
|
|
@ -19,6 +20,7 @@ export interface StoredDocumentMetadata {
|
||||||
export interface StoredDatabase {
|
export interface StoredDatabase {
|
||||||
documents: StoredDocumentMetadata[];
|
documents: StoredDocumentMetadata[];
|
||||||
lastSeenUpdateId: VaultUpdateId | undefined;
|
lastSeenUpdateId: VaultUpdateId | undefined;
|
||||||
|
hasInitialSyncCompleted: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -39,6 +41,7 @@ export interface DocumentRecord {
|
||||||
export class Database {
|
export class Database {
|
||||||
private documents: DocumentRecord[];
|
private documents: DocumentRecord[];
|
||||||
private lastSeenUpdateId: VaultUpdateId | undefined;
|
private lastSeenUpdateId: VaultUpdateId | undefined;
|
||||||
|
private hasInitialSyncCompleted: boolean;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
|
|
@ -66,6 +69,12 @@ export class Database {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Loaded last seen update id: ${this.lastSeenUpdateId}`
|
`Loaded last seen update id: ${this.lastSeenUpdateId}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.hasInitialSyncCompleted =
|
||||||
|
initialState.hasInitialSyncCompleted ?? false;
|
||||||
|
this.logger.debug(
|
||||||
|
`Loaded hasInitialSyncCompleted: ${this.hasInitialSyncCompleted}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get length(): number {
|
public get length(): number {
|
||||||
|
|
@ -105,21 +114,6 @@ export class Database {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getLastSeenUpdateId(): VaultUpdateId | undefined {
|
|
||||||
return this.lastSeenUpdateId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setLastSeenUpdateId(value: VaultUpdateId | undefined): void {
|
|
||||||
this.lastSeenUpdateId = value;
|
|
||||||
this.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public reset(): void {
|
|
||||||
this.documents = [];
|
|
||||||
this.lastSeenUpdateId = 0;
|
|
||||||
this.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public updateDocumentMetadata(
|
public updateDocumentMetadata(
|
||||||
metadata: {
|
metadata: {
|
||||||
parentVersionId: VaultUpdateId;
|
parentVersionId: VaultUpdateId;
|
||||||
|
|
@ -215,6 +209,29 @@ export class Database {
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public createNewEmptyDocument(
|
||||||
|
documentId: DocumentId,
|
||||||
|
parentVersionId: VaultUpdateId,
|
||||||
|
relativePath: RelativePath
|
||||||
|
): DocumentRecord {
|
||||||
|
const entry = {
|
||||||
|
relativePath,
|
||||||
|
documentId,
|
||||||
|
metadata: {
|
||||||
|
parentVersionId,
|
||||||
|
hash: EMPTY_HASH
|
||||||
|
},
|
||||||
|
isDeleted: false,
|
||||||
|
updates: [],
|
||||||
|
parallelVersion: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
this.documents.push(entry);
|
||||||
|
this.save();
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
public getDocumentByDocumentId(
|
public getDocumentByDocumentId(
|
||||||
find: DocumentId
|
find: DocumentId
|
||||||
): DocumentRecord | undefined {
|
): DocumentRecord | undefined {
|
||||||
|
|
@ -260,6 +277,31 @@ export class Database {
|
||||||
candidate.isDeleted = true;
|
candidate.isDeleted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getHasInitialSyncCompleted(): boolean {
|
||||||
|
return this.hasInitialSyncCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setHasInitialSyncCompleted(value: boolean): void {
|
||||||
|
this.hasInitialSyncCompleted = value;
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLastSeenUpdateId(): VaultUpdateId | undefined {
|
||||||
|
return this.lastSeenUpdateId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setLastSeenUpdateId(value: VaultUpdateId | undefined): void {
|
||||||
|
this.lastSeenUpdateId = value;
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
this.documents = [];
|
||||||
|
this.lastSeenUpdateId = 0;
|
||||||
|
this.hasInitialSyncCompleted = false;
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
|
||||||
private save(): void {
|
private save(): void {
|
||||||
this.ensureConsistency();
|
this.ensureConsistency();
|
||||||
void this.saveData({
|
void this.saveData({
|
||||||
|
|
@ -268,10 +310,11 @@ export class Database {
|
||||||
documentId,
|
documentId,
|
||||||
relativePath,
|
relativePath,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
...metadata! // resolvedDocuments only returns docs with metadata set
|
...metadata! // `resolvedDocuments` only returns docs with metadata set
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
lastSeenUpdateId: this.lastSeenUpdateId
|
lastSeenUpdateId: this.lastSeenUpdateId,
|
||||||
|
hasInitialSyncCompleted: this.hasInitialSyncCompleted
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export class Syncer {
|
||||||
concurrency: settings.getSettings().syncConcurrency
|
concurrency: settings.getSettings().syncConcurrency
|
||||||
});
|
});
|
||||||
|
|
||||||
settings.addOnSettingsChangeHandlers((newSettings, oldSettings) => {
|
settings.addOnSettingsChangeListener((newSettings, oldSettings) => {
|
||||||
if (newSettings.syncConcurrency === oldSettings.syncConcurrency) {
|
if (newSettings.syncConcurrency === oldSettings.syncConcurrency) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -310,6 +310,8 @@ export class Syncer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async internalScheduleSyncForOfflineChanges(): Promise<void> {
|
private async internalScheduleSyncForOfflineChanges(): Promise<void> {
|
||||||
|
await this.createFakeDocumentsFromRemoteState();
|
||||||
|
|
||||||
const allLocalFiles = await this.operations.listAllFiles();
|
const allLocalFiles = await this.operations.listAllFiles();
|
||||||
|
|
||||||
let locallyPossiblyDeletedFiles = [
|
let locallyPossiblyDeletedFiles = [
|
||||||
|
|
@ -387,4 +389,38 @@ export class Syncer {
|
||||||
|
|
||||||
await Promise.all([updates, deletes]);
|
await Promise.all([updates, deletes]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create fake documents in the database for all files that are present locally
|
||||||
|
* and also exist remotely. This will stop the subequent syncs from duplicating
|
||||||
|
* the documents by creating the same documents from multiple clients.
|
||||||
|
*/
|
||||||
|
private async createFakeDocumentsFromRemoteState(): Promise<void> {
|
||||||
|
if (this.database.getHasInitialSyncCompleted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [allLocalFiles, remote] = await Promise.all([
|
||||||
|
this.operations.listAllFiles(),
|
||||||
|
this.syncQueue.add(async () => this.syncService.getAll())
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (remote !== undefined) {
|
||||||
|
remote.latestDocuments
|
||||||
|
.filter(
|
||||||
|
(remoteDocument) =>
|
||||||
|
allLocalFiles.includes(remoteDocument.relativePath) &&
|
||||||
|
!remoteDocument.isDeleted
|
||||||
|
)
|
||||||
|
.forEach((remoteDocument) => {
|
||||||
|
this.database.createNewEmptyDocument(
|
||||||
|
remoteDocument.documentId,
|
||||||
|
remoteDocument.vaultUpdateId,
|
||||||
|
remoteDocument.relativePath
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.database.setHasInitialSyncCompleted(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue