github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/bitpage/manifest.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 bitpage
    16  
    17  import (
    18  	"bufio"
    19  	"encoding/binary"
    20  	"errors"
    21  	"io/fs"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/zuoyebang/bitalosdb/internal/list2"
    26  	"github.com/zuoyebang/bitalosdb/internal/mmap"
    27  )
    28  
    29  const (
    30  	versionV1 uint16 = iota + 1
    31  )
    32  
    33  const (
    34  	versionCurrent = versionV1
    35  
    36  	pageMetadataNum = 10000
    37  	pageMetadataLen = 40
    38  	pagemetaMapLen  = pageMetadataLen * pageMetadataNum
    39  
    40  	manifestHeaderLen = 8
    41  	manifestMagicLen  = 8
    42  	manifestFooterLen = manifestMagicLen
    43  	manifestMagic     = "\xf7\xcf\xf4\x85\xb7\x41\xe2\x88"
    44  	manifestLen       = manifestHeaderLen + pagemetaMapLen + manifestFooterLen
    45  
    46  	versionOffset     = 0
    47  	nextPageNumOffset = 4
    48  	pagemetaMapOffset = 8
    49  	footerOffset      = manifestLen - manifestFooterLen
    50  
    51  	itemPageNumOffset      = 0
    52  	itemStFileNumOffset    = 4
    53  	itemAtFileNumOffset    = 8
    54  	itemMinUnflushedOffset = 12
    55  	itemSplitState         = 16
    56  	itemCreateTimestamp    = 17
    57  	itemUpdateTimestamp    = 25
    58  )
    59  
    60  type bitpagemeta struct {
    61  	b       *Bitpage
    62  	version uint16
    63  	mu      struct {
    64  		sync.RWMutex
    65  		manifest      *mmap.MMap
    66  		curPageNum    PageNum
    67  		pageFreelist  *list2.IntQueue
    68  		pagemetaMap   map[PageNum]*pagemetaItem
    69  		pagemetaArray [pageMetadataNum]pagemetaItem
    70  	}
    71  }
    72  
    73  type pagemetaItem struct {
    74  	pagemeta
    75  	pos int
    76  }
    77  
    78  type pagemeta struct {
    79  	pageNum               PageNum
    80  	nextStFileNum         FileNum
    81  	curAtFileNum          FileNum
    82  	minUnflushedStFileNum FileNum
    83  	splitState            uint8
    84  	createTimestamp       uint64
    85  	updateTimestamp       uint64
    86  }
    87  
    88  func openManifest(b *Bitpage) error {
    89  	b.meta = &bitpagemeta{b: b}
    90  	b.meta.mu.pagemetaMap = make(map[PageNum]*pagemetaItem, 1<<10)
    91  	b.meta.mu.pageFreelist = list2.NewIntQueue(pageMetadataNum)
    92  
    93  	filename := makeFilepath(b.dirname, fileTypeManifest, 0, 0)
    94  	if _, err := b.opts.FS.Stat(filename); errors.Is(err, fs.ErrNotExist) {
    95  		if err = b.meta.createManifest(filename); err != nil {
    96  			return err
    97  		}
    98  	}
    99  
   100  	if err := b.meta.loadManifest(filename); err != nil {
   101  		return err
   102  	}
   103  
   104  	b.opts.Logger.Infof("[BITPAGE %d] open manifest success version:%d len:%d uses:%d frees:%d",
   105  		b.index,
   106  		b.meta.version,
   107  		b.meta.mu.manifest.Len(),
   108  		len(b.meta.mu.pagemetaMap),
   109  		b.meta.mu.pageFreelist.Len())
   110  
   111  	return nil
   112  }
   113  
   114  func (m *bitpagemeta) createManifest(filename string) (err error) {
   115  	var (
   116  		manifestFile File
   117  		manifest     *bufio.Writer
   118  	)
   119  
   120  	manifestFile, err = m.b.opts.FS.Create(filename)
   121  	if err != nil {
   122  		return err
   123  	}
   124  
   125  	defer func() {
   126  		if err != nil {
   127  			err = m.b.opts.FS.Remove(filename)
   128  		}
   129  		if manifestFile != nil {
   130  			err = manifestFile.Close()
   131  		}
   132  	}()
   133  
   134  	manifest = bufio.NewWriterSize(manifestFile, manifestLen)
   135  	buf := make([]byte, manifestLen)
   136  	binary.LittleEndian.PutUint16(buf[0:2], versionCurrent)
   137  	copy(buf[footerOffset:footerOffset+manifestFooterLen], manifestMagic)
   138  
   139  	if _, err = manifest.Write(buf); err != nil {
   140  		return err
   141  	}
   142  	if err = manifest.Flush(); err != nil {
   143  		return err
   144  	}
   145  	if err = manifestFile.Sync(); err != nil {
   146  		return err
   147  	}
   148  	return nil
   149  }
   150  
   151  func (m *bitpagemeta) loadManifest(filename string) (err error) {
   152  	m.mu.Lock()
   153  	defer m.mu.Unlock()
   154  
   155  	m.mu.manifest, err = mmap.Open(filename, 0)
   156  	if err != nil {
   157  		return err
   158  	}
   159  
   160  	m.version = m.mu.manifest.ReadUInt16At(versionOffset)
   161  	m.mu.curPageNum = PageNum(m.mu.manifest.ReadUInt32At(nextPageNumOffset))
   162  
   163  	pos := pagemetaMapOffset
   164  	for arrIdx := 0; arrIdx < pageMetadataNum; arrIdx++ {
   165  		pm := m.pagemetaInBuffer(pos)
   166  		m.mu.pagemetaArray[arrIdx] = pagemetaItem{pm, pos}
   167  		if pm.pageNum > 0 {
   168  			m.mu.pagemetaMap[pm.pageNum] = &(m.mu.pagemetaArray[arrIdx])
   169  		} else {
   170  			m.mu.pageFreelist.Push(int32(pos))
   171  		}
   172  
   173  		pos += pageMetadataLen
   174  	}
   175  
   176  	return nil
   177  }
   178  
   179  func (m *bitpagemeta) close() error {
   180  	if m.mu.manifest != nil {
   181  		return m.mu.manifest.Close()
   182  	}
   183  	return nil
   184  }
   185  
   186  func (m *bitpagemeta) getPagesFreelistLen() int {
   187  	return m.mu.pageFreelist.Len()
   188  }
   189  
   190  func (m *bitpagemeta) getNextPagemetaPos() int {
   191  	if m.mu.pageFreelist.Empty() {
   192  		panic("bitpage has no free meta")
   193  	}
   194  
   195  	value, _ := m.mu.pageFreelist.Pop()
   196  	return int(value)
   197  }
   198  
   199  func (m *bitpagemeta) newPagemetaItem(pageNum PageNum) *pagemetaItem {
   200  	m.mu.Lock()
   201  	defer m.mu.Unlock()
   202  
   203  	pmItem := m.getPagemetaItemLocked(pageNum)
   204  	if pmItem != nil {
   205  		return pmItem
   206  	}
   207  
   208  	pos := m.getNextPagemetaPos()
   209  
   210  	arrIdx := (pos - pagemetaMapOffset) / pageMetadataLen
   211  	m.reusePagemeta(pageNum, pos, arrIdx)
   212  	pmItem = &(m.mu.pagemetaArray[arrIdx])
   213  	m.mu.pagemetaMap[pageNum] = pmItem
   214  
   215  	return pmItem
   216  }
   217  
   218  func (m *bitpagemeta) getPagemetaItem(pageNum PageNum) *pagemetaItem {
   219  	m.mu.RLock()
   220  	defer m.mu.RUnlock()
   221  
   222  	pmItem, ok := m.mu.pagemetaMap[pageNum]
   223  	if !ok {
   224  		return nil
   225  	}
   226  	return pmItem
   227  }
   228  
   229  func (m *bitpagemeta) updatePagemetaTimestamp(pageNum PageNum) {
   230  	m.mu.Lock()
   231  	defer m.mu.Unlock()
   232  
   233  	pmItem := m.getPagemetaItemLocked(pageNum)
   234  	if pmItem == nil {
   235  		return
   236  	}
   237  
   238  	pmItem.updateTimestamp = uint64(time.Now().UnixMilli())
   239  	m.mu.manifest.WriteUInt64At(pmItem.updateTimestamp, pmItem.pos+itemUpdateTimestamp)
   240  }
   241  
   242  func (m *bitpagemeta) freePagemetaItem(pageNum PageNum) {
   243  	m.mu.Lock()
   244  	defer m.mu.Unlock()
   245  
   246  	pmItem := m.getPagemetaItemLocked(pageNum)
   247  	if pmItem == nil {
   248  		return
   249  	}
   250  
   251  	arrIdx := (pmItem.pos - pagemetaMapOffset) / pageMetadataLen
   252  	m.resetPagemeta(PageNum(0), pmItem.pos, arrIdx)
   253  	m.mu.pageFreelist.Push(int32(pmItem.pos))
   254  	delete(m.mu.pagemetaMap, pageNum)
   255  }
   256  
   257  func (m *bitpagemeta) pagemetaInBuffer(pos int) pagemeta {
   258  	return pagemeta{
   259  		pageNum:               PageNum(m.mu.manifest.ReadUInt32At(pos + itemPageNumOffset)),
   260  		nextStFileNum:         FileNum(m.mu.manifest.ReadUInt32At(pos + itemStFileNumOffset)),
   261  		curAtFileNum:          FileNum(m.mu.manifest.ReadUInt32At(pos + itemAtFileNumOffset)),
   262  		minUnflushedStFileNum: FileNum(m.mu.manifest.ReadUInt32At(pos + itemMinUnflushedOffset)),
   263  		splitState:            m.mu.manifest.ReadUInt8At(pos + itemSplitState),
   264  		createTimestamp:       m.mu.manifest.ReadUInt64At(pos + itemCreateTimestamp),
   265  		updateTimestamp:       m.mu.manifest.ReadUInt64At(pos + itemUpdateTimestamp),
   266  	}
   267  }
   268  
   269  func (m *bitpagemeta) resetPagemeta(pn PageNum, pos, arrIdx int) {
   270  	pm := pagemeta{
   271  		pageNum:               pn,
   272  		nextStFileNum:         FileNum(1),
   273  		curAtFileNum:          FileNum(0),
   274  		minUnflushedStFileNum: FileNum(1),
   275  		splitState:            0,
   276  		createTimestamp:       0,
   277  		updateTimestamp:       0,
   278  	}
   279  
   280  	m.mu.pagemetaArray[arrIdx] = pagemetaItem{pm, pos}
   281  
   282  	m.mu.manifest.WriteUInt32At(uint32(pm.pageNum), pos+itemPageNumOffset)
   283  	m.mu.manifest.WriteUInt32At(uint32(pm.nextStFileNum), pos+itemStFileNumOffset)
   284  	m.mu.manifest.WriteUInt32At(uint32(pm.curAtFileNum), pos+itemAtFileNumOffset)
   285  	m.mu.manifest.WriteUInt32At(uint32(pm.minUnflushedStFileNum), pos+itemMinUnflushedOffset)
   286  	m.mu.manifest.WriteUInt8At(pm.splitState, pos+itemSplitState)
   287  	m.mu.manifest.WriteUInt64At(pm.createTimestamp, pos+itemCreateTimestamp)
   288  	m.mu.manifest.WriteUInt64At(pm.updateTimestamp, pos+itemUpdateTimestamp)
   289  }
   290  
   291  func (m *bitpagemeta) reusePagemeta(pn PageNum, pos, arrIdx int) {
   292  	pm := pagemeta{
   293  		pageNum:               pn,
   294  		nextStFileNum:         FileNum(1),
   295  		curAtFileNum:          FileNum(0),
   296  		minUnflushedStFileNum: FileNum(1),
   297  		splitState:            0,
   298  		createTimestamp:       uint64(time.Now().UnixMilli()),
   299  		updateTimestamp:       0,
   300  	}
   301  
   302  	m.mu.pagemetaArray[arrIdx] = pagemetaItem{pm, pos}
   303  
   304  	m.mu.manifest.WriteUInt32At(uint32(pm.pageNum), pos+itemPageNumOffset)
   305  	m.mu.manifest.WriteUInt32At(uint32(pm.nextStFileNum), pos+itemStFileNumOffset)
   306  	m.mu.manifest.WriteUInt32At(uint32(pm.curAtFileNum), pos+itemAtFileNumOffset)
   307  	m.mu.manifest.WriteUInt32At(uint32(pm.minUnflushedStFileNum), pos+itemMinUnflushedOffset)
   308  	m.mu.manifest.WriteUInt8At(pm.splitState, pos+itemSplitState)
   309  	m.mu.manifest.WriteUInt64At(pm.createTimestamp, pos+itemCreateTimestamp)
   310  	m.mu.manifest.WriteUInt64At(pm.updateTimestamp, pos+itemUpdateTimestamp)
   311  }
   312  
   313  func (m *bitpagemeta) getPagemetaItemLocked(pageNum PageNum) *pagemetaItem {
   314  	pmItem, ok := m.mu.pagemetaMap[pageNum]
   315  	if !ok {
   316  		return nil
   317  	}
   318  	return pmItem
   319  }
   320  
   321  func (m *bitpagemeta) getCurrentPageNum() PageNum {
   322  	return m.mu.curPageNum
   323  }
   324  
   325  func (m *bitpagemeta) getNextPageNum() PageNum {
   326  	m.mu.Lock()
   327  	defer m.mu.Unlock()
   328  
   329  	m.mu.curPageNum++
   330  	m.mu.manifest.WriteUInt32At(uint32(m.mu.curPageNum), nextPageNumOffset)
   331  	return m.mu.curPageNum
   332  }
   333  
   334  func (m *bitpagemeta) getNextStFileNum(pageNum PageNum) FileNum {
   335  	m.mu.Lock()
   336  	defer m.mu.Unlock()
   337  
   338  	pmItem := m.getPagemetaItemLocked(pageNum)
   339  	if pmItem == nil {
   340  		return FileNum(0)
   341  	}
   342  
   343  	fn := pmItem.nextStFileNum
   344  	pmItem.nextStFileNum++
   345  	m.mu.manifest.WriteUInt32At(uint32(pmItem.nextStFileNum), pmItem.pos+itemStFileNumOffset)
   346  	return fn
   347  }
   348  
   349  func (m *bitpagemeta) getNextAtFileNum(pageNum PageNum) FileNum {
   350  	m.mu.RLock()
   351  	defer m.mu.RUnlock()
   352  
   353  	pmItem := m.getPagemetaItemLocked(pageNum)
   354  	if pmItem == nil {
   355  		return FileNum(0)
   356  	}
   357  
   358  	fn := pmItem.curAtFileNum + FileNum(1)
   359  	return fn
   360  }
   361  
   362  func (m *bitpagemeta) setNextArrayTableFileNum(pageNum PageNum) {
   363  	m.mu.Lock()
   364  	defer m.mu.Unlock()
   365  
   366  	pmItem := m.getPagemetaItemLocked(pageNum)
   367  	if pmItem == nil {
   368  		return
   369  	}
   370  
   371  	pmItem.curAtFileNum++
   372  	m.mu.manifest.WriteUInt32At(uint32(pmItem.curAtFileNum), pmItem.pos+itemAtFileNumOffset)
   373  }
   374  
   375  func (m *bitpagemeta) getMinUnflushedStFileNum(pageNum PageNum) FileNum {
   376  	m.mu.RLock()
   377  	defer m.mu.RUnlock()
   378  
   379  	pmItem := m.getPagemetaItemLocked(pageNum)
   380  	if pmItem == nil {
   381  		return FileNum(0)
   382  	}
   383  
   384  	return pmItem.minUnflushedStFileNum
   385  }
   386  
   387  func (m *bitpagemeta) setMinUnflushedStFileNum(pageNum PageNum, fn FileNum) {
   388  	m.mu.Lock()
   389  	defer m.mu.Unlock()
   390  
   391  	pmItem := m.getPagemetaItemLocked(pageNum)
   392  	if pmItem == nil {
   393  		return
   394  	}
   395  
   396  	pmItem.minUnflushedStFileNum = fn
   397  	m.mu.manifest.WriteUInt32At(uint32(fn), pmItem.pos+itemMinUnflushedOffset)
   398  }
   399  
   400  func (m *bitpagemeta) getSplitState(pageNum PageNum) uint8 {
   401  	m.mu.RLock()
   402  	defer m.mu.RUnlock()
   403  
   404  	pmItem := m.getPagemetaItemLocked(pageNum)
   405  	if pmItem == nil {
   406  		return 0
   407  	}
   408  
   409  	return pmItem.splitState
   410  }
   411  
   412  func (m *bitpagemeta) setSplitState(pageNum PageNum, state uint8) {
   413  	m.mu.Lock()
   414  	defer m.mu.Unlock()
   415  
   416  	pmItem := m.getPagemetaItemLocked(pageNum)
   417  	if pmItem == nil || pmItem.splitState == state {
   418  		return
   419  	}
   420  
   421  	pmItem.splitState = state
   422  	m.mu.manifest.WriteUInt8At(state, pmItem.pos+itemSplitState)
   423  }