1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00
joplin/packages/server/src/utils/TransactionHandler.ts
Laurent 0765cf5955
All: Add support for sharing notebooks with Joplin Server (#4772)
- Joplin Server: Adds support for sharing a notebook
- Desktop: Adds support for sharing a notebook with Joplin Server
- Mobile: Adds support for reading and writing to a shared notebook (not possible to share a notebook)
- Cli: Adds support for reading and writing to a shared notebook (not possible to share a notebook)
2021-05-13 17:57:37 +01:00

82 lines
2.7 KiB
TypeScript

import { Knex } from 'knex';
import { DbConnection } from '../db';
// This transaction handler allows abstracting away the complexity of managing nested transactions
// within models.
// Any method in a model can start a transaction and, if one is already started, it
// simply won't do anything. The last active transaction commits the results. If a rollback
// happens, the following calls to rollback will be a no-op.
// Set logEnabled_ to `true` to see what happens with nested transactions.
export default class TransactionHandler {
private transactionStack_: number[] = [];
private activeTransaction_: Knex.Transaction = null;
private transactionIndex_: number = 0;
private logEnabled_: boolean = false;
private db_: Knex = null;
public constructor(db: DbConnection) {
this.db_ = db;
}
private get db(): DbConnection {
return this.db_;
}
public setDb(db: DbConnection) {
this.db_ = db;
}
private log(s: string): void {
if (!this.logEnabled_) return;
console.info(`TransactionHandler: ${s}`);
}
public get activeTransaction(): Knex.Transaction {
return this.activeTransaction_;
}
public async start(): Promise<number> {
const txIndex = ++this.transactionIndex_;
this.log(`Starting transaction: ${txIndex}`);
if (!this.transactionStack_.length) {
if (this.activeTransaction_) throw new Error('An active transaction was found when no transaction was in stack'); // Sanity check
this.log(`Trying to acquire transaction: ${txIndex}`);
this.activeTransaction_ = await this.db.transaction();
this.log(`Got transaction: ${txIndex}`);
}
this.transactionStack_.push(txIndex);
return txIndex;
}
private finishTransaction(txIndex: number): boolean {
if (!this.transactionStack_.length) throw new Error('Committing but no transaction was started');
const lastTxIndex = this.transactionStack_.pop();
if (lastTxIndex !== txIndex) throw new Error(`Committing a transaction but was not last to start one: ${txIndex}. Expected: ${lastTxIndex}`);
return !this.transactionStack_.length;
}
public async commit(txIndex: number): Promise<void> {
this.log(`Commit transaction: ${txIndex}`);
const isLastTransaction = this.finishTransaction(txIndex);
if (isLastTransaction) {
this.log(`Is last transaction - doing commit: ${txIndex}`);
await this.activeTransaction_.commit();
this.activeTransaction_ = null;
}
}
public async rollback(txIndex: number): Promise<void> {
this.log(`Rollback transaction: ${txIndex}`);
this.finishTransaction(txIndex);
if (this.activeTransaction_) {
this.log(`Transaction is active - doing rollback: ${txIndex}`);
await this.activeTransaction_.rollback();
this.activeTransaction_ = null;
}
}
}