rxdb
Version:
A local-first realtime NoSQL Database for JavaScript applications - https://rxdb.info/
98 lines (85 loc) • 3.21 kB
text/typescript
import type { LokiDatabaseSettings } from '../../types/index.d.ts';
import {
PROMISE_RESOLVE_VOID,
requestIdlePromise
} from '../utils/index.ts';
/**
* The autosave feature of lokijs has strange behaviors
* and often runs a save in critical moments when other
* more important tasks are running.
* So instead we use a custom save queue that ensures we
* only run loki.saveDatabase() when nothing else is running.
*/
export class LokiSaveQueue {
public writesSinceLastRun: number = 0;
/**
* Ensures that we do not run multiple saves
* in parallel
*/
public saveQueue: Promise<void> = PROMISE_RESOLVE_VOID;
// track amount of non-finished save calls in the queue.
public saveQueueC = 0;
constructor(
public readonly lokiDatabase: any,
public readonly databaseSettings: LokiDatabaseSettings
) {
}
public addWrite() {
this.writesSinceLastRun = this.writesSinceLastRun + 1;
this.run();
}
public run() {
if (
// no persistence adapter given, so we do not need to save
!this.databaseSettings.adapter ||
// do not add more then two pending calls to the queue.
this.saveQueueC > 2
) {
return this.saveQueue;
}
this.saveQueueC = this.saveQueueC + 1;
this.saveQueue = this.saveQueue
.then(async () => {
/**
* Always wait until the JavaScript process is idle.
* This ensures that CPU blocking writes are finished
* before we proceed.
*/
await requestIdlePromise();
// no write happened since the last save call
if (this.writesSinceLastRun === 0) {
return;
}
/**
* Because LokiJS is a in-memory database,
* we can just wait until the JavaScript process is idle
* via requestIdlePromise(). Then we know that nothing important
* is running at the moment.
*/
await requestIdlePromise().then(() => requestIdlePromise());
if (this.writesSinceLastRun === 0) {
return;
}
const writeAmount = this.writesSinceLastRun;
this.writesSinceLastRun = 0;
return new Promise<void>((res, rej) => {
this.lokiDatabase.saveDatabase((err: any) => {
if (err) {
this.writesSinceLastRun = this.writesSinceLastRun + writeAmount;
rej(err);
} else {
if (this.databaseSettings.autosaveCallback) {
this.databaseSettings.autosaveCallback();
}
res();
}
});
});
})
.catch(() => { })
.then(() => {
this.saveQueueC = this.saveQueueC - 1;
});
return this.saveQueue;
}
}