最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

go - Why transaction timeout in pgx doesn't work - Stack Overflow

programmeradmin1浏览0评论

I used to use the pq library, now I want to use pgx. I have a task to abort a transaction if it has been running in the db for too long. pq is able to abort a transaction as soon as the context has expired, but pgx waits for the query to be completed and only returns an error on the next query or commit.

Is this how it should be, or am I doing something wrong?

How can I terminate the transaction in this case?

import (
    "context"
    "database/sql"
    "fmt"
    _ "github/jackc/pgx/v5/stdlib"
    _ "github/lib/pq"
    "log"
    "time"
)

func main() {
    fmt.Println("================= PQ ==============")
    pqWork()
    fmt.Println("================= ==============")

    fmt.Println("================= PGX ==============")
    pqxWork()
    fmt.Println("================= ==============")
}

func pqWork() {
    connStr := "host=localhost port=5432 user=... password=... dbname=... "
    db, err := sql.Open("postgres", connStr)
    if err != nil {
        panic(err)
    }
    defer db.Close()

    exec(db)
}

func pqxWork() {
    connStr := "host=localhost port=5432 user=... password=... dbname=... "
    db, err := sql.Open("pgx", connStr)
    if err != nil {
        panic(err)
    }
    defer db.Close()

    exec(db)
}

func exec(db *sql.DB) {
    ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
    defer cancel()
    begin := time.Now()
    tx, err := db.BeginTx(ctx, nil)
    if err != nil {
        panic(err)
    }

    _, err = tx.Exec("select pg_sleep(10)")
    if err != nil {
        fmt.Println("err. time:", time.Since(begin).Milliseconds(), "ms")
        tx.Rollback()
        return
    }
    fmt.Println("success. time:", time.Since(begin).Milliseconds(), "ms")
    err = tx.Commit()
    if err != nil {
        panic(err)
    }
}

Result

================= PQ ==============
err. time:  524 ms
================= ==============
================= PGX ==============
success. time:  10032 ms
panic: sql: transaction has already been committed or rolled back

I used to use the pq library, now I want to use pgx. I have a task to abort a transaction if it has been running in the db for too long. pq is able to abort a transaction as soon as the context has expired, but pgx waits for the query to be completed and only returns an error on the next query or commit.

Is this how it should be, or am I doing something wrong?

How can I terminate the transaction in this case?

import (
    "context"
    "database/sql"
    "fmt"
    _ "github/jackc/pgx/v5/stdlib"
    _ "github/lib/pq"
    "log"
    "time"
)

func main() {
    fmt.Println("================= PQ ==============")
    pqWork()
    fmt.Println("================= ==============")

    fmt.Println("================= PGX ==============")
    pqxWork()
    fmt.Println("================= ==============")
}

func pqWork() {
    connStr := "host=localhost port=5432 user=... password=... dbname=... "
    db, err := sql.Open("postgres", connStr)
    if err != nil {
        panic(err)
    }
    defer db.Close()

    exec(db)
}

func pqxWork() {
    connStr := "host=localhost port=5432 user=... password=... dbname=... "
    db, err := sql.Open("pgx", connStr)
    if err != nil {
        panic(err)
    }
    defer db.Close()

    exec(db)
}

func exec(db *sql.DB) {
    ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
    defer cancel()
    begin := time.Now()
    tx, err := db.BeginTx(ctx, nil)
    if err != nil {
        panic(err)
    }

    _, err = tx.Exec("select pg_sleep(10)")
    if err != nil {
        fmt.Println("err. time:", time.Since(begin).Milliseconds(), "ms")
        tx.Rollback()
        return
    }
    fmt.Println("success. time:", time.Since(begin).Milliseconds(), "ms")
    err = tx.Commit()
    if err != nil {
        panic(err)
    }
}

Result

================= PQ ==============
err. time:  524 ms
================= ==============
================= PGX ==============
success. time:  10032 ms
panic: sql: transaction has already been committed or rolled back
Share Improve this question asked Mar 17 at 18:59 ViolettaVioletta 6237 silver badges26 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 2

From `Exec` description:

Exec executes a query that doesn't return rows. For example: an INSERT and UPDATE.

Exec uses [context.Background] internally; to specify the context, use [Tx.ExecContext].

So you just need to replace Exec call with ExecContext like this and that does the trick:

_, err = tx.ExecContext(ctx, "select pg_sleep(10)")
发布评论

评论列表(0)

  1. 暂无评论