core: allow only 1 transaction at a time

This commit is contained in:
Abdullah Atta
2023-12-11 11:43:29 +05:00
parent 270e7a5542
commit 12188b19fd
15 changed files with 79 additions and 110 deletions

View File

@@ -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(

View File

@@ -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() {

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<Tag>) {
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, "");
}
}

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -31,7 +31,12 @@ export class Notebooks implements ICollection {
*/
collection: SQLCollection<"notebooks", TrashOrItem<Notebook>>;
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() {

View File

@@ -47,7 +47,12 @@ export class Notes implements ICollection {
collection: SQLCollection<"notes", TrashOrItem<Note>>;
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() {

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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
);

View File

@@ -69,6 +69,7 @@ export class Settings implements ICollection {
constructor(db: Database) {
this.collection = new SQLCachedCollection(
db.sql,
db.transaction,
"settings",
db.eventManager
);

View File

@@ -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
);

View File

@@ -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() {

View File

@@ -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<DatabaseSchema>) => void | Promise<void>
) => Promise<void>,
type: TCollectionType,
eventManager: EventManager
) {
this.collection = new SQLCollection(sql, type, eventManager);
this.collection = new SQLCollection(
sql,
startTransaction,
type,
eventManager
);
}
async init() {

View File

@@ -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<DatabaseSchema>) => void | Promise<void>
) => Promise<void>,
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<keyof DatabaseSchema>(this.type)
.values(chunk)
.execute();
}
});
await this.startTransaction(async (tx) => {
for (const chunk of toChunks(entries, 200)) {
await tx
.replaceInto<keyof DatabaseSchema>(this.type)
.values(chunk)
.execute();
}
});
}
async update(ids: string[], partial: Partial<SQLiteItem<T>>) {