github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/segment/writer/segwriter.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 writer
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"encoding/json"
    23  	"errors"
    24  	"fmt"
    25  	"math"
    26  	"math/rand"
    27  	"os"
    28  	"strconv"
    29  	"strings"
    30  	"sync"
    31  	"time"
    32  
    33  	"github.com/cespare/xxhash"
    34  	"github.com/klauspost/compress/zstd"
    35  	"github.com/siglens/siglens/pkg/blob"
    36  	"github.com/siglens/siglens/pkg/common/fileutils"
    37  	"github.com/siglens/siglens/pkg/segment/pqmr"
    38  	"github.com/siglens/siglens/pkg/segment/structs"
    39  	. "github.com/siglens/siglens/pkg/segment/utils"
    40  	"github.com/siglens/siglens/pkg/segment/writer/metrics"
    41  	"github.com/siglens/siglens/pkg/segment/writer/suffix"
    42  	"github.com/siglens/siglens/pkg/utils"
    43  
    44  	log "github.com/sirupsen/logrus"
    45  
    46  	"github.com/bits-and-blooms/bloom/v3"
    47  	"github.com/siglens/siglens/pkg/config"
    48  	"github.com/siglens/siglens/pkg/instrumentation"
    49  	bbp "github.com/valyala/bytebufferpool"
    50  )
    51  
    52  // Throttle the number of indexes to help prevent excessive memory usage.
    53  const maxAllowedSegStores = 1000
    54  
    55  // global map
    56  var allSegStores = map[string]*SegStore{}
    57  var allSegStoresLock sync.RWMutex = sync.RWMutex{}
    58  var maxSegFileSize uint64
    59  
    60  var KibanaInternalBaseDir string
    61  
    62  var smrLock sync.Mutex = sync.Mutex{}
    63  var localSegmetaFname string
    64  
    65  // Create a writer that caches compressors.
    66  // For this operation type we supply a nil Reader.
    67  var encoder, _ = zstd.NewWriter(nil)
    68  var decoder, _ = zstd.NewReader(nil)
    69  
    70  func InitKibanaInternalData() {
    71  	KibanaInternalBaseDir = config.GetDataPath() + "common/kibanainternaldata/"
    72  	err := os.MkdirAll(KibanaInternalBaseDir, 0764)
    73  	if err != nil {
    74  		log.Error(err)
    75  	}
    76  }
    77  
    78  type SegfileRotateInfo struct {
    79  	FinalName   string
    80  	TimeRotated uint64
    81  }
    82  
    83  type ColWip struct {
    84  	cbufidx   uint32              // end index of buffer, only cbuf[:cbufidx] exists
    85  	cstartidx uint32              // start index of last record, so cbuf[cstartidx:cbufidx] is the encoded last record
    86  	cbuf      [WIP_SIZE]byte      // in progress bytes
    87  	csgFname  string              // file name of csg file
    88  	deMap     map[string][]uint16 // dictWordKey ==> recordNums that match this key
    89  	deCount   uint16              // keeps track of cardinality count for this COL_WIP
    90  }
    91  
    92  type RangeIndex struct {
    93  	Ranges map[string]*structs.Numbers
    94  }
    95  
    96  type BloomIndex struct {
    97  	Bf              *bloom.BloomFilter
    98  	uniqueWordCount uint32
    99  	HistoricalCount []uint32
   100  }
   101  
   102  type RolledRecs struct {
   103  	lastRecNum uint16
   104  	MatchedRes *pqmr.PQMatchResults
   105  }
   106  
   107  // All WIP BLOCK related info will be stored here
   108  
   109  type WipBlock struct {
   110  	columnBlooms       map[string]*BloomIndex
   111  	blockSummary       structs.BlockSummary
   112  	columnRangeIndexes map[string]*RangeIndex
   113  	colWips            map[string]*ColWip
   114  	columnsInBlock     map[string]bool
   115  	pqMatches          map[string]*pqmr.PQMatchResults
   116  	maxIdx             uint32
   117  	blockTs            []uint64
   118  	tomRollup          map[uint64]*RolledRecs // top-of-minute rollup
   119  	tohRollup          map[uint64]*RolledRecs // top-of-hour rollup
   120  	todRollup          map[uint64]*RolledRecs // top-of-day rollup
   121  	bb                 *bbp.ByteBuffer        // byte buffer pool for HLL byte inserts
   122  }
   123  
   124  // returns in memory size of a single wip block
   125  func (wp *WipBlock) getSize() uint64 {
   126  	size := uint64(0)
   127  	for _, v := range wp.columnBlooms {
   128  		size += uint64(v.Bf.Cap() / 8)
   129  	}
   130  	size += wp.blockSummary.GetSize()
   131  	size += uint64(24 * len(wp.columnRangeIndexes))
   132  	size += uint64(WIP_SIZE * len(wp.colWips))
   133  	for _, v := range wp.pqMatches {
   134  		size += v.GetInMemSize()
   135  	}
   136  	return size
   137  }
   138  
   139  func HostnameDir() {
   140  	var sb strings.Builder
   141  	sb.WriteString(config.GetDataPath())
   142  	sb.WriteString("ingestnodes/")
   143  	sb.WriteString(config.GetHostID())
   144  	hostnamesDir := sb.String()
   145  	err := os.MkdirAll(hostnamesDir, 0764)
   146  	if err != nil {
   147  		log.Error(err)
   148  	}
   149  }
   150  
   151  // returns the total size used by AllSegStores
   152  func GetInMemorySize() uint64 {
   153  	allSegStoresLock.RLock()
   154  	defer allSegStoresLock.RUnlock()
   155  
   156  	totalSize := uint64(0)
   157  	for _, s := range allSegStores {
   158  		totalSize += s.wipBlock.getSize()
   159  	}
   160  
   161  	totalSize += metrics.GetTotalEncodedSize()
   162  
   163  	return uint64(math.Ceil(ConvertFloatBytesToMB(float64(totalSize) * float64(1.10))))
   164  }
   165  
   166  func InitWriterNode() {
   167  	// one time initialization
   168  	AllUnrotatedSegmentInfo = make(map[string]*UnrotatedSegmentInfo)
   169  	RecentlyRotatedSegmentFiles = make(map[string]*SegfileRotateInfo)
   170  	metrics.InitMetricsSegStore()
   171  
   172  	initSmr()
   173  
   174  	go timeBasedWIPFlushToFile()
   175  	go timeBasedRotateSegment()
   176  	go cleanRecentlyRotatedInfo()
   177  	go timeBasedUploadIngestNodeDir()
   178  	HostnameDir()
   179  	InitKibanaInternalData()
   180  }
   181  
   182  func initSmr() {
   183  
   184  	localSegmetaFname = GetLocalSegmetaFName()
   185  
   186  	fd, err := os.OpenFile(localSegmetaFname, os.O_RDONLY, 0666)
   187  	if err != nil {
   188  		if errors.Is(err, os.ErrNotExist) {
   189  			// for first time during bootup this will occur
   190  			_, err := os.OpenFile(localSegmetaFname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
   191  			if err != nil {
   192  				log.Errorf("initSmr: failed to open a new filename=%v: err=%v", localSegmetaFname, err)
   193  				return
   194  			}
   195  		}
   196  		return
   197  	}
   198  	fd.Close()
   199  }
   200  
   201  // TODO: this should be pushed based & we should have checks in uploadingestnode function to prevent uploading unupdated files.
   202  func timeBasedUploadIngestNodeDir() {
   203  	for {
   204  		time.Sleep(UPLOAD_INGESTNODE_DIR)
   205  		err := blob.UploadIngestNodeDir()
   206  		if err != nil {
   207  			log.Errorf("timeBasedUploadIngestNodeDir: failed to upload ingestnode dir: err=%v", err)
   208  		}
   209  	}
   210  }
   211  
   212  func cleanRecentlyRotatedInternal() {
   213  	currTime := utils.GetCurrentTimeInMs()
   214  	recentlyRotatedSegmentFilesLock.Lock()
   215  	defer recentlyRotatedSegmentFilesLock.Unlock()
   216  	for key, value := range RecentlyRotatedSegmentFiles {
   217  		if currTime-value.TimeRotated > STALE_RECENTLY_ROTATED_ENTRY_MS {
   218  			delete(RecentlyRotatedSegmentFiles, key)
   219  		}
   220  	}
   221  }
   222  
   223  func cleanRecentlyRotatedInfo() {
   224  	for {
   225  		sleepDuration := time.Millisecond * STALE_RECENTLY_ROTATED_ENTRY_MS
   226  		time.Sleep(sleepDuration)
   227  		cleanRecentlyRotatedInternal()
   228  	}
   229  }
   230  
   231  // This is the only function that needs to be exported from this package, since this is the only
   232  // place where we play with the locks
   233  
   234  func AddEntryToInMemBuf(streamid string, rawJson []byte, ts_millis uint64,
   235  	indexName string, bytesReceived uint64, flush bool, signalType SIGNAL_TYPE, orgid uint64) error {
   236  
   237  	segstore, err := getSegStore(streamid, ts_millis, indexName, orgid)
   238  	if err != nil {
   239  		log.Errorf("AddEntryToInMemBuf, getSegstore err=%v", err)
   240  		return err
   241  	}
   242  
   243  	segstore.lock.Lock()
   244  	defer segstore.lock.Unlock()
   245  	if segstore.wipBlock.maxIdx+MAX_RECORD_SIZE >= WIP_SIZE ||
   246  		segstore.wipBlock.blockSummary.RecCount >= MAX_RECS_PER_WIP {
   247  		err = segstore.AppendWipToSegfile(streamid, false, false, false)
   248  		if err != nil {
   249  			log.Errorf("AddEntryToInMemBuf: failed to append segkey=%v, err=%v", segstore.SegmentKey, err)
   250  			return err
   251  		}
   252  		instrumentation.IncrementInt64Counter(instrumentation.WIP_BUFFER_FLUSH_COUNT, 1)
   253  	}
   254  
   255  	segstore.adjustEarliestLatestTimes(ts_millis)
   256  	segstore.wipBlock.adjustEarliestLatestTimes(ts_millis)
   257  	err = segstore.WritePackedRecord(rawJson, ts_millis, signalType)
   258  	if err != nil {
   259  		return err
   260  	}
   261  	segstore.BytesReceivedCount += bytesReceived
   262  
   263  	if flush {
   264  		err = segstore.AppendWipToSegfile(streamid, false, false, false)
   265  		if err != nil {
   266  			log.Errorf("AddEntryToInMemBuf: failed to append during flush segkey=%v, err=%v", segstore.SegmentKey, err)
   267  			return err
   268  		}
   269  	}
   270  	return nil
   271  }
   272  
   273  func AddTimeSeriesEntryToInMemBuf(rawJson []byte, signalType SIGNAL_TYPE, orgid uint64) error {
   274  	switch signalType {
   275  	case SIGNAL_METRICS_OTSDB:
   276  		tagsHolder := metrics.GetTagsHolder()
   277  		mName, dp, ts, err := metrics.ExtractOTSDBPayload(rawJson, tagsHolder)
   278  		if err != nil {
   279  			metrics.ReturnTagsHolder(tagsHolder)
   280  			return err
   281  		}
   282  		err = metrics.EncodeDatapoint(mName, tagsHolder, dp, ts, uint64(len(rawJson)), orgid)
   283  		if err != nil {
   284  			metrics.ReturnTagsHolder(tagsHolder)
   285  			return err
   286  		}
   287  		metrics.ReturnTagsHolder(tagsHolder)
   288  	case SIGNAL_METRICS_INFLUX:
   289  		tagsHolder := metrics.GetTagsHolder()
   290  		mName, dp, ts, err := metrics.ExtractInfluxPayload(rawJson, tagsHolder)
   291  		if err != nil {
   292  			metrics.ReturnTagsHolder(tagsHolder)
   293  			return err
   294  		}
   295  		err = metrics.EncodeDatapoint(mName, tagsHolder, dp, ts, uint64(len(rawJson)), orgid)
   296  		if err != nil {
   297  			metrics.ReturnTagsHolder(tagsHolder)
   298  			return err
   299  		}
   300  		metrics.ReturnTagsHolder(tagsHolder)
   301  	default:
   302  		return fmt.Errorf("unknown signal type %+v", signalType)
   303  	}
   304  
   305  	return nil
   306  }
   307  
   308  // This function is used when os.Interrupt is caught
   309  // meta files need to be updated to not lose range/bloom/file path info on node failure
   310  func ForcedFlushToSegfile() {
   311  	log.Warnf("Flushing %+v segment files on server exit", len(allSegStores))
   312  	allSegStoresLock.Lock()
   313  	for streamid, segstore := range allSegStores {
   314  		segstore.lock.Lock()
   315  		err := segstore.AppendWipToSegfile(streamid, true, false, false)
   316  		if err != nil {
   317  			log.Errorf("ForcedFlushToSegfile: failed to append err=%v", err)
   318  		}
   319  		log.Warnf("Flushing segment file for streamid %s server exit", streamid)
   320  		segstore.lock.Unlock()
   321  		delete(allSegStores, streamid)
   322  	}
   323  	allSegStoresLock.Unlock()
   324  }
   325  
   326  func updateValuesFromConfig() {
   327  	maxSegFileSize = *config.GetMaxSegFileSize()
   328  }
   329  
   330  func timeBasedWIPFlushToFile() {
   331  	for {
   332  		sleepDuration := time.Duration(config.GetSegFlushIntervalSecs()) * time.Second
   333  		time.Sleep(sleepDuration)
   334  		FlushWipBufferToFile(&sleepDuration)
   335  	}
   336  }
   337  
   338  func rotateSegmentOnTime() {
   339  	segRotateDuration := time.Duration(SEGMENT_ROTATE_DURATION_SECONDS) * time.Second
   340  	allSegStoresLock.RLock()
   341  	wg := sync.WaitGroup{}
   342  	for sid, ss := range allSegStores {
   343  
   344  		if ss.firstTime {
   345  			rnm := rand.Intn(SEGMENT_ROTATE_DURATION_SECONDS) + 60
   346  			segRotateDuration = time.Duration(rnm) * time.Second
   347  		} else {
   348  			segRotateDuration = time.Duration(SEGMENT_ROTATE_DURATION_SECONDS) * time.Second
   349  		}
   350  
   351  		if time.Since(ss.timeCreated) < segRotateDuration {
   352  			continue
   353  		}
   354  		wg.Add(1)
   355  		go func(streamid string, segstore *SegStore) {
   356  			defer wg.Done()
   357  			segstore.lock.Lock()
   358  			segstore.firstTime = false
   359  			err := segstore.AppendWipToSegfile(streamid, false, false, true)
   360  			if err != nil {
   361  				log.Errorf("rotateSegmentOnTime: failed to append,  streamid=%s err=%v", err, streamid)
   362  			} else {
   363  				if time.Since(segstore.lastUpdated) > segRotateDuration*2 && segstore.RecordCount == 0 {
   364  					log.Infof("Deleting the segstore for streamid=%s", streamid)
   365  					delete(allSegStores, streamid)
   366  				} else {
   367  					log.Infof("Rotating segment due to time. streamid=%s and table=%s", streamid, segstore.VirtualTableName)
   368  				}
   369  			}
   370  			segstore.lock.Unlock()
   371  		}(sid, ss)
   372  	}
   373  	wg.Wait()
   374  	allSegStoresLock.RUnlock()
   375  }
   376  
   377  func ForceRotateSegmentsForTest() {
   378  	allSegStoresLock.Lock()
   379  	for streamid, segstore := range allSegStores {
   380  		segstore.lock.Lock()
   381  		err := segstore.AppendWipToSegfile(streamid, false, false, true)
   382  		if err != nil {
   383  			log.Errorf("ForceRotateSegmentsForTest: failed to append,  streamid=%s err=%v", err, streamid)
   384  		} else {
   385  			log.Infof("Rotating segment due to time. streamid=%s and table=%s", streamid, segstore.VirtualTableName)
   386  		}
   387  		segstore.lock.Unlock()
   388  	}
   389  	allSegStoresLock.Unlock()
   390  }
   391  
   392  func timeBasedRotateSegment() {
   393  	for {
   394  		time.Sleep(SEGMENT_ROTATE_SLEEP_DURATION_SECONDS * time.Second)
   395  		rotateSegmentOnTime()
   396  	}
   397  
   398  }
   399  
   400  func FlushWipBufferToFile(sleepDuration *time.Duration) {
   401  	allSegStoresLock.RLock()
   402  	for streamid, segstore := range allSegStores {
   403  		segstore.lock.Lock()
   404  		if segstore.wipBlock.maxIdx > 0 && time.Since(segstore.lastUpdated) > *sleepDuration {
   405  			err := segstore.AppendWipToSegfile(streamid, false, false, false)
   406  			if err != nil {
   407  				log.Errorf("FlushWipBufferToFile: failed to append, err=%v", err)
   408  			}
   409  			log.Infof("Flushed WIP buffer due to time. streamid=%s and table=%s", streamid, segstore.VirtualTableName)
   410  		}
   411  		segstore.lock.Unlock()
   412  	}
   413  	allSegStoresLock.RUnlock()
   414  }
   415  
   416  func InitColWip(segKey string, colName string) *ColWip {
   417  
   418  	return &ColWip{
   419  		csgFname: fmt.Sprintf("%v_%v.csg", segKey, xxhash.Sum64String(colName)),
   420  		deMap:    make(map[string][]uint16),
   421  		deCount:  0,
   422  	}
   423  }
   424  
   425  // In-mem Buf Format
   426  // [varint Record-0 varint Record-1 ....]
   427  // varint stores length of Record , it would occupy 1-9 bytes
   428  // The first bit of each byte of varint specifies whether there are follow on bytes
   429  // rest 7 bits are used to store the number
   430  func getSegStore(streamid string, ts_millis uint64, table string, orgId uint64) (*SegStore, error) {
   431  
   432  	allSegStoresLock.Lock()
   433  	defer allSegStoresLock.Unlock()
   434  
   435  	var segstore *SegStore
   436  	segstore, present := allSegStores[streamid]
   437  	if !present {
   438  		if len(allSegStores) >= maxAllowedSegStores {
   439  			return nil, fmt.Errorf("getSegStore: max allowed segstores reached (%d)", maxAllowedSegStores)
   440  		}
   441  
   442  		suffIndex, err := suffix.GetSuffix(streamid, table)
   443  		if err != nil {
   444  			return nil, err
   445  		}
   446  		segstore = &SegStore{suffix: suffIndex, lock: sync.Mutex{}, OrgId: orgId, firstTime: true}
   447  		segstore.initWipBlock()
   448  		err = segstore.resetSegStore(streamid, table)
   449  		if err != nil {
   450  			return nil, err
   451  		}
   452  		allSegStores[streamid] = segstore
   453  		instrumentation.SetWriterSegstoreCountGauge(int64(len(allSegStores)))
   454  	}
   455  
   456  	updateValuesFromConfig()
   457  	return segstore, nil
   458  }
   459  
   460  func getBlockBloomSize(bi *BloomIndex) uint32 {
   461  
   462  	if len(bi.HistoricalCount) == 0 {
   463  		bi.HistoricalCount = make([]uint32, 0)
   464  		return BLOCK_BLOOM_SIZE
   465  	}
   466  
   467  	startIdx := len(bi.HistoricalCount) - BLOOM_SIZE_HISTORY
   468  	if startIdx < 0 {
   469  		startIdx = 0
   470  	}
   471  
   472  	runningSum := uint32(0)
   473  	count := uint32(0)
   474  	for _, val := range bi.HistoricalCount[startIdx:] {
   475  		runningSum += val
   476  		count += 1
   477  	}
   478  	if count <= 0 {
   479  		return BLOCK_BLOOM_SIZE
   480  	}
   481  
   482  	nextBloomSize := runningSum / count
   483  	if nextBloomSize <= 0 {
   484  		return 1
   485  	}
   486  	return nextBloomSize
   487  }
   488  
   489  func getActiveBaseSegDir(streamid string, virtualTableName string, suffix uint64) string {
   490  	var sb strings.Builder
   491  	sb.WriteString(config.GetDataPath())
   492  	sb.WriteString(config.GetHostID())
   493  	sb.WriteString("/active/")
   494  	sb.WriteString(virtualTableName + "/")
   495  	sb.WriteString(streamid + "/")
   496  	sb.WriteString(strconv.FormatUint(suffix, 10) + "/")
   497  	basedir := sb.String()
   498  	return basedir
   499  }
   500  
   501  func getFinalBaseSegDir(streamid string, virtualTableName string, suffix uint64) string {
   502  	var sb strings.Builder
   503  	sb.WriteString(config.GetDataPath())
   504  	sb.WriteString(config.GetHostID())
   505  	sb.WriteString("/final/")
   506  	sb.WriteString(virtualTableName + "/")
   507  	sb.WriteString(streamid + "/")
   508  	sb.WriteString(strconv.FormatUint(suffix, 10) + "/")
   509  	basedir := sb.String()
   510  	return basedir
   511  }
   512  
   513  /*
   514  Adds the fullWord and sub-words to the bloom
   515  Subwords are gotten by splitting the fullWord by whitespace
   516  */
   517  func addToBlockBloom(blockBloom *bloom.BloomFilter, fullWord []byte) uint32 {
   518  
   519  	var blockWordCount uint32 = 0
   520  	copy := fullWord[:]
   521  
   522  	if !blockBloom.TestAndAdd(copy) {
   523  		blockWordCount += 1
   524  	}
   525  
   526  	var foundWord bool
   527  	for {
   528  		i := bytes.Index(copy, BYTE_SPACE)
   529  		if i == -1 {
   530  			break
   531  		}
   532  		foundWord = true
   533  		if !blockBloom.TestAndAdd(copy[:i]) {
   534  			blockWordCount += 1
   535  		}
   536  		copy = copy[i+BYTE_SPACE_LEN:]
   537  	}
   538  
   539  	// handle last word. If no word was found, then we have already added the full word
   540  	if foundWord && len(copy) > 0 {
   541  		if !blockBloom.TestAndAdd(copy) {
   542  			blockWordCount += 1
   543  		}
   544  	}
   545  	return blockWordCount
   546  }
   547  
   548  func updateRangeIndex(key string, rangeIndexPtr map[string]*structs.Numbers, numType SS_IntUintFloatTypes, intVal int64,
   549  	uintVal uint64, fltVal float64) {
   550  	switch numType {
   551  	case SS_INT8, SS_INT16, SS_INT32, SS_INT64:
   552  		addIntToRangeIndex(key, intVal, rangeIndexPtr)
   553  	case SS_UINT8, SS_UINT16, SS_UINT32, SS_UINT64:
   554  		addUintToRangeIndex(key, uintVal, rangeIndexPtr)
   555  	case SS_FLOAT64:
   556  		addFloatToRangeIndex(key, fltVal, rangeIndexPtr)
   557  	}
   558  }
   559  
   560  func addUintToRangeIndex(key string, incomingVal uint64, rangeIndexPtr map[string]*structs.Numbers) {
   561  	existingRI, present := rangeIndexPtr[key]
   562  	if present {
   563  		inMemType := existingRI.NumType
   564  		switch inMemType {
   565  		case RNT_SIGNED_INT:
   566  			newVal := int64(incomingVal)
   567  			if newVal < existingRI.Min_int64 {
   568  				existingRI.Min_int64 = newVal
   569  			} else if newVal > existingRI.Max_int64 {
   570  				existingRI.Max_int64 = newVal
   571  			}
   572  		case RNT_FLOAT64:
   573  			newVal := float64(incomingVal)
   574  			if newVal < existingRI.Min_float64 {
   575  				existingRI.Min_float64 = newVal
   576  			} else if newVal > existingRI.Max_float64 {
   577  				existingRI.Max_float64 = newVal
   578  			}
   579  		default:
   580  			if incomingVal < existingRI.Min_uint64 {
   581  				existingRI.Min_uint64 = incomingVal
   582  			} else if incomingVal > existingRI.Max_uint64 {
   583  				existingRI.Max_uint64 = incomingVal
   584  			}
   585  		}
   586  		rangeIndexPtr[key] = existingRI
   587  	} else {
   588  		rangeIndexPtr[key] = &structs.Numbers{Min_uint64: incomingVal, Max_uint64: incomingVal, NumType: RNT_UNSIGNED_INT}
   589  	}
   590  }
   591  func addIntToRangeIndex(key string, incomingVal int64, rangeIndexPtr map[string]*structs.Numbers) {
   592  	existingRI, present := rangeIndexPtr[key]
   593  	if present {
   594  		inMemType := existingRI.NumType
   595  		switch inMemType {
   596  		case RNT_UNSIGNED_INT:
   597  			existingRI = &structs.Numbers{Min_int64: int64(rangeIndexPtr[key].Min_uint64), Max_int64: int64(rangeIndexPtr[key].Max_uint64),
   598  				NumType: RNT_SIGNED_INT}
   599  			if incomingVal < existingRI.Min_int64 {
   600  				existingRI.Min_int64 = incomingVal
   601  			} else if incomingVal > existingRI.Max_int64 {
   602  				existingRI.Max_int64 = incomingVal
   603  			}
   604  		case RNT_FLOAT64:
   605  			newVal := float64(incomingVal)
   606  			if newVal < existingRI.Min_float64 {
   607  				existingRI.Min_float64 = newVal
   608  			} else if newVal > existingRI.Max_float64 {
   609  				existingRI.Max_float64 = newVal
   610  			}
   611  		default:
   612  			if incomingVal < existingRI.Min_int64 {
   613  				existingRI.Min_int64 = incomingVal
   614  			} else if incomingVal > existingRI.Max_int64 {
   615  				existingRI.Max_int64 = incomingVal
   616  			}
   617  		}
   618  		rangeIndexPtr[key] = existingRI
   619  		//fmt.Printf("present %v\n", (*rangeIndexPtr))
   620  	} else {
   621  		rangeIndexPtr[key] = &structs.Numbers{Min_int64: incomingVal, Max_int64: incomingVal, NumType: RNT_SIGNED_INT}
   622  		//fmt.Printf("ADDED %v\n", (*rangeIndexPtr))
   623  	}
   624  }
   625  func addFloatToRangeIndex(key string, incomingVal float64, rangeIndexPtr map[string]*structs.Numbers) {
   626  	existingRI, present := rangeIndexPtr[key]
   627  	if present {
   628  		inMemType := existingRI.NumType
   629  		switch inMemType {
   630  		case RNT_UNSIGNED_INT:
   631  			existingRI = &structs.Numbers{Min_float64: float64(rangeIndexPtr[key].Min_uint64), Max_float64: float64(rangeIndexPtr[key].Max_uint64), NumType: RNT_FLOAT64}
   632  		case RNT_SIGNED_INT:
   633  			existingRI = &structs.Numbers{Min_float64: float64(rangeIndexPtr[key].Min_int64), Max_float64: float64(rangeIndexPtr[key].Max_int64), NumType: RNT_FLOAT64}
   634  		}
   635  		if incomingVal < existingRI.Min_float64 {
   636  			existingRI.Min_float64 = incomingVal
   637  		} else if incomingVal > existingRI.Max_float64 {
   638  			existingRI.Max_float64 = incomingVal
   639  		}
   640  		rangeIndexPtr[key] = existingRI
   641  	} else {
   642  		rangeIndexPtr[key] = &structs.Numbers{Min_float64: incomingVal, Max_float64: incomingVal, NumType: RNT_FLOAT64}
   643  	}
   644  }
   645  
   646  /*
   647     ******************* WIP BLOCK Encoding ********************
   648  
   649    [Actual Block]
   650    // lens are stored in the bsu file
   651  
   652  */
   653  // returns number of written bytes, offset of block in file, and any errors
   654  
   655  func writeWip(colWip *ColWip, encType []byte) (uint32, int64, error) {
   656  
   657  	blkLen := uint32(0)
   658  	// todo better error handling should not exit
   659  	fd, err := os.OpenFile(colWip.csgFname, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
   660  	if err != nil {
   661  		log.Errorf("WriteWip: open failed fname=%v, err=%v", colWip.csgFname, err)
   662  		return 0, 0, err
   663  	}
   664  	defer fd.Close()
   665  
   666  	blkOffset, err := fd.Seek(0, 2) // go to the end of the file
   667  	if err != nil {
   668  		log.Errorf("WriteWip: failed to get offset of current block offset %+v", err)
   669  		return 0, 0, err
   670  	}
   671  
   672  	_, err = fd.Write(encType)
   673  	if err != nil {
   674  		log.Errorf("WriteWip: compression Type write failed fname=%v, err=%v", colWip.csgFname, err)
   675  		return 0, blkOffset, err
   676  	}
   677  	blkLen += 1 // for compression type
   678  
   679  	compressed, compLen, err := compressWip(colWip, encType)
   680  	if err != nil {
   681  		log.Errorf("WriteWip: compression of wip failed fname=%v, err=%v", colWip.csgFname, err)
   682  		return 0, blkOffset, err
   683  	}
   684  	_, err = fd.Write(compressed)
   685  	if err != nil {
   686  		log.Errorf("WriteWip: compressed write failed fname=%v, err=%v", colWip.csgFname, err)
   687  		return 0, blkOffset, err
   688  	}
   689  	blkLen += compLen
   690  
   691  	return blkLen, blkOffset, nil
   692  }
   693  
   694  func compressWip(colWip *ColWip, encType []byte) ([]byte, uint32, error) {
   695  	var compressed []byte
   696  	if bytes.Equal(encType, ZSTD_COMLUNAR_BLOCK) {
   697  		compressed = encoder.EncodeAll(colWip.cbuf[0:colWip.cbufidx], make([]byte, 0, colWip.cbufidx))
   698  	} else if bytes.Equal(encType, TIMESTAMP_TOPDIFF_VARENC) {
   699  		compressed = colWip.cbuf[0:colWip.cbufidx]
   700  	} else if bytes.Equal(encType, ZSTD_DICTIONARY_BLOCK) {
   701  		PackDictEnc(colWip)
   702  		compressed = colWip.cbuf[0:colWip.cbufidx]
   703  	} else {
   704  		log.Errorf("compressWip got an unknown encoding type: %+v", encType)
   705  		return nil, 0, fmt.Errorf("got an unknown encoding type: %+v", encType)
   706  	}
   707  	compLen := uint32(len(compressed))
   708  
   709  	return compressed, compLen, nil
   710  }
   711  
   712  func writeRunningSegMeta(fname string, rsm *structs.SegMeta) error {
   713  
   714  	fd, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
   715  	if err != nil {
   716  		log.Errorf("writeRunningSegMeta: open failed fname=%v, err=%v", fname, err)
   717  		return err
   718  	}
   719  	defer fd.Close()
   720  
   721  	rsmjson, err := json.Marshal(rsm)
   722  	if err != nil {
   723  		log.Errorf("writeRunningSegMeta: failed to Marshal: err=%v", err)
   724  		return err
   725  	}
   726  
   727  	if _, err := fd.Write(rsmjson); err != nil {
   728  		log.Errorf("writeRunningSegMeta: failed to write rsmjson filename=%v: err=%v", fname, err)
   729  		return err
   730  	}
   731  
   732  	return nil
   733  }
   734  
   735  func GetUnrotatedVTableCounts(vtable string, orgid uint64) (uint64, int, uint64) {
   736  	bytesCount := uint64(0)
   737  	onDiskBytesCount := uint64(0)
   738  	recCount := 0
   739  	allSegStoresLock.RLock()
   740  	defer allSegStoresLock.RUnlock()
   741  	for _, segstore := range allSegStores {
   742  		if segstore.VirtualTableName == vtable && segstore.OrgId == orgid {
   743  			bytesCount += segstore.BytesReceivedCount
   744  			recCount += segstore.RecordCount
   745  			onDiskBytesCount += segstore.OnDiskBytes
   746  		}
   747  	}
   748  	return bytesCount, recCount, onDiskBytesCount
   749  }
   750  
   751  func getActiveBaseDirVTable(virtualTableName string) string {
   752  	var sb strings.Builder
   753  	sb.WriteString(config.GetRunningConfig().DataPath)
   754  	sb.WriteString(config.GetHostID())
   755  	sb.WriteString("/active/")
   756  	sb.WriteString(virtualTableName + "/")
   757  	basedir := sb.String()
   758  	return basedir
   759  }
   760  
   761  func DeleteVirtualTableSegStore(virtualTableName string) {
   762  	allSegStoresLock.Lock()
   763  	for streamid, segstore := range allSegStores {
   764  		if segstore.VirtualTableName == virtualTableName {
   765  			delete(allSegStores, streamid)
   766  		}
   767  	}
   768  	activedir := getActiveBaseDirVTable(virtualTableName)
   769  	os.RemoveAll(activedir)
   770  	allSegStoresLock.Unlock()
   771  }
   772  
   773  func DeleteSegmentsForIndex(segmetaFName, indexName string) {
   774  	smrLock.Lock()
   775  	defer smrLock.Unlock()
   776  
   777  	removeSegmentsByIndexOrList(segmetaFName, indexName, nil)
   778  }
   779  
   780  func RemoveSegments(segmetaFName string, segmentsToDelete map[string]*structs.SegMeta) {
   781  	smrLock.Lock()
   782  	defer smrLock.Unlock()
   783  
   784  	removeSegmentsByIndexOrList(segmetaFName, "", segmentsToDelete)
   785  }
   786  
   787  func removeSegmentsByIndexOrList(segMetaFile string, indexName string, segmentsToDelete map[string]*structs.SegMeta) {
   788  
   789  	if indexName == "" && segmentsToDelete == nil {
   790  		return // nothing to remove
   791  	}
   792  
   793  	preservedSmEntries := make([]*structs.SegMeta, 0)
   794  
   795  	entriesRead := 0
   796  	entriesRemoved := 0
   797  
   798  	fr, err := os.OpenFile(segMetaFile, os.O_RDONLY, 0644)
   799  	if err != nil {
   800  		log.Errorf("removeSegmentsByIndexOrList: Failed to open SegMetaFile name=%v, err:%v", segMetaFile, err)
   801  		return
   802  	}
   803  	defer fr.Close()
   804  
   805  	reader := bufio.NewScanner(fr)
   806  	for reader.Scan() {
   807  		segMetaData := structs.SegMeta{}
   808  		err = json.Unmarshal(reader.Bytes(), &segMetaData)
   809  		if err != nil {
   810  			log.Errorf("removeSegmentsByIndexOrList: Failed to unmarshal fileName=%v, err:%v", segMetaFile, err)
   811  			continue
   812  		}
   813  		entriesRead++
   814  
   815  		// only append the ones that we want to preserve
   816  		// check if it was based on indexName
   817  		if indexName != "" {
   818  			if segMetaData.VirtualTableName != indexName {
   819  				preservedSmEntries = append(preservedSmEntries, &segMetaData)
   820  				continue
   821  			}
   822  		} else {
   823  			// check if based on segmetas
   824  			_, ok := segmentsToDelete[segMetaData.SegmentKey]
   825  			if !ok {
   826  				preservedSmEntries = append(preservedSmEntries, &segMetaData)
   827  				continue
   828  			}
   829  		}
   830  
   831  		entriesRemoved++
   832  		if err := os.RemoveAll(segMetaData.SegbaseDir); err != nil {
   833  			log.Errorf("removeSegmentsByIndexOrList: Failed to remove directory name=%v, err:%v",
   834  				segMetaData.SegbaseDir, err)
   835  		}
   836  		fileutils.RecursivelyDeleteEmptyParentDirectories(segMetaData.SegbaseDir)
   837  	}
   838  
   839  	if entriesRemoved > 0 {
   840  
   841  		// if we removed entries and there was nothing preserveed then we must delete this segmetafile
   842  		if len(preservedSmEntries) == 0 {
   843  			if err := os.RemoveAll(segMetaFile); err != nil {
   844  				log.Errorf("removeSegmentsByIndexOrList: Failed to remove smfile name=%v, err:%v", segMetaFile, err)
   845  			}
   846  			return
   847  		}
   848  
   849  		wfd, err := os.OpenFile(segMetaFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
   850  		if err != nil {
   851  			log.Errorf("removeSegmentsByIndexOrList: Failed to open temp SegMetaFile name=%v, err:%v", segMetaFile, err)
   852  			return
   853  		}
   854  		defer wfd.Close()
   855  
   856  		for _, smentry := range preservedSmEntries {
   857  
   858  			segmetajson, err := json.Marshal(*smentry)
   859  			if err != nil {
   860  				log.Errorf("removeSegmentsByIndexOrList: failed to Marshal: err=%v", err)
   861  				return
   862  			}
   863  
   864  			if _, err := wfd.Write(segmetajson); err != nil {
   865  				log.Errorf("removeSegmentsByIndexOrList: failed to write segmeta filename=%v: err=%v", segMetaFile, err)
   866  				return
   867  			}
   868  
   869  			if _, err := wfd.WriteString("\n"); err != nil {
   870  				log.Errorf("removeSegmentsByIndexOrList: failed to write newline filename=%v: err=%v", segMetaFile, err)
   871  				return
   872  			}
   873  		}
   874  	}
   875  }
   876  
   877  func DeleteOldKibanaDoc(indexNameConverted string, idVal string) {
   878  	indexNameDirPath := KibanaInternalBaseDir + indexNameConverted
   879  	hashDocId := utils.HashString(idVal)
   880  	docFilePath := indexNameDirPath + "/" + hashDocId
   881  
   882  	err := os.RemoveAll(docFilePath)
   883  	if err != nil {
   884  		log.Errorf("DeleteOldKibanaDoc: Failed to delete, indexNameDirPath %+v idVal %+v folderpath %+v, err %+v",
   885  			indexNameDirPath, idVal, docFilePath, err)
   886  	}
   887  }
   888  
   889  func (cw *ColWip) GetBufAndIdx() ([]byte, uint32) {
   890  	return cw.cbuf[0:cw.cbufidx], cw.cbufidx
   891  }
   892  
   893  func (cw *ColWip) SetDeCount(val uint16) {
   894  	cw.deCount = val
   895  }
   896  
   897  func (cw *ColWip) SetDeMap(val map[string][]uint16) {
   898  	cw.deMap = val
   899  }
   900  
   901  func (cw *ColWip) WriteSingleString(value string) {
   902  	copy(cw.cbuf[cw.cbufidx:], VALTYPE_ENC_SMALL_STRING[:])
   903  	cw.cbufidx += 1
   904  	n := uint16(len(value))
   905  	copy(cw.cbuf[cw.cbufidx:], utils.Uint16ToBytesLittleEndian(n))
   906  	cw.cbufidx += 2
   907  	copy(cw.cbuf[cw.cbufidx:], value)
   908  	cw.cbufidx += uint32(n)
   909  }
   910  
   911  func AddNewRotatedSegment(segmeta structs.SegMeta) {
   912  
   913  	smrLock.Lock()
   914  	defer smrLock.Unlock()
   915  
   916  	fileName := GetLocalSegmetaFName()
   917  
   918  	segmetajson, err := json.Marshal(segmeta)
   919  	if err != nil {
   920  		log.Errorf("AddNewRotatedSegment: failed to Marshal: err=%v", err)
   921  		return
   922  	}
   923  
   924  	fd, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
   925  	if err != nil {
   926  		if errors.Is(err, os.ErrNotExist) {
   927  			fd, err = os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
   928  			if err != nil {
   929  				log.Errorf("AddNewRotatedSegment: failed to open a new filename=%v: err=%v", fileName, err)
   930  				return
   931  			}
   932  
   933  		} else {
   934  			log.Errorf("AddNewRotatedSegment: failed to open filename=%v: err=%v", fileName, err)
   935  			return
   936  		}
   937  	}
   938  
   939  	defer fd.Close()
   940  
   941  	if _, err := fd.Write(segmetajson); err != nil {
   942  		log.Errorf("AddNewRotatedSegment: failed to write segmeta filename=%v: err=%v", fileName, err)
   943  		return
   944  	}
   945  
   946  	if _, err := fd.WriteString("\n"); err != nil {
   947  		log.Errorf("AddNewRotatedSegment: failed to write newline filename=%v: err=%v", fileName, err)
   948  		return
   949  	}
   950  	err = fd.Sync()
   951  	if err != nil {
   952  		log.Errorf("AddNewRotatedSegment: failed to sync filename=%v: err=%v", fileName, err)
   953  		return
   954  	}
   955  }
   956  
   957  func BackFillPQSSegmetaEntry(segsetkey string, newpqid string) {
   958  	smrLock.Lock()
   959  	defer smrLock.Unlock()
   960  
   961  	preservedSmEntries := make([]*structs.SegMeta, 0)
   962  	allPqids := make(map[string]bool)
   963  
   964  	// Read segmeta files
   965  	allSegMetas, err := getAllSegmetaToMap(localSegmetaFname)
   966  	if err != nil {
   967  		log.Errorf("BackFillPQSSegmetaEntry: failed to get Segmeta: err=%v", err)
   968  		return
   969  	}
   970  	for segkey, segMetaEntry := range allSegMetas {
   971  		if segkey != segsetkey {
   972  			preservedSmEntries = append(preservedSmEntries, segMetaEntry)
   973  			continue
   974  		} else {
   975  			for pqid := range segMetaEntry.AllPQIDs {
   976  				allPqids[pqid] = true
   977  			}
   978  			allPqids[newpqid] = true
   979  			segMetaEntry.AllPQIDs = allPqids
   980  			preservedSmEntries = append(preservedSmEntries, segMetaEntry)
   981  			continue
   982  		}
   983  	}
   984  	wfd, err := os.OpenFile(localSegmetaFname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
   985  	if err != nil {
   986  		log.Errorf("BackFillPQSSegmetaEntry: Failed to open SegMetaFile name=%v, err:%v", localSegmetaFname, err)
   987  		return
   988  	}
   989  	defer wfd.Close()
   990  
   991  	for _, smentry := range preservedSmEntries {
   992  
   993  		segmetajson, err := json.Marshal(*smentry)
   994  		if err != nil {
   995  			log.Errorf("BackFillPQSSegmetaEntry: failed to Marshal: err=%v", err)
   996  			return
   997  		}
   998  
   999  		if _, err := wfd.Write(segmetajson); err != nil {
  1000  			log.Errorf("BackFillPQSSegmetaEntry: failed to write segmeta filename=%v: err=%v", localSegmetaFname, err)
  1001  			return
  1002  		}
  1003  
  1004  		if _, err := wfd.WriteString("\n"); err != nil {
  1005  			log.Errorf("BackFillPQSSegmetaEntry: failed to write newline filename=%v: err=%v", localSegmetaFname, err)
  1006  			return
  1007  		}
  1008  	}
  1009  }