Skip to content

Occasional prepared statement already exist errors when using statement timeout #2223

@oschwald

Description

@oschwald

Describe the bug

We have a web service where we set a statement_timeout on our connections to ensure that queries do not run for an extended period of time. As part of this web service, we have a large, generated query. We occasionally see errors such as the following:

ERROR: prepared statement "stmtcache_2b7087dc2ab6296b87fa5baa7f329541c23d17376faa03b7" already exists (SQLSTATE 42P05)

My hypothesis is that there is a statement timeout in the PREPARE but after Postgres has actually created the prepared statement. This causes pgx to end up in a state where the prepared statement isn't added to the statement cache but does exist for the connection in Postgres. I was able to reproduce this with the code below.

To Reproduce

The following sample code reliably produces the error with a local Postgres server for me. This probably could be simplified a bit further.

package main

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"log"
	"math/rand"
	"os"

	"github.com/jackc/pgx/v5/pgconn"
	_ "github.com/jackc/pgx/v5/stdlib"
)

func main() {
	db, err := sql.Open("pgx", os.Getenv("DATABASE_URL"))
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()

	conn, err := db.Conn(ctx)
	if err != nil {
		log.Fatal(err)
	}

	_, err = conn.ExecContext(ctx, "SET statement_timeout TO 1")
	if err != nil {
		log.Fatal(err)
	}

	p := "s"
	for {
		var v uint

		q := fmt.Sprintf("SELECT 1 AS %s", p)
		err := conn.QueryRowContext(
			ctx,
			q,
		).Scan(&v)
		if err != nil && !errors.Is(err, context.Canceled) {
			var pgErr *pgconn.PgError
			if errors.As(err, &pgErr) && pgErr.Code == "57014" {
				continue
			}
			log.Fatal(err)
		}
		p = randString(100000)
	}
}

var letterRunes = []rune("abAB")

func randString(n int) string {
	b := make([]rune, n)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

Expected behavior

No prepared statement already exists error.

Actual behavior

ERROR: prepared statement "stmtcache_087f9f36620ff21723cd288c17d7806ba6fdd9b160f73186" already exists (SQLSTATE 42P05)

Version

  • Go: go version go1.23.4 linux/amd64
  • PostgreSQL: PostgreSQL 16.6 (Ubuntu 16.6-1.pgdg24.10+1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 14.2.0-4ubuntu2) 14.2.0, 64-bit
  • pgx: v5.7.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions