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  }