diff --git a/async_postgres/pg_client/transaction.nim b/async_postgres/pg_client/transaction.nim index fd745b7..1a0c320 100644 --- a/async_postgres/pg_client/transaction.nim +++ b/async_postgres/pg_client/transaction.nim @@ -321,13 +321,7 @@ macro withTransaction*(conn: PgConnection, args: varargs[untyped]): untyped = let connExpr = conn let connSym = genSym(nskLet, "conn") let eSym = genSym(nskLet, "e") - let cleanupErrSym = genSym(nskLet, "cleanupErr") - let tsInTxSym = bindSym"tsInTransaction" - let tsInFailedSym = bindSym"tsInFailedTransaction" - let csReadySym = bindSym"csReady" - let (fireCleanupSkippedSym, csrConnInvalidatedSym, csrCleanupFailedSym) = - bindCleanupSkippedSyms() - let ckTxRollbackSym = bindSym"ckTxRollback" + let bodyCleanup = buildRollbackCleanup(connSym, txTimeout) result = quote: let `connSym` = `connExpr` try: @@ -335,23 +329,7 @@ macro withTransaction*(conn: PgConnection, args: varargs[untyped]): untyped = `body` discard await `connSym`.simpleExec("COMMIT", timeout = `txTimeout`) except CatchableError as `eSym`: - # Only ROLLBACK if the server is still in a transaction AND the connection - # is usable. After a failed COMMIT, PostgreSQL has already ended the - # transaction (txStatus = tsIdle), so an extra ROLLBACK would just emit - # "no transaction in progress". On per-call timeout the connection is - # csClosed but txStatus is stale (no RFQ received) — the state guard - # avoids dispatching a ROLLBACK that would fail at checkReady(). Both - # the csClosed skip and any swallowed inner-ROLLBACK failure are - # reported via `onCleanupSkipped` so the timeout path is not silent. - if `connSym`.state != `csReadySym`: - `fireCleanupSkippedSym`(`connSym`, `ckTxRollbackSym`, `csrConnInvalidatedSym`) - elif `connSym`.txStatus in {`tsInTxSym`, `tsInFailedSym`}: - try: - discard await `connSym`.simpleExec("ROLLBACK", timeout = `txTimeout`) - except CatchableError as `cleanupErrSym`: - `fireCleanupSkippedSym`( - `connSym`, `ckTxRollbackSym`, `csrCleanupFailedSym`, `cleanupErrSym` - ) + `bodyCleanup` raise `eSym` macro withTransactionRetry*( @@ -606,21 +584,15 @@ macro withTransactionDeadline*(conn: PgConnection, args: varargs[untyped]): unty let connExpr = conn let connSym = genSym(nskLet, "conn") let eSym = genSym(nskLet, "e") - let cleanupErrSym = genSym(nskLet, "cleanupErr") let totalDurSym = genSym(nskLet, "totalDur") let deadlineMomentSym = genSym(nskLet, "deadlineMoment") let bodyFnSym = genSym(nskProc, "txBodyDeadline") let bodyFutSym = genSym(nskLet, "bodyFut") - let tsInTxSym = bindSym"tsInTransaction" - let tsInFailedSym = bindSym"tsInFailedTransaction" - let csReadySym = bindSym"csReady" let timeoutErrSym = bindSym"AsyncTimeoutError" let waitSym = bindSym"wait" let remainingSym = bindSym"remainingDeadlineDuration" let graceSym = bindSym"rollbackGrace" - let (fireCleanupSkippedSym, csrConnInvalidatedSym, csrCleanupFailedSym) = - bindCleanupSkippedSyms() - let ckTxRollbackSym = bindSym"ckTxRollback" + let bodyCleanup = buildRollbackCleanup(connSym, graceSym) result = quote: let `connSym` = `connExpr` let `totalDurSym` = `deadline` @@ -652,20 +624,7 @@ macro withTransactionDeadline*(conn: PgConnection, args: varargs[untyped]): unty # PgTimeoutError — control does not return from this call. `connSym`.invalidateOnTimeout("withTransactionDeadline exceeded") except CatchableError as `eSym`: - # Skip ROLLBACK when the connection is already csClosed (e.g. a per-call - # timeout inside body invalidated it) — checkReady would just raise and - # the failure would be swallowed by the inner except. Both the - # csClosed skip and any swallowed inner-ROLLBACK failure are reported - # via `onCleanupSkipped`. - if `connSym`.state != `csReadySym`: - `fireCleanupSkippedSym`(`connSym`, `ckTxRollbackSym`, `csrConnInvalidatedSym`) - elif `connSym`.txStatus in {`tsInTxSym`, `tsInFailedSym`}: - try: - discard await `connSym`.simpleExec("ROLLBACK", timeout = `graceSym`) - except CatchableError as `cleanupErrSym`: - `fireCleanupSkippedSym`( - `connSym`, `ckTxRollbackSym`, `csrCleanupFailedSym`, `cleanupErrSym` - ) + `bodyCleanup` raise `eSym` macro withTransactionRetryDeadline*( diff --git a/async_postgres/pg_pool.nim b/async_postgres/pg_pool.nim index 233981d..f6cd299 100644 --- a/async_postgres/pg_pool.nim +++ b/async_postgres/pg_pool.nim @@ -1701,15 +1701,8 @@ macro withTransaction*(pool: PgPool, args: varargs[untyped]): untyped = let poolExpr = pool let poolSym = genSym(nskLet, "pool") let eSym = genSym(nskLet, "e") - let cleanupErrSym = genSym(nskLet, "cleanupErr") let resetSessionSym = bindSym"resetSession" - let csReadySym = bindSym"csReady" - let tsInTxSym = bindSym"tsInTransaction" - let tsInFailedSym = bindSym"tsInFailedTransaction" - let fireCleanupSkippedSym = bindSym"fireCleanupSkipped" - let ckTxRollbackSym = bindSym"ckTxRollback" - let csrConnInvalidatedSym = bindSym"csrConnInvalidated" - let csrCleanupFailedSym = bindSym"csrCleanupFailed" + let bodyCleanup = buildRollbackCleanup(connIdent, txTimeout) result = quote: let `poolSym` = `poolExpr` let `connIdent` = await `poolSym`.acquire() @@ -1719,24 +1712,7 @@ macro withTransaction*(pool: PgPool, args: varargs[untyped]): untyped = `body` discard await `connIdent`.simpleExec("COMMIT", timeout = `txTimeout`) except CatchableError as `eSym`: - # Mirror the per-call rationale from pg_client's `withTransaction`: - # skip ROLLBACK on an already-invalidated connection (per-call - # timeout marked it csClosed but txStatus may still be stale) and - # avoid a futile cleanup when the server has already ended the - # transaction. The csClosed skip and any swallowed inner-ROLLBACK - # failure are surfaced through `onCleanupSkipped` so the diagnostic - # asymmetry with the body-error path goes away. - if `connIdent`.state != `csReadySym`: - `fireCleanupSkippedSym`( - `connIdent`, `ckTxRollbackSym`, `csrConnInvalidatedSym` - ) - elif `connIdent`.txStatus in {`tsInTxSym`, `tsInFailedSym`}: - try: - discard await `connIdent`.simpleExec("ROLLBACK", timeout = `txTimeout`) - except CatchableError as `cleanupErrSym`: - `fireCleanupSkippedSym`( - `connIdent`, `ckTxRollbackSym`, `csrCleanupFailedSym`, `cleanupErrSym` - ) + `bodyCleanup` raise `eSym` finally: await `resetSessionSym`(`poolSym`, `connIdent`) @@ -1874,7 +1850,6 @@ macro withTransactionDeadline*(pool: PgPool, args: varargs[untyped]): untyped = let poolExpr = pool let poolSym = genSym(nskLet, "pool") let eSym = genSym(nskLet, "e") - let cleanupErrSym = genSym(nskLet, "cleanupErr") let totalDurSym = genSym(nskLet, "totalDur") let deadlineMomentSym = genSym(nskLet, "deadlineMoment") let bodyFnSym = genSym(nskProc, "poolTxBodyDeadline") @@ -1886,17 +1861,12 @@ macro withTransactionDeadline*(pool: PgPool, args: varargs[untyped]): untyped = let csReadySym = bindSym"csReady" let csClosedSym = bindSym"csClosed" let cancelNoWaitSym = bindSym"cancelNoWait" - let tsInTxSym = bindSym"tsInTransaction" - let tsInFailedSym = bindSym"tsInFailedTransaction" let timeoutErrSym = bindSym"AsyncTimeoutError" let waitSym = bindSym"wait" let remainingSym = bindSym"remainingDeadlineDuration" let graceSym = bindSym"rollbackGrace" let invalidateSym = bindSym"invalidateOnTimeout" - let fireCleanupSkippedSym = bindSym"fireCleanupSkipped" - let ckTxRollbackSym = bindSym"ckTxRollback" - let csrConnInvalidatedSym = bindSym"csrConnInvalidated" - let csrCleanupFailedSym = bindSym"csrCleanupFailed" + let bodyCleanup = buildRollbackCleanup(connIdent, graceSym) result = quote: let `poolSym` = `poolExpr` @@ -1917,21 +1887,7 @@ macro withTransactionDeadline*(pool: PgPool, args: varargs[untyped]): untyped = "COMMIT", timeout = `remainingSym`(`deadlineMomentSym`) ) except CatchableError as `eSym`: - # Mirror pg_client's `withTransactionDeadline`: skip ROLLBACK on a - # csClosed connection (per-call body timeout invalidated it) and - # report the skip / any swallowed inner-ROLLBACK failure through - # `onCleanupSkipped` so the timeout path is observable. - if `connIdent`.state != `csReadySym`: - `fireCleanupSkippedSym`( - `connIdent`, `ckTxRollbackSym`, `csrConnInvalidatedSym` - ) - elif `connIdent`.txStatus in {`tsInTxSym`, `tsInFailedSym`}: - try: - discard await `connIdent`.simpleExec("ROLLBACK", timeout = `graceSym`) - except CatchableError as `cleanupErrSym`: - `fireCleanupSkippedSym`( - `connIdent`, `ckTxRollbackSym`, `csrCleanupFailedSym`, `cleanupErrSym` - ) + `bodyCleanup` raise `eSym` except CancelledError as `cancelledSym`: # Cancelled mid-request (chronos deadline): abort it server-side and