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 }