db: only allow one open connection for in memory databases
sqlite3 in memory databases do not support concurrent writes. Limit number of open connections to prevent race conditions.
This commit is contained in:
parent
f51125f555
commit
22180c697f
2 changed files with 47 additions and 1 deletions
|
@ -67,7 +67,14 @@ func NewConnection(cfg Config) (*gorp.DbMap, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// NOTE(ericchiang): sqlite does NOT work with SetMaxIdleConns.
|
if u.Host == ":memory:" {
|
||||||
|
// NOTE(ericchiang): sqlite3 coordinates concurrent clients through file locks.
|
||||||
|
// In memory databases do not support concurrent calls. Limit the number of
|
||||||
|
// open connections to 1.
|
||||||
|
//
|
||||||
|
// See: https://www.sqlite.org/faq.html#q5
|
||||||
|
db.SetMaxOpenConns(1)
|
||||||
|
}
|
||||||
dialect = gorp.SqliteDialect{}
|
dialect = gorp.SqliteDialect{}
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unrecognized database driver")
|
return nil, errors.New("unrecognized database driver")
|
||||||
|
|
39
db/conn_test.go
Normal file
39
db/conn_test.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/coreos/dex/connector"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestConcurrentSqliteConns tests concurrent writes to a single in memory database.
|
||||||
|
func TestConcurrentSqliteConns(t *testing.T) {
|
||||||
|
dbMap := NewMemDB()
|
||||||
|
repo := NewConnectorConfigRepo(dbMap)
|
||||||
|
|
||||||
|
var (
|
||||||
|
once sync.Once
|
||||||
|
wg sync.WaitGroup
|
||||||
|
)
|
||||||
|
|
||||||
|
n := 1000
|
||||||
|
wg.Add(n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
go func() {
|
||||||
|
configs := []connector.ConnectorConfig{
|
||||||
|
&connector.LocalConnectorConfig{ID: "local"},
|
||||||
|
}
|
||||||
|
// Setting connector configs both deletes and writes to a single table
|
||||||
|
// within a transaction.
|
||||||
|
if err := repo.Set(configs); err != nil {
|
||||||
|
// Don't print 1000 errors, only the first.
|
||||||
|
once.Do(func() {
|
||||||
|
t.Errorf("concurrent connections to sqlite3: %v", err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
Reference in a new issue