jackc / puddle

Generic resource pool for Go
MIT License
305 stars 25 forks source link

Application Hangs on `defer p.destructWG.Wait()` in Docker Environment #37

Open rubenhazelaar opened 1 month ago

rubenhazelaar commented 1 month ago

Description: As described in issue #25, my application hangs on the statement defer p.destructWG.Wait(). Any deferred statement or code that should execute after defer pool.Close() does not run. This issue occurs when running the application in a Docker container (FROM golang:1.22-alpine) on a Windows host machine through Docker Desktop. I have not tested this in other environments.

Steps to Reproduce:

  1. Run the application in a Docker container with the base image golang:1.22-alpine.
  2. Execute the provided code snippet.
  3. Observe that the application hangs on defer p.destructWG.Wait().

Expected Behavior: Deferred statements, including those after defer pool.Close(), should execute as expected.

Actual Behavior: Deferred statements after defer pool.Close() do not execute, causing the application to hang.

Environment:

Additional Context: This issue occurs in the context where an actual connection has not been made through the pool. Below is the simplified code used in my application:

package main

import (
    "context"
    "errors"
    "github.com/jackc/pgx/v5"
    "github.com/jackc/pgx/v5/pgxpool"
    "os"
)

func main() {
    err := run(context.Background())
    if err != nil {
        os.Exit(1)
    }
}

func run(ctx context.Context) error {
    // Create the pool in a run func which is called by main func

    poolConfig, err := pgxpool.ParseConfig(/* applicationConfig.Dsn */)
    if err != nil {
        return err
    }
    poolConfig.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
        // Here I register some custom types
        return nil
    }

    pool, err := pgxpool.NewWithConfig(ctx, poolConfig)
    if err != nil {
        return err
    }
    defer pool.Close()

    // More code where an error is returned from run to main func, like so:
    err = aCallWhichFails()
    if err != nil {
        return err
    }

    return nil
}

func aCallWhichFails() error {
    return errors.New("test")
}

Feel free to adjust any part of this as needed!

jackc commented 1 month ago

I can't reproduce with this example. But Pool.Close blocks until all resources are released. My guess is that a connection is not being released.