Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce a freelist interface #775

Merged
merged 1 commit into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 24 additions & 20 deletions allocate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,36 @@ import (
"testing"

"go.etcd.io/bbolt/internal/common"
"go.etcd.io/bbolt/internal/freelist"
)

func TestTx_allocatePageStats(t *testing.T) {
f := newTestFreelist()
ids := []common.Pgid{2, 3}
f.readIDs(ids)
for n, f := range map[string]freelist.Interface{"hashmap": freelist.NewHashMapFreelist(), "array": freelist.NewArrayFreelist()} {
t.Run(n, func(t *testing.T) {
ids := []common.Pgid{2, 3}
f.Init(ids)

tx := &Tx{
db: &DB{
freelist: f,
pageSize: common.DefaultPageSize,
},
meta: &common.Meta{},
pages: make(map[common.Pgid]*common.Page),
}
tx := &Tx{
db: &DB{
freelist: f,
pageSize: common.DefaultPageSize,
},
meta: &common.Meta{},
pages: make(map[common.Pgid]*common.Page),
}

txStats := tx.Stats()
prePageCnt := txStats.GetPageCount()
allocateCnt := f.free_count()
txStats := tx.Stats()
prePageCnt := txStats.GetPageCount()
allocateCnt := f.FreeCount()

if _, err := tx.allocate(allocateCnt); err != nil {
t.Fatal(err)
}
if _, err := tx.allocate(allocateCnt); err != nil {
t.Fatal(err)
}

txStats = tx.Stats()
if txStats.GetPageCount() != prePageCnt+int64(allocateCnt) {
t.Errorf("Allocated %d but got %d page in stats", allocateCnt, txStats.GetPageCount())
txStats = tx.Stats()
if txStats.GetPageCount() != prePageCnt+int64(allocateCnt) {
t.Errorf("Allocated %d but got %d page in stats", allocateCnt, txStats.GetPageCount())
}
})
}
}
2 changes: 1 addition & 1 deletion bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -903,7 +903,7 @@ func (b *Bucket) free() {
var tx = b.tx
b.forEachPageNode(func(p *common.Page, n *node, _ int) {
if p != nil {
tx.db.freelist.free(tx.meta.Txid(), p)
tx.db.freelist.Free(tx.meta.Txid(), p)
} else {
n.free()
}
Expand Down
3 changes: 3 additions & 0 deletions bucket_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,9 @@ func TestBucket_Delete_FreelistOverflow(t *testing.T) {
if reopenFreePages := db.Stats().FreePageN; freePages != reopenFreePages {
t.Fatalf("expected %d free pages, got %+v", freePages, db.Stats())
}
if reopenPendingPages := db.Stats().PendingPageN; reopenPendingPages != 0 {
t.Fatalf("expected no pending pages, got %+v", db.Stats())
}
}

// Ensure that deleting of non-existing key is a no-op.
Expand Down
1 change: 1 addition & 0 deletions cmd/bbolt/command_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"runtime"

"github.com/spf13/cobra"

"go.etcd.io/bbolt/version"
)

Expand Down
24 changes: 16 additions & 8 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

berrors "go.etcd.io/bbolt/errors"
"go.etcd.io/bbolt/internal/common"
fl "go.etcd.io/bbolt/internal/freelist"
)

// The time elapsed between consecutive file locking attempts.
Expand Down Expand Up @@ -133,7 +134,7 @@ type DB struct {
rwtx *Tx
txs []*Tx

freelist *freelist
freelist fl.Interface
freelistLoad sync.Once

pagePool sync.Pool
Expand Down Expand Up @@ -418,12 +419,12 @@ func (db *DB) loadFreelist() {
db.freelist = newFreelist(db.FreelistType)
if !db.hasSyncedFreelist() {
// Reconstruct free list by scanning the DB.
db.freelist.readIDs(db.freepages())
db.freelist.Init(db.freepages())
} else {
// Read free list from freelist page.
db.freelist.read(db.page(db.meta().Freelist()))
db.freelist.Read(db.page(db.meta().Freelist()))
}
db.stats.FreePageN = db.freelist.free_count()
db.stats.FreePageN = db.freelist.FreeCount()
})
}

Expand Down Expand Up @@ -797,7 +798,7 @@ func (db *DB) beginTx() (*Tx, error) {
db.txs = append(db.txs, t)
n := len(db.txs)
if db.freelist != nil {
db.freelist.addReadonlyTXID(t.meta.Txid())
db.freelist.AddReadonlyTXID(t.meta.Txid())
}

// Unlock the meta pages.
Expand Down Expand Up @@ -843,7 +844,7 @@ func (db *DB) beginRWTx() (*Tx, error) {
t := &Tx{writable: true}
t.init(db)
db.rwtx = t
db.freelist.freePages()
db.freelist.ReleasePendingPages()
return t, nil
}

Expand All @@ -867,7 +868,7 @@ func (db *DB) removeTx(tx *Tx) {
}
n := len(db.txs)
if db.freelist != nil {
db.freelist.removeReadonlyTXID(tx.meta.Txid())
db.freelist.RemoveReadonlyTXID(tx.meta.Txid())
}

// Unlock the meta pages.
Expand Down Expand Up @@ -1155,7 +1156,7 @@ func (db *DB) allocate(txid common.Txid, count int) (*common.Page, error) {
p.SetOverflow(uint32(count - 1))

// Use pages from the freelist if they are available.
p.SetId(db.freelist.allocate(txid, count))
p.SetId(db.freelist.Allocate(txid, count))
if p.Id() != 0 {
return p, nil
}
Expand Down Expand Up @@ -1261,6 +1262,13 @@ func (db *DB) freepages() []common.Pgid {
return fids
}

func newFreelist(freelistType FreelistType) fl.Interface {
if freelistType == FreelistMapType {
return fl.NewHashMapFreelist()
}
return fl.NewArrayFreelist()
}

// Options represents the options that can be set when opening a database.
type Options struct {
// Timeout is the amount of time to wait to obtain a file lock.
Expand Down
Loading