github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/bitree/bdb/freelist.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  	"fmt"
    19  	"sort"
    20  	"unsafe"
    21  
    22  	"github.com/RoaringBitmap/roaring/roaring64"
    23  	"github.com/zuoyebang/bitalosdb/internal/consts"
    24  )
    25  
    26  type txPending struct {
    27  	ids              []pgid
    28  	alloctx          []txid
    29  	lastReleaseBegin txid
    30  }
    31  
    32  type pidSet map[pgid]struct{}
    33  
    34  type freelist struct {
    35  	freelistType   string
    36  	ids            []pgid
    37  	allocs         map[pgid]txid
    38  	pending        map[txid]*txPending
    39  	cache          map[pgid]bool
    40  	freemaps       map[uint64]pidSet
    41  	forwardMap     map[pgid]uint64
    42  	backwardMap    map[pgid]uint64
    43  	allocate       func(txid txid, n int) pgid
    44  	free_count     func() int
    45  	mergeSpans     func(ids pgids)
    46  	getFreePageIDs func() []pgid
    47  	readIDs        func(pgids []pgid)
    48  	convertBitmap  func() (rb *roaring64.Bitmap, count int)
    49  }
    50  
    51  func newFreelist(freelistType string) *freelist {
    52  	f := &freelist{
    53  		freelistType: freelistType,
    54  		allocs:       make(map[pgid]txid),
    55  		pending:      make(map[txid]*txPending),
    56  		cache:        make(map[pgid]bool),
    57  		freemaps:     make(map[uint64]pidSet),
    58  		forwardMap:   make(map[pgid]uint64),
    59  		backwardMap:  make(map[pgid]uint64),
    60  	}
    61  
    62  	if freelistType == consts.BdbFreelistMapType {
    63  		f.allocate = f.hashmapAllocate
    64  		f.free_count = f.hashmapFreeCount
    65  		f.mergeSpans = f.hashmapMergeSpans
    66  		f.getFreePageIDs = f.hashmapGetFreePageIDs
    67  		f.readIDs = f.hashmapReadIDs
    68  		f.convertBitmap = f.hashmapConvertBitmap
    69  	} else {
    70  		f.allocate = f.arrayAllocate
    71  		f.free_count = f.arrayFreeCount
    72  		f.mergeSpans = f.arrayMergeSpans
    73  		f.getFreePageIDs = f.arrayGetFreePageIDs
    74  		f.readIDs = f.arrayReadIDs
    75  		f.convertBitmap = f.arrayConvertBitmap
    76  	}
    77  
    78  	return f
    79  }
    80  
    81  func (f *freelist) size() int {
    82  	n := f.count()
    83  	if n >= 0xFFFF {
    84  		n++
    85  	}
    86  	return int(pageHeaderSize) + (int(unsafe.Sizeof(pgid(0))) * n)
    87  }
    88  
    89  func (f *freelist) count() int {
    90  	return f.free_count() + f.pending_count()
    91  }
    92  
    93  func (f *freelist) arrayFreeCount() int {
    94  	return len(f.ids)
    95  }
    96  
    97  func (f *freelist) pending_count() int {
    98  	var count int
    99  	for _, txp := range f.pending {
   100  		count += len(txp.ids)
   101  	}
   102  	return count
   103  }
   104  
   105  func (f *freelist) copyall(dst []pgid) {
   106  	m := make(pgids, 0, f.pending_count())
   107  	for _, txp := range f.pending {
   108  		m = append(m, txp.ids...)
   109  	}
   110  	sort.Sort(m)
   111  	mergepgids(dst, f.getFreePageIDs(), m)
   112  }
   113  
   114  func (f *freelist) arrayAllocate(txid txid, n int) pgid {
   115  	if len(f.ids) == 0 {
   116  		return 0
   117  	}
   118  
   119  	var initial, previd pgid
   120  	for i, id := range f.ids {
   121  		if id <= 1 {
   122  			panic(fmt.Sprintf("invalid page allocation: %d", id))
   123  		}
   124  
   125  		if previd == 0 || id-previd != 1 {
   126  			initial = id
   127  		}
   128  
   129  		if (id-initial)+1 == pgid(n) {
   130  			if (i + 1) == n {
   131  				f.ids = f.ids[i+1:]
   132  			} else {
   133  				copy(f.ids[i-n+1:], f.ids[i+1:])
   134  				f.ids = f.ids[:len(f.ids)-n]
   135  			}
   136  
   137  			for i := pgid(0); i < pgid(n); i++ {
   138  				delete(f.cache, initial+i)
   139  			}
   140  			f.allocs[initial] = txid
   141  			return initial
   142  		}
   143  
   144  		previd = id
   145  	}
   146  	return 0
   147  }
   148  
   149  func (f *freelist) free(txid txid, p *page) {
   150  	if p.id <= 1 {
   151  		panic(fmt.Sprintf("cannot free page 0 or 1: %d", p.id))
   152  	}
   153  
   154  	txp := f.pending[txid]
   155  	if txp == nil {
   156  		txp = &txPending{}
   157  		f.pending[txid] = txp
   158  	}
   159  	allocTxid, ok := f.allocs[p.id]
   160  	if ok {
   161  		delete(f.allocs, p.id)
   162  	} else if (p.flags & freelistPageFlag) != 0 {
   163  		allocTxid = txid - 1
   164  	}
   165  
   166  	for id := p.id; id <= p.id+pgid(p.overflow); id++ {
   167  		if f.cache[id] {
   168  			panic(fmt.Sprintf("page %d already freed", id))
   169  		}
   170  		txp.ids = append(txp.ids, id)
   171  		txp.alloctx = append(txp.alloctx, allocTxid)
   172  		f.cache[id] = true
   173  	}
   174  }
   175  
   176  func (f *freelist) release(txid txid) pgids {
   177  	m := make(pgids, 0)
   178  	for tid, txp := range f.pending {
   179  		if tid <= txid {
   180  			m = append(m, txp.ids...)
   181  			delete(f.pending, tid)
   182  		}
   183  	}
   184  	f.mergeSpans(m)
   185  	return m
   186  }
   187  
   188  func (f *freelist) releaseRange(begin, end txid) pgids {
   189  	if begin > end {
   190  		return nil
   191  	}
   192  	var m pgids
   193  	for tid, txp := range f.pending {
   194  		if tid < begin || tid > end {
   195  			continue
   196  		}
   197  		if txp.lastReleaseBegin == begin {
   198  			continue
   199  		}
   200  		for i := 0; i < len(txp.ids); i++ {
   201  			if atx := txp.alloctx[i]; atx < begin || atx > end {
   202  				continue
   203  			}
   204  			m = append(m, txp.ids[i])
   205  			txp.ids[i] = txp.ids[len(txp.ids)-1]
   206  			txp.ids = txp.ids[:len(txp.ids)-1]
   207  			txp.alloctx[i] = txp.alloctx[len(txp.alloctx)-1]
   208  			txp.alloctx = txp.alloctx[:len(txp.alloctx)-1]
   209  			i--
   210  		}
   211  		txp.lastReleaseBegin = begin
   212  		if len(txp.ids) == 0 {
   213  			delete(f.pending, tid)
   214  		}
   215  	}
   216  	f.mergeSpans(m)
   217  	return m
   218  }
   219  
   220  func (f *freelist) rollback(txid txid) {
   221  	txp := f.pending[txid]
   222  	if txp == nil {
   223  		return
   224  	}
   225  	var m pgids
   226  	for i, pgid := range txp.ids {
   227  		delete(f.cache, pgid)
   228  		tx := txp.alloctx[i]
   229  		if tx == 0 {
   230  			continue
   231  		}
   232  		if tx != txid {
   233  			f.allocs[pgid] = tx
   234  		} else {
   235  			m = append(m, pgid)
   236  		}
   237  	}
   238  
   239  	delete(f.pending, txid)
   240  	f.mergeSpans(m)
   241  }
   242  
   243  func (f *freelist) freed(pgid pgid) bool {
   244  	return f.cache[pgid]
   245  }
   246  
   247  func (f *freelist) read(p *page) {
   248  	if (p.flags & freelistPageFlag) == 0 {
   249  		panic(fmt.Sprintf("invalid freelist page: %d, page type is %s", p.id, p.typ()))
   250  	}
   251  	var idx, count = 0, int(p.count)
   252  	if count == 0xFFFF {
   253  		idx = 1
   254  		c := *(*pgid)(unsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p)))
   255  		count = int(c)
   256  		if count < 0 {
   257  			panic(fmt.Sprintf("leading element count %d overflows int", c))
   258  		}
   259  	}
   260  
   261  	if count == 0 {
   262  		f.ids = nil
   263  	} else {
   264  		var ids []pgid
   265  		data := unsafeIndex(unsafe.Pointer(p), unsafe.Sizeof(*p), unsafe.Sizeof(ids[0]), idx)
   266  		unsafeSlice(unsafe.Pointer(&ids), data, count)
   267  
   268  		idsCopy := make([]pgid, count)
   269  		copy(idsCopy, ids)
   270  		sort.Sort(pgids(idsCopy))
   271  
   272  		f.readIDs(idsCopy)
   273  	}
   274  }
   275  
   276  func (f *freelist) arrayReadIDs(ids []pgid) {
   277  	f.ids = ids
   278  	f.reindex()
   279  }
   280  
   281  func (f *freelist) arrayGetFreePageIDs() []pgid {
   282  	return f.ids
   283  }
   284  
   285  func (f *freelist) arrayConvertBitmap() (rb *roaring64.Bitmap, count int) {
   286  	rb = roaring64.NewBitmap()
   287  	for _, txp := range f.pending {
   288  		for _, id := range txp.ids {
   289  			rb.Add(uint64(id))
   290  			count++
   291  		}
   292  	}
   293  	for _, id := range f.ids {
   294  		rb.Add(uint64(id))
   295  		count++
   296  	}
   297  
   298  	return
   299  }
   300  
   301  func (f *freelist) write(p *page) error {
   302  	p.flags |= freelistPageFlag
   303  	l := f.count()
   304  	if l == 0 {
   305  		p.count = uint16(l)
   306  	} else if l < 0xFFFF {
   307  		p.count = uint16(l)
   308  		var ids []pgid
   309  		data := unsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p))
   310  		unsafeSlice(unsafe.Pointer(&ids), data, l)
   311  		f.copyall(ids)
   312  	} else {
   313  		p.count = 0xFFFF
   314  		var ids []pgid
   315  		data := unsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p))
   316  		unsafeSlice(unsafe.Pointer(&ids), data, l+1)
   317  		ids[0] = pgid(l)
   318  		f.copyall(ids[1:])
   319  	}
   320  
   321  	return nil
   322  }
   323  
   324  func (f *freelist) reload(p *page, version uint32) {
   325  	if version == versionFreelistBitmap {
   326  		f.readFromBitmap(p)
   327  	} else {
   328  		f.read(p)
   329  	}
   330  
   331  	pcache := make(map[pgid]bool)
   332  	for _, txp := range f.pending {
   333  		for _, pendingID := range txp.ids {
   334  			pcache[pendingID] = true
   335  		}
   336  	}
   337  
   338  	var a []pgid
   339  	for _, id := range f.getFreePageIDs() {
   340  		if !pcache[id] {
   341  			a = append(a, id)
   342  		}
   343  	}
   344  
   345  	f.readIDs(a)
   346  }
   347  
   348  func (f *freelist) noSyncReload(pgids []pgid) {
   349  	pcache := make(map[pgid]bool)
   350  	for _, txp := range f.pending {
   351  		for _, pendingID := range txp.ids {
   352  			pcache[pendingID] = true
   353  		}
   354  	}
   355  
   356  	var a []pgid
   357  	for _, id := range pgids {
   358  		if !pcache[id] {
   359  			a = append(a, id)
   360  		}
   361  	}
   362  
   363  	f.readIDs(a)
   364  }
   365  
   366  func (f *freelist) reindex() {
   367  	ids := f.getFreePageIDs()
   368  	f.cache = make(map[pgid]bool, len(ids))
   369  	for _, id := range ids {
   370  		f.cache[id] = true
   371  	}
   372  	for _, txp := range f.pending {
   373  		for _, pendingID := range txp.ids {
   374  			f.cache[pendingID] = true
   375  		}
   376  	}
   377  }
   378  
   379  func (f *freelist) arrayMergeSpans(ids pgids) {
   380  	sort.Sort(ids)
   381  	f.ids = pgids(f.ids).merge(ids)
   382  }