diff --git a/freelist.go b/freelist.go index 731d75c46..97d3b9c57 100644 --- a/freelist.go +++ b/freelist.go @@ -389,5 +389,29 @@ func (f *freelist) reindex() { // arrayMergeSpans try to merge list of pages(represented by pgids) with existing spans but using array func (f *freelist) arrayMergeSpans(ids common.Pgids) { sort.Sort(ids) + common.Verify(func() { + idsIdx := make(map[common.Pgid]struct{}) + for _, id := range f.ids { + // The existing f.ids shouldn't have duplicated free ID. + if _, ok := idsIdx[id]; ok { + panic(fmt.Sprintf("detected duplicated free page ID: %d in existing f.ids: %v", id, f.ids)) + } + idsIdx[id] = struct{}{} + } + + prev := common.Pgid(0) + for _, id := range ids { + // The ids shouldn't have duplicated free ID. + if prev == id { + panic(fmt.Sprintf("detected duplicated free ID: %d in ids: %v", id, ids)) + } + prev = id + + // The ids shouldn't have any overlap with the existing f.ids. + if _, ok := idsIdx[id]; ok { + panic(fmt.Sprintf("detected overlapped free page ID: %d between ids: %v and existing f.ids: %v", id, ids, f.ids)) + } + } + }) f.ids = common.Pgids(f.ids).Merge(ids) } diff --git a/freelist_hmap.go b/freelist_hmap.go index 0d38976a1..c5dfa9cde 100644 --- a/freelist_hmap.go +++ b/freelist_hmap.go @@ -1,6 +1,8 @@ package bbolt import ( + "fmt" + "reflect" "sort" "go.etcd.io/bbolt/internal/common" @@ -108,6 +110,33 @@ func (f *freelist) hashmapGetFreePageIDs() []common.Pgid { // hashmapMergeSpans try to merge list of pages(represented by pgids) with existing spans func (f *freelist) hashmapMergeSpans(ids common.Pgids) { + common.Verify(func() { + ids1 := f.idsFromFreemaps() + ids2 := f.idsFromForwardMap() + ids3 := f.idsFromBackwardMap() + + if !reflect.DeepEqual(ids1, ids2) { + panic(fmt.Sprintf("Detected mismatch, f.freemaps: %v, f.forwardMap: %v", f.freemaps, f.forwardMap)) + } + if !reflect.DeepEqual(ids1, ids3) { + panic(fmt.Sprintf("Detected mismatch, f.freemaps: %v, f.backwardMap: %v", f.freemaps, f.backwardMap)) + } + + sort.Sort(ids) + prev := common.Pgid(0) + for _, id := range ids { + // The ids shouldn't have duplicated free ID. + if prev == id { + panic(fmt.Sprintf("detected duplicated free ID: %d in ids: %v", id, ids)) + } + prev = id + + // The ids shouldn't have any overlap with the existing f.freemaps. + if _, ok := ids1[id]; ok { + panic(fmt.Sprintf("detected overlapped free page ID: %d between ids: %v and existing f.freemaps: %v", id, ids, f.freemaps)) + } + } + }) for _, id := range ids { // try to see if we can merge and update f.mergeWithExistingSpan(id) @@ -200,3 +229,53 @@ func (f *freelist) init(pgids []common.Pgid) { f.addSpan(start, size) } } + +// idsFromFreemaps get all free page IDs from f.freemaps. +// used by test only. +func (f *freelist) idsFromFreemaps() map[common.Pgid]struct{} { + ids := make(map[common.Pgid]struct{}) + for size, idSet := range f.freemaps { + for start := range idSet { + for i := 0; i < int(size); i++ { + id := start + common.Pgid(i) + if _, ok := ids[id]; ok { + panic(fmt.Sprintf("detected duplicated free page ID: %d in f.freemaps: %v", id, f.freemaps)) + } + ids[id] = struct{}{} + } + } + } + return ids +} + +// idsFromForwardMap get all free page IDs from f.forwardMap. +// used by test only. +func (f *freelist) idsFromForwardMap() map[common.Pgid]struct{} { + ids := make(map[common.Pgid]struct{}) + for start, size := range f.forwardMap { + for i := 0; i < int(size); i++ { + id := start + common.Pgid(i) + if _, ok := ids[id]; ok { + panic(fmt.Sprintf("detected duplicated free page ID: %d in f.forwardMap: %v", id, f.forwardMap)) + } + ids[id] = struct{}{} + } + } + return ids +} + +// idsFromBackwardMap get all free page IDs from f.backwardMap. +// used by test only. +func (f *freelist) idsFromBackwardMap() map[common.Pgid]struct{} { + ids := make(map[common.Pgid]struct{}) + for end, size := range f.backwardMap { + for i := 0; i < int(size); i++ { + id := end - common.Pgid(i) + if _, ok := ids[id]; ok { + panic(fmt.Sprintf("detected duplicated free page ID: %d in f.backwardMap: %v", id, f.backwardMap)) + } + ids[id] = struct{}{} + } + } + return ids +}