From 12188b19fd8841f9691b58501175b4cd3e73e53b Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Mon, 11 Dec 2023 11:43:29 +0500 Subject: [PATCH] core: allow only 1 transaction at a time --- packages/core/src/collections/attachments.ts | 7 +- packages/core/src/collections/colors.ts | 7 +- packages/core/src/collections/conflicts.ts | 90 ------------------- packages/core/src/collections/content.ts | 7 +- packages/core/src/collections/note-history.ts | 7 +- packages/core/src/collections/notebooks.ts | 7 +- packages/core/src/collections/notes.ts | 7 +- packages/core/src/collections/relations.ts | 7 +- packages/core/src/collections/reminders.ts | 7 +- .../core/src/collections/session-content.ts | 1 + packages/core/src/collections/settings.ts | 1 + packages/core/src/collections/shortcuts.ts | 1 + packages/core/src/collections/tags.ts | 7 +- .../src/database/sql-cached-collection.ts | 11 ++- packages/core/src/database/sql-collection.ts | 22 ++--- 15 files changed, 79 insertions(+), 110 deletions(-) delete mode 100644 packages/core/src/collections/conflicts.ts diff --git a/packages/core/src/collections/attachments.ts b/packages/core/src/collections/attachments.ts index ea7751af9..51cd29ca9 100644 --- a/packages/core/src/collections/attachments.ts +++ b/packages/core/src/collections/attachments.ts @@ -36,7 +36,12 @@ export class Attachments implements ICollection { key: Cipher<"base64"> | null = null; readonly collection: SQLCollection<"attachments", Attachment>; constructor(private readonly db: Database) { - this.collection = new SQLCollection(db.sql, "attachments", db.eventManager); + this.collection = new SQLCollection( + db.sql, + db.transaction, + "attachments", + db.eventManager + ); this.key = null; EV.subscribe( diff --git a/packages/core/src/collections/colors.ts b/packages/core/src/collections/colors.ts index 2803fadba..2a5e2a39b 100644 --- a/packages/core/src/collections/colors.ts +++ b/packages/core/src/collections/colors.ts @@ -39,7 +39,12 @@ export class Colors implements ICollection { name = "colors"; readonly collection: SQLCollection<"colors", Color>; constructor(private readonly db: Database) { - this.collection = new SQLCollection(db.sql, "colors", db.eventManager); + this.collection = new SQLCollection( + db.sql, + db.transaction, + "colors", + db.eventManager + ); } init() { diff --git a/packages/core/src/collections/conflicts.ts b/packages/core/src/collections/conflicts.ts deleted file mode 100644 index 5b7e8a3af..000000000 --- a/packages/core/src/collections/conflicts.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* -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 . -*/ - -import { getId } from "../utils/id"; -import { Tag } from "../types"; -import Database from "../api"; -import { ICollection } from "./collection"; -import { SQLCollection } from "../database/sql-collection"; - -export class Tags implements ICollection { - name = "tags"; - readonly collection: SQLCollection<"tags", Tag>; - constructor(private readonly db: Database) { - this.collection = new SQLCollection(db.sql, "tags", db.eventManager); - } - - init() { - return this.collection.init(); - } - - tag(id: string) { - return this.collection.get(id); - } - - // find(idOrTitle: string) { - // return this.all.find( - // (tag) => tag.title === idOrTitle || tag.id === idOrTitle - // ); - // } - - async add(item: Partial) { - if (item.remote) - throw new Error("Please use db.tags.merge to merge remote tags."); - - const id = item.id || getId(item.dateCreated); - const oldTag = await this.tag(id); - - item.title = item.title ? Tags.sanitize(item.title) : item.title; - if (!item.title && !oldTag?.title) throw new Error("Title is required."); - - await this.collection.upsert({ - id, - dateCreated: item.dateCreated || oldTag?.dateCreated || Date.now(), - dateModified: item.dateModified || oldTag?.dateModified || Date.now(), - title: item.title || oldTag?.title || "", - type: "tag", - remote: false - }); - return id; - } - - // get raw() { - // return this.collection.raw(); - // } - - // get all() { - // return this.collection.items(); - // } - - async remove(...ids: string[]) { - await this.db.transaction(async () => { - await this.db.relations.unlinkOfType("tag", ids); - await this.collection.softDelete(ids); - }); - } - - exists(id: string) { - return this.collection.exists(id); - } - - static sanitize(title: string) { - return title.replace(/^\s+|\s+$/gm, ""); - } -} diff --git a/packages/core/src/collections/content.ts b/packages/core/src/collections/content.ts index 0e32dee7c..6203f52f6 100644 --- a/packages/core/src/collections/content.ts +++ b/packages/core/src/collections/content.ts @@ -51,7 +51,12 @@ export class Content implements ICollection { name = "content"; readonly collection: SQLCollection<"content", ContentItem>; constructor(private readonly db: Database) { - this.collection = new SQLCollection(db.sql, "content", db.eventManager); + this.collection = new SQLCollection( + db.sql, + db.transaction, + "content", + db.eventManager + ); } async init() { diff --git a/packages/core/src/collections/note-history.ts b/packages/core/src/collections/note-history.ts index d03c16075..006f3c37e 100644 --- a/packages/core/src/collections/note-history.ts +++ b/packages/core/src/collections/note-history.ts @@ -31,7 +31,12 @@ export class NoteHistory implements ICollection { sessionContent = new SessionContent(this.db); readonly collection: SQLCollection<"notehistory", HistorySession>; constructor(private readonly db: Database) { - this.collection = new SQLCollection(db.sql, "notehistory", db.eventManager); + this.collection = new SQLCollection( + db.sql, + db.transaction, + "notehistory", + db.eventManager + ); } async init() { diff --git a/packages/core/src/collections/notebooks.ts b/packages/core/src/collections/notebooks.ts index 61e313540..2f6d2f61b 100644 --- a/packages/core/src/collections/notebooks.ts +++ b/packages/core/src/collections/notebooks.ts @@ -31,7 +31,12 @@ export class Notebooks implements ICollection { */ collection: SQLCollection<"notebooks", TrashOrItem>; constructor(private readonly db: Database) { - this.collection = new SQLCollection(db.sql, "notebooks", db.eventManager); + this.collection = new SQLCollection( + db.sql, + db.transaction, + "notebooks", + db.eventManager + ); } init() { diff --git a/packages/core/src/collections/notes.ts b/packages/core/src/collections/notes.ts index 6e5221993..71a1d851e 100644 --- a/packages/core/src/collections/notes.ts +++ b/packages/core/src/collections/notes.ts @@ -47,7 +47,12 @@ export class Notes implements ICollection { collection: SQLCollection<"notes", TrashOrItem>; totalNotes = 0; constructor(private readonly db: Database) { - this.collection = new SQLCollection(db.sql, "notes", db.eventManager); + this.collection = new SQLCollection( + db.sql, + db.transaction, + "notes", + db.eventManager + ); } async init() { diff --git a/packages/core/src/collections/relations.ts b/packages/core/src/collections/relations.ts index e73794b8d..3de4415da 100644 --- a/packages/core/src/collections/relations.ts +++ b/packages/core/src/collections/relations.ts @@ -30,7 +30,12 @@ export class Relations implements ICollection { name = "relations"; readonly collection: SQLCollection<"relations", Relation>; constructor(private readonly db: Database) { - this.collection = new SQLCollection(db.sql, "relations", db.eventManager); + this.collection = new SQLCollection( + db.sql, + db.transaction, + "relations", + db.eventManager + ); } async init() { diff --git a/packages/core/src/collections/reminders.ts b/packages/core/src/collections/reminders.ts index eb700b9ac..1f7bddab5 100644 --- a/packages/core/src/collections/reminders.ts +++ b/packages/core/src/collections/reminders.ts @@ -39,7 +39,12 @@ export class Reminders implements ICollection { name = "reminders"; readonly collection: SQLCollection<"reminders", Reminder>; constructor(private readonly db: Database) { - this.collection = new SQLCollection(db.sql, "reminders", db.eventManager); + this.collection = new SQLCollection( + db.sql, + db.transaction, + "reminders", + db.eventManager + ); } async init() { diff --git a/packages/core/src/collections/session-content.ts b/packages/core/src/collections/session-content.ts index d32d1138f..af6924578 100644 --- a/packages/core/src/collections/session-content.ts +++ b/packages/core/src/collections/session-content.ts @@ -37,6 +37,7 @@ export class SessionContent implements ICollection { constructor(private readonly db: Database) { this.collection = new SQLCollection( db.sql, + db.transaction, "sessioncontent", db.eventManager ); diff --git a/packages/core/src/collections/settings.ts b/packages/core/src/collections/settings.ts index 1065a3c71..499ae80bc 100644 --- a/packages/core/src/collections/settings.ts +++ b/packages/core/src/collections/settings.ts @@ -69,6 +69,7 @@ export class Settings implements ICollection { constructor(db: Database) { this.collection = new SQLCachedCollection( db.sql, + db.transaction, "settings", db.eventManager ); diff --git a/packages/core/src/collections/shortcuts.ts b/packages/core/src/collections/shortcuts.ts index cd8e13082..750b14d62 100644 --- a/packages/core/src/collections/shortcuts.ts +++ b/packages/core/src/collections/shortcuts.ts @@ -36,6 +36,7 @@ export class Shortcuts implements ICollection { constructor(private readonly db: Database) { this.collection = new SQLCachedCollection( db.sql, + db.transaction, "shortcuts", db.eventManager ); diff --git a/packages/core/src/collections/tags.ts b/packages/core/src/collections/tags.ts index 2b959f907..59a351f9e 100644 --- a/packages/core/src/collections/tags.ts +++ b/packages/core/src/collections/tags.ts @@ -28,7 +28,12 @@ export class Tags implements ICollection { name = "tags"; readonly collection: SQLCollection<"tags", Tag>; constructor(private readonly db: Database) { - this.collection = new SQLCollection(db.sql, "tags", db.eventManager); + this.collection = new SQLCollection( + db.sql, + db.transaction, + "tags", + db.eventManager + ); } init() { diff --git a/packages/core/src/database/sql-cached-collection.ts b/packages/core/src/database/sql-cached-collection.ts index c6e6d58a3..8b3e859cb 100644 --- a/packages/core/src/database/sql-cached-collection.ts +++ b/packages/core/src/database/sql-cached-collection.ts @@ -21,6 +21,7 @@ import { GroupOptions, MaybeDeletedItem, isDeleted } from "../types"; import EventManager from "../utils/event-manager"; import { DatabaseAccessor, DatabaseCollection, DatabaseSchema } from "."; import { SQLCollection } from "./sql-collection"; +import { Transaction } from "kysely"; export class SQLCachedCollection< TCollectionType extends keyof DatabaseSchema, @@ -33,10 +34,18 @@ export class SQLCachedCollection< constructor( sql: DatabaseAccessor, + startTransaction: ( + executor: (tr: Transaction) => void | Promise + ) => Promise, type: TCollectionType, eventManager: EventManager ) { - this.collection = new SQLCollection(sql, type, eventManager); + this.collection = new SQLCollection( + sql, + startTransaction, + type, + eventManager + ); } async init() { diff --git a/packages/core/src/database/sql-collection.ts b/packages/core/src/database/sql-collection.ts index 5ff4d1482..1c188d884 100644 --- a/packages/core/src/database/sql-collection.ts +++ b/packages/core/src/database/sql-collection.ts @@ -39,6 +39,7 @@ import { ExpressionOrFactory, SelectQueryBuilder, SqlBool, + Transaction, sql } from "kysely"; import { VirtualizedGrouping } from "../utils/virtualized-grouping"; @@ -52,6 +53,9 @@ export class SQLCollection< { constructor( private readonly db: DatabaseAccessor, + private readonly startTransaction: ( + executor: (tr: Transaction) => void | Promise + ) => Promise, private readonly type: TCollectionType, private readonly eventManager: EventManager ) {} @@ -151,16 +155,14 @@ export class SQLCollection< if (entries.length <= 0) return; - await this.db() - .transaction() - .execute(async (tx) => { - for (const chunk of toChunks(entries, 200)) { - await tx - .replaceInto(this.type) - .values(chunk) - .execute(); - } - }); + await this.startTransaction(async (tx) => { + for (const chunk of toChunks(entries, 200)) { + await tx + .replaceInto(this.type) + .values(chunk) + .execute(); + } + }); } async update(ids: string[], partial: Partial>) {