Transaction doesn't support Promise.all

This works:
await db.transaction(async (tx) => {
await tx.insert...
await tx.insert...
});
await db.transaction(async (tx) => {
await tx.insert...
await tx.insert...
});
This does not:
await db.transaction(async (tx) => {
const p0 = tx.insert...
const p1 = tx.insert...
await Promise.all([
p0,
p1,
]);
});
await db.transaction(async (tx) => {
const p0 = tx.insert...
const p1 = tx.insert...
await Promise.all([
p0,
p1,
]);
});
44 Replies
Angelelz
Angelelz10mo ago
There is no benefit in doing it in a promise.all. You can't insert values at the same time, that's the reason you have you use a transaction, because database operations are sequencial
shikishikichangchang
The two statements are for two tables. Is there a reason why they can't be executd in parallel? Yes they're part of one transaction, but I'd imageine the statemnets get executed in parallel, and if no errors occured, the transaction is completed. Else, rollback.
Angelelz
Angelelz10mo ago
Have you tried this?
await db.transaction(async (tx) => {
const p0 = tx.insert...
const p1 = tx.insert...
await Promise.all([
await p0,
await p1,
]);
});
await db.transaction(async (tx) => {
const p0 = tx.insert...
const p1 = tx.insert...
await Promise.all([
await p0,
await p1,
]);
});
Now, the limitation is in the database itself. A transaction is designed so that the operations performed are sequential, because if you're using a transaction each operation is supposed to depend on the operation before it If you don't have that requirement, then you don't need a transaction In your case, it looks like you'd want to do it in a transaction anyway, just in case one of the inserts fails, the other will fail as well
predragnikolic
predragnikolic10mo ago
Not necessarily Drizzle related, but rather how JavaScript Promise.all behaves, for more info see this thread(from TypeOrm) https://github.com/typeorm/typeorm/issues/1014#issuecomment-336397434
GitHub
Transaction doesn't rollback · Issue #1014 · typeorm/typeorm
Version: 0.0.11 The "Database entity is not set for the given subject" error does not rollback transactions // this will remove row with id 1 but not id 2 repository.transaction(async rep...
sakura
sakura10mo ago
Having this same issue, are you using mysql/planetscale? This worked fine for me with postgres
shikishikichangchang
I don't see how that's helpful/different from
await db.transaction(async (tx) => {
await tx.insert...
await tx.insert...
});
await db.transaction(async (tx) => {
await tx.insert...
await tx.insert...
});
?
Angelelz
Angelelz10mo ago
Because all the query builder implement a lazy promise
Luxaritas
Luxaritas10mo ago
The promise.all is doing nothing in your case though Angelelz What you'd be passing are the resolved promise values
shikishikichangchang
"because if you're using a transaction each operation is supposed to depend on the operation before it" This is simply not true. Imagine you have a user account table and a profile info table and when ther user deletes their account you wanna delete both. The operations are independent but you definitely want them both in the same transaction Likle Luxaritas said, the promise.all doesn't do anything in your code
Angelelz
Angelelz10mo ago
I don't know how the lazy promises would work in a promise.all then But my suspicion is that's probably why it's not working how you want it That's the usual purpose of using a transaction anyway
shikishikichangchang
Transactions are used to represent a single logical unit of work, not for dependent operations although the latter usually represents itself as the former say if I transfer money from account A to B. I need to increment B and decrement A. These two operations are not necessarily dependent (incrementing one number is independent of decrementing the other), but they need to happen "as a whole". Hence, the use of transactions
Angelelz
Angelelz10mo ago
Yeah but can you parallelize them?
Luxaritas
Luxaritas10mo ago
No description
Luxaritas
Luxaritas10mo ago
Once you pass them to Promise.all, they will all evaluate, since Promise.all is implicitly calling the then method on the promises passed in, which is what Drizzle uses to trigger the actual evaluation I am in agreement with @shikishikichangchang here - it is completely reasonable to want to use a transaction as a means to commit if all succeed or fail if anything fails. What you're describing maybe relates more to transaction isolation level, and it's actually possible to relax the isolation level so that you don't have the same ordering garuntees Or actually maybe not realted to isolation level, as we're talking about operations within the transaction, not how it responds to operations outside of the transaction
Angelelz
Angelelz10mo ago
I understand that and I partially agree, my point is I don't think using promise.all would achieve any performance benefit over just running the promises as at the DB level it won't be parallel anyway?
Want results from more Discord servers?
Add your server