github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/bithash/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 bithash
    16  
    17  import (
    18  	"bufio"
    19  	"encoding/binary"
    20  	"errors"
    21  	"fmt"
    22  	"io/fs"
    23  	"sync"
    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  	fileMetadataNum = 10000
    37  	fileMetadataLen = 18
    38  	fileMetaMapLen  = fileMetadataNum * fileMetadataLen
    39  
    40  	manifestHeaderLen = 8
    41  	manifestMagicLen  = 8
    42  	manifestFooterLen = manifestMagicLen
    43  	manifestMagic     = "\xf7\xcf\xf4\x85\xb7\x41\xe2\x88"
    44  	manifestLen       = manifestHeaderLen + fileMetaMapLen + manifestFooterLen
    45  
    46  	versionOffset          = 0
    47  	nextFileNumOffset      = 4
    48  	nextFileMetadataOffset = 8
    49  	footerOffset           = manifestLen - manifestFooterLen
    50  )
    51  
    52  const (
    53  	fileMetaStateNone uint16 = iota
    54  	fileMetaStateCompact
    55  	fileMetaStateWrite
    56  	fileMetaStateClosed
    57  	fileMetaStateImmutable
    58  )
    59  
    60  var fileMetaStateNames = []string{
    61  	fileMetaStateNone:      "NONE",
    62  	fileMetaStateCompact:   "COMPACT",
    63  	fileMetaStateWrite:     "WRITING",
    64  	fileMetaStateClosed:    "CLOSEDNOTCHECK",
    65  	fileMetaStateImmutable: "IMMUTABLE",
    66  }
    67  
    68  func getFileMetaStateName(state uint16) string {
    69  	if int(state) < len(fileMetaStateNames) {
    70  		return fileMetaStateNames[state]
    71  	}
    72  	return fmt.Sprintf("UNKNOWN:%d", state)
    73  }
    74  
    75  type BithashMetadata struct {
    76  	b       *Bithash
    77  	version uint16
    78  	mu      struct {
    79  		sync.RWMutex
    80  		manifestMmap   *mmap.MMap
    81  		curFileNum     FileNum
    82  		freesPos       *list2.IntQueue
    83  		filesPos       map[FileNum]int
    84  		filesMeta      map[FileNum]*fileMetadata
    85  		filesMetaArray [fileMetadataNum]fileMetadata
    86  	}
    87  }
    88  
    89  type fileMetadata struct {
    90  	fileNum        FileNum
    91  	state          uint16
    92  	keyNum         uint32
    93  	delKeyNum      uint32
    94  	conflictKeyNum uint32
    95  }
    96  
    97  func (f *fileMetadata) String() string {
    98  	return fmt.Sprintf("fileNum=%d state=%s keyNum=%d conflictKeyNum=%d delKeyNum=%d",
    99  		f.fileNum, getFileMetaStateName(f.state), f.keyNum, f.conflictKeyNum, f.delKeyNum)
   100  }
   101  
   102  func initManifest(b *Bithash) error {
   103  	b.meta = &BithashMetadata{b: b}
   104  	b.meta.mu.filesPos = make(map[FileNum]int, 1<<10)
   105  	b.meta.mu.filesMeta = make(map[FileNum]*fileMetadata, 1<<10)
   106  	b.meta.mu.freesPos = list2.NewIntQueue(fileMetadataNum)
   107  
   108  	filename := MakeFilepath(b.fs, b.dirname, fileTypeManifest, 0)
   109  	if _, err := b.fs.Stat(filename); errors.Is(err, fs.ErrNotExist) {
   110  		if err = b.meta.createManifest(filename); err != nil {
   111  			return err
   112  		}
   113  	}
   114  
   115  	if err := b.meta.loadManifest(filename); err != nil {
   116  		return err
   117  	}
   118  
   119  	b.logger.Infof("[BITHASH %d] openManifest success version:%d len:%d uses:%d frees:%d",
   120  		b.index,
   121  		b.meta.version,
   122  		b.meta.mu.manifestMmap.Len(),
   123  		len(b.meta.mu.filesPos),
   124  		b.meta.mu.freesPos.Len())
   125  
   126  	return nil
   127  }
   128  
   129  func (m *BithashMetadata) createManifest(filename string) (err error) {
   130  	var (
   131  		manifestFile File
   132  		manifest     *bufio.Writer
   133  	)
   134  
   135  	manifestFile, err = m.b.fs.Create(filename)
   136  	if err != nil {
   137  		return err
   138  	}
   139  
   140  	defer func() {
   141  		if err != nil {
   142  			err = m.b.fs.Remove(filename)
   143  		}
   144  		if manifestFile != nil {
   145  			err = manifestFile.Close()
   146  		}
   147  	}()
   148  
   149  	manifest = bufio.NewWriterSize(manifestFile, manifestLen)
   150  	buf := make([]byte, manifestLen)
   151  	binary.LittleEndian.PutUint16(buf[0:2], versionCurrent)
   152  	binary.LittleEndian.PutUint32(buf[4:8], 1)
   153  	copy(buf[footerOffset:footerOffset+manifestFooterLen], manifestMagic)
   154  
   155  	if _, err = manifest.Write(buf); err != nil {
   156  		return err
   157  	}
   158  	if err = manifest.Flush(); err != nil {
   159  		return err
   160  	}
   161  	if err = manifestFile.Sync(); err != nil {
   162  		return err
   163  	}
   164  	return nil
   165  }
   166  
   167  func (m *BithashMetadata) loadManifest(filename string) (err error) {
   168  	m.mu.Lock()
   169  	defer m.mu.Unlock()
   170  
   171  	m.mu.manifestMmap, err = mmap.Open(filename, 0)
   172  	if err != nil {
   173  		return err
   174  	}
   175  
   176  	m.version = m.mu.manifestMmap.ReadUInt16At(versionOffset)
   177  	m.mu.curFileNum = FileNum(m.mu.manifestMmap.ReadUInt32At(nextFileNumOffset))
   178  
   179  	pos := nextFileMetadataOffset
   180  	for arrIdx := 0; arrIdx < fileMetadataNum; arrIdx++ {
   181  		fileMeta := m.readFileMetadata(pos)
   182  		if fileMeta.fileNum > 0 {
   183  			m.mu.filesPos[fileMeta.fileNum] = pos
   184  			m.mu.filesMetaArray[arrIdx] = fileMeta
   185  			m.mu.filesMeta[fileMeta.fileNum] = &(m.mu.filesMetaArray[arrIdx])
   186  			m.b.stats.KeyTotal.Add(uint64(fileMeta.keyNum))
   187  			m.b.stats.DelKeyTotal.Add(uint64(fileMeta.delKeyNum))
   188  			m.b.stats.FileTotal.Add(1)
   189  		} else {
   190  			m.mu.freesPos.Push(int32(pos))
   191  		}
   192  
   193  		pos += fileMetadataLen
   194  	}
   195  
   196  	return nil
   197  }
   198  
   199  func (m *BithashMetadata) close() error {
   200  	if m.mu.manifestMmap != nil {
   201  		return m.mu.manifestMmap.Close()
   202  	}
   203  	return nil
   204  }
   205  
   206  func (m *BithashMetadata) getCurrentFileNum() FileNum {
   207  	return m.mu.curFileNum
   208  }
   209  
   210  func (m *BithashMetadata) getNextFileNum() FileNum {
   211  	m.mu.Lock()
   212  	defer m.mu.Unlock()
   213  
   214  	curFileNum := m.mu.curFileNum
   215  	m.mu.curFileNum++
   216  	m.mu.manifestMmap.WriteUInt32At(uint32(m.mu.curFileNum), nextFileNumOffset)
   217  	return curFileNum
   218  }
   219  
   220  func (m *BithashMetadata) getNextFreePos() int {
   221  	if m.mu.freesPos.Empty() {
   222  		panic("bithash has no freemeta")
   223  	}
   224  
   225  	value, _ := m.mu.freesPos.Pop()
   226  	return int(value)
   227  }
   228  
   229  func (m *BithashMetadata) getPos(fileNum FileNum) int {
   230  	m.mu.RLock()
   231  	defer m.mu.RUnlock()
   232  	return m.getFilePos(fileNum)
   233  }
   234  
   235  func (m *BithashMetadata) getFilePos(fileNum FileNum) int {
   236  	pos, ok := m.mu.filesPos[fileNum]
   237  	if !ok {
   238  		return 0
   239  	}
   240  
   241  	return pos
   242  }
   243  
   244  func (m *BithashMetadata) newFileMetadata(fileNum FileNum, compact bool) int {
   245  	var state uint16
   246  	if compact {
   247  		state = fileMetaStateCompact
   248  	} else {
   249  		state = fileMetaStateWrite
   250  	}
   251  
   252  	m.mu.Lock()
   253  	pos := m.getNextFreePos()
   254  	fileMeta := fileMetadata{
   255  		fileNum:        fileNum,
   256  		state:          state,
   257  		keyNum:         0,
   258  		conflictKeyNum: 0,
   259  		delKeyNum:      0,
   260  	}
   261  	m.writeFileMetadata(pos, &fileMeta)
   262  	arrIdx := (pos - nextFileMetadataOffset) / fileMetadataLen
   263  	m.mu.filesMetaArray[arrIdx] = fileMeta
   264  	m.mu.filesPos[fileNum] = pos
   265  	m.mu.filesMeta[fileNum] = &(m.mu.filesMetaArray[arrIdx])
   266  	m.mu.Unlock()
   267  	return pos
   268  }
   269  
   270  func (m *BithashMetadata) freeFileMetadata(fileNum FileNum) {
   271  	m.mu.Lock()
   272  	defer m.mu.Unlock()
   273  
   274  	pos := m.getFilePos(fileNum)
   275  	if pos == 0 {
   276  		return
   277  	}
   278  
   279  	delete(m.mu.filesPos, fileNum)
   280  	delete(m.mu.filesMeta, fileNum)
   281  
   282  	fileMeta := fileMetadata{
   283  		fileNum:        FileNum(0),
   284  		state:          fileMetaStateNone,
   285  		keyNum:         0,
   286  		conflictKeyNum: 0,
   287  		delKeyNum:      0,
   288  	}
   289  	arrIdx := (pos - nextFileMetadataOffset) / fileMetadataLen
   290  	m.mu.filesMetaArray[arrIdx] = fileMeta
   291  
   292  	m.writeFileMetadata(pos, &fileMeta)
   293  
   294  	m.mu.freesPos.Push(int32(pos))
   295  }
   296  
   297  func (m *BithashMetadata) readFileMetadata(pos int) fileMetadata {
   298  	return fileMetadata{
   299  		fileNum:        FileNum(m.mu.manifestMmap.ReadUInt32At(pos)),
   300  		state:          m.mu.manifestMmap.ReadUInt16At(pos + 4),
   301  		keyNum:         m.mu.manifestMmap.ReadUInt32At(pos + 6),
   302  		conflictKeyNum: m.mu.manifestMmap.ReadUInt32At(pos + 10),
   303  		delKeyNum:      m.mu.manifestMmap.ReadUInt32At(pos + 14),
   304  	}
   305  }
   306  
   307  func (m *BithashMetadata) getFileMetadata(fileNum FileNum) (fileMeta *fileMetadata) {
   308  	m.mu.RLock()
   309  	defer m.mu.RUnlock()
   310  
   311  	var ok bool
   312  	fileMeta, ok = m.mu.filesMeta[fileNum]
   313  	if !ok {
   314  		pos := m.getFilePos(fileNum)
   315  		if pos > 0 {
   316  			arrIdx := (pos - nextFileMetadataOffset) / fileMetadataLen
   317  			m.mu.filesMetaArray[arrIdx] = m.readFileMetadata(pos)
   318  			fileMeta = &(m.mu.filesMetaArray[arrIdx])
   319  			m.mu.filesMeta[fileNum] = fileMeta
   320  		}
   321  	}
   322  	return
   323  }
   324  
   325  func (m *BithashMetadata) updateFileByClosed(fileNum FileNum, wm WriterMetadata) {
   326  	m.mu.Lock()
   327  	pos := m.getFilePos(fileNum)
   328  	if pos > 0 {
   329  		m.mu.manifestMmap.WriteUInt32At(uint32(fileNum), pos)
   330  		m.mu.manifestMmap.WriteUInt16At(fileMetaStateClosed, pos+4)
   331  		m.mu.manifestMmap.WriteUInt32At(wm.keyNum, pos+6)
   332  		m.mu.manifestMmap.WriteUInt32At(wm.conflictKeyNum, pos+10)
   333  
   334  		m.mu.filesMeta[fileNum].state = fileMetaStateClosed
   335  		m.mu.filesMeta[fileNum].keyNum = wm.keyNum
   336  		m.mu.filesMeta[fileNum].conflictKeyNum = wm.conflictKeyNum
   337  	}
   338  	m.mu.Unlock()
   339  }
   340  
   341  func (m *BithashMetadata) updateFileState(fileNum FileNum, state uint16) {
   342  	m.mu.Lock()
   343  	pos := m.getFilePos(fileNum)
   344  	if pos > 0 {
   345  		m.mu.manifestMmap.WriteUInt16At(state, pos+4)
   346  		m.mu.filesMeta[fileNum].state = state
   347  	}
   348  	m.mu.Unlock()
   349  }
   350  
   351  func (m *BithashMetadata) updateFileDelKeyNum(fileNum FileNum, delta uint32) {
   352  	m.mu.Lock()
   353  	pos := m.getFilePos(fileNum)
   354  	if pos > 0 {
   355  		pos += 14
   356  		delKeyNum := m.mu.manifestMmap.ReadUInt32At(pos) + delta
   357  		m.mu.manifestMmap.WriteUInt32At(delKeyNum, pos)
   358  		m.mu.filesMeta[fileNum].delKeyNum = delKeyNum
   359  	}
   360  	m.mu.Unlock()
   361  }
   362  
   363  func (m *BithashMetadata) writeFileMetadata(pos int, fileMeta *fileMetadata) {
   364  	m.mu.manifestMmap.WriteUInt32At(uint32(fileMeta.fileNum), pos)
   365  	m.mu.manifestMmap.WriteUInt16At(fileMeta.state, pos+4)
   366  	m.mu.manifestMmap.WriteUInt32At(fileMeta.keyNum, pos+6)
   367  	m.mu.manifestMmap.WriteUInt32At(fileMeta.conflictKeyNum, pos+10)
   368  	m.mu.manifestMmap.WriteUInt32At(fileMeta.delKeyNum, pos+14)
   369  
   370  }
   371  
   372  func (m *BithashMetadata) isFileWriting(fileNum FileNum) bool {
   373  	fileMeta := m.getFileMetadata(fileNum)
   374  	if fileMeta == nil {
   375  		return false
   376  	}
   377  	if fileMeta.state == fileMetaStateWrite || fileMeta.state == fileMetaStateCompact {
   378  		return true
   379  	}
   380  	return false
   381  }
   382  
   383  func (m *BithashMetadata) isFileImmutable(fileNum FileNum) bool {
   384  	fileMeta := m.getFileMetadata(fileNum)
   385  	if fileMeta == nil {
   386  		return false
   387  	}
   388  	return fileMeta.state == fileMetaStateImmutable
   389  }