简体   繁体   中英

How to use database transactions in JavaScript with async functions?

Consider the code below that gets some data from an external service and inserts it into some table in a database. It starts the transactions and commits it when all the data is inserted or rolls it back if an error occurres:

const mysql = require('mysql2/promise');

class SomeClass
{
    async connect(params)
    {
        this.db = await mysql.createConnection(params);
    }

    async insertSomeRowsIntoSomeTable()
    {
        await this.db.execute('START TRANSACTION;');

        try {
            for (let i = 0; i < 100; i++) {
                //get the values to insert from an external service, for example.
                await const values = service.getValues();
                await this.db.execute('INSERT SomeTable .... <the values>...');
            }
            await this.db.execute('COMMIT;');
        } 
        catch (e) {
            await this.db.execute('ROLLBACK;');
        }
    }
}

but what if there is another async method updateSomething that updates a row in some table with a single update query and the client calls it while insertSomeRowsIntoSomeTable method is still running? Obviously, the updated data will be lost if the an error occurres in insertSomeRowsIntoSomeTable .

If there is another method that initiates its own transaction, for example, we unexpectedly get the nested transaction.

The only idea I have at the moment is to make all the operations with the database atomic by moving all the transaction logic to the stored procedures, for example, but it look like a workaround but not a solution.

Having a separate database connection in each async method is not an option, for the obvious reasons.

Does anyone have a better idea?

Probably the easiest solution is to use pool:

const mysql = require('mysql2/promise');

class SomeClass
{
    async connect(params)
    {
        this.db = mysql.createPool(params);
    }

    async insertSomeRowsIntoSomeTable()
    {
        const connection = await this.db.getConnection();
        await connection.execute('START TRANSACTION;');

        try {
            for (let i = 0; i < 100; i++) {
                //get the values to insert from an external service, for example.
                const values = await service.getValues();
                await conn.execute('INSERT SomeTable .... <the values>...');
            }
            await connection.execute('COMMIT');
            await connection.release();
        } 
        catch (e) {
            await connection.execute('ROLLBACK');
            await connection.release();
        }
    }
}

Here you borrow connection from the pool in your insertSomeRowsIntoSomeTable() and if other async functions follow the same pattern this connection will be exclusively used by one transaction code path only until released

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM