mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
185 lines
5.3 KiB
Go
185 lines
5.3 KiB
Go
package livetraces
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"math/rand/v2"
|
|
"testing"
|
|
"time"
|
|
|
|
kitlog "github.com/go-kit/log"
|
|
"github.com/grafana/tempo/pkg/tempopb"
|
|
v1 "github.com/grafana/tempo/pkg/tempopb/trace/v1"
|
|
"github.com/grafana/tempo/pkg/util/log"
|
|
"github.com/grafana/tempo/pkg/util/test"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const (
|
|
testTenantID = "fake"
|
|
)
|
|
|
|
func TestLiveTracesSizesAndLen(t *testing.T) {
|
|
lt := New[*v1.ResourceSpans](func(rs *v1.ResourceSpans) uint64 { return uint64(rs.Size()) }, time.Millisecond, time.Second, testTenantID)
|
|
|
|
expectedSz := uint64(0)
|
|
expectedLen := uint64(0)
|
|
|
|
for i := 0; i < 100; i++ {
|
|
id := test.ValidTraceID(nil)
|
|
tr := test.MakeTrace(rand.IntN(5)+1, id)
|
|
|
|
nowTime := time.Now()
|
|
|
|
// add some traces and confirm size/len
|
|
expectedLen++
|
|
for _, rs := range tr.ResourceSpans {
|
|
expectedSz += uint64(rs.Size())
|
|
lt.Push(id, rs, 0)
|
|
}
|
|
|
|
require.Equal(t, expectedSz, lt.Size())
|
|
require.Equal(t, expectedLen, lt.Len())
|
|
|
|
// cut some traces and confirm size/len
|
|
cutTraces := lt.CutIdle(nowTime, false)
|
|
for _, tr := range cutTraces {
|
|
for _, rs := range tr.Batches {
|
|
expectedSz -= uint64(rs.Size())
|
|
}
|
|
expectedLen--
|
|
}
|
|
|
|
require.Equal(t, expectedSz, lt.Size())
|
|
require.Equal(t, expectedLen, lt.Len())
|
|
}
|
|
}
|
|
|
|
func TestCutIdleDueToIdleTime(t *testing.T) {
|
|
lt := New(func(rs *v1.ResourceSpans) uint64 { return uint64(rs.Size()) }, time.Second, time.Hour, testTenantID)
|
|
|
|
id := test.ValidTraceID(nil)
|
|
tr := test.MakeTrace(1, id)
|
|
|
|
rootTime := time.Unix(0, 0)
|
|
|
|
err := lt.PushWithTimestampAndLimits(rootTime, id, tr.ResourceSpans[0], 0, 0)
|
|
require.NoError(t, err)
|
|
|
|
// cut at 500 ms, should cut nothing
|
|
cutTraces := lt.CutIdle(rootTime.Add(500*time.Millisecond), false)
|
|
require.Equal(t, 0, len(cutTraces))
|
|
|
|
// push at 1 second
|
|
err = lt.PushWithTimestampAndLimits(rootTime.Add(1000*time.Millisecond), id, tr.ResourceSpans[0], 0, 0)
|
|
require.NoError(t, err)
|
|
|
|
// cut at 1.5 seconds, should cut nothing
|
|
cutTraces = lt.CutIdle(rootTime.Add(1500*time.Millisecond), false)
|
|
require.Equal(t, 0, len(cutTraces))
|
|
|
|
// cut at 2.5 seconds, should cut the trace b/c it's been idle for 1.5 seconds
|
|
cutTraces = lt.CutIdle(rootTime.Add(2500*time.Millisecond), false)
|
|
require.Equal(t, 1, len(cutTraces))
|
|
require.Equal(t, id, cutTraces[0].ID)
|
|
|
|
require.Equal(t, 0, len(lt.Traces))
|
|
}
|
|
|
|
func TestCutIdleDueToLiveTime(t *testing.T) {
|
|
lt := New(func(rs *v1.ResourceSpans) uint64 { return uint64(rs.Size()) }, time.Hour, time.Second, testTenantID)
|
|
|
|
id := test.ValidTraceID(nil)
|
|
tr := test.MakeTrace(1, id)
|
|
|
|
rootTime := time.Unix(0, 0)
|
|
|
|
err := lt.PushWithTimestampAndLimits(rootTime, id, tr.ResourceSpans[0], 0, 0)
|
|
require.NoError(t, err)
|
|
|
|
// cut at 500 ms, should cut nothing
|
|
cutTraces := lt.CutIdle(rootTime.Add(500*time.Millisecond), false)
|
|
require.Equal(t, 0, len(cutTraces))
|
|
|
|
// push at 1 second
|
|
err = lt.PushWithTimestampAndLimits(rootTime.Add(1000*time.Millisecond), id, tr.ResourceSpans[0], 0, 0)
|
|
require.NoError(t, err)
|
|
|
|
// cut at 1.5 seconds, should cut the trace b/c it's been live for 1.5 seconds!
|
|
cutTraces = lt.CutIdle(rootTime.Add(1500*time.Millisecond), false)
|
|
require.Equal(t, 1, len(cutTraces))
|
|
require.Equal(t, id, cutTraces[0].ID)
|
|
|
|
// cut at 2.5 seconds, should cut nothing
|
|
cutTraces = lt.CutIdle(rootTime.Add(2500*time.Millisecond), false)
|
|
require.Equal(t, 0, len(cutTraces))
|
|
|
|
require.Equal(t, 0, len(lt.Traces))
|
|
}
|
|
|
|
func TestMaxTraceSizeExceededWithAccumulation(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
logger := kitlog.NewJSONLogger(kitlog.NewSyncWriter(&buf))
|
|
|
|
originalLogger := log.Logger
|
|
log.Logger = logger
|
|
defer func() { log.Logger = originalLogger }()
|
|
|
|
const (
|
|
batchSize = uint64(300)
|
|
maxTraceSize = uint64(500)
|
|
)
|
|
|
|
lt := New(func(_ *v1.ResourceSpans) uint64 { return batchSize }, time.Hour, time.Hour, testTenantID)
|
|
|
|
id := test.ValidTraceID(nil)
|
|
tr := test.MakeTrace(1, id)
|
|
|
|
// First push should succeed
|
|
lt.Push(id, tr.ResourceSpans[0], 0)
|
|
|
|
// Second push should fail: batchSize + batchSize > maxTraceSize
|
|
err := lt.PushWithTimestampAndLimits(time.Now(), id, tr.ResourceSpans[0], 0, maxTraceSize)
|
|
|
|
require.Equal(t, ErrMaxTraceSizeExceeded, err)
|
|
|
|
logOutput := buf.String()
|
|
require.Contains(t, logOutput, "max trace size exceeded")
|
|
require.Contains(t, logOutput, fmt.Sprintf(`"max":%d`, maxTraceSize))
|
|
require.Contains(t, logOutput, fmt.Sprintf(`"reqSize":%d`, batchSize))
|
|
require.Contains(t, logOutput, fmt.Sprintf(`"totalSize":%d`, batchSize))
|
|
}
|
|
|
|
func BenchmarkLiveTracesWrite(b *testing.B) {
|
|
lt := New(func(rs *v1.ResourceSpans) uint64 { return uint64(rs.Size()) }, 0, 0, testTenantID)
|
|
|
|
var traces []*tempopb.Trace
|
|
for i := 0; i < 100_000; i++ {
|
|
traces = append(traces, test.MakeTrace(1, nil))
|
|
}
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
for _, tr := range traces {
|
|
lt.Push(tr.ResourceSpans[0].ScopeSpans[0].Spans[0].TraceId, tr.ResourceSpans[0], 0)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkLiveTracesRead(b *testing.B) {
|
|
lt := New(func(rs *v1.ResourceSpans) uint64 { return uint64(rs.Size()) }, 0, 0, testTenantID)
|
|
|
|
for i := 0; i < 100_000; i++ {
|
|
tr := test.MakeTrace(1, nil)
|
|
lt.Push(tr.ResourceSpans[0].ScopeSpans[0].Spans[0].TraceId, tr.ResourceSpans[0], 0)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
// This won't anything, instead of will benchmark the map iteration performance.
|
|
lt.CutIdle(time.Now().Add(-time.Hour), false)
|
|
}
|
|
}
|