github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/ingester/encoding.go (about)

     1  package ingester
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/pkg/errors"
     7  	"github.com/prometheus/prometheus/tsdb/chunks"
     8  	"github.com/prometheus/prometheus/tsdb/record"
     9  
    10  	"github.com/grafana/loki/pkg/logproto"
    11  	"github.com/grafana/loki/pkg/util/encoding"
    12  )
    13  
    14  // RecordType represents the type of the WAL/Checkpoint record.
    15  type RecordType byte
    16  
    17  const (
    18  	_ = iota // ignore first value so the zero value doesn't look like a record type.
    19  	// WALRecordSeries is the type for the WAL record for series.
    20  	WALRecordSeries RecordType = iota
    21  	// WALRecordEntriesV1 is the type for the WAL record for samples.
    22  	WALRecordEntriesV1
    23  	// CheckpointRecord is the type for the Checkpoint record based on protos.
    24  	CheckpointRecord
    25  	// WALRecordEntriesV2 is the type for the WAL record for samples with an
    26  	// additional counter value for use in replaying without the ordering constraint.
    27  	WALRecordEntriesV2
    28  )
    29  
    30  // The current type of Entries that this distribution writes.
    31  // Loki can read in a backwards compatible manner, but will write the newest variant.
    32  const CurrentEntriesRec RecordType = WALRecordEntriesV2
    33  
    34  // WALRecord is a struct combining the series and samples record.
    35  type WALRecord struct {
    36  	UserID string
    37  	Series []record.RefSeries
    38  
    39  	// entryIndexMap coordinates the RefEntries index associated with a particular fingerprint.
    40  	// This is helpful for constant time lookups during ingestion and is ignored when restoring
    41  	// from the WAL.
    42  	entryIndexMap map[uint64]int
    43  	RefEntries    []RefEntries
    44  }
    45  
    46  func (r *WALRecord) IsEmpty() bool {
    47  	return len(r.Series) == 0 && len(r.RefEntries) == 0
    48  }
    49  
    50  func (r *WALRecord) Reset() {
    51  	r.UserID = ""
    52  	if len(r.Series) > 0 {
    53  		r.Series = r.Series[:0]
    54  	}
    55  
    56  	for _, ref := range r.RefEntries {
    57  		recordPool.PutEntries(ref.Entries)
    58  	}
    59  	r.RefEntries = r.RefEntries[:0]
    60  	r.entryIndexMap = make(map[uint64]int)
    61  }
    62  
    63  func (r *WALRecord) AddEntries(fp uint64, counter int64, entries ...logproto.Entry) {
    64  	if idx, ok := r.entryIndexMap[fp]; ok {
    65  		r.RefEntries[idx].Entries = append(r.RefEntries[idx].Entries, entries...)
    66  		r.RefEntries[idx].Counter = counter
    67  		return
    68  	}
    69  
    70  	r.entryIndexMap[fp] = len(r.RefEntries)
    71  	r.RefEntries = append(r.RefEntries, RefEntries{
    72  		Counter: counter,
    73  		Ref:     chunks.HeadSeriesRef(fp),
    74  		Entries: entries,
    75  	})
    76  }
    77  
    78  type RefEntries struct {
    79  	Counter int64
    80  	Ref     chunks.HeadSeriesRef
    81  	Entries []logproto.Entry
    82  }
    83  
    84  func (r *WALRecord) encodeSeries(b []byte) []byte {
    85  	buf := encoding.EncWith(b)
    86  	buf.PutByte(byte(WALRecordSeries))
    87  	buf.PutUvarintStr(r.UserID)
    88  
    89  	var enc record.Encoder
    90  	// The 'encoded' already has the type header and userID here, hence re-using
    91  	// the remaining part of the slice (i.e. encoded[len(encoded):])) to encode the series.
    92  	encoded := buf.Get()
    93  	encoded = append(encoded, enc.Series(r.Series, encoded[len(encoded):])...)
    94  
    95  	return encoded
    96  }
    97  
    98  func (r *WALRecord) encodeEntries(version RecordType, b []byte) []byte {
    99  	buf := encoding.EncWith(b)
   100  	buf.PutByte(byte(version))
   101  	buf.PutUvarintStr(r.UserID)
   102  
   103  	// Placeholder for the first timestamp of any sample encountered.
   104  	// All others in this record will store their timestamps as diffs relative to this
   105  	// as a space optimization.
   106  	var first int64
   107  
   108  outer:
   109  	for _, ref := range r.RefEntries {
   110  		for _, entry := range ref.Entries {
   111  			first = entry.Timestamp.UnixNano()
   112  			buf.PutBE64int64(first)
   113  			break outer
   114  		}
   115  	}
   116  
   117  	for _, ref := range r.RefEntries {
   118  		// ignore refs with 0 entries
   119  		if len(ref.Entries) < 1 {
   120  			continue
   121  		}
   122  		buf.PutBE64(uint64(ref.Ref)) // write fingerprint
   123  
   124  		if version >= WALRecordEntriesV2 {
   125  			buf.PutBE64int64(ref.Counter) // write highest counter value
   126  		}
   127  
   128  		buf.PutUvarint(len(ref.Entries)) // write number of entries
   129  
   130  		for _, s := range ref.Entries {
   131  			buf.PutVarint64(s.Timestamp.UnixNano() - first)
   132  			buf.PutUvarint(len(s.Line))
   133  			buf.PutString(s.Line)
   134  		}
   135  	}
   136  	return buf.Get()
   137  }
   138  
   139  func decodeEntries(b []byte, version RecordType, rec *WALRecord) error {
   140  	if len(b) == 0 {
   141  		return nil
   142  	}
   143  
   144  	dec := encoding.DecWith(b)
   145  	baseTime := dec.Be64int64()
   146  
   147  	for len(dec.B) > 0 && dec.Err() == nil {
   148  		refEntries := RefEntries{
   149  			Ref: chunks.HeadSeriesRef(dec.Be64()),
   150  		}
   151  
   152  		if version >= WALRecordEntriesV2 {
   153  			refEntries.Counter = dec.Be64int64()
   154  		}
   155  
   156  		nEntries := dec.Uvarint()
   157  		refEntries.Entries = make([]logproto.Entry, 0, nEntries)
   158  		rem := nEntries
   159  		for ; dec.Err() == nil && rem > 0; rem-- {
   160  			timeOffset := dec.Varint64()
   161  			lineLength := dec.Uvarint()
   162  			line := dec.Bytes(lineLength)
   163  
   164  			refEntries.Entries = append(refEntries.Entries, logproto.Entry{
   165  				Timestamp: time.Unix(0, baseTime+timeOffset),
   166  				Line:      string(line),
   167  			})
   168  		}
   169  
   170  		if dec.Err() != nil {
   171  			return errors.Wrapf(dec.Err(), "entry decode error after %d RefEntries", nEntries-rem)
   172  		}
   173  
   174  		rec.RefEntries = append(rec.RefEntries, refEntries)
   175  	}
   176  
   177  	if dec.Err() != nil {
   178  		return errors.Wrap(dec.Err(), "refEntry decode error")
   179  	}
   180  
   181  	if len(dec.B) > 0 {
   182  		return errors.Errorf("unexpected %d bytes left in entry", len(dec.B))
   183  	}
   184  	return nil
   185  }
   186  
   187  func decodeWALRecord(b []byte, walRec *WALRecord) (err error) {
   188  	var (
   189  		userID  string
   190  		dec     record.Decoder
   191  		rSeries []record.RefSeries
   192  
   193  		decbuf = encoding.DecWith(b)
   194  		t      = RecordType(decbuf.Byte())
   195  	)
   196  
   197  	switch t {
   198  	case WALRecordSeries:
   199  		userID = decbuf.UvarintStr()
   200  		rSeries, err = dec.Series(decbuf.B, walRec.Series)
   201  	case WALRecordEntriesV1, WALRecordEntriesV2:
   202  		userID = decbuf.UvarintStr()
   203  		err = decodeEntries(decbuf.B, t, walRec)
   204  	default:
   205  		return errors.New("unknown record type")
   206  	}
   207  
   208  	// We reach here only if its a record with type header.
   209  	if decbuf.Err() != nil {
   210  		return decbuf.Err()
   211  	}
   212  
   213  	if err != nil {
   214  		return err
   215  	}
   216  
   217  	walRec.UserID = userID
   218  	walRec.Series = rSeries
   219  	return nil
   220  }