From 8a161bc42333695d471938a9bbb7b4ed7684f4ad Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Fri, 7 May 2021 11:10:47 -0600 Subject: [PATCH] Fix RBF root record cache build This commit fixes an issue where the root record cache is only built when a write transaction successfully commits. However, if no write transactions are occurring then the the cache is never built and saved so it is recomputed on every read tx. --- rbf/db.go | 11 +++++++++++ rbf/tx.go | 4 +++- rbf/tx_test.go | 24 ++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/rbf/db.go b/rbf/db.go index 5976d5cc6..75f357e5b 100644 --- a/rbf/db.go +++ b/rbf/db.go @@ -496,6 +496,17 @@ func (db *DB) Begin(writable bool) (_ *Tx, err error) { // Track transaction with the DB. db.txs[tx] = struct{}{} + // If no root records are cached, build the cache the first time. + // Normally the cache is updated by successful write transactions but + // this avoids recomputing the cache if there are no write txs for a while. + if db.rootRecords == nil { + if db.rootRecords, err = tx.RootRecords(); err != nil { + db.mu.Unlock() + tx.Rollback() + return nil, err + } + } + db.mu.Unlock() return tx, nil } diff --git a/rbf/tx.go b/rbf/tx.go index deeb6a9ba..2996195bc 100644 --- a/rbf/tx.go +++ b/rbf/tx.go @@ -1217,7 +1217,9 @@ func (tx *Tx) Count(name string) (uint64, error) { } defer c.Close() - if err := c.First(); err != nil { + if err := c.First(); err == io.EOF { + return 0, nil + } else if err != nil { return 0, err } diff --git a/rbf/tx_test.go b/rbf/tx_test.go index be647a351..3ca9aaa1d 100644 --- a/rbf/tx_test.go +++ b/rbf/tx_test.go @@ -95,6 +95,30 @@ func TestTx_CommitRollback(t *testing.T) { } }) + t.Run("ReopenReadOnly", func(t *testing.T) { + db := MustOpenDB(t) + defer func() { MustCloseDB(t, db) }() + + // Create bitmap in transaction and commit. + if tx, err := db.Begin(true); err != nil { + t.Fatal(err) + } else if err := tx.CreateBitmap("x"); err != nil { + t.Fatal(err) + } else if err := tx.Commit(); err != nil { + t.Fatal(err) + } + db = MustReopenDB(t, db) + + // Create bitmap again but it should fail as it already exists. + if tx, err := db.Begin(false); err != nil { + t.Fatal(err) + } else if _, err := tx.Count("x"); err != nil { + t.Fatal(err) + } else if err := tx.Commit(); err != nil { + t.Fatal(err) + } + }) + t.Run("SingleWriter", func(t *testing.T) { db := MustOpenDB(t) defer MustCloseDB(t, db)