-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Clarify if queries are guaranteed to be serial in transactions #1488
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The commands are serialized per client but you should await each step along the way as otherwise you could end up with an unhandled promise rejection. If the |
@sehrope is right, you shouldn't have possible unhandled rejections, but there is a way to do what you want. The proper incantations look something like this: const client = await pool.connect();
try {
await Promise.all([
client.query('BEGIN'),
client.query("update tbl set a = b"),
client.query("update tbl2 set c = d")
]);
await client.query('COMMIT');
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
} This way you can enqueue most of the queries ahead of time, and still catch every error. You could even work around the fail-fast behavior of const client = await pool.connect();
try {
let error = null;
const handleError = e => error = error || e;
await Promise.all([
client.query('BEGIN').catch(handleError),
client.query("update tbl set a = b").catch(handleError),
client.query("update tbl2 set c = d").catch(handleError),
client.query('COMMIT').catch(handleError)
]);
if (error) throw error;
} finally {
client.release();
} |
Wouldn't "DatabaseError: current transaction is aborted, commands ignored until end of transaction block"
Which would mean it is enough to do const client = await pool.connect()
await client.query('BEGIN');
client.query("update tbl set a = b").catch(e => {});
client.query("update tbl2 set c = d").catch(e => {});
await client.query('COMMIT') (still without await, just preventing any unhandled promise rejections from going up all of the call stack) |
Yes and you would need to issue a I think it's generally easier to have multiple To handle the out of sync connection issue the code should also attempt to issue a rollback if an error occurs at any point in the transaction including the final commit. If the rollback fails then the connection should be dropped. If it succeeds, then the connection can be returned to the pool. Alternatively you can discard any connection that has an error though this has a slight DOS issue if your app is a web app and a user can initiate a failed transaction through a bad request as it'll thrash your pool. So something like this:
|
@phiresky: That should work. But the @sehrope: Could you give an example, when ROLLBACK would fail? As far as I know, Even if you are not in a transaction, it should only raise a warning (WARNING: there is no transaction in progress). Closed / aborted connections should already be handled by |
Ok, so my first question (Is it allowed to call @boromisp Thank you. I just tried it, and sending My goal was actually to not receive any response from the intermediary queries at all but this doesn't even seem possible in the postgresql protocol. But I can solve my problem by using |
@boromisp If the backend process on the server is killed or there is a network issue causing the client's TCP socket to close then the rollback would fail. The pool handles those situations when they happen for a connection that is in use but it's up to the user to handle it during use and instruct the pool to discard the client by passing in a non-null error object as a parameter to |
@phiresky: I have no idea weather it is in the SQL standard (or a documented extension of PostgreSQL), but it seems that Back to your original question: I did not find any documentation for it, but all queries are pushed into a queue synchronously, and are processed in order. If the connection is "ready for query" when you call @sehrope: You are right. If I call client.query('... aborted query ...')
.catch(() => client.release())
.then(() => client.release()) could either drop the failed client, or return it to the |
I'm trying to find out whether it is necessary to await every single subquery in a transaction.
The transaction documentation has the following example:
which uses await for the inner query, even though the return value of that query is ignored. It is not clear if the await is required at that point:
The documentation for client.query() also does not specify whether it is necessary to wait for the callback / promise to return before adding another query to the client. My reason for trying to do this this is avoiding a round trip to the server and clearing the JS stack for every single query.
Looking at the code, it seems like it should work fine because everything is synchronously pushed to a queue for the client, but it is not clear whether it is even possible to ignore the result value of a query.
The text was updated successfully, but these errors were encountered: