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 }