Skip to content
Closed
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
2 changes: 1 addition & 1 deletion db.go
Original file line number Diff line number Diff line change
Expand Up @@ -1251,7 +1251,7 @@ func (db *DB) freepages() []common.Pgid {
}

reachable := make(map[common.Pgid]*common.Page)
nofreed := make(map[common.Pgid]bool)
nofreed := make(common.PgidSet)
ech := make(chan error)
go func() {
for e := range ech {
Expand Down
20 changes: 20 additions & 0 deletions internal/common/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,3 +389,23 @@ func Mergepgids(dst, a, b Pgids) {
// Append what's left in follow.
_ = append(merged, follow...)
}

type PgidSet map[Pgid]struct{}

func (s *PgidSet) Add(key Pgid) {
if *s == nil {
*s = make(map[Pgid]struct{})
}

(*s)[key] = struct{}{}
}

func (s *PgidSet) Has(key Pgid) bool {
if *s == nil {
return false
}

_, ok := (*s)[key]

return ok
}
40 changes: 40 additions & 0 deletions internal/common/page_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,43 @@ func TestPgids_merge_quick(t *testing.T) {
t.Fatal(err)
}
}

func TestPgidSet_initialization(t *testing.T) {
var s PgidSet
if s != nil {
t.Fatal("Set must be nil")
}

s.Add(0)
if s == nil {
t.Fatal("Set must be initialized")
}
}

func TestPgidSet_set_has_added_values(t *testing.T) {
var s PgidSet
s.Add(100)
if len(s) != 1 || !s.Has(100) {
t.Fatal("Set must contain exactly one element")
}

s.Add(200)
if len(s) != 2 || !s.Has(200) {
t.Fatal("Set must contain exactly two elements")
}
}

func TestPgidSet_duplicates(t *testing.T) {
var s PgidSet
s.Add(5)
s.Add(5)
if len(s) != 1 {
t.Fatal("Set must still contain exactly one element after adding duplicate")
}

s.Add(15)
s.Add(15)
if len(s) != 2 {
t.Fatal("Set must still contain exactly two elements after adding duplicate")
}
}
6 changes: 3 additions & 3 deletions internal/freelist/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,18 +220,18 @@ func (t *shared) Reload(p *common.Page) {

func (t *shared) NoSyncReload(pgIds common.Pgids) {
// Build a cache of only pending pages.
pcache := make(map[common.Pgid]struct{})
pcache := make(common.PgidSet)
for _, txp := range t.pending {
for _, pendingID := range txp.ids {
pcache[pendingID] = struct{}{}
pcache.Add(pendingID)
}
}

// Check each page in the freelist and build a new available freelist
// with any pages not in the pending lists.
a := []common.Pgid{}
for _, id := range pgIds {
if _, ok := pcache[id]; !ok {
if !pcache.Has(id) {
a = append(a, id)
}
}
Expand Down
20 changes: 10 additions & 10 deletions tx_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ func (tx *Tx) check(cfg checkConfig, ch chan error) {
tx.db.loadFreelist()

// Check if any pages are double freed.
freed := make(map[common.Pgid]bool)
freed := make(common.PgidSet)
all := make([]common.Pgid, tx.db.freelist.Count())
tx.db.freelist.Copyall(all)
for _, id := range all {
if freed[id] {
if freed.Has(id) {
ch <- fmt.Errorf("page %d: already freed", id)
}
freed[id] = true
freed.Add(id)
}

// Track every reachable page.
Expand All @@ -68,7 +68,7 @@ func (tx *Tx) check(cfg checkConfig, ch chan error) {
// Ensure all pages below high water mark are either reachable or freed.
for i := common.Pgid(0); i < tx.meta.Pgid(); i++ {
_, isReachable := reachable[i]
if !isReachable && !freed[i] {
if !isReachable && !freed.Has(i) {
ch <- fmt.Errorf("page %d: unreachable unfreed", int(i))
}
}
Expand All @@ -83,13 +83,13 @@ func (tx *Tx) check(cfg checkConfig, ch chan error) {
}
}

func (tx *Tx) recursivelyCheckPage(pageId common.Pgid, reachable map[common.Pgid]*common.Page, freed map[common.Pgid]bool,
func (tx *Tx) recursivelyCheckPage(pageId common.Pgid, reachable map[common.Pgid]*common.Page, freed common.PgidSet,
kvStringer KVStringer, ch chan error) {
tx.checkInvariantProperties(pageId, reachable, freed, kvStringer, ch)
tx.recursivelyCheckBucketInPage(pageId, reachable, freed, kvStringer, ch)
}

func (tx *Tx) recursivelyCheckBucketInPage(pageId common.Pgid, reachable map[common.Pgid]*common.Page, freed map[common.Pgid]bool,
func (tx *Tx) recursivelyCheckBucketInPage(pageId common.Pgid, reachable map[common.Pgid]*common.Page, freed common.PgidSet,
kvStringer KVStringer, ch chan error) {
p := tx.page(pageId)

Expand Down Expand Up @@ -120,7 +120,7 @@ func (tx *Tx) recursivelyCheckBucketInPage(pageId common.Pgid, reachable map[com
}
}

func (tx *Tx) recursivelyCheckBucket(b *Bucket, reachable map[common.Pgid]*common.Page, freed map[common.Pgid]bool,
func (tx *Tx) recursivelyCheckBucket(b *Bucket, reachable map[common.Pgid]*common.Page, freed common.PgidSet,
kvStringer KVStringer, ch chan error) {
// Ignore inline buckets.
if b.RootPage() == 0 {
Expand All @@ -138,7 +138,7 @@ func (tx *Tx) recursivelyCheckBucket(b *Bucket, reachable map[common.Pgid]*commo
})
}

func (tx *Tx) checkInvariantProperties(pageId common.Pgid, reachable map[common.Pgid]*common.Page, freed map[common.Pgid]bool,
func (tx *Tx) checkInvariantProperties(pageId common.Pgid, reachable map[common.Pgid]*common.Page, freed common.PgidSet,
kvStringer KVStringer, ch chan error) {
tx.forEachPage(pageId, func(p *common.Page, _ int, stack []common.Pgid) {
verifyPageReachable(p, tx.meta.Pgid(), stack, reachable, freed, ch)
Expand All @@ -147,7 +147,7 @@ func (tx *Tx) checkInvariantProperties(pageId common.Pgid, reachable map[common.
tx.recursivelyCheckPageKeyOrder(pageId, kvStringer.KeyToString, ch)
}

func verifyPageReachable(p *common.Page, hwm common.Pgid, stack []common.Pgid, reachable map[common.Pgid]*common.Page, freed map[common.Pgid]bool, ch chan error) {
func verifyPageReachable(p *common.Page, hwm common.Pgid, stack []common.Pgid, reachable map[common.Pgid]*common.Page, freed common.PgidSet, ch chan error) {
if p.Id() > hwm {
ch <- fmt.Errorf("page %d: out of bounds: %d (stack: %v)", int(p.Id()), int(hwm), stack)
}
Expand All @@ -162,7 +162,7 @@ func verifyPageReachable(p *common.Page, hwm common.Pgid, stack []common.Pgid, r
}

// We should only encounter un-freed leaf and branch pages.
if freed[p.Id()] {
if freed.Has(p.Id()) {
ch <- fmt.Errorf("page %d: reachable freed", int(p.Id()))
} else if !p.IsBranchPage() && !p.IsLeafPage() {
ch <- fmt.Errorf("page %d: invalid type: %s (stack: %v)", int(p.Id()), p.Typ(), stack)
Expand Down