Skip to content

Commit 49c7697

Browse files
authored
Merge pull request #794 from tjungblu/791_inconsistency
ensure hashmap init clears maps
2 parents 4a77dd9 + f4de460 commit 49c7697

File tree

4 files changed

+77
-35
lines changed

4 files changed

+77
-35
lines changed

internal/freelist/array.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ func (f *array) mergeSpans(ids common.Pgids) {
101101
func NewArrayFreelist() Interface {
102102
a := &array{
103103
shared: newShared(),
104+
ids: []common.Pgid{},
104105
}
105106
a.Interface = a
106107
return a

internal/freelist/freelist_test.go

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"testing"
99
"unsafe"
1010

11+
"github.com/stretchr/testify/require"
12+
1113
"go.etcd.io/bbolt/internal/common"
1214
)
1315

@@ -85,7 +87,7 @@ func TestFreelist_releaseRange(t *testing.T) {
8587
title: "Single pending outsize minimum end range",
8688
pagesIn: []testPage{{id: 3, n: 1, allocTxn: 100, freeTxn: 200}},
8789
releaseRanges: []testRange{{1, 199}},
88-
wantFree: nil,
90+
wantFree: []common.Pgid{},
8991
},
9092
{
9193
title: "Single pending with minimum begin range",
@@ -97,7 +99,7 @@ func TestFreelist_releaseRange(t *testing.T) {
9799
title: "Single pending outside minimum begin range",
98100
pagesIn: []testPage{{id: 3, n: 1, allocTxn: 100, freeTxn: 200}},
99101
releaseRanges: []testRange{{101, 300}},
100-
wantFree: nil,
102+
wantFree: []common.Pgid{},
101103
},
102104
{
103105
title: "Single pending in minimum range",
@@ -109,7 +111,7 @@ func TestFreelist_releaseRange(t *testing.T) {
109111
title: "Single pending and read transaction at 199",
110112
pagesIn: []testPage{{id: 3, n: 1, allocTxn: 199, freeTxn: 200}},
111113
releaseRanges: []testRange{{100, 198}, {200, 300}},
112-
wantFree: nil,
114+
wantFree: []common.Pgid{},
113115
},
114116
{
115117
title: "Adjacent pending and read transactions at 199, 200",
@@ -122,7 +124,7 @@ func TestFreelist_releaseRange(t *testing.T) {
122124
{200, 199}, // Simulate the ranges db.freePages might produce.
123125
{201, 300},
124126
},
125-
wantFree: nil,
127+
wantFree: []common.Pgid{},
126128
},
127129
{
128130
title: "Out of order ranges",
@@ -135,7 +137,7 @@ func TestFreelist_releaseRange(t *testing.T) {
135137
{201, 200},
136138
{200, 200},
137139
},
138-
wantFree: nil,
140+
wantFree: []common.Pgid{},
139141
},
140142
{
141143
title: "Multiple pending, read transaction at 150",
@@ -153,32 +155,71 @@ func TestFreelist_releaseRange(t *testing.T) {
153155
}
154156

155157
for _, c := range releaseRangeTests {
156-
f := newTestFreelist()
157-
var ids []common.Pgid
158-
for _, p := range c.pagesIn {
159-
for i := uint64(0); i < uint64(p.n); i++ {
160-
ids = append(ids, common.Pgid(uint64(p.id)+i))
158+
t.Run(c.title, func(t *testing.T) {
159+
f := newTestFreelist()
160+
var ids []common.Pgid
161+
for _, p := range c.pagesIn {
162+
for i := uint64(0); i < uint64(p.n); i++ {
163+
ids = append(ids, common.Pgid(uint64(p.id)+i))
164+
}
165+
}
166+
f.Init(ids)
167+
for _, p := range c.pagesIn {
168+
f.Allocate(p.allocTxn, p.n)
161169
}
162-
}
163-
f.Init(ids)
164-
for _, p := range c.pagesIn {
165-
f.Allocate(p.allocTxn, p.n)
166-
}
167170

168-
for _, p := range c.pagesIn {
169-
f.Free(p.freeTxn, common.NewPage(p.id, 0, 0, uint32(p.n-1)))
170-
}
171+
for _, p := range c.pagesIn {
172+
f.Free(p.freeTxn, common.NewPage(p.id, 0, 0, uint32(p.n-1)))
173+
}
171174

172-
for _, r := range c.releaseRanges {
173-
f.releaseRange(r.begin, r.end)
174-
}
175+
for _, r := range c.releaseRanges {
176+
f.releaseRange(r.begin, r.end)
177+
}
175178

176-
if exp := common.Pgids(c.wantFree); !reflect.DeepEqual(exp, f.freePageIds()) {
177-
t.Errorf("exp=%v; got=%v for %s", exp, f.freePageIds(), c.title)
178-
}
179+
require.Equal(t, common.Pgids(c.wantFree), f.freePageIds())
180+
})
179181
}
180182
}
181183

184+
func TestFreeList_init(t *testing.T) {
185+
buf := make([]byte, 4096)
186+
f := newTestFreelist()
187+
f.Init(common.Pgids{5, 6, 8})
188+
189+
p := common.LoadPage(buf)
190+
f.Write(p)
191+
192+
f2 := newTestFreelist()
193+
f2.Read(p)
194+
require.Equal(t, common.Pgids{5, 6, 8}, f2.freePageIds())
195+
196+
// When initializing the freelist with an empty list of page ID,
197+
// it should reset the freelist page IDs.
198+
f2.Init([]common.Pgid{})
199+
require.Equal(t, common.Pgids{}, f2.freePageIds())
200+
}
201+
202+
func TestFreeList_reload(t *testing.T) {
203+
buf := make([]byte, 4096)
204+
f := newTestFreelist()
205+
f.Init(common.Pgids{5, 6, 8})
206+
207+
p := common.LoadPage(buf)
208+
f.Write(p)
209+
210+
f2 := newTestFreelist()
211+
f2.Read(p)
212+
require.Equal(t, common.Pgids{5, 6, 8}, f2.freePageIds())
213+
214+
f2.Free(common.Txid(5), common.NewPage(10, common.LeafPageFlag, 0, 2))
215+
216+
// reload shouldn't affect the pending list
217+
f2.Reload(p)
218+
219+
require.Equal(t, common.Pgids{5, 6, 8}, f2.freePageIds())
220+
require.Equal(t, []common.Pgid{10, 11, 12}, f2.pendingPageIds()[5].ids)
221+
}
222+
182223
// Ensure that a freelist can deserialize from a freelist page.
183224
func TestFreelist_read(t *testing.T) {
184225
// Create a page.
@@ -263,7 +304,7 @@ func Test_freelist_ReadIDs_and_getFreePageIDs(t *testing.T) {
263304
}
264305

265306
f2 := newTestFreelist()
266-
var exp2 []common.Pgid
307+
exp2 := []common.Pgid{}
267308
f2.Init(exp2)
268309

269310
if got2 := f2.freePageIds(); !reflect.DeepEqual(got2, common.Pgids(exp2)) {

internal/freelist/hashmap.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,22 @@ type hashMap struct {
2121
}
2222

2323
func (f *hashMap) Init(pgids common.Pgids) {
24+
// reset the counter when freelist init
25+
f.freePagesCount = 0
26+
f.freemaps = make(map[uint64]pidSet)
27+
f.forwardMap = make(map[common.Pgid]uint64)
28+
f.backwardMap = make(map[common.Pgid]uint64)
29+
2430
if len(pgids) == 0 {
2531
return
2632
}
2733

28-
size := uint64(1)
29-
start := pgids[0]
30-
// reset the counter when freelist init
31-
f.freePagesCount = 0
32-
3334
if !sort.SliceIsSorted([]common.Pgid(pgids), func(i, j int) bool { return pgids[i] < pgids[j] }) {
3435
panic("pgids not sorted")
3536
}
3637

37-
f.freemaps = make(map[uint64]pidSet)
38-
f.forwardMap = make(map[common.Pgid]uint64)
39-
f.backwardMap = make(map[common.Pgid]uint64)
38+
size := uint64(1)
39+
start := pgids[0]
4040

4141
for i := 1; i < len(pgids); i++ {
4242
// continuous page
@@ -117,7 +117,7 @@ func (f *hashMap) FreeCount() int {
117117
func (f *hashMap) freePageIds() common.Pgids {
118118
count := f.FreeCount()
119119
if count == 0 {
120-
return nil
120+
return common.Pgids{}
121121
}
122122

123123
m := make([]common.Pgid, 0, count)

internal/freelist/shared.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ func (t *shared) releaseRange(begin, end common.Txid) {
164164
if begin > end {
165165
return
166166
}
167-
var m common.Pgids
167+
m := common.Pgids{}
168168
for tid, txp := range t.pending {
169169
if tid < begin || tid > end {
170170
continue

0 commit comments

Comments
 (0)