github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/manifest/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 manifest 16 17 import ( 18 "bufio" 19 "sync" 20 21 "github.com/cockroachdb/errors/oserror" 22 "github.com/zuoyebang/bitalosdb/internal/base" 23 "github.com/zuoyebang/bitalosdb/internal/consts" 24 "github.com/zuoyebang/bitalosdb/internal/mmap" 25 "github.com/zuoyebang/bitalosdb/internal/vfs" 26 ) 27 28 const ( 29 metaHeaderOffset = 0 30 metaHeaderLen = 16 31 metaFieldOffset = 16 32 metaFieldLen = 1024 33 metaMagicLen = 8 34 metaFooterLen = metaMagicLen 35 metaMagic = "\xf7\xcf\xf4\x85\xb7\x41\xe2\x88" 36 metaLen = metaHeaderLen + metaFieldLen + metaMagicLen 37 footerOffset = metaLen - metaFooterLen 38 ) 39 40 const ( 41 fieldOffsetMinUnflushedLogNumV1 = metaFieldOffset 42 fieldOffsetNextFileNumV1 = fieldOffsetMinUnflushedLogNumV1 + 8 43 fieldOffsetLastSeqNumV1 = fieldOffsetNextFileNumV1 + 8 44 fieldOffsetIsBitableFlushedV1 = fieldOffsetLastSeqNumV1 + 8 45 ) 46 47 const ( 48 fieldOffsetIsBitableFlushedV2 = metaFieldOffset 49 fieldOffsetLastSeqNumV2 = fieldOffsetIsBitableFlushedV2 + 1 50 fieldOffsetBitowerNextFileNumV2 = fieldOffsetLastSeqNumV2 + 8 51 fieldOffsetBitowerMinUnflushedLogNumV2 = fieldOffsetBitowerNextFileNumV2 + 8*consts.DefaultBitowerNum 52 ) 53 54 const ( 55 metaVersion1 uint16 = 1 + iota 56 metaVersion2 57 ) 58 59 type FileNum = base.FileNum 60 61 type Metadata struct { 62 MetaEditor 63 64 Bmes [consts.DefaultBitowerNum]BitowerMetaEditor 65 66 header *metaHeader 67 file *mmap.MMap 68 fs vfs.FS 69 mu sync.RWMutex 70 } 71 72 type metaHeader struct { 73 version uint16 74 } 75 76 type MetaEditor struct { 77 LastSeqNum uint64 78 FlushedBitable uint8 79 } 80 81 type BitowerMetaEditor struct { 82 Index int 83 MinUnflushedLogNum FileNum 84 NextFileNum FileNum 85 } 86 87 func (s *BitowerMetaEditor) MarkFileNumUsed(fileNum FileNum) { 88 if s.NextFileNum < fileNum { 89 s.NextFileNum = fileNum + 1 90 } 91 } 92 93 func (s *BitowerMetaEditor) GetNextFileNum() FileNum { 94 x := s.NextFileNum 95 s.NextFileNum++ 96 return x 97 } 98 99 func NewMetadata(path string, fs vfs.FS) (*Metadata, error) { 100 meta := &Metadata{fs: fs} 101 102 if _, err := fs.Stat(path); oserror.IsNotExist(err) { 103 if err = meta.create(path); err != nil { 104 return nil, err 105 } 106 } 107 108 if err := meta.load(path); err != nil { 109 return nil, err 110 } 111 112 return meta, nil 113 } 114 115 func (m *Metadata) create(filename string) (err error) { 116 var metaFile vfs.File 117 var meta *bufio.Writer 118 119 metaFile, err = m.fs.Create(filename) 120 if err != nil { 121 return err 122 } 123 124 defer func() { 125 if err != nil { 126 err = m.fs.Remove(filename) 127 } 128 if metaFile != nil { 129 err = metaFile.Close() 130 } 131 }() 132 133 var buf [metaLen]byte 134 meta = bufio.NewWriterSize(metaFile, metaLen) 135 copy(buf[footerOffset:footerOffset+metaFooterLen], metaMagic) 136 if _, err = meta.Write(buf[:]); err != nil { 137 return err 138 } 139 if err = meta.Flush(); err != nil { 140 return err 141 } 142 if err = metaFile.Sync(); err != nil { 143 return err 144 } 145 return nil 146 } 147 148 func (m *Metadata) load(filename string) (err error) { 149 m.mu.Lock() 150 defer m.mu.Unlock() 151 152 m.file, err = mmap.Open(filename, 0) 153 if err != nil { 154 return err 155 } 156 157 m.readHeader() 158 m.updateV1toV2() 159 m.readFields() 160 161 return nil 162 } 163 164 func (m *Metadata) readHeader() { 165 m.header = &metaHeader{} 166 version := m.file.ReadUInt16At(metaHeaderOffset) 167 if version == 0 { 168 version = metaVersion2 169 m.file.WriteUInt16At(version, metaHeaderOffset) 170 } 171 m.header.version = version 172 } 173 174 func (m *Metadata) writeHeader() { 175 m.file.WriteUInt16At(m.header.version, metaHeaderOffset) 176 } 177 178 func (m *Metadata) readFields() { 179 m.FlushedBitable = m.file.ReadUInt8At(fieldOffsetIsBitableFlushedV2) 180 m.LastSeqNum = m.file.ReadUInt64At(fieldOffsetLastSeqNumV2) 181 182 for i := range m.Bmes { 183 m.Bmes[i].Index = i 184 m.Bmes[i].NextFileNum = FileNum(m.file.ReadUInt64At(fieldOffsetBitowerNextFileNumV2 + i*8)) 185 m.Bmes[i].MinUnflushedLogNum = FileNum(m.file.ReadUInt64At(fieldOffsetBitowerMinUnflushedLogNumV2 + i*8)) 186 } 187 } 188 189 func (m *Metadata) Flush() error { 190 return m.file.Flush() 191 } 192 193 func (m *Metadata) Close() error { 194 return m.file.Close() 195 } 196 197 func (m *Metadata) GetFieldFlushedBitable() uint8 { 198 m.mu.RLock() 199 flushed := m.FlushedBitable 200 m.mu.RUnlock() 201 return flushed 202 } 203 204 func (m *Metadata) SetFieldFlushedBitable() { 205 m.mu.Lock() 206 m.FlushedBitable = 1 207 m.file.WriteUInt8At(m.FlushedBitable, fieldOffsetIsBitableFlushedV2) 208 m.Flush() 209 m.mu.Unlock() 210 } 211 212 func (m *Metadata) writeMetaEditLocked(me *MetaEditor) { 213 if me.LastSeqNum != 0 { 214 m.LastSeqNum = me.LastSeqNum 215 m.file.WriteUInt64At(m.LastSeqNum, fieldOffsetLastSeqNumV2) 216 m.Flush() 217 } 218 } 219 220 func (m *Metadata) writeBitowerMetaEditLocked(me *BitowerMetaEditor) { 221 index := me.Index 222 if me.NextFileNum != 0 { 223 m.Bmes[index].NextFileNum = me.NextFileNum 224 m.file.WriteUInt64At(uint64(me.NextFileNum), fieldOffsetBitowerNextFileNumV2+index*8) 225 } 226 if me.MinUnflushedLogNum != 0 { 227 m.Bmes[index].MinUnflushedLogNum = me.MinUnflushedLogNum 228 m.file.WriteUInt64At(uint64(me.MinUnflushedLogNum), fieldOffsetBitowerMinUnflushedLogNumV2+index*8) 229 } 230 m.Flush() 231 } 232 233 func (m *Metadata) Write(me *MetaEditor, bme *BitowerMetaEditor) { 234 m.mu.Lock() 235 defer m.mu.Unlock() 236 237 m.writeMetaEditLocked(me) 238 m.writeBitowerMetaEditLocked(bme) 239 } 240 241 func (m *Metadata) WriteMetaEdit(me *MetaEditor) { 242 m.mu.Lock() 243 m.writeMetaEditLocked(me) 244 m.mu.Unlock() 245 } 246 247 func (m *Metadata) WriteBitowerMetaEdit(bme *BitowerMetaEditor) { 248 m.mu.Lock() 249 m.writeBitowerMetaEditLocked(bme) 250 m.mu.Unlock() 251 } 252 253 func (m *Metadata) updateV1toV2() { 254 if m.header.version != metaVersion1 { 255 return 256 } 257 258 lastSeqNum := m.file.ReadUInt64At(fieldOffsetLastSeqNumV1) 259 flushedBitable := m.file.ReadUInt8At(fieldOffsetIsBitableFlushedV1) 260 minUnflushedLogNum := m.file.ReadUInt64At(fieldOffsetMinUnflushedLogNumV1) 261 nextFileNum := m.file.ReadUInt64At(fieldOffsetNextFileNumV1) 262 263 m.file.WriteUInt64At(lastSeqNum, fieldOffsetLastSeqNumV2) 264 m.file.WriteUInt8At(flushedBitable, fieldOffsetIsBitableFlushedV2) 265 266 for i := 0; i < len(m.Bmes); i++ { 267 m.file.WriteUInt64At(nextFileNum, fieldOffsetBitowerNextFileNumV2+i*8) 268 m.file.WriteUInt64At(minUnflushedLogNum, fieldOffsetBitowerMinUnflushedLogNumV2+i*8) 269 } 270 271 m.header.version = metaVersion2 272 m.writeHeader() 273 m.file.Flush() 274 }