github.com/zhiqiangxu/util@v0.0.0-20230112053021-0a7aee056cd5/diskqueue/qfile.go (about)

     1  package diskqueue
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"net"
     8  	"os"
     9  	"path/filepath"
    10  	"sync"
    11  	"sync/atomic"
    12  
    13  	"github.com/zhiqiangxu/util/logger"
    14  	"github.com/zhiqiangxu/util/mapped"
    15  	"go.uber.org/zap"
    16  )
    17  
    18  type refCountInterface interface {
    19  	IncrRef() int32
    20  	DecrRef() int32
    21  }
    22  type qfileInterface interface {
    23  	refCountInterface
    24  	Shrink() error
    25  	writeBuffers(buffs *net.Buffers) (int64, error)
    26  	WrotePosition() int64
    27  	DoneWrite() int64
    28  	Commit() int64
    29  	Read(ctx context.Context, offset int64) ([]byte, error)
    30  	StreamRead(ctx context.Context, offset int64, ch chan<- StreamBytes) (bool, error)
    31  	StreamOffsetRead(ctx context.Context, offset int64, offsetCh <-chan int64, ch chan<- StreamBytes) (bool, int64, error)
    32  	Sync() error
    33  	Close() error
    34  }
    35  
    36  // qfile has no write-write races, but has read-write races
    37  type qfile struct {
    38  	ref            int32
    39  	q              *Queue
    40  	idx            int
    41  	startOffset    int64
    42  	mappedFile     *mapped.File
    43  	notLatest      bool
    44  	readLockedFunc func(ctx context.Context, r *QfileSizeReader) (otherFile bool, startOffset int64, dataBytes []byte, err error)
    45  }
    46  
    47  const (
    48  	qfSubDir         = "qf"
    49  	qfileDefaultSize = 1024 * 1024 * 1024
    50  )
    51  
    52  var _ qfileInterface = (*qfile)(nil)
    53  
    54  func qfilePath(startOffset int64, conf *Conf) string {
    55  	return filepath.Join(conf.Directory, qfSubDir, fmt.Sprintf("%020d", startOffset))
    56  }
    57  
    58  func openQfile(q *Queue, idx int, isLatest bool) (qf *qfile, err error) {
    59  	fm := q.meta.FileMeta(idx)
    60  
    61  	qf = &qfile{q: q, idx: idx, startOffset: fm.StartOffset, ref: 1}
    62  	var pool *sync.Pool
    63  	if isLatest {
    64  		pool = q.writeBufferPool()
    65  	}
    66  	qf.mappedFile, err = mapped.OpenFile(qfilePath(fm.StartOffset, &q.conf), int64(fm.EndOffset-fm.StartOffset), os.O_RDWR, q.conf.WriteMmap, pool)
    67  	if err != nil {
    68  		return
    69  	}
    70  	qf.init()
    71  
    72  	return
    73  }
    74  
    75  func createQfile(q *Queue, idx int, startOffset int64) (qf *qfile, err error) {
    76  	qf = &qfile{q: q, idx: idx, startOffset: startOffset, ref: 1}
    77  	var pool *sync.Pool
    78  	if q.conf.EnableWriteBuffer {
    79  		pool = q.writeBufferPool()
    80  	}
    81  	qf.mappedFile, err = mapped.CreateFile(qfilePath(startOffset, &q.conf), q.conf.MaxFileSize, q.conf.WriteMmap, pool)
    82  	if err != nil {
    83  		return
    84  	}
    85  	qf.init()
    86  
    87  	if q.meta.NumFiles() != idx {
    88  		logger.Instance().Fatal("createQfile idx != NumFiles", zap.Int("NumFiles", q.meta.NumFiles()), zap.Int("idx", idx))
    89  	}
    90  
    91  	nowNano := NowNano()
    92  	q.meta.AddFile(FileMeta{StartOffset: startOffset, EndOffset: startOffset, StartTime: nowNano, EndTime: nowNano})
    93  
    94  	return
    95  }
    96  
    97  func (qf *qfile) init() {
    98  	if qf.q.conf.customDecoder {
    99  		qf.readLockedFunc = qf.readLockedCustom
   100  	} else {
   101  		qf.readLockedFunc = qf.readLockedDefault
   102  	}
   103  }
   104  
   105  func (qf *qfile) IncrRef() int32 {
   106  	return atomic.AddInt32(&qf.ref, 1)
   107  }
   108  
   109  func (qf *qfile) DecrRef() (newRef int32) {
   110  	newRef = atomic.AddInt32(&qf.ref, -1)
   111  	if newRef > 0 {
   112  		return
   113  	}
   114  
   115  	err := qf.Close()
   116  	if err != nil {
   117  		logger.Instance().Error("qf.Close", zap.Error(err))
   118  	}
   119  
   120  	err = qf.remove()
   121  	if err != nil {
   122  		logger.Instance().Error("qf.remove", zap.Error(err))
   123  	}
   124  
   125  	return
   126  }
   127  
   128  func (qf *qfile) writeBuffers(buffs *net.Buffers) (n int64, err error) {
   129  	n, err = qf.mappedFile.WriteBuffers(buffs)
   130  	return
   131  	// n, err = buffs.WriteTo(qf.mappedFile)
   132  	// return
   133  }
   134  
   135  func (qf *qfile) WrotePosition() int64 {
   136  	return qf.startOffset + qf.mappedFile.GetWrotePosition()
   137  }
   138  
   139  func (qf *qfile) DoneWrite() int64 {
   140  	return qf.startOffset + qf.mappedFile.DoneWrite()
   141  }
   142  
   143  func (qf *qfile) Commit() int64 {
   144  	return qf.startOffset + qf.mappedFile.Commit()
   145  }
   146  
   147  // isLatest can be called concurrently :)
   148  func (qf *qfile) isLatest() bool {
   149  	if qf.notLatest {
   150  		return false
   151  	}
   152  	isLatest := qf.idx == qf.q.meta.NumFiles()-1
   153  	if !isLatest {
   154  		qf.notLatest = true
   155  	}
   156  	return isLatest
   157  }
   158  
   159  func (qf *qfile) readLockedCustom(ctx context.Context, r *QfileSizeReader) (otherFile bool, startOffset int64, dataBytes []byte, err error) {
   160  
   161  	startOffset = r.NextOffset()
   162  	otherFile, dataBytes, err = qf.q.conf.CustomDecoder(ctx, r)
   163  	if err == mapped.ErrReadBeyond && !qf.isLatest() {
   164  		otherFile = true
   165  	}
   166  	return
   167  }
   168  
   169  func (qf *qfile) readLockedDefault(ctx context.Context, r *QfileSizeReader) (otherFile bool, startOffset int64, dataBytes []byte, err error) {
   170  
   171  	startOffset = r.NextOffset()
   172  	var sizeBytes [sizeLength]byte
   173  	err = r.Read(ctx, sizeBytes[:])
   174  	if err != nil {
   175  		if err == mapped.ErrReadBeyond && !qf.isLatest() {
   176  			otherFile = true
   177  		}
   178  		return
   179  	}
   180  
   181  	size := int(binary.BigEndian.Uint32(sizeBytes[:]))
   182  	if size > qf.q.conf.MaxMsgSize {
   183  		err = errInvalidOffset
   184  		return
   185  	}
   186  
   187  	dataBytes = make([]byte, size)
   188  	err = r.Read(ctx, dataBytes)
   189  	return
   190  }
   191  
   192  func (qf *qfile) calcFileOffset(offset int64) (fileOffset int64, err error) {
   193  	fileOffset = offset - qf.startOffset
   194  	if fileOffset < 0 {
   195  		logger.Instance().Error("calcFileOffset negative fileOffset", zap.Int64("offset", offset), zap.Int64("startOffset", qf.startOffset))
   196  		err = errInvalidOffset
   197  		return
   198  	}
   199  
   200  	return
   201  }
   202  
   203  func (qf *qfile) Read(ctx context.Context, offset int64) (data []byte, err error) {
   204  	fileOffset, err := qf.calcFileOffset(offset)
   205  	if err != nil {
   206  		return
   207  	}
   208  
   209  	qf.mappedFile.RLock()
   210  	defer qf.mappedFile.RUnlock()
   211  
   212  	r := qf.getSizeReader(fileOffset)
   213  	_, _, data, err = qf.readLockedFunc(ctx, r)
   214  	qf.putSizeReader(r)
   215  	return
   216  }
   217  
   218  var sizeReaderPool = sync.Pool{
   219  	New: func() interface{} {
   220  		return &QfileSizeReader{}
   221  	},
   222  }
   223  
   224  func (qf *qfile) getSizeReader(fileOffset int64) *QfileSizeReader {
   225  	r := sizeReaderPool.Get().(*QfileSizeReader)
   226  	r.qf = qf
   227  	r.fileOffset = fileOffset
   228  	r.isLatest = qf.isLatest()
   229  	return r
   230  }
   231  
   232  func (qf *qfile) putSizeReader(r *QfileSizeReader) {
   233  	r.qf = nil
   234  	sizeReaderPool.Put(r)
   235  }
   236  
   237  // when StreamRead returns , err is guaranteed not nil
   238  func (qf *qfile) StreamRead(ctx context.Context, offset int64, ch chan<- StreamBytes) (otherFile bool, err error) {
   239  	fileOffset, err := qf.calcFileOffset(offset)
   240  	if err != nil {
   241  		logger.Instance().Fatal("calcFileOffset err", zap.Int64("offset", offset), zap.Int64("startOffset", qf.startOffset))
   242  		return
   243  	}
   244  
   245  	qf.mappedFile.RLock()
   246  	defer qf.mappedFile.RUnlock()
   247  
   248  	r := qf.getSizeReader(fileOffset)
   249  	defer qf.putSizeReader(r)
   250  
   251  	var (
   252  		dataBytes   []byte
   253  		startOffset int64
   254  	)
   255  
   256  	for {
   257  
   258  		otherFile, startOffset, dataBytes, err = qf.readLockedFunc(ctx, r)
   259  		if err != nil {
   260  			return
   261  		}
   262  
   263  		select {
   264  		case ch <- StreamBytes{Bytes: dataBytes, Offset: startOffset}:
   265  		case <-ctx.Done():
   266  			err = ctx.Err()
   267  			return
   268  		}
   269  
   270  	}
   271  
   272  }
   273  
   274  func (qf *qfile) StreamOffsetRead(ctx context.Context, offset int64, offsetCh <-chan int64, ch chan<- StreamBytes) (otherFile bool, lastOffset int64, err error) {
   275  	fileOffset, err := qf.calcFileOffset(offset)
   276  	if err != nil {
   277  		logger.Instance().Fatal("calcFileOffset err", zap.Int64("offset", offset), zap.Int64("startOffset", qf.startOffset))
   278  		return
   279  	}
   280  
   281  	qf.mappedFile.RLock()
   282  	defer qf.mappedFile.RUnlock()
   283  
   284  	r := qf.getSizeReader(fileOffset)
   285  	defer func() {
   286  		lastOffset = r.fileOffset + qf.startOffset
   287  		qf.putSizeReader(r)
   288  	}()
   289  
   290  	var (
   291  		dataBytes   []byte
   292  		nextOffset  int64
   293  		ok          bool
   294  		startOffset int64
   295  	)
   296  
   297  	for {
   298  
   299  		otherFile, startOffset, dataBytes, err = qf.readLockedFunc(ctx, r)
   300  		if err != nil {
   301  			return
   302  		}
   303  
   304  		select {
   305  		case ch <- StreamBytes{Bytes: dataBytes, Offset: startOffset}:
   306  		case <-ctx.Done():
   307  			err = ctx.Err()
   308  			return
   309  		}
   310  
   311  		select {
   312  		case nextOffset, ok = <-offsetCh:
   313  			if !ok {
   314  				err = errOffsetChClosed
   315  				return
   316  			}
   317  			r.fileOffset = nextOffset - qf.startOffset
   318  			if r.fileOffset < 0 {
   319  				err = errInvalidOffset
   320  				otherFile = true
   321  				return
   322  			}
   323  		case <-ctx.Done():
   324  			err = ctx.Err()
   325  			return
   326  		}
   327  
   328  	}
   329  }
   330  
   331  func (qf *qfile) Shrink() error {
   332  	return qf.mappedFile.Shrink()
   333  }
   334  
   335  func (qf *qfile) Sync() error {
   336  	return qf.mappedFile.Sync()
   337  }
   338  
   339  func (qf *qfile) Close() error {
   340  	return qf.mappedFile.Close()
   341  }
   342  
   343  func (qf *qfile) remove() (err error) {
   344  	err = qf.mappedFile.Remove()
   345  	return
   346  }