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  }