From 98cef5afde3b3495bfa80cabb2d55f90162df637 Mon Sep 17 00:00:00 2001 From: Wael Nasreddine Date: Thu, 5 Dec 2024 19:34:38 -0800 Subject: [PATCH] pkg/database: Limit the number of open connections to 1 (#39) Getting a `database is locked` error. This [upstream issue](https://github.com/mattn/go-sqlite3/issues/274#issuecomment-191597862) suggested limiting the number of connection to one and it seems to have helped. It's not idea, but I need to move on. ref #38 --- pkg/database/sqlite.go | 5 +++ pkg/database/sqlite_test.go | 61 +++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/pkg/database/sqlite.go b/pkg/database/sqlite.go index 9a231c6..8e06b61 100644 --- a/pkg/database/sqlite.go +++ b/pkg/database/sqlite.go @@ -101,6 +101,11 @@ func Open(logger log15.Logger, dbpath string) (*DB, error) { return nil, fmt.Errorf("error opening the SQLite3 database at %q: %w", dbpath, err) } + // Getting an error `database is locked` when data is being inserted in the + // database at a fast rate. This will slow down read/write from the database + // but at least none of them will fail due to connection issues. + sdb.SetMaxOpenConns(1) + db := &DB{DB: sdb, logger: logger.New("dbpath", dbpath)} return db, db.createTables() diff --git a/pkg/database/sqlite_test.go b/pkg/database/sqlite_test.go index 0e4e857..ce7c6f0 100644 --- a/pkg/database/sqlite_test.go +++ b/pkg/database/sqlite_test.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "sync" "testing" "time" @@ -254,6 +255,66 @@ func TestInsertNarInfoRecord(t *testing.T) { t.Errorf("want %q got %q", want, got) } }) + + t.Run("can write many narinfos", func(t *testing.T) { + var wg sync.WaitGroup + + errC := make(chan error) + + for i := 0; i < 10000; i++ { + wg.Add(1) + + go func() { + defer wg.Done() + + hash, err := helper.RandString(128, nil) + if err != nil { + errC <- fmt.Errorf("expected no error but got: %w", err) + + return + } + + tx, err := db.Begin() + if err != nil { + errC <- fmt.Errorf("expected no error but got: %w", err) + + return + } + + //nolint:errcheck + defer tx.Rollback() + + if _, err := db.InsertNarInfoRecord(tx, hash); err != nil { + errC <- fmt.Errorf("expected no error got: %w", err) + + return + } + + if err := tx.Commit(); err != nil { + errC <- fmt.Errorf("expected no error got: %w", err) + + return + } + }() + } + + done := make(chan struct{}) + + go func() { + wg.Wait() + + close(done) + }() + + for { + select { + case err := <-errC: + t.Errorf("got an error: %s", err) + case <-done: + return + } + } + }) } //nolint:paralleltest