github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/segment/reader/segread/timereader.go (about)

     1  /*
     2  Copyright 2023.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package segread
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"os"
    24  	"sort"
    25  	"sync"
    26  
    27  	"github.com/cespare/xxhash"
    28  	"github.com/siglens/siglens/pkg/blob"
    29  	"github.com/siglens/siglens/pkg/config"
    30  	"github.com/siglens/siglens/pkg/segment/structs"
    31  	"github.com/siglens/siglens/pkg/segment/utils"
    32  	toputils "github.com/siglens/siglens/pkg/utils"
    33  	log "github.com/sirupsen/logrus"
    34  )
    35  
    36  var rawTimestampsBufferPool = sync.Pool{
    37  	New: func() interface{} {
    38  		// The Pool's New function should generally only return pointer
    39  		// types, since a pointer can be put into the return interface
    40  		// value without an allocation:
    41  		slice := make([]uint64, utils.DEFAULT_TIME_SLICE_SIZE)
    42  		return &slice
    43  	},
    44  }
    45  
    46  type timeBlockRequest struct {
    47  	tsRec   []byte
    48  	blkNum  uint16
    49  	numRecs uint16
    50  }
    51  
    52  type TimeRangeReader struct {
    53  	timeFD        *os.File
    54  	timestampKey  string
    55  	timeMetadata  map[uint16]*structs.BlockMetadataHolder
    56  	blockRecCount map[uint16]uint16
    57  
    58  	loadedBlockNum          uint16
    59  	blockTimestamps         []uint64
    60  	blockReadBuffer         []byte
    61  	blockUncompressedBuffer []byte // raw buffer to re-use for decompressing
    62  	numBlockReadTimestamps  uint16
    63  	loadedBlock             bool
    64  	allInUseFiles           []string
    65  }
    66  
    67  // returns a new TimeRangeReader and any errors encountered
    68  // the caller is responsible for calling TimeRangeReader.Close() when finished using it to close the fd
    69  func InitNewTimeReader(segKey string, tsKey string, blockMetadata map[uint16]*structs.BlockMetadataHolder,
    70  	blkRecCount map[uint16]uint16, qid uint64) (*TimeRangeReader, error) {
    71  	allInUseFiles := make([]string, 0)
    72  	var err error
    73  	fName := fmt.Sprintf("%v_%v.csg", segKey, xxhash.Sum64String(tsKey))
    74  	if tsKey != "" {
    75  		err = blob.DownloadSegmentBlob(fName, true)
    76  	} else {
    77  		err = fmt.Errorf("InitNewTimeReader: failed to download segsetfile due to unknown segset col %+v", fName)
    78  	}
    79  	if err != nil {
    80  		log.Errorf("qid=%d, InitNewTimeReader failed to download file. %+v, err=%v", qid, fName, err)
    81  		return nil, err
    82  	}
    83  	fd, err := os.OpenFile(fName, os.O_RDONLY, 0644)
    84  	if err != nil {
    85  		log.Errorf("qid=%d, InitNewSegFileReader: failed to open time column file %s. Error: %+v", qid, fName, err)
    86  		return &TimeRangeReader{}, err
    87  	}
    88  	allInUseFiles = append(allInUseFiles, fName)
    89  
    90  	return &TimeRangeReader{
    91  		timeFD:                  fd,
    92  		timestampKey:            tsKey,
    93  		timeMetadata:            blockMetadata,
    94  		blockRecCount:           blkRecCount,
    95  		blockTimestamps:         *rawTimestampsBufferPool.Get().(*[]uint64),
    96  		blockReadBuffer:         *fileReadBufferPool.Get().(*[]byte),
    97  		blockUncompressedBuffer: *uncompressedReadBufferPool.Get().(*[]byte),
    98  		loadedBlock:             false,
    99  		allInUseFiles:           allInUseFiles,
   100  	}, nil
   101  }
   102  
   103  func InitNewTimeReaderWithFD(tsFD *os.File, tsKey string, blockMetadata map[uint16]*structs.BlockMetadataHolder,
   104  	blkRecCount map[uint16]uint16, qid uint64) (*TimeRangeReader, error) {
   105  
   106  	return &TimeRangeReader{
   107  		timeFD:                  tsFD,
   108  		timestampKey:            tsKey,
   109  		timeMetadata:            blockMetadata,
   110  		blockRecCount:           blkRecCount,
   111  		blockTimestamps:         *rawTimestampsBufferPool.Get().(*[]uint64),
   112  		blockReadBuffer:         *fileReadBufferPool.Get().(*[]byte),
   113  		blockUncompressedBuffer: *uncompressedReadBufferPool.Get().(*[]byte),
   114  		loadedBlock:             false,
   115  	}, nil
   116  }
   117  
   118  func InitNewTimeReaderFromBlockSummaries(segKey string, tsKey string, blockMetadata map[uint16]*structs.BlockMetadataHolder,
   119  	blockSummaries []*structs.BlockSummary, qid uint64) (*TimeRangeReader, error) {
   120  
   121  	blkRecCount := make(map[uint16]uint16)
   122  	for blkIdx, blkSum := range blockSummaries {
   123  		blkRecCount[uint16(blkIdx)] = blkSum.RecCount
   124  	}
   125  	return InitNewTimeReader(segKey, tsKey, blockMetadata, blkRecCount, qid)
   126  }
   127  
   128  // highly optimized for subsequent calls to handle the same blockNum
   129  func (trr *TimeRangeReader) GetTimeStampForRecord(blockNum uint16, recordNum uint16, qid uint64) (uint64, error) {
   130  	if !trr.loadedBlock || trr.loadedBlockNum != blockNum {
   131  		err := trr.readAllTimestampsForBlock(blockNum)
   132  		if err != nil {
   133  			return 0, err
   134  		}
   135  	}
   136  
   137  	if recordNum >= uint16(trr.numBlockReadTimestamps) {
   138  		log.Errorf("qid=%v, GetTimeStampForRecord: failed to get timestamp for recNum %d blkNum %d. Number of read timestamps is %d",
   139  			qid, recordNum, blockNum, trr.numBlockReadTimestamps)
   140  		return 0, errors.New("record number is out of range")
   141  	}
   142  	return trr.blockTimestamps[recordNum], nil
   143  }
   144  
   145  func (trr *TimeRangeReader) GetAllTimeStampsForBlock(blockNum uint16) ([]uint64, error) {
   146  	if !trr.loadedBlock || trr.loadedBlockNum != blockNum {
   147  		err := trr.readAllTimestampsForBlock(blockNum)
   148  		if err != nil {
   149  			return nil, err
   150  		}
   151  	}
   152  
   153  	return trr.blockTimestamps[:trr.numBlockReadTimestamps], nil
   154  }
   155  
   156  func (trr *TimeRangeReader) readAllTimestampsForBlock(blockNum uint16) error {
   157  
   158  	err := trr.resizeSliceForBlock(blockNum)
   159  	if err != nil {
   160  		log.Errorf("readAllTimestampsForBlock: failed to resize internal slice for block %d. Err: %+v", blockNum, err)
   161  		return errors.New("requested blockNum does not exist")
   162  	}
   163  
   164  	blockMeta, ok := trr.timeMetadata[blockNum]
   165  	if !ok {
   166  		log.Errorf("readAllTimestampsForBlock: failed to find block %d in all block metadata %+v", blockNum, trr.timeMetadata)
   167  		return errors.New("requested blockNum does not exist")
   168  	}
   169  	blkOff, ok := blockMeta.ColumnBlockOffset[trr.timestampKey]
   170  	if !ok {
   171  		log.Errorf("readAllTimestampsForBlock: failed to find block offset for timestamp key %+v in block %d. All block offsets %+v",
   172  			trr.timestampKey, blockNum, blockMeta.ColumnBlockOffset)
   173  		return errors.New("requested blockNum does not exist")
   174  	}
   175  	blkLen, ok := blockMeta.ColumnBlockLen[trr.timestampKey]
   176  	if !ok {
   177  		log.Errorf("readAllTimestampsForBlock: failed to find block length for timestamp key %+v in block %d. All block offsets %+v",
   178  			trr.timestampKey, blockNum, blockMeta.ColumnBlockLen)
   179  		return errors.New("requested blockNum does not exist")
   180  	}
   181  
   182  	if uint32(len(trr.blockReadBuffer)) < blkLen {
   183  		newArr := make([]byte, blkLen-uint32(len(trr.blockReadBuffer)))
   184  		trr.blockReadBuffer = append(trr.blockReadBuffer, newArr...)
   185  	}
   186  	_, err = trr.timeFD.ReadAt(trr.blockReadBuffer[:blkLen], blkOff)
   187  	if err != nil {
   188  		if err != io.EOF {
   189  			trr.loadedBlock = false
   190  			log.Errorf("readAllTimestampsForBlock: error reading file at blkLen: %+v blkOff: %+v error: %+v", blkLen, blkOff, err)
   191  			return err
   192  		}
   193  		return nil
   194  	}
   195  
   196  	rawTSVal := trr.blockReadBuffer[:blkLen]
   197  	numRecs := trr.blockRecCount[blockNum]
   198  	decoded, err := convertRawRecordsToTimestamps(rawTSVal, numRecs, trr.blockTimestamps)
   199  	if err != nil {
   200  		log.Errorf("convertRawRecordsToTimestamps failed %+v", err)
   201  		return err
   202  	}
   203  
   204  	trr.numBlockReadTimestamps = numRecs
   205  	trr.blockTimestamps = decoded
   206  	trr.loadedBlock = true
   207  	trr.loadedBlockNum = blockNum
   208  
   209  	return nil
   210  }
   211  
   212  func (trr *TimeRangeReader) resizeSliceForBlock(blockNum uint16) error {
   213  
   214  	numRecs, ok := trr.blockRecCount[blockNum]
   215  	if !ok {
   216  		log.Errorf("readAllTimestampsForBlock: failed to find block %d in all blocks: %+v", blockNum, trr.blockRecCount)
   217  		return errors.New("blockNum not found")
   218  	}
   219  	if currBufSize := len(trr.blockTimestamps); int(numRecs) > currBufSize {
   220  		toAdd := int(numRecs) - currBufSize
   221  		newSlice := make([]uint64, toAdd)
   222  		trr.blockTimestamps = append(trr.blockTimestamps, newSlice...)
   223  	}
   224  	return nil
   225  }
   226  
   227  func (trr *TimeRangeReader) Close() error {
   228  	if trr.timeFD == nil {
   229  		return errors.New("tried to close an unopened time reader")
   230  	}
   231  	trr.returnBuffers()
   232  	err := blob.SetSegSetFilesAsNotInUse(trr.allInUseFiles)
   233  	if err != nil {
   234  		log.Errorf("Failed to release needed segment files from local storage %+v!  Err: %+v", trr.allInUseFiles, err)
   235  	}
   236  	return trr.timeFD.Close()
   237  }
   238  
   239  func (trr *TimeRangeReader) returnBuffers() {
   240  	uncompressedReadBufferPool.Put(&trr.blockUncompressedBuffer)
   241  	fileReadBufferPool.Put(&trr.blockReadBuffer)
   242  	rawTimestampsBufferPool.Put(&trr.blockTimestamps)
   243  }
   244  
   245  func convertRawRecordsToTimestamps(rawRec []byte, numRecs uint16, bufToUse []uint64) ([]uint64, error) {
   246  	if bufToUse == nil {
   247  		bufToUse = make([]uint64, numRecs)
   248  	}
   249  	if currBufSize := len(bufToUse); int(numRecs) > currBufSize {
   250  		toAdd := int(numRecs) - currBufSize
   251  		newSlice := make([]uint64, toAdd)
   252  		bufToUse = append(bufToUse, newSlice...)
   253  	}
   254  
   255  	oPtr := uint32(0)
   256  	if rawRec[oPtr] != utils.TIMESTAMP_TOPDIFF_VARENC[0] {
   257  		log.Errorf("received an unknown encoding type for typestamp column! expected %+v got %+v",
   258  			utils.TIMESTAMP_TOPDIFF_VARENC[0], rawRec[oPtr])
   259  		return nil, fmt.Errorf("received an unknown encoding type for typestamp column! expected %+v got %+v",
   260  			utils.TIMESTAMP_TOPDIFF_VARENC[0], rawRec[oPtr])
   261  	}
   262  	oPtr++
   263  
   264  	tsType := rawRec[oPtr]
   265  	oPtr++
   266  
   267  	lowTs := toputils.BytesToUint64LittleEndian(rawRec[oPtr:])
   268  	oPtr += 8
   269  
   270  	switch tsType {
   271  	case structs.TS_Type8:
   272  		var tsVal uint8
   273  		for i := uint16(0); i < numRecs; i++ {
   274  			tsVal = uint8(rawRec[oPtr])
   275  			bufToUse[i] = uint64(tsVal) + lowTs
   276  			oPtr += 1
   277  		}
   278  	case structs.TS_Type16:
   279  		var tsVal uint16
   280  		for i := uint16(0); i < numRecs; i++ {
   281  			tsVal = toputils.BytesToUint16LittleEndian(rawRec[oPtr:])
   282  			bufToUse[i] = uint64(tsVal) + lowTs
   283  			oPtr += 2
   284  		}
   285  	case structs.TS_Type32:
   286  		var tsVal uint32
   287  		for i := uint16(0); i < numRecs; i++ {
   288  			tsVal = toputils.BytesToUint32LittleEndian(rawRec[oPtr:])
   289  			bufToUse[i] = uint64(tsVal) + lowTs
   290  			oPtr += 4
   291  		}
   292  	case structs.TS_Type64:
   293  		var tsVal uint64
   294  		for i := uint16(0); i < numRecs; i++ {
   295  			tsVal = toputils.BytesToUint64LittleEndian(rawRec[oPtr:])
   296  			bufToUse[i] = uint64(tsVal) + lowTs
   297  			oPtr += 8
   298  		}
   299  	}
   300  
   301  	return bufToUse, nil
   302  }
   303  
   304  func readChunkFromFile(fd *os.File, buf []byte, blkLen uint32, blkOff int64) ([]byte, error) {
   305  	if uint32(len(buf)) < blkLen {
   306  		newArr := make([]byte, blkLen-uint32(len(buf)))
   307  		buf = append(buf, newArr...)
   308  	}
   309  	buf = buf[:blkLen]
   310  	_, err := fd.ReadAt(buf, blkOff)
   311  	if err != nil {
   312  		log.Errorf("readChunkFromFile: failed to read timestamp file! %+v bytes at offset %+v. error %+v",
   313  			blkLen, blkOff, err)
   314  		return nil, err
   315  	}
   316  	return buf, nil
   317  }
   318  
   319  // When the caller of this function is done with retVal, they should call
   320  // ReturnTimeBuffers(retVal) to return the buffers to rawTimestampsBufferPool.
   321  func processTimeBlocks(allRequests chan *timeBlockRequest, wg *sync.WaitGroup, retVal map[uint16][]uint64,
   322  	retLock *sync.Mutex) {
   323  	defer wg.Done()
   324  	for req := range allRequests {
   325  		bufToUse := *rawTimestampsBufferPool.Get().(*[]uint64)
   326  		decoded, err := convertRawRecordsToTimestamps(req.tsRec, req.numRecs, bufToUse)
   327  		if err != nil {
   328  			log.Errorf("convertRawRecordsToTimestamps failed %+v", err)
   329  			continue
   330  		}
   331  		retLock.Lock()
   332  		retVal[req.blkNum] = decoded
   333  		retLock.Unlock()
   334  	}
   335  }
   336  
   337  // When the caller of this function is done with the returned map, they should
   338  // call ReturnTimeBuffers() on it to return the buffers to rawTimestampsBufferPool.
   339  func ReadAllTimestampsForBlock(blks map[uint16]*structs.BlockMetadataHolder, segKey string,
   340  	blockSummaries []*structs.BlockSummary, parallelism int64) (map[uint16][]uint64, error) {
   341  
   342  	if len(blks) == 0 {
   343  		return make(map[uint16][]uint64), nil
   344  	}
   345  	tsKey := config.GetTimeStampKey()
   346  	fName := fmt.Sprintf("%s_%v.csg", segKey, xxhash.Sum64String(tsKey))
   347  	err := blob.DownloadSegmentBlob(fName, true)
   348  	if err != nil {
   349  		log.Errorf("ReadAllTimestampsForBlock: failed to download time column file %s. Error: %+v", fName, err)
   350  		return nil, err
   351  	}
   352  
   353  	defer func() {
   354  		err := blob.SetBlobAsNotInUse(fName)
   355  		if err != nil {
   356  			log.Errorf("ReadAllTimestampsForBlock: failed to set blob as not in use %s. Error: %+v", fName, err)
   357  		}
   358  	}()
   359  	fd, err := os.OpenFile(fName, os.O_RDONLY, 0644)
   360  	if err != nil {
   361  		log.Errorf("ReadAllTimestampsForBlock: failed to open time column file %s. Error: %+v", fName, err)
   362  		return nil, err
   363  	}
   364  	defer fd.Close()
   365  
   366  	allBlocks := make([]uint16, 0)
   367  	for n := range blks {
   368  		allBlocks = append(allBlocks, n)
   369  	}
   370  
   371  	sort.Slice(allBlocks, func(i, j int) bool {
   372  		return allBlocks[i] < allBlocks[j]
   373  	})
   374  
   375  	retVal := make(map[uint16][]uint64)
   376  	var retLock sync.Mutex
   377  	minIdx := 0
   378  	allReadJob := make(chan *timeBlockRequest)
   379  	var readerWG sync.WaitGroup
   380  	for i := int64(0); i < parallelism; i++ {
   381  		readerWG.Add(1)
   382  		go processTimeBlocks(allReadJob, &readerWG, retVal, &retLock)
   383  	}
   384  
   385  	for minIdx < len(allBlocks) {
   386  		minBlkNum := allBlocks[minIdx]
   387  		lastBlkNum := minBlkNum
   388  		firstBlkOff := blks[minBlkNum].ColumnBlockOffset[tsKey]
   389  		blkLen := blks[minBlkNum].ColumnBlockLen[tsKey]
   390  		maxIdx := minIdx
   391  		for {
   392  			nextIdx := maxIdx + 1
   393  			if nextIdx >= len(allBlocks) {
   394  				break
   395  			}
   396  			nextBlkNum := allBlocks[nextIdx]
   397  			if nextBlkNum == lastBlkNum+1 {
   398  				maxIdx++
   399  				blkLen += blks[nextBlkNum].ColumnBlockLen[tsKey]
   400  				lastBlkNum = nextBlkNum
   401  			} else {
   402  				break
   403  			}
   404  		}
   405  		buffer := *uncompressedReadBufferPool.Get().(*[]byte)
   406  		rawChunk, err := readChunkFromFile(fd, buffer, blkLen, firstBlkOff)
   407  		defer uncompressedReadBufferPool.Put(&rawChunk)
   408  		if err != nil {
   409  			log.Errorf("ReadAllTimestampsForBlock: Failed to read chunk from file! %+v", err)
   410  			continue
   411  		}
   412  
   413  		for currBlk := minBlkNum; currBlk <= allBlocks[maxIdx]; currBlk++ {
   414  			readOffset := blks[currBlk].ColumnBlockOffset[tsKey] - firstBlkOff
   415  			readLen := int64(blks[currBlk].ColumnBlockLen[tsKey])
   416  			rawBlock := rawChunk[readOffset : readOffset+readLen]
   417  			numRecs := blockSummaries[currBlk].RecCount
   418  			allReadJob <- &timeBlockRequest{tsRec: rawBlock, blkNum: currBlk, numRecs: numRecs}
   419  		}
   420  		minIdx = maxIdx + 1
   421  	}
   422  	close(allReadJob)
   423  	readerWG.Wait()
   424  	return retVal, nil
   425  }
   426  
   427  func ReturnTimeBuffers(og map[uint16][]uint64) {
   428  	for _, v := range og {
   429  		rawTimestampsBufferPool.Put(&v)
   430  	}
   431  }