github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/bithash/writer.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 "bytes" 20 "encoding/binary" 21 "io" 22 "sort" 23 "strconv" 24 "sync" 25 "sync/atomic" 26 27 "github.com/zuoyebang/bitalosdb/internal/base" 28 "github.com/zuoyebang/bitalosdb/internal/bindex" 29 "github.com/zuoyebang/bitalosdb/internal/bytepools" 30 "github.com/zuoyebang/bitalosdb/internal/compress" 31 "github.com/zuoyebang/bitalosdb/internal/consts" 32 "github.com/zuoyebang/bitalosdb/internal/crc" 33 "github.com/zuoyebang/bitalosdb/internal/hash" 34 "github.com/zuoyebang/bitalosdb/internal/unsafe2" 35 "github.com/zuoyebang/bitalosdb/internal/utils" 36 "github.com/zuoyebang/bitalosdb/internal/vfs" 37 ) 38 39 const ( 40 maxKeySize = 33 << 10 41 maxValueSize = 16 << 20 42 blockRestartInterval = 16 43 ) 44 45 type writeCloseSyncer interface { 46 io.WriteCloser 47 Sync() error 48 } 49 50 type WriterOption interface { 51 writerApply(*Writer) 52 } 53 54 type hashHandle struct { 55 bh BlockHandle 56 userKey []byte 57 conflict bool 58 } 59 60 type WriterMetadata struct { 61 Size uint32 62 keyNum uint32 63 conflictKeyNum uint32 64 } 65 66 type Writer struct { 67 b *Bithash 68 closed bool 69 closing atomic.Bool 70 writer io.Writer 71 syncer writeCloseSyncer 72 bufWriter *bufio.Writer 73 reader ReadableFile 74 meta WriterMetadata 75 err error 76 file File 77 filename string 78 fileNum FileNum 79 closeLock sync.RWMutex 80 indexLock sync.RWMutex 81 indexHash map[uint32]*hashHandle 82 indexArray []hashHandle 83 conflictKeys map[string]BlockHandle 84 footer footer 85 currentOffset uint32 86 dataBlockSize uint32 87 dataBlock block2Writer 88 dataBlockReader block2Reader 89 metaBlock blockWriter 90 indexBlock blockWriter 91 conflictBlock blockWriter 92 indexHashBH BlockHandle 93 conflictBH BlockHandle 94 dataBH BlockHandle 95 compressedBuf []byte 96 } 97 98 func newWriter(b *Bithash, f File, filename string, fileNum FileNum) *Writer { 99 w := &Writer{ 100 b: b, 101 closed: false, 102 syncer: f.(writeCloseSyncer), 103 reader: f.(ReadableFile), 104 meta: WriterMetadata{Size: 0}, 105 file: f, 106 filename: filename, 107 fileNum: fileNum, 108 dataBlockSize: uint32(b.tableMaxSize), 109 dataBlock: block2Writer{}, 110 indexBlock: blockWriter{restartInterval: blockRestartInterval}, 111 metaBlock: blockWriter{restartInterval: blockRestartInterval}, 112 conflictBlock: blockWriter{restartInterval: blockRestartInterval}, 113 indexHash: make(map[uint32]*hashHandle, 1<<18), 114 indexArray: make([]hashHandle, 0, 1<<18), 115 conflictKeys: make(map[string]BlockHandle, 10), 116 currentOffset: 0, 117 } 118 return w 119 } 120 121 func NewTableWriter(b *Bithash, isCompact bool) (*Writer, error) { 122 fileNum := b.meta.getNextFileNum() 123 filename := MakeFilepath(b.fs, b.dirname, fileTypeTable, fileNum) 124 file, err := b.fs.Create(filename) 125 if err != nil { 126 return nil, ErrBhCreateTableFile 127 } 128 129 file = vfs.NewSyncingFile(file, vfs.SyncingFileOptions{ 130 BytesPerSync: b.bytesPerSync, 131 }) 132 133 w := newWriter(b, file, filename, fileNum) 134 if err = w.setIoWriter(file); err != nil { 135 return nil, err 136 } 137 138 b.meta.newFileMetadata(fileNum, isCompact) 139 b.addRwwWriters(w) 140 b.stats.FileTotal.Add(1) 141 142 if !isCompact { 143 b.SetFileNumMap(fileNum, fileNum) 144 } 145 146 return w, nil 147 } 148 149 func (w *Writer) setIoWriter(f writeCloseSyncer) error { 150 if _, err := w.file.(io.Seeker).Seek(int64(w.currentOffset), io.SeekStart); err != nil { 151 return err 152 } 153 154 w.bufWriter = bufio.NewWriterSize(f, consts.BufioWriterBufSize) 155 w.writer = w.bufWriter 156 w.dataBlock.wr = w.bufWriter 157 return nil 158 } 159 160 func (w *Writer) fileStatSize() int64 { 161 info, err := w.file.Stat() 162 if err != nil { 163 return 0 164 } 165 return info.Size() 166 } 167 168 func (w *Writer) Get(key []byte, khash uint32) ([]byte, func(), error) { 169 var err error 170 var bh BlockHandle 171 172 w.indexLock.RLock() 173 if handle, ok := w.indexHash[khash]; ok { 174 if handle.conflict { 175 bh = w.conflictKeys[unsafe2.String(key)] 176 } else { 177 bh = handle.bh 178 } 179 } 180 w.indexLock.RUnlock() 181 182 if bh.Length <= 0 { 183 return nil, nil, nil 184 } 185 186 length := int(bh.Length) 187 buf, closer := bytepools.ReaderBytePools.GetBytePool(length) 188 defer func() { 189 if err != nil { 190 closer() 191 } 192 }() 193 buf = buf[:length] 194 195 w.closeLock.RLock() 196 defer w.closeLock.RUnlock() 197 198 if w.closed { 199 err = ErrBhWriterClosed 200 return nil, nil, err 201 } 202 203 var n int 204 n, err = w.reader.ReadAt(buf, int64(bh.Offset)) 205 if err != nil { 206 return nil, nil, err 207 } 208 if n != length { 209 err = ErrBhReadAtIncomplete 210 return nil, nil, err 211 } 212 _, val, _ := w.dataBlockReader.readRecord(buf) 213 if val == nil { 214 err = ErrBhReadRecordNil 215 return nil, nil, err 216 } 217 218 var v []byte 219 v, err = w.b.compressor.Decode(nil, val) 220 if err != nil { 221 return nil, nil, err 222 } 223 224 return v, closer, nil 225 } 226 227 func (w *Writer) Add(ikey base.InternalKey, value []byte) error { 228 if w.err != nil { 229 return w.err 230 } 231 232 var compressed []byte 233 switch w.b.compressor.Type() { 234 case compress.CompressTypeNo: 235 compressed = value 236 case compress.CompressTypeSnappy: 237 compressed = w.b.compressor.Encode(w.compressedBuf, value) 238 if cap(compressed) > cap(w.compressedBuf) { 239 w.compressedBuf = compressed[:cap(compressed)] 240 } 241 } 242 243 return w.add(ikey, compressed, hash.Crc32(ikey.UserKey), w.fileNum) 244 } 245 246 func (w *Writer) AddIkey(ikey InternalKey, value []byte, khash uint32, fileNum FileNum) error { 247 if w.err != nil { 248 return w.err 249 } 250 251 return w.add(ikey, value, khash, fileNum) 252 } 253 254 func (w *Writer) add(ikey InternalKey, value []byte, khash uint32, fileNum FileNum) error { 255 if ikey.Size() > maxKeySize { 256 return ErrBhKeyTooLarge 257 } else if len(value) > maxValueSize { 258 return ErrBhValueTooLarge 259 } 260 261 n, err := w.dataBlock.set(ikey, value, fileNum) 262 if err != nil { 263 return err 264 } 265 266 length := uint32(n) 267 bh := BlockHandle{w.currentOffset, length} 268 w.currentOffset += length 269 w.meta.Size += length 270 w.meta.keyNum++ 271 w.updateHash(ikey, khash, bh) 272 return nil 273 } 274 275 func (w *Writer) updateHash(ikey InternalKey, khash uint32, bh BlockHandle) { 276 w.indexLock.Lock() 277 defer w.indexLock.Unlock() 278 279 key := ikey.UserKey 280 if ih, ok := w.indexHash[khash]; !ok { 281 w.indexArray = append(w.indexArray, hashHandle{ 282 bh: bh, 283 userKey: key, 284 conflict: false, 285 }) 286 w.indexHash[khash] = &(w.indexArray[len(w.indexArray)-1]) 287 } else { 288 if ih.conflict { 289 w.conflictKeys[unsafe2.String(key)] = bh 290 } else { 291 if bytes.Equal(ih.userKey, key) { 292 ih.bh = bh 293 } else { 294 ih.conflict = true 295 w.conflictKeys[unsafe2.String(key)] = bh 296 w.conflictKeys[unsafe2.String(ih.userKey)] = ih.bh 297 } 298 } 299 } 300 } 301 302 func (w *Writer) writeTable(force bool) (err error) { 303 if w.err != nil { 304 return w.err 305 } 306 307 if force || w.isWriteFull() { 308 if err = w.writeData(); err != nil { 309 return err 310 } 311 if err = w.writeConflict(); err != nil { 312 return err 313 } 314 if err = w.writeIndexHash(); err != nil { 315 return err 316 } 317 if err = w.writeMeta(); err != nil { 318 return err 319 } 320 if err = w.writeFooter(); err != nil { 321 return err 322 } 323 324 w.err = ErrBhWriterClosed 325 } 326 327 return w.Flush() 328 } 329 330 func (w *Writer) close() (err error) { 331 if w.closed { 332 return 333 } 334 335 w.closeLock.Lock() 336 err = w.syncer.Close() 337 w.closed = true 338 w.syncer = nil 339 w.indexHash = nil 340 w.indexArray = nil 341 w.conflictKeys = nil 342 w.compressedBuf = nil 343 w.closeLock.Unlock() 344 return err 345 } 346 347 func (w *Writer) Close() (err error) { 348 if w.closing.Load() { 349 return nil 350 } 351 352 defer func() { 353 err1 := w.close() 354 if err == nil { 355 err = err1 356 } 357 }() 358 if w.err != nil { 359 return w.err 360 } 361 362 return w.writeTable(false) 363 } 364 365 func (w *Writer) Flush() error { 366 if w.bufWriter != nil { 367 if err := w.bufWriter.Flush(); err != nil { 368 w.err = err 369 return err 370 } 371 } 372 373 if w.syncer != nil { 374 if err := w.syncer.Sync(); err != nil { 375 w.err = err 376 return err 377 } 378 } 379 380 return nil 381 } 382 383 func (w *Writer) writeData() error { 384 n, err := w.dataBlock.setEmptyHeader() 385 if err != nil { 386 w.err = err 387 return w.err 388 } 389 390 w.currentOffset += uint32(n) 391 w.dataBH = BlockHandle{ 392 Offset: 0, 393 Length: w.currentOffset, 394 } 395 396 return nil 397 } 398 399 func (w *Writer) writeConflict() error { 400 var buf [blockHandleLen]byte 401 402 w.meta.conflictKeyNum = uint32(len(w.conflictKeys)) 403 if w.meta.conflictKeyNum == 0 { 404 w.conflictBH = BlockHandle{ 405 Offset: w.currentOffset, 406 Length: 0, 407 } 408 return nil 409 } 410 411 keyStrs := make([]string, 0, w.meta.conflictKeyNum) 412 for k := range w.conflictKeys { 413 keyStrs = append(keyStrs, k) 414 } 415 416 sort.Strings(keyStrs) 417 for _, k := range keyStrs { 418 encodeBlockHandle(buf[:], w.conflictKeys[k]) 419 ikey := base.MakeInternalKey(unsafe2.ByteSlice(k), 1, InternalKeyKindSet) 420 w.conflictBlock.add(ikey, buf[:]) 421 } 422 423 b := w.conflictBlock.finish() 424 n, err := w.writer.Write(b) 425 if err != nil { 426 w.err = err 427 return w.err 428 } 429 430 length := uint32(n) 431 w.conflictBH = BlockHandle{ 432 Offset: w.currentOffset, 433 Length: length, 434 } 435 w.currentOffset += length 436 return nil 437 } 438 439 func (w *Writer) writeIndexHash() error { 440 var buf [blockHandleLen]byte 441 var data []byte 442 443 if len(w.indexHash) > 0 { 444 hindex := bindex.NewHashIndex(false) 445 hindex.InitWriter() 446 447 for khash, ih := range w.indexHash { 448 if ih.conflict { 449 encodeBlockHandle(buf[:], w.conflictBH) 450 } else { 451 encodeBlockHandle(buf[:], ih.bh) 452 } 453 hindex.Add(khash, binary.LittleEndian.Uint64(buf[:])) 454 } 455 456 if r := hindex.Serialize(); !r { 457 w.err = ErrBhHashIndexWriteFail 458 return w.err 459 } 460 461 data = hindex.GetData() 462 w.indexBlock.add(base.MakeInternalKey([]byte(IndexHashData), 1, InternalKeyKindSet), data) 463 464 hindex.Finish() 465 } 466 467 checksum := crc.New(data).Value() 468 w.indexBlock.add(base.MakeInternalKey([]byte(IndexHashChecksum), 1, InternalKeyKindSet), []byte(strconv.FormatUint(uint64(checksum), 10))) 469 b := w.indexBlock.finish() 470 if _, err := w.writer.Write(b); err != nil { 471 w.err = err 472 return w.err 473 } 474 475 indexLength := uint32(len(b)) 476 w.indexHashBH = BlockHandle{w.currentOffset, indexLength} 477 w.currentOffset += indexLength 478 479 return nil 480 } 481 482 func (w *Writer) writeMeta() error { 483 var buf [blockHandleLen]byte 484 485 w.footer.metaBH = BlockHandle{w.currentOffset, 0} 486 487 encodeBlockHandle(buf[:], w.dataBH) 488 ikey := base.MakeInternalKey([]byte(MetaDataBH), 1, InternalKeyKindSet) 489 w.metaBlock.add(ikey, buf[:]) 490 491 encodeBlockHandle(buf[:], w.conflictBH) 492 ikey = base.MakeInternalKey([]byte(MetaConflictBH), 1, InternalKeyKindSet) 493 w.metaBlock.add(ikey, buf[:]) 494 495 encodeBlockHandle(buf[:], w.indexHashBH) 496 ikey = base.MakeInternalKey([]byte(MetaIndexHashBH), 1, InternalKeyKindSet) 497 w.metaBlock.add(ikey, buf[:]) 498 499 b := w.metaBlock.finish() 500 n, err := w.writer.Write(b) 501 if err != nil { 502 w.err = err 503 return w.err 504 } 505 506 length := uint32(n) 507 w.footer.metaBH = BlockHandle{w.currentOffset, length} 508 w.currentOffset += length 509 510 return nil 511 } 512 513 func (w *Writer) writeFooter() error { 514 footer := footer{ 515 metaBH: w.footer.metaBH, 516 } 517 footerBuf := [bithashFooterLen]byte{} 518 if _, err := w.writer.Write(footer.encode(footerBuf[:])); err != nil { 519 w.err = err 520 return w.err 521 } 522 return nil 523 } 524 525 func (w *Writer) isWriteFull() bool { 526 return w.meta.Size >= w.dataBlockSize 527 } 528 529 func (w *Writer) rebuild() (err error) { 530 var ( 531 ikeySize uint32 532 valueSize uint32 533 br block2Reader 534 headerBuf = [recordHeaderSize]byte{} 535 recordLen uint32 536 offset = 0 537 n = 0 538 ) 539 540 for { 541 n, err = w.reader.ReadAt(headerBuf[:], int64(offset)) 542 if err != nil || n != recordHeaderSize { 543 break 544 } 545 offset += n 546 547 ikeySize, valueSize, _ = br.readRecordHeader(headerBuf[:]) 548 if ikeySize == 0 { 549 break 550 } 551 552 key := make([]byte, ikeySize) 553 n, err = w.reader.ReadAt(key, int64(offset)) 554 if err != nil || n != int(ikeySize) { 555 break 556 } 557 offset += int(ikeySize + valueSize) 558 559 recordLen = recordHeaderSize + ikeySize + valueSize 560 bh := BlockHandle{w.currentOffset, recordLen} 561 w.currentOffset += recordLen 562 w.meta.Size += recordLen 563 w.meta.keyNum++ 564 ikey := base.DecodeInternalKey(key) 565 w.updateHash(ikey, hash.Crc32(ikey.UserKey), bh) 566 } 567 568 if err != nil && err != io.EOF { 569 return err 570 } 571 572 return w.setIoWriter(w.file) 573 } 574 575 func (w *Writer) Remove() error { 576 w.b.deleteRwwWriters(w.fileNum) 577 w.b.meta.freeFileMetadata(w.fileNum) 578 if utils.IsFileExist(w.filename) { 579 return w.b.fs.Remove(w.filename) 580 } 581 return nil 582 }