From 4b916dff471c6693bb118c24eb37c37b7ca3ca73 Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Sat, 10 Feb 2024 10:59:27 +0500 Subject: [PATCH] core: add support for nested transactions --- packages/core/src/api/index.ts | 39 +++++++++++++++----------- packages/core/src/utils/queue-value.ts | 35 +++++++++++++++++++++++ 2 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 packages/core/src/utils/queue-value.ts diff --git a/packages/core/src/api/index.ts b/packages/core/src/api/index.ts index 5df80bfb6..de5cdc909 100644 --- a/packages/core/src/api/index.ts +++ b/packages/core/src/api/index.ts @@ -71,6 +71,7 @@ import { Kysely, Transaction, sql } from "kysely"; import { CachedCollection } from "../database/cached-collection"; import { Vaults } from "../collections/vaults"; import { KVStorage } from "../database/kv"; +import { QueueValue } from "../utils/queue-value"; type EventSourceConstructor = new ( uri: string, @@ -126,7 +127,7 @@ class Database { private _sql?: Kysely; sql: DatabaseAccessor = () => { - if (this._transaction) return this._transaction; + if (this._transaction) return this._transaction.value; if (!this._sql) throw new Error( @@ -138,23 +139,27 @@ class Database { private _kv?: KVStorage; kv: KVStorageAccessor = () => this._kv || new KVStorage(this.sql); - private _transaction?: Transaction; - private transactionMutex = new Mutex(); - transaction = ( - executor: (tr: Transaction) => void | Promise + private _transaction?: QueueValue>; + transaction = async ( + executor: (tr: Transaction) => Promise ) => { - return this.transactionMutex.runExclusive(() => - this.sql() - .transaction() - .execute(async (tr) => { - this._transaction = tr; - await executor(tr); - this._transaction = undefined; - }) - .finally(() => { - this._transaction = undefined; - }) - ); + if (this._transaction) { + await executor(this._transaction.use()).finally(() => + this._transaction?.discard() + ); + return; + } + + return this.sql() + .transaction() + .execute(async (tr) => { + this._transaction = new QueueValue( + tr, + () => (this._transaction = undefined) + ); + await executor(this._transaction.use()); + }) + .finally(() => this._transaction?.discard()); }; options!: Options; diff --git a/packages/core/src/utils/queue-value.ts b/packages/core/src/utils/queue-value.ts new file mode 100644 index 000000000..cd6658f95 --- /dev/null +++ b/packages/core/src/utils/queue-value.ts @@ -0,0 +1,35 @@ +/* +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 . +*/ + +export class QueueValue { + #counter: number; + constructor(readonly value: T, private readonly destructor: () => void) { + this.#counter = 0; + } + + use() { + this.#counter++; + return this.value; + } + + discard() { + this.#counter--; + if (this.#counter === 0) this.destructor(); + } +}