github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/logproto/timeseries.go (about) 1 package logproto 2 3 import ( 4 "flag" 5 "fmt" 6 "io" 7 "strings" 8 "sync" 9 "unsafe" 10 11 "github.com/prometheus/prometheus/model/labels" 12 ) 13 14 var ( 15 expectedTimeseries = 100 16 expectedLabels = 20 17 expectedSamplesPerSeries = 10 18 19 /* 20 We cannot pool these as pointer-to-slice because the place we use them is in WriteRequest which is generated from Protobuf 21 and we don't have an option to make it a pointer. There is overhead here 24 bytes of garbage every time a PreallocTimeseries 22 is re-used. But since the slices are far far larger, we come out ahead. 23 */ 24 slicePool = sync.Pool{ 25 New: func() interface{} { 26 return make([]PreallocTimeseries, 0, expectedTimeseries) 27 }, 28 } 29 30 timeSeriesPool = sync.Pool{ 31 New: func() interface{} { 32 return &TimeSeries{ 33 Labels: make([]LabelAdapter, 0, expectedLabels), 34 Samples: make([]LegacySample, 0, expectedSamplesPerSeries), 35 } 36 }, 37 } 38 ) 39 40 // PreallocConfig configures how structures will be preallocated to optimise 41 // proto unmarshalling. 42 type PreallocConfig struct{} 43 44 // RegisterFlags registers configuration settings. 45 func (PreallocConfig) RegisterFlags(f *flag.FlagSet) { 46 f.IntVar(&expectedTimeseries, "ingester-client.expected-timeseries", expectedTimeseries, "Expected number of timeseries per request, used for preallocations.") 47 f.IntVar(&expectedLabels, "ingester-client.expected-labels", expectedLabels, "Expected number of labels per timeseries, used for preallocations.") 48 f.IntVar(&expectedSamplesPerSeries, "ingester-client.expected-samples-per-series", expectedSamplesPerSeries, "Expected number of samples per timeseries, used for preallocations.") 49 } 50 51 // PreallocWriteRequest is a WriteRequest which preallocs slices on Unmarshal. 52 type PreallocWriteRequest struct { 53 WriteRequest 54 } 55 56 // Unmarshal implements proto.Message. 57 func (p *PreallocWriteRequest) Unmarshal(dAtA []byte) error { 58 p.Timeseries = PreallocTimeseriesSliceFromPool() 59 return p.WriteRequest.Unmarshal(dAtA) 60 } 61 62 // PreallocTimeseries is a TimeSeries which preallocs slices on Unmarshal. 63 type PreallocTimeseries struct { 64 *TimeSeries 65 } 66 67 // Unmarshal implements proto.Message. 68 func (p *PreallocTimeseries) Unmarshal(dAtA []byte) error { 69 p.TimeSeries = TimeseriesFromPool() 70 return p.TimeSeries.Unmarshal(dAtA) 71 } 72 73 // LabelAdapter is a labels.Label that can be marshalled to/from protos. 74 type LabelAdapter labels.Label 75 76 // Marshal implements proto.Marshaller. 77 func (bs *LabelAdapter) Marshal() ([]byte, error) { 78 size := bs.Size() 79 buf := make([]byte, size) 80 n, err := bs.MarshalToSizedBuffer(buf[:size]) 81 if err != nil { 82 return nil, err 83 } 84 return buf[:n], err 85 } 86 87 func (bs *LabelAdapter) MarshalTo(dAtA []byte) (int, error) { 88 size := bs.Size() 89 return bs.MarshalToSizedBuffer(dAtA[:size]) 90 } 91 92 // MarshalTo implements proto.Marshaller. 93 func (bs *LabelAdapter) MarshalToSizedBuffer(buf []byte) (n int, err error) { 94 ls := (*labels.Label)(bs) 95 i := len(buf) 96 if len(ls.Value) > 0 { 97 i -= len(ls.Value) 98 copy(buf[i:], ls.Value) 99 i = encodeVarintMetrics(buf, i, uint64(len(ls.Value))) 100 i-- 101 buf[i] = 0x12 102 } 103 if len(ls.Name) > 0 { 104 i -= len(ls.Name) 105 copy(buf[i:], ls.Name) 106 i = encodeVarintMetrics(buf, i, uint64(len(ls.Name))) 107 i-- 108 buf[i] = 0xa 109 } 110 return len(buf) - i, nil 111 } 112 113 // Unmarshal a LabelAdapter, implements proto.Unmarshaller. 114 // NB this is a copy of the autogenerated code to unmarshal a LabelPair, 115 // with the byte copying replaced with a yoloString. 116 func (bs *LabelAdapter) Unmarshal(dAtA []byte) error { 117 l := len(dAtA) 118 iNdEx := 0 119 for iNdEx < l { 120 preIndex := iNdEx 121 var wire uint64 122 for shift := uint(0); ; shift += 7 { 123 if shift >= 64 { 124 return ErrIntOverflowMetrics 125 } 126 if iNdEx >= l { 127 return io.ErrUnexpectedEOF 128 } 129 b := dAtA[iNdEx] 130 iNdEx++ 131 wire |= uint64(b&0x7F) << shift 132 if b < 0x80 { 133 break 134 } 135 } 136 fieldNum := int32(wire >> 3) 137 wireType := int(wire & 0x7) 138 if wireType == 4 { 139 return fmt.Errorf("proto: LabelPair: wiretype end group for non-group") 140 } 141 if fieldNum <= 0 { 142 return fmt.Errorf("proto: LabelPair: illegal tag %d (wire type %d)", fieldNum, wire) 143 } 144 switch fieldNum { 145 case 1: 146 if wireType != 2 { 147 return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) 148 } 149 var byteLen int 150 for shift := uint(0); ; shift += 7 { 151 if shift >= 64 { 152 return ErrIntOverflowMetrics 153 } 154 if iNdEx >= l { 155 return io.ErrUnexpectedEOF 156 } 157 b := dAtA[iNdEx] 158 iNdEx++ 159 byteLen |= int(b&0x7F) << shift 160 if b < 0x80 { 161 break 162 } 163 } 164 if byteLen < 0 { 165 return ErrInvalidLengthMetrics 166 } 167 postIndex := iNdEx + byteLen 168 if postIndex < 0 { 169 return ErrInvalidLengthMetrics 170 } 171 if postIndex > l { 172 return io.ErrUnexpectedEOF 173 } 174 bs.Name = yoloString(dAtA[iNdEx:postIndex]) 175 iNdEx = postIndex 176 case 2: 177 if wireType != 2 { 178 return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) 179 } 180 var byteLen int 181 for shift := uint(0); ; shift += 7 { 182 if shift >= 64 { 183 return ErrIntOverflowMetrics 184 } 185 if iNdEx >= l { 186 return io.ErrUnexpectedEOF 187 } 188 b := dAtA[iNdEx] 189 iNdEx++ 190 byteLen |= int(b&0x7F) << shift 191 if b < 0x80 { 192 break 193 } 194 } 195 if byteLen < 0 { 196 return ErrInvalidLengthMetrics 197 } 198 postIndex := iNdEx + byteLen 199 if postIndex < 0 { 200 return ErrInvalidLengthMetrics 201 } 202 if postIndex > l { 203 return io.ErrUnexpectedEOF 204 } 205 bs.Value = yoloString(dAtA[iNdEx:postIndex]) 206 iNdEx = postIndex 207 default: 208 iNdEx = preIndex 209 skippy, err := skipMetrics(dAtA[iNdEx:]) 210 if err != nil { 211 return err 212 } 213 if skippy < 0 { 214 return ErrInvalidLengthMetrics 215 } 216 if (iNdEx + skippy) < 0 { 217 return ErrInvalidLengthMetrics 218 } 219 if (iNdEx + skippy) > l { 220 return io.ErrUnexpectedEOF 221 } 222 iNdEx += skippy 223 } 224 } 225 226 if iNdEx > l { 227 return io.ErrUnexpectedEOF 228 } 229 return nil 230 } 231 232 func yoloString(buf []byte) string { 233 return *((*string)(unsafe.Pointer(&buf))) 234 } 235 236 // Size implements proto.Sizer. 237 func (bs *LabelAdapter) Size() (n int) { 238 ls := (*labels.Label)(bs) 239 if bs == nil { 240 return 0 241 } 242 var l int 243 _ = l 244 l = len(ls.Name) 245 if l > 0 { 246 n += 1 + l + sovMetrics(uint64(l)) 247 } 248 l = len(ls.Value) 249 if l > 0 { 250 n += 1 + l + sovMetrics(uint64(l)) 251 } 252 return n 253 } 254 255 // Equal implements proto.Equaler. 256 func (bs *LabelAdapter) Equal(other LabelAdapter) bool { 257 return bs.Name == other.Name && bs.Value == other.Value 258 } 259 260 // Compare implements proto.Comparer. 261 func (bs *LabelAdapter) Compare(other LabelAdapter) int { 262 if c := strings.Compare(bs.Name, other.Name); c != 0 { 263 return c 264 } 265 return strings.Compare(bs.Value, other.Value) 266 } 267 268 // PreallocTimeseriesSliceFromPool retrieves a slice of PreallocTimeseries from a sync.Pool. 269 // ReuseSlice should be called once done. 270 func PreallocTimeseriesSliceFromPool() []PreallocTimeseries { 271 return slicePool.Get().([]PreallocTimeseries) 272 } 273 274 // ReuseSlice puts the slice back into a sync.Pool for reuse. 275 func ReuseSlice(ts []PreallocTimeseries) { 276 for i := range ts { 277 ReuseTimeseries(ts[i].TimeSeries) 278 } 279 280 slicePool.Put(ts[:0]) //nolint:staticcheck //see comment on slicePool for more details 281 } 282 283 // TimeseriesFromPool retrieves a pointer to a TimeSeries from a sync.Pool. 284 // ReuseTimeseries should be called once done, unless ReuseSlice was called on the slice that contains this TimeSeries. 285 func TimeseriesFromPool() *TimeSeries { 286 return timeSeriesPool.Get().(*TimeSeries) 287 } 288 289 // ReuseTimeseries puts the timeseries back into a sync.Pool for reuse. 290 func ReuseTimeseries(ts *TimeSeries) { 291 // Name and Value may point into a large gRPC buffer, so clear the reference to allow GC 292 for i := 0; i < len(ts.Labels); i++ { 293 ts.Labels[i].Name = "" 294 ts.Labels[i].Value = "" 295 } 296 ts.Labels = ts.Labels[:0] 297 ts.Samples = ts.Samples[:0] 298 timeSeriesPool.Put(ts) 299 }