github.com/flower-corp/rosedb@v1.1.2-0.20230117132829-21dc4f7b319a/logfile/log_file.go (about)

     1  package logfile
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"hash/crc32"
     7  	"path/filepath"
     8  	"sync"
     9  	"sync/atomic"
    10  
    11  	"github.com/flower-corp/rosedb/ioselector"
    12  )
    13  
    14  var (
    15  	// ErrInvalidCrc invalid crc.
    16  	ErrInvalidCrc = errors.New("logfile: invalid crc")
    17  
    18  	// ErrWriteSizeNotEqual write size is not equal to entry size.
    19  	ErrWriteSizeNotEqual = errors.New("logfile: write size is not equal to entry size")
    20  
    21  	// ErrEndOfEntry end of entry in log file.
    22  	ErrEndOfEntry = errors.New("logfile: end of entry in log file")
    23  
    24  	// ErrUnsupportedIoType unsupported io type, only mmap and fileIO now.
    25  	ErrUnsupportedIoType = errors.New("unsupported io type")
    26  
    27  	// ErrUnsupportedLogFileType unsupported log file type, only WAL and ValueLog now.
    28  	ErrUnsupportedLogFileType = errors.New("unsupported log file type")
    29  )
    30  
    31  const (
    32  	// InitialLogFileId initial log file id: 0.
    33  	InitialLogFileId = 0
    34  
    35  	// FilePrefix log file prefix.
    36  	FilePrefix = "log."
    37  )
    38  
    39  // FileType represents different types of log file: wal and value log.
    40  type FileType int8
    41  
    42  const (
    43  	Strs FileType = iota
    44  	List
    45  	Hash
    46  	Sets
    47  	ZSet
    48  )
    49  
    50  var (
    51  	FileNamesMap = map[FileType]string{
    52  		Strs: "log.strs.",
    53  		List: "log.list.",
    54  		Hash: "log.hash.",
    55  		Sets: "log.sets.",
    56  		ZSet: "log.zset.",
    57  	}
    58  
    59  	// FileTypesMap name->type
    60  	FileTypesMap = map[string]FileType{
    61  		"strs": Strs,
    62  		"list": List,
    63  		"hash": Hash,
    64  		"sets": Sets,
    65  		"zset": ZSet,
    66  	}
    67  )
    68  
    69  // IOType represents different types of file io: FileIO(standard file io) and MMap(Memory Map).
    70  type IOType int8
    71  
    72  const (
    73  	// FileIO standard file io.
    74  	FileIO IOType = iota
    75  	// MMap Memory Map.
    76  	MMap
    77  )
    78  
    79  // LogFile is an abstraction of a disk file, entry`s read and write will go through it.
    80  type LogFile struct {
    81  	sync.RWMutex
    82  	Fid        uint32
    83  	WriteAt    int64
    84  	IoSelector ioselector.IOSelector
    85  }
    86  
    87  // OpenLogFile open an existing or create a new log file.
    88  // fsize must be a postitive number.And we will create io selector according to ioType.
    89  func OpenLogFile(path string, fid uint32, fsize int64, ftype FileType, ioType IOType) (lf *LogFile, err error) {
    90  	lf = &LogFile{Fid: fid}
    91  	fileName, err := lf.getLogFileName(path, fid, ftype)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	var selector ioselector.IOSelector
    97  	switch ioType {
    98  	case FileIO:
    99  		if selector, err = ioselector.NewFileIOSelector(fileName, fsize); err != nil {
   100  			return
   101  		}
   102  	case MMap:
   103  		if selector, err = ioselector.NewMMapSelector(fileName, fsize); err != nil {
   104  			return
   105  		}
   106  	default:
   107  		return nil, ErrUnsupportedIoType
   108  	}
   109  
   110  	lf.IoSelector = selector
   111  	return
   112  }
   113  
   114  // ReadLogEntry read a LogEntry from log file at offset.
   115  // It returns a LogEntry, entry size and an error, if any.
   116  // If offset is invalid, the err is io.EOF.
   117  func (lf *LogFile) ReadLogEntry(offset int64) (*LogEntry, int64, error) {
   118  	// read entry header.
   119  	headerBuf, err := lf.readBytes(offset, MaxHeaderSize)
   120  	if err != nil {
   121  		return nil, 0, err
   122  	}
   123  	header, size := decodeHeader(headerBuf)
   124  	// the end of entries.
   125  	if header.crc32 == 0 && header.kSize == 0 && header.vSize == 0 {
   126  		return nil, 0, ErrEndOfEntry
   127  	}
   128  
   129  	e := &LogEntry{
   130  		ExpiredAt: header.expiredAt,
   131  		Type:      header.typ,
   132  	}
   133  	kSize, vSize := int64(header.kSize), int64(header.vSize)
   134  	var entrySize = size + kSize + vSize
   135  
   136  	// read entry key and value.
   137  	if kSize > 0 || vSize > 0 {
   138  		kvBuf, err := lf.readBytes(offset+size, kSize+vSize)
   139  		if err != nil {
   140  			return nil, 0, err
   141  		}
   142  		e.Key = kvBuf[:kSize]
   143  		e.Value = kvBuf[kSize:]
   144  	}
   145  
   146  	// crc32 check.
   147  	if crc := getEntryCrc(e, headerBuf[crc32.Size:size]); crc != header.crc32 {
   148  		return nil, 0, ErrInvalidCrc
   149  	}
   150  	return e, entrySize, nil
   151  }
   152  
   153  // Read a byte slice in the log file at offset, slice length is the given size.
   154  // It returns the byte slice and error, if any.
   155  func (lf *LogFile) Read(offset int64, size uint32) ([]byte, error) {
   156  	if size <= 0 {
   157  		return []byte{}, nil
   158  	}
   159  	buf := make([]byte, size)
   160  	if _, err := lf.IoSelector.Read(buf, offset); err != nil {
   161  		return nil, err
   162  	}
   163  	return buf, nil
   164  }
   165  
   166  // Write a byte slice at the end of log file.
   167  // Returns an error, if any.
   168  func (lf *LogFile) Write(buf []byte) error {
   169  	if len(buf) <= 0 {
   170  		return nil
   171  	}
   172  	offset := atomic.LoadInt64(&lf.WriteAt)
   173  	n, err := lf.IoSelector.Write(buf, offset)
   174  	if err != nil {
   175  		return err
   176  	}
   177  	if n != len(buf) {
   178  		return ErrWriteSizeNotEqual
   179  	}
   180  
   181  	atomic.AddInt64(&lf.WriteAt, int64(n))
   182  	return nil
   183  }
   184  
   185  // Sync commits the current contents of the log file to stable storage.
   186  func (lf *LogFile) Sync() error {
   187  	return lf.IoSelector.Sync()
   188  }
   189  
   190  // Close current log file.
   191  func (lf *LogFile) Close() error {
   192  	return lf.IoSelector.Close()
   193  }
   194  
   195  // Delete delete current log file.
   196  // File can`t be retrieved if do this, so use it carefully.
   197  func (lf *LogFile) Delete() error {
   198  	return lf.IoSelector.Delete()
   199  }
   200  
   201  func (lf *LogFile) readBytes(offset, n int64) (buf []byte, err error) {
   202  	buf = make([]byte, n)
   203  	_, err = lf.IoSelector.Read(buf, offset)
   204  	return
   205  }
   206  
   207  func (lf *LogFile) getLogFileName(path string, fid uint32, ftype FileType) (name string, err error) {
   208  	if _, ok := FileNamesMap[ftype]; !ok {
   209  		return "", ErrUnsupportedLogFileType
   210  	}
   211  
   212  	fname := FileNamesMap[ftype] + fmt.Sprintf("%09d", fid)
   213  	name = filepath.Join(path, fname)
   214  	return
   215  }