1 package bbolt
2
3 import (
4 "math/rand"
5 "os"
6 "reflect"
7 "sort"
8 "testing"
9 "unsafe"
10 )
11
12
13 const TestFreelistType = "TEST_FREELIST_TYPE"
14
15
16 func TestFreelist_free(t *testing.T) {
17 f := newTestFreelist()
18 f.free(100, &page{id: 12})
19 if !reflect.DeepEqual([]pgid{12}, f.pending[100].ids) {
20 t.Fatalf("exp=%v; got=%v", []pgid{12}, f.pending[100].ids)
21 }
22 }
23
24
25 func TestFreelist_free_overflow(t *testing.T) {
26 f := newTestFreelist()
27 f.free(100, &page{id: 12, overflow: 3})
28 if exp := []pgid{12, 13, 14, 15}; !reflect.DeepEqual(exp, f.pending[100].ids) {
29 t.Fatalf("exp=%v; got=%v", exp, f.pending[100].ids)
30 }
31 }
32
33
34 func TestFreelist_release(t *testing.T) {
35 f := newTestFreelist()
36 f.free(100, &page{id: 12, overflow: 1})
37 f.free(100, &page{id: 9})
38 f.free(102, &page{id: 39})
39 f.release(100)
40 f.release(101)
41 if exp := []pgid{9, 12, 13}; !reflect.DeepEqual(exp, f.getFreePageIDs()) {
42 t.Fatalf("exp=%v; got=%v", exp, f.getFreePageIDs())
43 }
44
45 f.release(102)
46 if exp := []pgid{9, 12, 13, 39}; !reflect.DeepEqual(exp, f.getFreePageIDs()) {
47 t.Fatalf("exp=%v; got=%v", exp, f.getFreePageIDs())
48 }
49 }
50
51
52 func TestFreelist_releaseRange(t *testing.T) {
53 type testRange struct {
54 begin, end txid
55 }
56
57 type testPage struct {
58 id pgid
59 n int
60 allocTxn txid
61 freeTxn txid
62 }
63
64 var releaseRangeTests = []struct {
65 title string
66 pagesIn []testPage
67 releaseRanges []testRange
68 wantFree []pgid
69 }{
70 {
71 title: "Single pending in range",
72 pagesIn: []testPage{{id: 3, n: 1, allocTxn: 100, freeTxn: 200}},
73 releaseRanges: []testRange{{1, 300}},
74 wantFree: []pgid{3},
75 },
76 {
77 title: "Single pending with minimum end range",
78 pagesIn: []testPage{{id: 3, n: 1, allocTxn: 100, freeTxn: 200}},
79 releaseRanges: []testRange{{1, 200}},
80 wantFree: []pgid{3},
81 },
82 {
83 title: "Single pending outsize minimum end range",
84 pagesIn: []testPage{{id: 3, n: 1, allocTxn: 100, freeTxn: 200}},
85 releaseRanges: []testRange{{1, 199}},
86 wantFree: nil,
87 },
88 {
89 title: "Single pending with minimum begin range",
90 pagesIn: []testPage{{id: 3, n: 1, allocTxn: 100, freeTxn: 200}},
91 releaseRanges: []testRange{{100, 300}},
92 wantFree: []pgid{3},
93 },
94 {
95 title: "Single pending outside minimum begin range",
96 pagesIn: []testPage{{id: 3, n: 1, allocTxn: 100, freeTxn: 200}},
97 releaseRanges: []testRange{{101, 300}},
98 wantFree: nil,
99 },
100 {
101 title: "Single pending in minimum range",
102 pagesIn: []testPage{{id: 3, n: 1, allocTxn: 199, freeTxn: 200}},
103 releaseRanges: []testRange{{199, 200}},
104 wantFree: []pgid{3},
105 },
106 {
107 title: "Single pending and read transaction at 199",
108 pagesIn: []testPage{{id: 3, n: 1, allocTxn: 199, freeTxn: 200}},
109 releaseRanges: []testRange{{100, 198}, {200, 300}},
110 wantFree: nil,
111 },
112 {
113 title: "Adjacent pending and read transactions at 199, 200",
114 pagesIn: []testPage{
115 {id: 3, n: 1, allocTxn: 199, freeTxn: 200},
116 {id: 4, n: 1, allocTxn: 200, freeTxn: 201},
117 },
118 releaseRanges: []testRange{
119 {100, 198},
120 {200, 199},
121 {201, 300},
122 },
123 wantFree: nil,
124 },
125 {
126 title: "Out of order ranges",
127 pagesIn: []testPage{
128 {id: 3, n: 1, allocTxn: 199, freeTxn: 200},
129 {id: 4, n: 1, allocTxn: 200, freeTxn: 201},
130 },
131 releaseRanges: []testRange{
132 {201, 199},
133 {201, 200},
134 {200, 200},
135 },
136 wantFree: nil,
137 },
138 {
139 title: "Multiple pending, read transaction at 150",
140 pagesIn: []testPage{
141 {id: 3, n: 1, allocTxn: 100, freeTxn: 200},
142 {id: 4, n: 1, allocTxn: 100, freeTxn: 125},
143 {id: 5, n: 1, allocTxn: 125, freeTxn: 150},
144 {id: 6, n: 1, allocTxn: 125, freeTxn: 175},
145 {id: 7, n: 2, allocTxn: 150, freeTxn: 175},
146 {id: 9, n: 2, allocTxn: 175, freeTxn: 200},
147 },
148 releaseRanges: []testRange{{50, 149}, {151, 300}},
149 wantFree: []pgid{4, 9, 10},
150 },
151 }
152
153 for _, c := range releaseRangeTests {
154 f := newTestFreelist()
155 var ids []pgid
156 for _, p := range c.pagesIn {
157 for i := uint64(0); i < uint64(p.n); i++ {
158 ids = append(ids, pgid(uint64(p.id)+i))
159 }
160 }
161 f.readIDs(ids)
162 for _, p := range c.pagesIn {
163 f.allocate(p.allocTxn, p.n)
164 }
165
166 for _, p := range c.pagesIn {
167 f.free(p.freeTxn, &page{id: p.id, overflow: uint32(p.n - 1)})
168 }
169
170 for _, r := range c.releaseRanges {
171 f.releaseRange(r.begin, r.end)
172 }
173
174 if exp := c.wantFree; !reflect.DeepEqual(exp, f.getFreePageIDs()) {
175 t.Errorf("exp=%v; got=%v for %s", exp, f.getFreePageIDs(), c.title)
176 }
177 }
178 }
179
180 func TestFreelistHashmap_allocate(t *testing.T) {
181 f := newTestFreelist()
182 if f.freelistType != FreelistMapType {
183 t.Skip()
184 }
185
186 ids := []pgid{3, 4, 5, 6, 7, 9, 12, 13, 18}
187 f.readIDs(ids)
188
189 f.allocate(1, 3)
190 if x := f.free_count(); x != 6 {
191 t.Fatalf("exp=6; got=%v", x)
192 }
193
194 f.allocate(1, 2)
195 if x := f.free_count(); x != 4 {
196 t.Fatalf("exp=4; got=%v", x)
197 }
198 f.allocate(1, 1)
199 if x := f.free_count(); x != 3 {
200 t.Fatalf("exp=3; got=%v", x)
201 }
202
203 f.allocate(1, 0)
204 if x := f.free_count(); x != 3 {
205 t.Fatalf("exp=3; got=%v", x)
206 }
207 }
208
209
210 func TestFreelistArray_allocate(t *testing.T) {
211 f := newTestFreelist()
212 if f.freelistType != FreelistArrayType {
213 t.Skip()
214 }
215 ids := []pgid{3, 4, 5, 6, 7, 9, 12, 13, 18}
216 f.readIDs(ids)
217 if id := int(f.allocate(1, 3)); id != 3 {
218 t.Fatalf("exp=3; got=%v", id)
219 }
220 if id := int(f.allocate(1, 1)); id != 6 {
221 t.Fatalf("exp=6; got=%v", id)
222 }
223 if id := int(f.allocate(1, 3)); id != 0 {
224 t.Fatalf("exp=0; got=%v", id)
225 }
226 if id := int(f.allocate(1, 2)); id != 12 {
227 t.Fatalf("exp=12; got=%v", id)
228 }
229 if id := int(f.allocate(1, 1)); id != 7 {
230 t.Fatalf("exp=7; got=%v", id)
231 }
232 if id := int(f.allocate(1, 0)); id != 0 {
233 t.Fatalf("exp=0; got=%v", id)
234 }
235 if id := int(f.allocate(1, 0)); id != 0 {
236 t.Fatalf("exp=0; got=%v", id)
237 }
238 if exp := []pgid{9, 18}; !reflect.DeepEqual(exp, f.getFreePageIDs()) {
239 t.Fatalf("exp=%v; got=%v", exp, f.getFreePageIDs())
240 }
241
242 if id := int(f.allocate(1, 1)); id != 9 {
243 t.Fatalf("exp=9; got=%v", id)
244 }
245 if id := int(f.allocate(1, 1)); id != 18 {
246 t.Fatalf("exp=18; got=%v", id)
247 }
248 if id := int(f.allocate(1, 1)); id != 0 {
249 t.Fatalf("exp=0; got=%v", id)
250 }
251 if exp := []pgid{}; !reflect.DeepEqual(exp, f.getFreePageIDs()) {
252 t.Fatalf("exp=%v; got=%v", exp, f.getFreePageIDs())
253 }
254 }
255
256
257 func TestFreelist_read(t *testing.T) {
258
259 var buf [4096]byte
260 page := (*page)(unsafe.Pointer(&buf[0]))
261 page.flags = freelistPageFlag
262 page.count = 2
263
264
265 ids := (*[3]pgid)(unsafe.Pointer(uintptr(unsafe.Pointer(page)) + unsafe.Sizeof(*page)))
266 ids[0] = 23
267 ids[1] = 50
268
269
270 f := newTestFreelist()
271 f.read(page)
272
273
274 if exp := []pgid{23, 50}; !reflect.DeepEqual(exp, f.getFreePageIDs()) {
275 t.Fatalf("exp=%v; got=%v", exp, f.getFreePageIDs())
276 }
277 }
278
279
280 func TestFreelist_write(t *testing.T) {
281
282 var buf [4096]byte
283 f := newTestFreelist()
284
285 f.readIDs([]pgid{12, 39})
286 f.pending[100] = &txPending{ids: []pgid{28, 11}}
287 f.pending[101] = &txPending{ids: []pgid{3}}
288 p := (*page)(unsafe.Pointer(&buf[0]))
289 if err := f.write(p); err != nil {
290 t.Fatal(err)
291 }
292
293
294 f2 := newTestFreelist()
295 f2.read(p)
296
297
298
299 if exp := []pgid{3, 11, 12, 28, 39}; !reflect.DeepEqual(exp, f2.getFreePageIDs()) {
300 t.Fatalf("exp=%v; got=%v", exp, f2.getFreePageIDs())
301 }
302 }
303
304 func Benchmark_FreelistRelease10K(b *testing.B) { benchmark_FreelistRelease(b, 10000) }
305 func Benchmark_FreelistRelease100K(b *testing.B) { benchmark_FreelistRelease(b, 100000) }
306 func Benchmark_FreelistRelease1000K(b *testing.B) { benchmark_FreelistRelease(b, 1000000) }
307 func Benchmark_FreelistRelease10000K(b *testing.B) { benchmark_FreelistRelease(b, 10000000) }
308
309 func benchmark_FreelistRelease(b *testing.B, size int) {
310 ids := randomPgids(size)
311 pending := randomPgids(len(ids) / 400)
312 b.ResetTimer()
313 for i := 0; i < b.N; i++ {
314 txp := &txPending{ids: pending}
315 f := newTestFreelist()
316 f.pending = map[txid]*txPending{1: txp}
317 f.readIDs(ids)
318 f.release(1)
319 }
320 }
321
322 func randomPgids(n int) []pgid {
323 rand.Seed(42)
324 pgids := make(pgids, n)
325 for i := range pgids {
326 pgids[i] = pgid(rand.Int63())
327 }
328 sort.Sort(pgids)
329 return pgids
330 }
331
332 func Test_freelist_ReadIDs_and_getFreePageIDs(t *testing.T) {
333 f := newTestFreelist()
334 exp := []pgid{3, 4, 5, 6, 7, 9, 12, 13, 18}
335
336 f.readIDs(exp)
337
338 if got := f.getFreePageIDs(); !reflect.DeepEqual(exp, got) {
339 t.Fatalf("exp=%v; got=%v", exp, got)
340 }
341
342 f2 := newTestFreelist()
343 var exp2 []pgid
344 f2.readIDs(exp2)
345
346 if got2 := f2.getFreePageIDs(); !reflect.DeepEqual(got2, exp2) {
347 t.Fatalf("exp2=%#v; got2=%#v", exp2, got2)
348 }
349
350 }
351
352 func Test_freelist_mergeWithExist(t *testing.T) {
353 bm1 := pidSet{1: struct{}{}}
354
355 bm2 := pidSet{5: struct{}{}}
356 tests := []struct {
357 name string
358 ids []pgid
359 pgid pgid
360 want []pgid
361 wantForwardmap map[pgid]uint64
362 wantBackwardmap map[pgid]uint64
363 wantfreemap map[uint64]pidSet
364 }{
365 {
366 name: "test1",
367 ids: []pgid{1, 2, 4, 5, 6},
368 pgid: 3,
369 want: []pgid{1, 2, 3, 4, 5, 6},
370 wantForwardmap: map[pgid]uint64{1: 6},
371 wantBackwardmap: map[pgid]uint64{6: 6},
372 wantfreemap: map[uint64]pidSet{6: bm1},
373 },
374 {
375 name: "test2",
376 ids: []pgid{1, 2, 5, 6},
377 pgid: 3,
378 want: []pgid{1, 2, 3, 5, 6},
379 wantForwardmap: map[pgid]uint64{1: 3, 5: 2},
380 wantBackwardmap: map[pgid]uint64{6: 2, 3: 3},
381 wantfreemap: map[uint64]pidSet{3: bm1, 2: bm2},
382 },
383 {
384 name: "test3",
385 ids: []pgid{1, 2},
386 pgid: 3,
387 want: []pgid{1, 2, 3},
388 wantForwardmap: map[pgid]uint64{1: 3},
389 wantBackwardmap: map[pgid]uint64{3: 3},
390 wantfreemap: map[uint64]pidSet{3: bm1},
391 },
392 {
393 name: "test4",
394 ids: []pgid{2, 3},
395 pgid: 1,
396 want: []pgid{1, 2, 3},
397 wantForwardmap: map[pgid]uint64{1: 3},
398 wantBackwardmap: map[pgid]uint64{3: 3},
399 wantfreemap: map[uint64]pidSet{3: bm1},
400 },
401 }
402 for _, tt := range tests {
403 f := newTestFreelist()
404 if f.freelistType == FreelistArrayType {
405 t.Skip()
406 }
407 f.readIDs(tt.ids)
408
409 f.mergeWithExistingSpan(tt.pgid)
410
411 if got := f.getFreePageIDs(); !reflect.DeepEqual(tt.want, got) {
412 t.Fatalf("name %s; exp=%v; got=%v", tt.name, tt.want, got)
413 }
414 if got := f.forwardMap; !reflect.DeepEqual(tt.wantForwardmap, got) {
415 t.Fatalf("name %s; exp=%v; got=%v", tt.name, tt.wantForwardmap, got)
416 }
417 if got := f.backwardMap; !reflect.DeepEqual(tt.wantBackwardmap, got) {
418 t.Fatalf("name %s; exp=%v; got=%v", tt.name, tt.wantBackwardmap, got)
419 }
420 if got := f.freemaps; !reflect.DeepEqual(tt.wantfreemap, got) {
421 t.Fatalf("name %s; exp=%v; got=%v", tt.name, tt.wantfreemap, got)
422 }
423 }
424 }
425
426
427 func newTestFreelist() *freelist {
428 freelistType := FreelistArrayType
429 if env := os.Getenv(TestFreelistType); env == string(FreelistMapType) {
430 freelistType = FreelistMapType
431 }
432
433 return newFreelist(freelistType)
434 }
435
View as plain text