question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Concurrency and Transaction

See original GitHub issue
import { Table } from "../Table";
import { SqliteConnection } from "../connections/SqliteConnection";
import { BetterSqlite3QueryRunner } from "../queryRunners/BetterSqlite3QueryRunner";
import * as betterSqlite3 from 'better-sqlite3'

class DBConection extends SqliteConnection<'DBConnection'> {
}

const tCompany = new class TCompany extends Table<DBConection, 'TCompany'> {
    id = this.autogeneratedPrimaryKey('id', 'int');
    name = this.column('name', 'string');
    constructor() {
        super('company'); // table name in the database
    }
}()

const db = betterSqlite3(':memory:')
const global_conn = new DBConection(new BetterSqlite3QueryRunner(db))

koaApp.use(async (ctx, next) => {
  // await global_conn.beginTransaction(); // I can not write code like this, it may cause concurrency bug. I must write like:
  const scope_conn = new DBConection(new BetterSqlite3QueryRunner(db));
  scope_conn.beginTransaction();
  xxxx;
});

Why not:

const global_conn = new DBConection(new BetterSqlite3QueryRunner(db))

koaApp.use(async (ctx, next) => {
  const t = await global_conn.beginTransaction(); // t is of interface DBConection
  t.selectFrom();
  t.commit();
  t.rollback();
});

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:10 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
juanluispazcommented, Mar 7, 2021

Let me explain my synchronous-promise proposal for use with better-sqlite3

I think the best approach for you regarding better-sqlite3 is to have the possibility to use synchronous-promise; I’m going to add this feature for you. This will allows you to use all the advantages offered by ts-sql-query.

The approach of have a executeOnDB function adapted for synchronous calls will work; all the code executed inside the executeOnDB will be executed synchronously even when you program it async. The important part is you cannot use the async/await syntax. You will need to use a complementary sync function that transforms the promises in sync code. For example, your code can look like this:

app.get('/def', async ctx => {
  const result = executeOnDB(conn => {
    const id1 = sync(conn
        .insertInto(tCompany)
        .values({ name: Math.random().toString(36) })
        .executeInsert());
    const id2 = sync(conn
        .insertInto(tCompany)
        .values({ name: Math.random().toString(36) })
        .executeInsert());
    return { id1, id2 }
  });
  // write the request output
});

Or even simpler if you perform only one operation (the sync call will be made automatically):

app.get('/def', async ctx => {
  const result = executeOnDB(conn =>
    conn
        .insertInto(tCompany)
        .values({ name: Math.random().toString(36) })
        .executeInsert();
  );
  // write the request output
});

The required code can be something like this:

import * as betterSqlite3 from 'better-sqlite3'
import { SynchronousPromise } from "synchronous-promise";

const db = betterSqlite3(':memory:')

/**
 * This function unwrap the syncronous promise in a syncronous way returning the result.
 */
function sync<T>(promise: Promise<T>): T {
    let returned = false;
    let result: any;
    let error: any;
    promise.then(r => {
        returned = true;
        result = r;
    }, e => {
        returned = true;
        error = e;
    })

    if (!returned) {
        throw new Error('You performed an real async operation, not a database operation, inside the function dedicated to call the database');
    }
    if (error) {
        throw error;
    }
    return result;
}

/**
 * This function expects the internal function return a syncronous promise or a result
 * This function unwrap the syncronos promise in a syncronos way returning the result.
 */
function executeOnDB<T>(fn: (conn: DBConnection) => Promise<T>): T
function executeOnDB<T>(fn: (conn: DBConnection) => T): T
function executeOnDB<T>(fn: (conn: DBConnection) => any): T {
    const conn = new DBConection(new BetterSqlite3QueryRunner(db, { promise: SynchronousPromise }));
    sync(conn.beginTransaction());
    let result;
    try {
        const fnResult = fn(conn);
        if (fn instanceof SynchronousPromise) {
            result = sync(fnResult);
        } else if (fnResult instanceof Promise) {
            throw new Error('You performed an real async operation, not a database operation, inside the function dedicated to call the databse');
        } else {
            result = fnResult;
        }
    } catch (e) {
        try {
            sync(conn.rollback());
        } catch {
            // Ignore internal error in favour of the external one
        }
        throw e;
    }
    sync(conn.commit());
    return result;
}

Let me know if the synchronous-promise will work for you.

0reactions
juanluispazcommented, Mar 9, 2021

Synchronous queries implemented in ts-sql-query 1.1.0. See the Synchronous query runners section in the documentation and the example.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Concurrency Control in DBMS - GeeksforGeeks
In general, concurrency means, that more than one transaction can work on a system. The advantages of a concurrent system are: Waiting Time:...
Read more >
Transaction and Concurrency Control - W3schools
It is the method of managing concurrent operations on the database without getting any obstruction with one another. The Need for Concurrency Control....
Read more >
Transactions and Concurrency - ADO.NET - Microsoft Learn
A transaction consists of a single command or a group of commands that execute as a package. Transactions allow you to combine multiple ......
Read more >
Transaction Management - Concurrency — CSCI 4380 ...
To study concurrency, we will abstract how we view transactions. · The only operations that matter in a transaction are the data items...
Read more >
DBMS Concurrency Control - Javatpoint
The problem occurs when two different database transactions perform the read/write operations on the same database items in an interleaved manner (i.e., ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found