Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using driver/sql hangs database and other oddities #210

Open
tie opened this issue Sep 28, 2020 · 1 comment
Open

Using driver/sql hangs database and other oddities #210

tie opened this issue Sep 28, 2020 · 1 comment
Labels
bug Something isn't working sql SQL API and syntax

Comments

@tie
Copy link
Contributor

tie commented Sep 28, 2020

I can reproduce mattn/go-sqlite3#204 with minimal changes.

main1.go
// https://github.com/mattn/go-sqlite3/issues/705

package main

import (
	"database/sql"

	_ "github.com/genjidb/genji/sql/driver"
)

func main() {
	db, err := sql.Open("genji", ":memory:")
	if err != nil {
		panic(err)
	}
	_, err = db.Exec("CREATE TABLE m")
	if err != nil {
		panic(err)
	}
	_, err = db.Query("SELECT * FROM m") // note that I didn’t close the rows
	if err != nil {
		panic(err)
	}
	_, err = db.Query("SELECT * FROM m")
	if err != nil {
		panic(err) // table not found
	}
	panic("unreachable")
}
main2.go
package main

import (
	"context"
	"database/sql"

	_ "github.com/genjidb/genji/sql/driver"
)

func main() {
	ctx := context.Background()

	db, err := sql.Open("genji", "test.db")
	if err != nil {
		panic(err)
	}
	db.Conn(ctx)
	db.Conn(ctx)
	panic("unreachable")
}

I assume SQLite releases db locks when they are not needed. But this issue gets worse with Genji since BoltDB blocks on db lock (unless a timeout was specified, which is not currently possible with sql/driver), and BadgerDB I believe just returns an error. That is, things go terribly wrong when sql.DB attempts to open a new sql.Conn.

As a temporary workaround it’s possible to call SetMaxOpenConns(1) on *sql.DB from the user’s code. This should work just fine for in-memory and bolt engines, but afaik badger supports concurrent transactions whereas single driver.Conn can’t be used concurrently.

I’ve experimented a bit with possible APIs to fix those issues, and I think the best option would be to fix this use case on the Go side by allowing driver.Connector to implement optional io.Closer interface and close it in sql.DB.Close. Then driver.Connector would be able to hold genji.DB object and release the resources when sql.DB is no longer needed.

Alternatively, it’s possible to do reference counting on genji.DB in driver.Connector and driver.Conn, and have a special case for in-memory engines. That has a benefit of releasing the database handle when idle (e.g. so that other processes may perform some maintenance tasks).

@asdine
Copy link
Collaborator

asdine commented Sep 28, 2020

Thank you for the detailed issue @tie!
The sql/driver package was written quite some time ago and my focus was on the core until now. There is currently no concept of connection in Genji, so that what makes this sql/driver implementation a bit odd.

I don't think there should be a special case for the in-memory engine, but rather make sure that every instance of sql.DB represent an access to the same genji.DB instance, regardless of the engine used.

Concerning your proposals, I think they both make sense, I will look into them and try to propose a fix for the next release.

@asdine asdine added bug Something isn't working sql SQL API and syntax labels Sep 28, 2020
@asdine asdine added this to the v0.9.0 milestone Sep 28, 2020
tie added a commit to tie/go that referenced this issue Sep 30, 2020
This change allows driver implementations to manage resources in
driver.Connector, e.g. to share the same underlying database handle
between multiple connections. That is, it allows embedded databases
with in-memory backends like SQLite and Genji to safely release the
resources once the sql.DB is closed.

This makes it possible to address oddities with in-memory stores in
SQLite and Genji drivers without introducing too much complexity in
the driver implementations.

See also:
- mattn/go-sqlite3#204
- mattn/go-sqlite3#511
- chaisql/chai#210
@asdine asdine removed this from the v0.9.0 milestone Nov 11, 2020
tie added a commit to tie/go that referenced this issue Feb 25, 2021
This change allows driver implementations to manage resources in
driver.Connector, e.g. to share the same underlying database handle
between multiple connections. That is, it allows embedded databases
with in-memory backends like SQLite and Genji to safely release the
resources once the sql.DB is closed.

This makes it possible to address oddities with in-memory stores in
SQLite and Genji drivers without introducing too much complexity in
the driver implementations.

See also:
- mattn/go-sqlite3#204
- mattn/go-sqlite3#511
- chaisql/chai#210
gopherbot pushed a commit to golang/go that referenced this issue Feb 25, 2021
This change allows driver implementations to manage resources in
driver.Connector, e.g. to share the same underlying database handle
between multiple connections. That is, it allows embedded databases
with in-memory backends like SQLite and Genji to safely release the
resources once the sql.DB is closed.

This makes it possible to address oddities with in-memory stores in
SQLite and Genji drivers without introducing too much complexity in
the driver implementations.

See also:
- mattn/go-sqlite3#204
- mattn/go-sqlite3#511
- chaisql/chai#210

Fixes #41790

Change-Id: Idbd19763134438ed38288b9d44f16608e4e97fd7
GitHub-Last-Rev: 962c785
GitHub-Pull-Request: #41710
Reviewed-on: https://go-review.googlesource.com/c/go/+/258360
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
Reviewed-by: Daniel Theophanes <kardianos@gmail.com>
Trust: Emmanuel Odeke <emmanuel@orijtech.com>
Run-TryBot: Emmanuel Odeke <emmanuel@orijtech.com>
TryBot-Result: Go Bot <gobot@golang.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working sql SQL API and syntax
Projects
None yet
Development

No branches or pull requests

2 participants