Skip to content

A query cancelled before it is sent does not fail its transaction, so COMMIT succeeds without it #180

Closed

Description

@okomarov

query.cancel() on a query that hasn't reached its connection yet removes it from its queue and rejects it with 57014 on the client (src/index.js, cancel()). The server never sees it, so an open transaction carries on and a later COMMIT commits the other statements without it. A query that was already written is cancelled on the server, and that error fails the transaction as usual.

The query can be waiting in either queue:

  • the pool queue, which matters for a hand-run transaction with max: 1;
  • the transaction's or reserved connection's own queue, for example behind a barrier such as a first prepared run with values.
const tx = await sql.reserve()
tx`begin`.execute()
tx`insert into orders values (${ 1 })`.execute()
const audit = tx`insert into audit values (1)`
audit.execute()
audit.cancel()
await tx`commit`

When the cancel lands while audit is still queued, audit rejects with 57014, the order is committed, and the audit row is missing.

In sql.begin(), the rejection is recorded like any failed query, so the transaction rolls back if the cancel happens before the callback returns. A cancel after the callback has returned commits without the query, as in issue #70.

This is the same gap as issue #70 (fixed in PR #177 for queries that fail before they are sent), reached through cancel() instead of a client-side error. A likely fix is to send the same refused statement in the cancelled query's place when it belongs to a reserved connection or a transaction, instead of dropping it.

Upstream: no report of the commit. GrahamCampbell's comment on porsager#1252 describes the earlier form of this path: a query cancelled in a reserved or transaction connection's queue left the statements behind it unsent. porsager#1256 leaves it as it is. The fork already avoids that hang by taking the query off the queue, and that is what turns it into a silent commit.

Activity

  1. added this to the 4.7.0 milestone on Oct 23, 2026
  2. moved this to Next in Roadmapon Oct 23, 2026
  3. added 2 commits that reference this issue on Oct 23, 2026
    211d560
    9f2aed6
  4. added a commit that references this issue on Oct 24, 2026
  5. moved this from Next to Shipped in Roadmapon Oct 24, 2026
  6. okomarov commented on Oct 24, 2026

    @okomarov
    Author

    Shipped in 4.7.0: a query cancelled before it is sent on a reserved connection, in a transaction or on a pool with max: 1 now fails the transaction it is in, so COMMIT rolls back.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: transactionsTransactions, reserve() and leasesbugSomething isn't working

    Type

    No type

    Projects

    • Status
      Shipped

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions