Prevent SQLite busy errors
This commit is contained in:
@@ -54,6 +54,20 @@ func OpenSQLite(ctx context.Context, path string) (*Store, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SQLite permits one writer at a time. Keep access serialized inside this
|
||||||
|
// process and wait briefly for locks held during startup or maintenance.
|
||||||
|
db.SetMaxOpenConns(1)
|
||||||
|
db.SetMaxIdleConns(1)
|
||||||
|
for _, stmt := range []string{
|
||||||
|
`PRAGMA busy_timeout = 5000`,
|
||||||
|
`PRAGMA journal_mode = WAL`,
|
||||||
|
} {
|
||||||
|
if _, err := db.ExecContext(ctx, stmt); err != nil {
|
||||||
|
_ = db.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s := &Store{db: db}
|
s := &Store{db: db}
|
||||||
if err := s.Migrate(ctx); err != nil {
|
if err := s.Migrate(ctx); err != nil {
|
||||||
_ = db.Close()
|
_ = db.Close()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
_ "modernc.org/sqlite"
|
_ "modernc.org/sqlite"
|
||||||
@@ -63,3 +64,41 @@ VALUES ('rs_legacy', 'dev_legacy');
|
|||||||
t.Fatalf("got target %q, want ssh", sessions[0].Target)
|
t.Fatalf("got target %q, want ssh", sessions[0].Target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSQLiteConcurrentAccessDoesNotReturnBusy(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
st, err := OpenSQLite(ctx, filepath.Join(t.TempDir(), "concurrent.db"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
enrolled, err := st.EnrollDevice(ctx, "router", "OpenWrt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
errs := make(chan error, 80)
|
||||||
|
for range 40 {
|
||||||
|
wg.Add(2)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
_, _, err := st.ListRemoteSessions(ctx, enrolled.DeviceID, RemoteSessionListOptions{})
|
||||||
|
errs <- err
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
_, err := st.SaveHeartbeat(ctx, enrolled.DeviceID, []byte(`{"hostname":"router"}`), []byte(`{}`))
|
||||||
|
errs <- err
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
close(errs)
|
||||||
|
|
||||||
|
for err := range errs {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user