|
|
|
@ -8,8 +8,7 @@ import ( |
|
|
|
|
"fmt" |
|
|
|
|
"io" |
|
|
|
|
"sort" |
|
|
|
|
|
|
|
|
|
"github.com/dolthub/swiss" |
|
|
|
|
"sync" |
|
|
|
|
|
|
|
|
|
"github.com/prometheus/prometheus/model/labels" |
|
|
|
|
"github.com/prometheus/prometheus/storage" |
|
|
|
@ -24,8 +23,15 @@ import ( |
|
|
|
|
|
|
|
|
|
// LOKW is the magic number for the Loki WAL format.
|
|
|
|
|
var ( |
|
|
|
|
magicNumber = uint32(0x4C4F4B57) |
|
|
|
|
magicBuf [4]byte |
|
|
|
|
magicNumber = uint32(0x4C4F4B57) |
|
|
|
|
magicBuf [4]byte |
|
|
|
|
streamSegmentPool = sync.Pool{ |
|
|
|
|
New: func() interface{} { |
|
|
|
|
return &streamSegment{ |
|
|
|
|
entries: make([]*logproto.Entry, 0, 4096), |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
func init() { |
|
|
|
@ -37,9 +43,10 @@ type streamID struct { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type SegmentWriter struct { |
|
|
|
|
streams *swiss.Map[streamID, *streamSegment] |
|
|
|
|
streams map[streamID]*streamSegment |
|
|
|
|
buf1 encoding.Encbuf |
|
|
|
|
inputSize int64 |
|
|
|
|
idxWriter *index.Writer |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type streamSegment struct { |
|
|
|
@ -49,12 +56,21 @@ type streamSegment struct { |
|
|
|
|
maxt int64 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *streamSegment) Reset() { |
|
|
|
|
s.entries = s.entries[:0] |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// NewWalSegmentWriter creates a new WalSegmentWriter.
|
|
|
|
|
func NewWalSegmentWriter() *SegmentWriter { |
|
|
|
|
return &SegmentWriter{ |
|
|
|
|
streams: swiss.NewMap[streamID, *streamSegment](64), |
|
|
|
|
buf1: encoding.EncWith(make([]byte, 0, 4)), |
|
|
|
|
func NewWalSegmentWriter() (*SegmentWriter, error) { |
|
|
|
|
idxWriter, err := index.NewWriter() |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|
return &SegmentWriter{ |
|
|
|
|
streams: make(map[streamID]*streamSegment, 64), |
|
|
|
|
buf1: encoding.EncWith(make([]byte, 0, 4)), |
|
|
|
|
idxWriter: idxWriter, |
|
|
|
|
}, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Labels are passed a string `{foo="bar",baz="qux"}` `{foo="foo",baz="foo"}`. labels.Labels => Symbols foo, baz , qux
|
|
|
|
@ -66,22 +82,18 @@ func (b *SegmentWriter) Append(tenantID, labelsString string, lbls labels.Labels |
|
|
|
|
b.inputSize += int64(len(e.Line)) |
|
|
|
|
} |
|
|
|
|
id := streamID{labels: labelsString, tenant: tenantID} |
|
|
|
|
s, ok := b.streams.Get(id) |
|
|
|
|
s, ok := b.streams[id] |
|
|
|
|
if !ok { |
|
|
|
|
if lbls.Get(tsdb.TenantLabel) == "" { |
|
|
|
|
lbls = labels.NewBuilder(lbls).Set(tsdb.TenantLabel, tenantID).Labels() |
|
|
|
|
} |
|
|
|
|
s = &streamSegment{ |
|
|
|
|
// todo: should be pooled.
|
|
|
|
|
// prometheus bucketed pool
|
|
|
|
|
// https://pkg.go.dev/github.com/prometheus/prometheus/util/pool
|
|
|
|
|
entries: make([]*logproto.Entry, 0, 64), |
|
|
|
|
lbls: lbls, |
|
|
|
|
tenantID: tenantID, |
|
|
|
|
} |
|
|
|
|
s = streamSegmentPool.Get().(*streamSegment) |
|
|
|
|
s.Reset() |
|
|
|
|
s.lbls = lbls |
|
|
|
|
s.tenantID = tenantID |
|
|
|
|
s.maxt = entries[len(entries)-1].Timestamp.UnixNano() |
|
|
|
|
s.entries = append(s.entries, entries...) |
|
|
|
|
b.streams.Put(id, s) |
|
|
|
|
b.streams[id] = s |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -105,14 +117,17 @@ func (b *SegmentWriter) Append(tenantID, labelsString string, lbls labels.Labels |
|
|
|
|
func (b *SegmentWriter) WriteTo(w io.Writer) (int64, error) { |
|
|
|
|
var ( |
|
|
|
|
total int64 |
|
|
|
|
streams = make([]*streamSegment, 0, b.streams.Count()) |
|
|
|
|
streams = make([]*streamSegment, 0, len(b.streams)) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// Collect all streams and sort them by tenantID and labels.
|
|
|
|
|
b.streams.Iter(func(k streamID, v *streamSegment) bool { |
|
|
|
|
streams = append(streams, v) |
|
|
|
|
return false |
|
|
|
|
}) |
|
|
|
|
for _, s := range b.streams { |
|
|
|
|
if len(s.entries) == 0 { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
streams = append(streams, s) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sort.Slice(streams, func(i, j int) bool { |
|
|
|
|
if streams[i].tenantID != streams[j].tenantID { |
|
|
|
|
return streams[i].tenantID < streams[j].tenantID |
|
|
|
@ -120,7 +135,7 @@ func (b *SegmentWriter) WriteTo(w io.Writer) (int64, error) { |
|
|
|
|
return labels.Compare(streams[i].lbls, streams[j].lbls) < 0 |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
idxw, err := index.NewWriter(context.TODO()) |
|
|
|
|
err := b.idxWriter.Reset() |
|
|
|
|
if err != nil { |
|
|
|
|
return total, err |
|
|
|
|
} |
|
|
|
@ -143,7 +158,7 @@ func (b *SegmentWriter) WriteTo(w io.Writer) (int64, error) { |
|
|
|
|
|
|
|
|
|
// Add symbols
|
|
|
|
|
for _, symbol := range symbols { |
|
|
|
|
if err := idxw.AddSymbol(symbol); err != nil { |
|
|
|
|
if err := b.idxWriter.AddSymbol(symbol); err != nil { |
|
|
|
|
return total, err |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -163,7 +178,7 @@ func (b *SegmentWriter) WriteTo(w io.Writer) (int64, error) { |
|
|
|
|
if err != nil { |
|
|
|
|
return total, err |
|
|
|
|
} |
|
|
|
|
err = idxw.AddSeries(storage.SeriesRef(i), s.lbls, chunks.Meta{ |
|
|
|
|
err = b.idxWriter.AddSeries(storage.SeriesRef(i), s.lbls, chunks.Meta{ |
|
|
|
|
MinTime: s.entries[0].Timestamp.UnixNano(), |
|
|
|
|
MaxTime: s.entries[len(s.entries)-1].Timestamp.UnixNano(), |
|
|
|
|
Ref: chunks.NewChunkRef(uint64(total), uint64(n)), |
|
|
|
@ -175,11 +190,11 @@ func (b *SegmentWriter) WriteTo(w io.Writer) (int64, error) { |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := idxw.Close(); err != nil { |
|
|
|
|
if err := b.idxWriter.Close(); err != nil { |
|
|
|
|
return total, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
buf, closer, err := idxw.Buffer() |
|
|
|
|
buf, closer, err := b.idxWriter.Buffer() |
|
|
|
|
if err != nil { |
|
|
|
|
return total, err |
|
|
|
|
} |
|
|
|
@ -226,7 +241,11 @@ func (s *streamSegment) WriteTo(w io.Writer) (n int64, err error) { |
|
|
|
|
// Reset clears the writer.
|
|
|
|
|
// After calling Reset, the writer can be reused.
|
|
|
|
|
func (b *SegmentWriter) Reset() { |
|
|
|
|
b.streams.Clear() |
|
|
|
|
for _, s := range b.streams { |
|
|
|
|
s := s |
|
|
|
|
streamSegmentPool.Put(s) |
|
|
|
|
} |
|
|
|
|
b.streams = make(map[streamID]*streamSegment, 64) |
|
|
|
|
b.buf1.Reset() |
|
|
|
|
b.inputSize = 0 |
|
|
|
|
} |
|
|
|
|