Improve network usage for small text changes (#166)

This commit is contained in:
Andras Schmelczer 2025-11-16 22:10:22 +00:00 committed by GitHub
parent 1da17c462e
commit be1635c26e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 697 additions and 62 deletions

View file

@ -17,6 +17,7 @@ import { createPromise } from "../utils/create-promise";
import { SyncResetError } from "../services/sync-reset-error";
import { Locks } from "../utils/locks";
import type { DocumentVersionWithoutContent } from "../services/types/DocumentVersionWithoutContent";
import type { FixedSizeDocumentCache } from "../utils/fix-sized-cache";
export class Syncer {
private readonly remoteDocumentsLock: Locks<DocumentId>;
@ -33,7 +34,8 @@ export class Syncer {
settings: Settings,
private readonly syncService: SyncService,
private readonly operations: FileOperations,
private readonly internalSyncer: UnrestrictedSyncer
private readonly internalSyncer: UnrestrictedSyncer,
private readonly contentCache: FixedSizeDocumentCache
) {
this.syncQueue = new PQueue({
concurrency: settings.getSettings().syncConcurrency
@ -250,6 +252,7 @@ export class Syncer {
public async reset(): Promise<void> {
await this.waitUntilFinished();
this.contentCache.clear();
}
public async syncRemotelyUpdatedFile(

View file

@ -4,6 +4,7 @@ import type {
RelativePath
} from "../persistence/database";
import { diff } from "reconcile-text";
import type { SyncService } from "../services/sync-service";
import type { Logger } from "../tracing/logger";
import type {
@ -27,6 +28,9 @@ import { globsToRegexes } from "../utils/globs-to-regexes";
import type { DocumentVersion } from "../services/types/DocumentVersion";
import type { DocumentUpdateResponse } from "../services/types/DocumentUpdateResponse";
import type { DocumentVersionWithoutContent } from "../services/types/DocumentVersionWithoutContent";
import type { FixedSizeDocumentCache } from "../utils/fix-sized-cache";
import { isFileTypeMergable } from "../utils/is-file-type-mergable";
import { isBinary } from "../utils/is-binary";
export class UnrestrictedSyncer {
private ignorePatterns: RegExp[];
@ -37,7 +41,8 @@ export class UnrestrictedSyncer {
private readonly settings: Settings,
private readonly syncService: SyncService,
private readonly operations: FileOperations,
private readonly history: SyncHistory
private readonly history: SyncHistory,
private readonly contentCache: FixedSizeDocumentCache
) {
this.ignorePatterns = globsToRegexes(
this.settings.getSettings().ignorePatterns,
@ -87,8 +92,12 @@ export class UnrestrictedSyncer {
},
document
);
this.database.addSeenUpdateId(response.vaultUpdateId);
this.updateCache(
response.vaultUpdateId,
contentBytes,
response.relativePath
);
this.history.addHistoryEntry({
status: SyncStatus.SUCCESS,
@ -178,12 +187,32 @@ export class UnrestrictedSyncer {
undefined;
if (areThereLocalChanges) {
response = await this.syncService.put({
documentId: document.documentId,
parentVersionId: document.metadata.parentVersionId,
relativePath: document.relativePath,
contentBytes
});
const isText =
!isBinary(contentBytes) &&
isFileTypeMergable(document.relativePath);
const cachedVersion = this.contentCache.get(
document.metadata.parentVersionId
);
response =
isText && cachedVersion !== undefined
? await this.syncService.putText({
documentId: document.documentId,
parentVersionId:
document.metadata.parentVersionId,
relativePath: document.relativePath,
content: diff(
new TextDecoder().decode(cachedVersion),
new TextDecoder().decode(contentBytes)
)
})
: await this.syncService.putBinary({
documentId: document.documentId,
parentVersionId:
document.metadata.parentVersionId,
relativePath: document.relativePath,
contentBytes
});
} else {
if (!force) {
this.logger.debug(
@ -274,12 +303,16 @@ export class UnrestrictedSyncer {
},
document
);
await this.operations.write(
actualPath,
contentBytes,
responseBytes
);
this.updateCache(
response.vaultUpdateId,
responseBytes,
actualPath
);
if (!force) {
this.history.addHistoryEntry({
@ -297,6 +330,11 @@ export class UnrestrictedSyncer {
},
document
);
this.updateCache(
response.vaultUpdateId,
contentBytes,
actualPath
);
}
this.database.addSeenUpdateId(response.vaultUpdateId);
@ -423,6 +461,11 @@ export class UnrestrictedSyncer {
remoteVersion.relativePath,
contentBytes
);
this.updateCache(
remoteVersion.vaultUpdateId,
contentBytes,
remoteVersion.relativePath
);
resolve();
this.database.removeDocumentPromise(promise);
@ -513,4 +556,14 @@ export class UnrestrictedSyncer {
};
}
}
private updateCache(
updateId: number,
contentBytes: Uint8Array,
filePath: RelativePath
): void {
if (isFileTypeMergable(filePath) && !isBinary(contentBytes)) {
this.contentCache.put(updateId, contentBytes);
}
}
}