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