Running Query Is Not Cancelled on Context Cancelled
pracj3am opened this issue · comments
Describe the bug
I expect query is cancelled when context is cancelled.
To Reproduce
package main
import (
"context"
"fmt"
"log"
"os"
"time"
"github.com/jackc/pgx/v5"
)
func main() {
conn, err := pgx.Connect(context.Background(), os.Getenv("DATABASE_URL"))
if err != nil {
log.Fatalln(err)
return
}
ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
_, err = conn.Exec(ctx, "select pg_sleep(120);")
fmt.Println(err)
// err = conn.PgConn().CancelRequest(context.Background())
// fmt.Println(err)
}
Expected behavior
After 5 seconds the context is cancelled and the query is also cancelled.
Actual behavior
Context is cancelled
timeout: context deadline exceeded
but query remains active:
# select * from pg_stat_activity;
datid | datname | pid | usesysid | usename | application_name | client_addr | client_hostname | client_port | backend_start | xact_start | query_start | state_change | wait_event_type | wait_event | state | backend_xid | backend_xmin | query | backend_type
-------+---------+---------+----------+----------+------------------+-------------+-----------------+-------------+-------------------------------+-------------------------------+-------------------------------+-------------------------------+-----------------+------------+--------+-------------+--------------+-----------------------+----------------
16385 | dnsint2 | 1533206 | 10 | postgres | | | | -1 | 2024-05-15 12:36:22.382164+00 | 2024-05-15 12:36:22.383732+00 | 2024-05-15 12:36:22.383732+00 | 2024-05-15 12:36:22.383733+00 | Timeout | PgSleep | active | | 38710 | select pg_sleep(120); | client backend
The query needs to be cancelled manually – see the lines that are commented out.
Version
- Go: go version go1.22.3 linux/amd64
- PostgreSQL: PostgreSQL 10.23 (Debian 10.23-2.pgdg120+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
- pgx: v5.5.5
Additional context
Unix socket is used to connect to the database.
The query cancellation is sent in the background. Your program will exit before it has a chance to run. Add a sleep at the end of main and I suspect it will work.
Add a sleep at the end of main and I suspect it will work.
You are right, thanks 👍
Is there a way to detect the query cancellation programatically, so we are sure it really was run and cancelled?
The sleep seems a bit "random" (how long should it be? how can we be sure it was really cancelled?).
Is there a way to detect the query cancellation programatically, so we are sure it really was run and cancelled?
Very tricky thanks to the underlying protocol. From https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-CANCELING-REQUESTS:
The upshot of all this is that for reasons of both security and efficiency, the frontend has no direct way to tell whether a cancel request has succeeded. It must continue to wait for the backend to respond to the query. Issuing a cancel simply improves the odds that the current query will finish soon, and improves the odds that it will fail with an error message instead of succeeding.