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  }