jackc / pgx

PostgreSQL driver and toolkit for Go

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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.