From 22180c697f009a6571c51a0c75f186e1682eb6c5 Mon Sep 17 00:00:00 2001 From: Eric Chiang Date: Mon, 22 Feb 2016 16:30:48 -0800 Subject: [PATCH] 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. --- db/conn.go | 9 ++++++++- db/conn_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 db/conn_test.go diff --git a/db/conn.go b/db/conn.go index e6256cc5..118b1011 100644 --- a/db/conn.go +++ b/db/conn.go @@ -67,7 +67,14 @@ func NewConnection(cfg Config) (*gorp.DbMap, error) { if err != nil { 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{} default: return nil, errors.New("unrecognized database driver") diff --git a/db/conn_test.go b/db/conn_test.go new file mode 100644 index 00000000..b068523b --- /dev/null +++ b/db/conn_test.go @@ -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() +}