core: lazily use db in config & kv stores

This commit is contained in:
Abdullah Atta
2024-11-23 13:28:49 +05:00
parent 4afaa6a36e
commit af3fba5176
5 changed files with 101 additions and 45 deletions

View File

@@ -79,6 +79,7 @@ import { Sanitizer } from "../database/sanitizer.js";
import { createTriggers, dropTriggers } from "../database/triggers.js";
import { NNMigrationProvider } from "../database/migrations.js";
import { ConfigStorage } from "../database/config.js";
import { LazyPromise } from "../utils/lazy-promise.js";
type EventSourceConstructor = new (
uri: string,
@@ -101,6 +102,9 @@ class Database {
sseMutex = new Mutex();
_fs?: FileStorage;
_compressor?: Promise<ICompressor>;
private databaseReady = new LazyPromise<
Kysely<DatabaseSchema> | Transaction<DatabaseSchema>
>();
storage: StorageAccessor = () => {
if (!this.options?.storage)
@@ -148,11 +152,12 @@ class Database {
return this._sql;
};
private _kv?: KVStorage;
kv: KVStorageAccessor = () => this._kv || new KVStorage(this.sql);
private _config?: ConfigStorage;
config: ConfigStorageAccessor = () =>
this._config || new ConfigStorage(this.sql);
private _kv = new KVStorage(this.databaseReady.promise);
kv: KVStorageAccessor = () => this._kv;
private _config: ConfigStorage = new ConfigStorage(
this.databaseReady.promise
);
config: ConfigStorageAccessor = () => this._config;
private _transaction?: QueueValue<Transaction<DatabaseSchema>>;
transaction = async (
@@ -291,6 +296,7 @@ class Database {
migrationProvider: new NNMigrationProvider(),
onInit: (db) => this.onInit(db)
})) as unknown as Kysely<DatabaseSchema>;
this.databaseReady.resolve(this._sql);
await this.sanitizer.init();

View File

@@ -17,41 +17,47 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { DatabaseAccessor, RawDatabaseSchema } from "./index.js";
import { LazyDatabaseAccessor, RawDatabaseSchema } from "./index.js";
export class ConfigStorage {
private readonly db: DatabaseAccessor<RawDatabaseSchema>;
constructor(db: DatabaseAccessor) {
this.db = db as unknown as DatabaseAccessor<RawDatabaseSchema>;
private readonly db: LazyDatabaseAccessor<RawDatabaseSchema>;
constructor(db: LazyDatabaseAccessor) {
this.db = db as unknown as LazyDatabaseAccessor<RawDatabaseSchema>;
}
async getItem(name: string): Promise<unknown | undefined> {
const result = await this.db()
.selectFrom("config")
.where("name", "==", name)
.select("value")
.limit(1)
.executeTakeFirst();
const result = await this.db.then((db) =>
db
.selectFrom("config")
.where("name", "==", name)
.select("value")
.limit(1)
.executeTakeFirst()
);
if (!result?.value) return;
return JSON.parse(result.value);
}
async setItem(name: string, value: unknown) {
await this.db()
.replaceInto("config")
.values({
name,
value: JSON.stringify(value),
dateModified: Date.now()
})
.execute();
await this.db.then((db) =>
db
.replaceInto("config")
.values({
name,
value: JSON.stringify(value),
dateModified: Date.now()
})
.execute()
);
}
async removeItem(name: string) {
await this.db().deleteFrom("config").where("name", "==", name).execute();
await this.db.then((db) =>
db.deleteFrom("config").where("name", "==", name).execute()
);
}
async clear() {
await this.db().deleteFrom("config").execute();
await this.db.then((db) => db.deleteFrom("config").execute());
}
}

View File

@@ -198,6 +198,10 @@ export type DatabaseAccessor<TSchema = DatabaseSchema> = () =>
| Kysely<TSchema>
| Transaction<TSchema>;
export type LazyDatabaseAccessor<TSchema = DatabaseSchema> = Promise<
Kysely<TSchema> | Transaction<TSchema>
>;
type FilterBooleanProperties<T, Type> = keyof {
[K in keyof T as T[K] extends Type ? K : never]: T[K];
};

View File

@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { DatabaseAccessor, RawDatabaseSchema } from "./index.js";
import { LazyDatabaseAccessor, RawDatabaseSchema } from "./index.js";
import { Token } from "../api/token-manager.js";
import { User } from "../types.js";
@@ -44,38 +44,44 @@ export const KEYS: (keyof KV)[] = [
];
export class KVStorage {
private readonly db: DatabaseAccessor<RawDatabaseSchema>;
constructor(db: DatabaseAccessor) {
this.db = db as unknown as DatabaseAccessor<RawDatabaseSchema>;
private readonly db: LazyDatabaseAccessor<RawDatabaseSchema>;
constructor(db: LazyDatabaseAccessor) {
this.db = db as unknown as LazyDatabaseAccessor<RawDatabaseSchema>;
}
async read<T extends keyof KV>(key: T): Promise<KV[T] | undefined> {
const result = await this.db()
.selectFrom("kv")
.where("key", "==", key)
.select("value")
.limit(1)
.executeTakeFirst();
const result = await this.db.then((db) =>
db
.selectFrom("kv")
.where("key", "==", key)
.select("value")
.limit(1)
.executeTakeFirst()
);
if (!result?.value) return;
return JSON.parse(result.value) as KV[T];
}
async write<T extends keyof KV>(key: T, value: KV[T]) {
await this.db()
.replaceInto("kv")
.values({
key,
value: JSON.stringify(value),
dateModified: Date.now()
})
.execute();
await this.db.then((db) =>
db
.replaceInto("kv")
.values({
key,
value: JSON.stringify(value),
dateModified: Date.now()
})
.execute()
);
}
async delete<T extends keyof KV>(key: T) {
await this.db().deleteFrom("kv").where("key", "==", key).execute();
await this.db.then((db) =>
db.deleteFrom("kv").where("key", "==", key).execute()
);
}
async clear() {
await this.db().deleteFrom("kv").execute();
await this.db.then((db) => db.deleteFrom("kv").execute());
}
}

View File

@@ -0,0 +1,34 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export class LazyPromise<T> {
private _promise: Promise<T>;
private _resolve?: (result: T) => void;
constructor() {
this._promise = new Promise((resolve) => (this._resolve = resolve));
}
resolve(result: T) {
this._resolve?.(result);
}
get promise() {
return this._promise;
}
}