Skip to content

[Go] sqldriver doesn't release resources on db.Close #3728

@amoeba

Description

@amoeba

What happened?

I think the sqldriver package may not be cleaning up resources on db.Close().

I ran into a situation with the Go sqldriver package where it doesn't release resources after I call db.Close() but before my processes exits. I can't reproduce this with the drivermgr package directly so I think the issue may be in the sqldriver package.

Stack Trace

No response

How can we reproduce the bug?

Run main.go (below) and in another shell run:

$ uv run --with "duckdb" python -c "import duckdb; duckdb.connect(database='my.duckdb')"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
    import duckdb; duckdb.connect(database='my.duckdb')
                   ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
_duckdb.IOException: IO Error: Could not set lock on file "/tmp/my.duckdb": Conflicting lock is held in /private/var/folders/gf/3btcn6956190fx_xld0y_b9r0000gn/T/go-build2890680531/b001/exe/main (PID 4686) by user bryce. See also https://duckdb.org/docs/stable/connect/concurrency

Then comment out test_sqldriver in main.go and uncomment test_drivermgr and re-test. The Python code above should run fine.

main.go
package main

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/apache/arrow-adbc/go/adbc/drivermgr"
	"github.com/apache/arrow-adbc/go/adbc/sqldriver"
)

var drvname = "duckdb"
var dbpath = "my.duckdb"

func test_sqldriver() {
	fmt.Println("Running test with sqldriver...")

	dsn := fmt.Sprintf("driver=%s;path=%s", drvname, dbpath)

	var drv drivermgr.Driver
	sql.Register("test", &sqldriver.Driver{Driver: &drv})

	db, err := sql.Open("test", dsn)
	if err != nil {
		panic(err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	rows, err := db.QueryContext(ctx, "CREATE TABLE IF NOT EXISTS example(id INTEGER);")
	if err != nil {
		panic(err)
	}
	rows.Close()

	// Manually close db
	if err := db.Close(); err != nil {
		panic(err)
	}

	fmt.Println("Ctrl+C exits")
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
	<-sig
}

func test_drivermgr() {
	fmt.Println("Running test with drivermgr directly...")

	var drv drivermgr.Driver
	db, err := drv.NewDatabase(map[string]string{"driver": drvname, "path": dbpath})
	if err != nil {
		panic(err)
	}
	conn, err := db.Open(context.Background())
	if err != nil {
		panic(err)
	}

	stmt, err := conn.NewStatement()
	if err != nil {
		log.Fatal(err)
	}

	err = stmt.SetSqlQuery("CREATE TABLE IF NOT EXISTS example(id INTEGER);")
	if err != nil {
		log.Fatal(err)
	}

	stream, _, err := stmt.ExecuteQuery(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	defer stream.Release()

	// Manually close things instead of defer so we can reproduce the issue
	err = stmt.Close()
	if err != nil {
		panic(err)
	}
	err = conn.Close()
	if err != nil {
		panic(err)
	}
	err = db.Close()
	if err != nil {
		panic(err)
	}

	fmt.Println("Ctrl+C exits")
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
	<-sig
}

func main() {
	// The issue can be reproduce when this is run:
	test_sqldriver()
	// and I ran uv run --with "duckdb" python -c "import duckdb; duckdb.connect(database='my.duckdb')"

	// With the code in this function, I can't reproduce the issue
	// test_drivermgr()
}

Environment/Setup

  • go version go1.25.3 darwin/arm64
  • github.com/apache/arrow-adbc/go/adbc v1.9.0
  • libduckdb 1.4.2

Metadata

Metadata

Assignees

Labels

Type: bugSomething isn't working

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions