Search code examples
javascriptindexeddbdexie

Does Dexie.js transaction with same scope and mode run in parallel?


I am doing something like this, db is Dexie table instance

    var db = new Dexie("myDB");
    db.transaction("rw", ["table1", "table2", "table3"], async ()=>{
      console.log("[txn1] started");
      
      //function which reads something from db
      console.log("[txn1] reading from db");
      await read()
      
      // function which writes something in a nested transaction
      console.log("[txn1] writing to db");
      await write()
      
      console.log("[txn1] finished");
    })

    db.transaction("rw", ["table1", "table2", "table3"], async ()=>{
      console.log("[txn2] started");
      
      //function which reads something from db
      console.log("[txn2] reading from db");
      await read()
      
      // function which writes something in a nested transaction
      console.log("[txn2] writing to db");
      await write()
      
      console.log("[txn2] finished");
    })

I was expecting that since the transactions are in the same scope and same mode, the callback will not execute in parallel, i.e Output should have been

    [txn1] started
    [txn1] reading from db
    [txn1] writing to db
    [txn1] finished
    [txn2] started
    [txn2] reading from db
    [txn2] writing to db
    [txn2] finished

But instead the output is like

    [txn1] started
    [txn1] reading from db
    [txn2] started
    [txn2] reading from db
    [txn1] writing to db
    [txn1] finished
    [txn2] writing to db
    [txn2] finished

Solution

  • The two top-level transaction callbacks will run in parallel until first operation takes place - that's where the native IDB transaction handling comes in. The native transaction doesn't block an operation until first request on it. If you would have three operations in the transaction you would see in practice it doesn't run in parallell as the native transaction handler will block that from happening on two readwrite transactions on same object stores.

    Here's what happens in ever step:

    1. transaction block 1 scheduled to run.

    2. transaction block 2 scheduled to run.

    3. transaction callback 1 starts.

      console output: [txn1] started

      console output: [txn1] reading from db

    4. transaction callback 1 does await read(). At this point the native transaction locks the three tables for read/write.

    5. transaction callback 2 starts.

      console output: [txn2] started

      console output: [txn2] reading from db

    6. transaction callback 2 does await read(). The read operation is blocked.

    7. transaction 1's await read() completes.

      console output: [txn1] writing to db

    8. transaction callback 1 does await write()

      console output: [txn1] finished

    9. transaction 1 is committed.

    10. transaction callback 2's await read() is resumed now.

    11. transaction 2's await read() completes.

      console output: [txn2] writing to db

    12. transaction callback 2 does await write()

      console output: [txn2] finished

    13. transaction 2 is committed.