1// This file and its contents are licensed under the Apache License 2.0. 2// Please see the included NOTICE for copyright information and 3// LICENSE for a copy of the license. 4 5package model 6 7import ( 8 "fmt" 9 "strconv" 10 "strings" 11 "sync" 12 "unsafe" 13 14 "github.com/timescale/promscale/pkg/prompb" 15) 16 17// SeriesID represents a globally unique id for the series. This should be equivalent 18// to the PostgreSQL type in the series table (currently BIGINT). 19type SeriesID int64 20 21const ( 22 invalidSeriesID = -1 23 InvalidSeriesEpoch = -1 24) 25 26func (s SeriesID) String() string { 27 return strconv.FormatInt(int64(s), 10) 28} 29 30//Epoch represents the series epoch 31type SeriesEpoch int64 32 33// Series stores a labels.Series in its canonical string representation 34type Series struct { 35 //protects names, values, seriesID, epoch 36 //str and metricName are immutable and doesn't need a lock 37 lock sync.RWMutex 38 names []string 39 values []string 40 metricName string 41 str string 42 seriesID SeriesID 43 epoch SeriesEpoch 44} 45 46func NewSeries(key string, labelPairs []prompb.Label) *Series { 47 series := &Series{ 48 names: make([]string, len(labelPairs)), 49 values: make([]string, len(labelPairs)), 50 str: key, 51 seriesID: invalidSeriesID, 52 epoch: InvalidSeriesEpoch, 53 } 54 for i, l := range labelPairs { 55 series.names[i] = l.Name 56 series.values[i] = l.Value 57 if l.Name == MetricNameLabelName { 58 series.metricName = l.Value 59 } 60 } 61 return series 62} 63 64//NameValues returns the names and values, only valid if the seriesIDIsNotSet 65func (l *Series) NameValues() (names []string, values []string, ok bool) { 66 l.lock.RLock() 67 defer l.lock.RUnlock() 68 return l.names, l.values, !l.isSeriesIDSetNoLock() 69} 70 71func (l *Series) MetricName() string { 72 return l.metricName 73} 74 75// Get a string representation for hashing and comparison 76// This representation is guaranteed to uniquely represent the underlying label 77// set, though need not human-readable, or indeed, valid utf-8 78func (l *Series) String() string { 79 return l.str 80} 81 82// Compare returns a comparison int between two Labels 83func (l *Series) Compare(b *Series) int { 84 return strings.Compare(l.str, b.str) 85} 86 87// Equal returns true if two Labels are equal 88func (l *Series) Equal(b *Series) bool { 89 return l.str == b.str 90} 91 92func (l *Series) isSeriesIDSetNoLock() bool { 93 return l.seriesID != invalidSeriesID 94} 95 96func (l *Series) IsSeriesIDSet() bool { 97 l.lock.RLock() 98 defer l.lock.RUnlock() 99 100 return l.isSeriesIDSetNoLock() 101} 102 103//FinalSizeBytes returns the size in bytes /after/ the seriesID is set 104func (l *Series) FinalSizeBytes() uint64 { 105 //size is the base size of the struct + the str and metricName strings 106 //names and values are not counted since they will be nilled out 107 return uint64(unsafe.Sizeof(*l)) + uint64(len(l.str)+len(l.metricName)) // #nosec 108} 109 110func (l *Series) GetSeriesID() (SeriesID, SeriesEpoch, error) { 111 l.lock.RLock() 112 defer l.lock.RUnlock() 113 114 switch l.seriesID { 115 case invalidSeriesID: 116 return 0, 0, fmt.Errorf("Series id not set") 117 case 0: 118 return 0, 0, fmt.Errorf("Series id invalid") 119 default: 120 return l.seriesID, l.epoch, nil 121 } 122} 123 124//note this has to be idempotent 125func (l *Series) SetSeriesID(sid SeriesID, eid SeriesEpoch) { 126 l.lock.Lock() 127 defer l.lock.Unlock() 128 l.seriesID = sid 129 l.epoch = eid 130 l.names = nil 131 l.values = nil 132} 133