Best way to handle transactions efficiently using Promises
P粉226667290
P粉226667290 2024-03-30 20:44:23
0
1
508

I'm creating a utility class for NodeJs to help me manage database transactions.

My idea is to create a method like this:

transactionBlock(params) {
    let _db;
    return mySqlConnector.getConnection(params.db)
        .then(db => {
            _db = db;
            console.log(`beginTransaction`);
            return db.beginTransaction()
        })
        .then(() => {
            return Promise.resolve(_db);
        })
        .catch((e) => {
            console.log(`roolback`);
            _db.rollback();
        })
        .finally(() => {
            console.log(`commit`);
            _db.commit();
        })
}

and use it like this:

const params = {db:"my_db"};
transactionBlock(params)
    .then(db =>{
        console.log(`do query 1`);
        db.insert(....)
        console.log(`do query 2`);
        db.insert(....)
        console.log(`do query 3`);
        db.insert(....)
    })

I expected it to work, but apparently the result is this:

beginTransaction
commit
do query 1
do query 2
do query 3

Do you think it's possible to create a function transactionBlock that returns a promise where the user can execute a query and finally, if all queries are ok, the function transactionBlock does the commit?

I use this: npmjs.com/package/promise-mysql

Thanks goodbye

P粉226667290
P粉226667290

reply all(1)
P粉512363233

The big problem with your current approach is that finally() always runs regardless of whether the promise chain resolves or rejects, so you definitely don't want to commit a transaction there.

I only see one option here...requires a callback function representing the transaction body.

In general, I also recommend using the async / wait syntax for better readability.

const transactionBlock = async (connectionName, txBody) => {
  const db = await mySqlConnector.getConnection(connectionName);
  await db.beingTransaction();
  try {
    await txBody(db);
    console.log("commit");
    return db.commit(); // all good, commit
  } catch (err) {
    console.error("rollback", err);
    await db.rollback();
    // keep the rejection chain going by re-throwing the error or a new one
    throw err; // or perhaps `new Error("DB error: " + err.message)`
  }
};

Call like this

try {
  await transactionBlock("my_db", async (db) => {
    console.log(`do query 1`);
    await db.insert(/* ... */);
    console.log(`do query 2`);
    await db.insert(/* ... */);
    console.log(`do query 3`);
    await db.insert(/* ... */);
  });
} catch (err) {
  console.error("oh no, something went wrong", err);
}

If you use Typescript, the following interfaces and types will ensure smooth operation

type TxBody = (db: Connection) => Promise;
type TransactionBlock = (
  connectionName: string,
  txBody: TxBody
) => Promise;

const transactionBlock: TransactionBlock = async (connectionName, txBody) => {
  // ...
};
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template