Add diff cache size setting
This commit is contained in:
parent
c92aa63d71
commit
0bfd8a6df4
5 changed files with 75 additions and 19 deletions
|
|
@ -9,6 +9,7 @@ export interface SyncSettings {
|
||||||
maxFileSizeMB: number;
|
maxFileSizeMB: number;
|
||||||
ignorePatterns: string[];
|
ignorePatterns: string[];
|
||||||
webSocketRetryIntervalMs: number;
|
webSocketRetryIntervalMs: number;
|
||||||
|
diffCacheSizeMB: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_SETTINGS: SyncSettings = {
|
export const DEFAULT_SETTINGS: SyncSettings = {
|
||||||
|
|
@ -19,7 +20,8 @@ export const DEFAULT_SETTINGS: SyncSettings = {
|
||||||
isSyncEnabled: false,
|
isSyncEnabled: false,
|
||||||
maxFileSizeMB: 10,
|
maxFileSizeMB: 10,
|
||||||
ignorePatterns: [],
|
ignorePatterns: [],
|
||||||
webSocketRetryIntervalMs: 3500
|
webSocketRetryIntervalMs: 3500,
|
||||||
|
diffCacheSizeMB: 4
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Settings {
|
export class Settings {
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,8 @@ export class SyncClient {
|
||||||
private readonly _logger: Logger,
|
private readonly _logger: Logger,
|
||||||
private readonly connectionStatus: ConnectionStatus,
|
private readonly connectionStatus: ConnectionStatus,
|
||||||
private readonly cursorTracker: CursorTracker,
|
private readonly cursorTracker: CursorTracker,
|
||||||
private readonly fileChangeNotifier: FileChangeNotifier
|
private readonly fileChangeNotifier: FileChangeNotifier,
|
||||||
|
private readonly contentCache: FixedSizeDocumentCache
|
||||||
) {
|
) {
|
||||||
this.settings.addOnSettingsChangeListener(
|
this.settings.addOnSettingsChangeListener(
|
||||||
async (newSettings, oldSettings) => {
|
async (newSettings, oldSettings) => {
|
||||||
|
|
@ -53,6 +54,14 @@ export class SyncClient {
|
||||||
this.stop();
|
this.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
newSettings.diffCacheSizeMB !== oldSettings.diffCacheSizeMB
|
||||||
|
) {
|
||||||
|
this.contentCache.resize(
|
||||||
|
newSettings.diffCacheSizeMB * 1024 * 1024
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -65,6 +74,10 @@ export class SyncClient {
|
||||||
return this.database.length;
|
return this.database.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get isWebSocketConnected(): boolean {
|
||||||
|
return this.webSocketManager.isWebSocketConnected;
|
||||||
|
}
|
||||||
|
|
||||||
public static async create({
|
public static async create({
|
||||||
fs,
|
fs,
|
||||||
persistence,
|
persistence,
|
||||||
|
|
@ -152,8 +165,7 @@ export class SyncClient {
|
||||||
settings,
|
settings,
|
||||||
syncService,
|
syncService,
|
||||||
fileOperations,
|
fileOperations,
|
||||||
unrestrictedSyncer,
|
unrestrictedSyncer
|
||||||
contentCache
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const webSocketManager = new WebSocketManager(
|
const webSocketManager = new WebSocketManager(
|
||||||
|
|
@ -182,7 +194,8 @@ export class SyncClient {
|
||||||
logger,
|
logger,
|
||||||
connectionStatus,
|
connectionStatus,
|
||||||
cursorTracker,
|
cursorTracker,
|
||||||
fileChangeNotifier
|
fileChangeNotifier,
|
||||||
|
contentCache
|
||||||
);
|
);
|
||||||
|
|
||||||
logger.info("SyncClient initialised");
|
logger.info("SyncClient initialised");
|
||||||
|
|
@ -235,6 +248,7 @@ export class SyncClient {
|
||||||
public async reset(): Promise<void> {
|
public async reset(): Promise<void> {
|
||||||
this.stop();
|
this.stop();
|
||||||
this.connectionStatus.startReset();
|
this.connectionStatus.startReset();
|
||||||
|
this.contentCache.clear();
|
||||||
await this.syncer.reset();
|
await this.syncer.reset();
|
||||||
this.history.reset();
|
this.history.reset();
|
||||||
this.database.reset();
|
this.database.reset();
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,7 @@ export class Syncer {
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
private readonly syncService: SyncService,
|
private readonly syncService: SyncService,
|
||||||
private readonly operations: FileOperations,
|
private readonly operations: FileOperations,
|
||||||
private readonly internalSyncer: UnrestrictedSyncer,
|
private readonly internalSyncer: UnrestrictedSyncer
|
||||||
private readonly contentCache: FixedSizeDocumentCache
|
|
||||||
) {
|
) {
|
||||||
this.syncQueue = new PQueue({
|
this.syncQueue = new PQueue({
|
||||||
concurrency: settings.getSettings().syncConcurrency
|
concurrency: settings.getSettings().syncConcurrency
|
||||||
|
|
@ -252,7 +251,6 @@ export class Syncer {
|
||||||
|
|
||||||
public async reset(): Promise<void> {
|
public async reset(): Promise<void> {
|
||||||
await this.waitUntilFinished();
|
await this.waitUntilFinished();
|
||||||
this.contentCache.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async syncRemotelyUpdatedFile(
|
public async syncRemotelyUpdatedFile(
|
||||||
|
|
|
||||||
|
|
@ -236,4 +236,40 @@ describe("fixedSizeDocumentCache", () => {
|
||||||
assert.equal(cache.get(2), doc2);
|
assert.equal(cache.get(2), doc2);
|
||||||
assert.equal(cache.get(3), doc3);
|
assert.equal(cache.get(3), doc3);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("resizeToLargerSizeNoEviction", async () => {
|
||||||
|
const cache = new FixedSizeDocumentCache(4);
|
||||||
|
const doc1 = new Uint8Array([1, 2]);
|
||||||
|
const doc2 = new Uint8Array([3, 4]);
|
||||||
|
|
||||||
|
cache.put(1, doc1);
|
||||||
|
cache.put(2, doc2);
|
||||||
|
|
||||||
|
cache.resize(10);
|
||||||
|
|
||||||
|
assert.equal(cache.get(1), doc1);
|
||||||
|
assert.equal(cache.get(2), doc2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resizeCausesMultipleEvictions", async () => {
|
||||||
|
const cache = new FixedSizeDocumentCache(10);
|
||||||
|
const doc1 = new Uint8Array([1, 2]);
|
||||||
|
const doc2 = new Uint8Array([3, 4]);
|
||||||
|
const doc3 = new Uint8Array([5, 6]);
|
||||||
|
const doc4 = new Uint8Array([7, 8]);
|
||||||
|
|
||||||
|
cache.put(1, doc1);
|
||||||
|
cache.put(2, doc2);
|
||||||
|
cache.put(3, doc3);
|
||||||
|
cache.put(4, doc4);
|
||||||
|
// Cache has 8 bytes total
|
||||||
|
|
||||||
|
cache.resize(2);
|
||||||
|
|
||||||
|
// Should evict doc1, doc2, doc3 to get down to 2 bytes
|
||||||
|
assert.equal(cache.get(1), undefined);
|
||||||
|
assert.equal(cache.get(2), undefined);
|
||||||
|
assert.equal(cache.get(3), undefined);
|
||||||
|
assert.equal(cache.get(4), doc4);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,12 @@ class LRUNode {
|
||||||
|
|
||||||
// evicting the least recently used documents when the size limit is exceeded.
|
// evicting the least recently used documents when the size limit is exceeded.
|
||||||
export class FixedSizeDocumentCache {
|
export class FixedSizeDocumentCache {
|
||||||
private readonly maxSizeInBytes: number;
|
|
||||||
private currentSizeInBytes: number;
|
private currentSizeInBytes: number;
|
||||||
private readonly cache: Map<VaultUpdateId, LRUNode>;
|
private readonly cache: Map<VaultUpdateId, LRUNode>;
|
||||||
private head: LRUNode | null; // Least recently used
|
private head: LRUNode | null; // Least recently used
|
||||||
private tail: LRUNode | null; // Most recently used
|
private tail: LRUNode | null; // Most recently used
|
||||||
|
|
||||||
public constructor(maxSizeInBytes: number) {
|
public constructor(private maxSizeInBytes: number) {
|
||||||
this.maxSizeInBytes = maxSizeInBytes;
|
|
||||||
this.currentSizeInBytes = 0;
|
this.currentSizeInBytes = 0;
|
||||||
this.cache = new Map();
|
this.cache = new Map();
|
||||||
this.head = null;
|
this.head = null;
|
||||||
|
|
@ -56,14 +54,7 @@ export class FixedSizeDocumentCache {
|
||||||
this.cache.set(updateId, newNode);
|
this.cache.set(updateId, newNode);
|
||||||
this.addToTail(newNode);
|
this.addToTail(newNode);
|
||||||
this.currentSizeInBytes += content.byteLength;
|
this.currentSizeInBytes += content.byteLength;
|
||||||
|
this.fitBelowMaxSize();
|
||||||
// Evict least recently used documents if over size limit
|
|
||||||
while (this.currentSizeInBytes > this.maxSizeInBytes && this.head) {
|
|
||||||
const lruNode = this.head;
|
|
||||||
this.removeNode(lruNode);
|
|
||||||
this.cache.delete(lruNode.key);
|
|
||||||
this.currentSizeInBytes -= lruNode.value.byteLength;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public clear(): void {
|
public clear(): void {
|
||||||
|
|
@ -73,6 +64,21 @@ export class FixedSizeDocumentCache {
|
||||||
this.currentSizeInBytes = 0;
|
this.currentSizeInBytes = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public resize(newMaxSizeInBytes: number): void {
|
||||||
|
this.maxSizeInBytes = newMaxSizeInBytes;
|
||||||
|
this.fitBelowMaxSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private fitBelowMaxSize(): void {
|
||||||
|
// Evict least recently used documents if over size limit
|
||||||
|
while (this.currentSizeInBytes > this.maxSizeInBytes && this.head) {
|
||||||
|
const lruNode = this.head;
|
||||||
|
this.removeNode(lruNode);
|
||||||
|
this.cache.delete(lruNode.key);
|
||||||
|
this.currentSizeInBytes -= lruNode.value.byteLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private removeNode(node: LRUNode): void {
|
private removeNode(node: LRUNode): void {
|
||||||
if (node.prev) {
|
if (node.prev) {
|
||||||
node.prev.next = node.next;
|
node.prev.next = node.next;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue