Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate MaxSpansPerTrace in favour of MaxBytesPerTrace #612

Merged
merged 3 commits into from
Mar 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* [CHANGE] Update to Go 1.16, latest OpenTelemetry proto definition and collector [#546](https://github.com/grafana/tempo/pull/546)
* [CHANGE] Tempo Query Frontend now accepts queries at `/tempo/api/traces/{traceID}` as opposed to `/api/traces/{traceID}`.
This is a **breaking change**, make sure to change the Grafana Datasource endpoint accordingly. [#574](https://github.com/grafana/tempo/pull/574)
* [CHANGE] `max_spans_per_trace` limit override has been removed in favour of `max_bytes_per_trace`.
This is a **breaking change** to the overrides config section. [#612](https://github.com/grafana/tempo/pull/612)
* [FEATURE] Add page based access to the index file. [#557](https://github.com/grafana/tempo/pull/557)
* [ENHANCEMENT] Add a Shutdown handler to flush data to backend, at "/shutdown". [#526](https://github.com/grafana/tempo/pull/526)
* [ENHANCEMENT] Queriers now query all (healthy) ingesters for a trace to mitigate 404s on ingester rollouts/scaleups.
Expand Down
12 changes: 6 additions & 6 deletions docs/tempo/website/configuration/ingestion-limit.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The following options can be used to limit ingestion:

- `ingestion_burst_size` : Burst size used in span ingestion. Default is `100,000`.
- `ingestion_rate_limit` : Per-user ingestion rate limit in spans per second. Default is `100,000`.
- `max_spans_per_trace` : Maximum number of spans per trace. `0` to disable. Default is `50,000`.
- `max_bytes_per_trace` : Maximum size of a single trace in bytes. `0` to disable. Default is `5,000,000` (~5MB).
- `max_traces_per_user`: Maximum number of active traces per user, per ingester. `0` to disable. Default is `10,000`.

Both the `ingestion_burst_size` and `ingestion_rate_limit` parameters control the rate limit. When these limits exceed the following message is logged:
Expand All @@ -27,10 +27,10 @@ Both the `ingestion_burst_size` and `ingestion_rate_limit` parameters control th
RATE_LIMITED: ingestion rate limit (100000 spans) exceeded while adding 10 spans
```

When the limit for the `max_spans_per_trace` parameter exceeds the following message is logged:
When the limit for the `max_bytes_per_trace` parameter exceeds the following message is logged:

```
TRACE_TOO_LARGE: totalSpans (50000) exceeded while adding 2 spans
TRACE_TOO_LARGE: max size of trace (5000000) exceeded while adding 387 bytes
```

Finally, when the limit for the `max_traces_per_user` parameter exceeds the following message is logged:
Expand All @@ -50,7 +50,7 @@ To configure new ingestion limits that applies to all tenants of the cluster:
overrides:
ingestion_burst_size: 50000
ingestion_rate_limit: 50000
max_spans_per_trace: 50000
max_bytes_per_trace: 5_000_000
annanay25 marked this conversation as resolved.
Show resolved Hide resolved
max_traces_per_user: 10000
```

Expand All @@ -75,7 +75,7 @@ Sometimes you don't want all tenants within the cluster to have the same setting
"<tenant id>":
ingestion_max_batch_size: 5000
ingestion_rate_limit: 400000
max_spans_per_trace: 250000
max_bytes_per_trace: 25_000_000
max_traces_per_user: 100000
```

Expand All @@ -87,7 +87,7 @@ This overrides file is dynamically loaded. It can be changed at runtime and wil
"*":
ingestion_max_batch_size: 5000
ingestion_rate_limit: 400000
max_spans_per_trace: 250000
max_bytes_per_trace: 25_000_000
max_traces_per_user: 100000
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ In high volume tracing environments the default trace limits are sometimes not s

- `ingestion_burst_size` : Burst size used in span ingestion. Default is `100,000`.
- `ingestion_rate_limit` : Per-user ingestion rate limit in spans per second. Default is `100,000`.
- `max_spans_per_trace` : Maximum number of spans per trace. `0` to disable. Default is `50,000`.
- `max_bytes_per_trace` : Maximum size of a single trace in bytes. `0` to disable. Default is `5,000,000` (~5MB).
- `max_traces_per_user`: Maximum number of active traces per user, per ingester. `0` to disable. Default is `10,000`.
- Increase the maximum limit to a failsafe value. For example, increase the limit for the `max_traces_per_user` parameter from `10,000` like `50000`.
2 changes: 1 addition & 1 deletion integration/e2e/config-limits.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ distributor:
grpc:

overrides:
max_spans_per_trace: 1
max_bytes_per_trace: 100
max_traces_per_user: 1
ingestion_rate_limit: 5
ingestion_burst_size: 5
Expand Down
2 changes: 1 addition & 1 deletion integration/e2e/limits_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestLimits(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, c)

// should fail b/c the trace is too large
// should fail b/c the trace is too large. each batch should be ~80 bytes
batch := makeThriftBatchWithSpanCount(2)
require.Error(t, c.EmitBatch(context.Background(), batch))
// should fail b/c this will be too many traces
Expand Down
4 changes: 2 additions & 2 deletions modules/ingester/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,8 @@ func (i *instance) getOrCreateTrace(req *tempopb.PushRequest) (*trace, error) {
return nil, status.Errorf(codes.FailedPrecondition, "%s max live traces per tenant exceeded: %v", overrides.ErrorPrefixLiveTracesExceeded, err)
}

maxSpans := i.limiter.limits.MaxSpansPerTrace(i.instanceID)
trace = newTrace(maxSpans, fp, traceID)
maxBytes := i.limiter.limits.MaxBytesPerTrace(i.instanceID)
trace = newTrace(maxBytes, fp, traceID)
i.traces[fp] = trace
i.tracesCreatedTotal.Inc()

Expand Down
18 changes: 9 additions & 9 deletions modules/ingester/instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func TestInstanceDoesNotRace(t *testing.T) {

func TestInstanceLimits(t *testing.T) {
limits, err := overrides.NewOverrides(overrides.Limits{
MaxSpansPerTrace: 10,
MaxBytesPerTrace: 1000,
})
assert.NoError(t, err, "unexpected error creating limits")
limiter := NewLimiter(limits, &ringCountMock{count: 1}, 1)
Expand All @@ -242,39 +242,39 @@ func TestInstanceLimits(t *testing.T) {
name: "succeeds",
pushes: []push{
{
req: test.MakeRequest(3, []byte{}),
req: test.MakeRequestWithByteLimit(300, []byte{}),
},
{
req: test.MakeRequest(5, []byte{}),
req: test.MakeRequestWithByteLimit(500, []byte{}),
},
{
req: test.MakeRequest(9, []byte{}),
req: test.MakeRequestWithByteLimit(900, []byte{}),
},
},
},
{
name: "one fails",
pushes: []push{
{
req: test.MakeRequest(3, []byte{}),
req: test.MakeRequestWithByteLimit(300, []byte{}),
},
{
req: test.MakeRequest(15, []byte{}),
req: test.MakeRequestWithByteLimit(1500, []byte{}),
expectsError: true,
},
{
req: test.MakeRequest(9, []byte{}),
req: test.MakeRequestWithByteLimit(900, []byte{}),
},
},
},
{
name: "multiple pushes same trace",
pushes: []push{
{
req: test.MakeRequest(5, []byte{0x01}),
req: test.MakeRequestWithByteLimit(500, []byte{0x01}),
},
{
req: test.MakeRequest(7, []byte{0x01}),
req: test.MakeRequestWithByteLimit(700, []byte{0x01}),
expectsError: true,
},
},
Expand Down
23 changes: 9 additions & 14 deletions modules/ingester/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,29 @@ type trace struct {
token uint32
lastAppend time.Time
traceID []byte
maxSpans int
currentSpans int
maxBytes int
currentBytes int
}

func newTrace(maxSpans int, token uint32, traceID []byte) *trace {
func newTrace(maxBytes int, token uint32, traceID []byte) *trace {
return &trace{
token: token,
trace: &tempopb.Trace{},
lastAppend: time.Now(),
traceID: traceID,
maxSpans: maxSpans,
maxBytes: maxBytes,
}
}

func (t *trace) Push(_ context.Context, req *tempopb.PushRequest) error {
t.lastAppend = time.Now()
if t.maxSpans != 0 {
// count spans
spanCount := 0
for _, ils := range req.Batch.InstrumentationLibrarySpans {
spanCount += len(ils.Spans)
if t.maxBytes != 0 {
reqSize := req.Size()
if t.currentBytes+reqSize > t.maxBytes {
return status.Errorf(codes.FailedPrecondition, "%s max size of trace (%d) exceeded while adding %d bytes", overrides.ErrorPrefixTraceTooLarge, t.maxBytes, reqSize)
}

if t.currentSpans+spanCount > t.maxSpans {
return status.Errorf(codes.FailedPrecondition, "%s totalSpans (%d) exceeded while adding %d spans", overrides.ErrorPrefixTraceTooLarge, t.maxSpans, spanCount)
}

t.currentSpans += spanCount
t.currentBytes += reqSize
}

t.trace.Batches = append(t.trace.Batches, req.Batch)
Expand Down
4 changes: 2 additions & 2 deletions modules/overrides/limits.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type Limits struct {
// Ingester enforced limits.
MaxLocalTracesPerUser int `yaml:"max_traces_per_user"`
MaxGlobalTracesPerUser int `yaml:"max_global_traces_per_user"`
MaxSpansPerTrace int `yaml:"max_spans_per_trace"`
MaxBytesPerTrace int `yaml:"max_bytes_per_trace"`

// Compactor enforced limits.
BlockRetention time.Duration `yaml:"block_retention"`
Expand All @@ -50,7 +50,7 @@ func (l *Limits) RegisterFlags(f *flag.FlagSet) {
// Ingester limits
f.IntVar(&l.MaxLocalTracesPerUser, "ingester.max-traces-per-user", 10e3, "Maximum number of active traces per user, per ingester. 0 to disable.")
f.IntVar(&l.MaxGlobalTracesPerUser, "ingester.max-global-traces-per-user", 0, "Maximum number of active traces per user, across the cluster. 0 to disable.")
f.IntVar(&l.MaxSpansPerTrace, "ingester.max-spans-per-trace", 50e3, "Maximum number of spans per trace. 0 to disable.")
f.IntVar(&l.MaxBytesPerTrace, "ingester.max-bytes-per-trace", 50e5, "Maximum size of a trace in bytes. 0 to disable.")

f.StringVar(&l.PerTenantOverrideConfig, "limits.per-user-override-config", "", "File name of per-user overrides.")
f.DurationVar(&l.PerTenantOverridePeriod, "limits.per-user-override-period", 10*time.Second, "Period with this to reload the overrides.")
Expand Down
6 changes: 3 additions & 3 deletions modules/overrides/overrides.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,9 @@ func (o *Overrides) MaxGlobalTracesPerUser(userID string) int {
return o.getOverridesForUser(userID).MaxGlobalTracesPerUser
}

// MaxSpansPerTrace returns the maximum number of spans a user can have in a live trace.
func (o *Overrides) MaxSpansPerTrace(userID string) int {
return o.getOverridesForUser(userID).MaxSpansPerTrace
// MaxBytesPerTrace returns the maximum size of a single trace in bytes allowed for a user.
func (o *Overrides) MaxBytesPerTrace(userID string) int {
return o.getOverridesForUser(userID).MaxBytesPerTrace
}

// IngestionRateSpans is the number of spans per second allowed for this tenant
Expand Down
20 changes: 10 additions & 10 deletions modules/overrides/overrides_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func TestOverrides(t *testing.T) {
overrides *perTenantOverrides
expectedMaxLocalTraces map[string]int
expectedMaxGlobalTraces map[string]int
expectedMaxSpansPerTrace map[string]int
expectedMaxBytesPerTrace map[string]int
expectedIngestionRateSpans map[string]int
expectedIngestionBurstSpans map[string]int
}{
Expand All @@ -32,13 +32,13 @@ func TestOverrides(t *testing.T) {
limits: Limits{
MaxGlobalTracesPerUser: 1,
MaxLocalTracesPerUser: 2,
MaxSpansPerTrace: 3,
MaxBytesPerTrace: 3,
IngestionBurstSize: 4,
IngestionRateSpans: 5,
},
expectedMaxGlobalTraces: map[string]int{"user1": 1, "user2": 1},
expectedMaxLocalTraces: map[string]int{"user1": 2, "user2": 2},
expectedMaxSpansPerTrace: map[string]int{"user1": 3, "user2": 3},
expectedMaxBytesPerTrace: map[string]int{"user1": 3, "user2": 3},
expectedIngestionBurstSpans: map[string]int{"user1": 4, "user2": 4},
expectedIngestionRateSpans: map[string]int{"user1": 5, "user2": 5},
},
Expand All @@ -47,7 +47,7 @@ func TestOverrides(t *testing.T) {
limits: Limits{
MaxGlobalTracesPerUser: 1,
MaxLocalTracesPerUser: 2,
MaxSpansPerTrace: 3,
MaxBytesPerTrace: 3,
IngestionBurstSize: 4,
IngestionRateSpans: 5,
},
Expand All @@ -56,15 +56,15 @@ func TestOverrides(t *testing.T) {
"user1": {
MaxGlobalTracesPerUser: 6,
MaxLocalTracesPerUser: 7,
MaxSpansPerTrace: 8,
MaxBytesPerTrace: 8,
IngestionBurstSize: 9,
IngestionRateSpans: 10,
},
},
},
expectedMaxGlobalTraces: map[string]int{"user1": 6, "user2": 1},
expectedMaxLocalTraces: map[string]int{"user1": 7, "user2": 2},
expectedMaxSpansPerTrace: map[string]int{"user1": 8, "user2": 3},
expectedMaxBytesPerTrace: map[string]int{"user1": 8, "user2": 3},
expectedIngestionBurstSpans: map[string]int{"user1": 9, "user2": 4},
expectedIngestionRateSpans: map[string]int{"user1": 10, "user2": 5},
},
Expand All @@ -73,7 +73,7 @@ func TestOverrides(t *testing.T) {
limits: Limits{
MaxGlobalTracesPerUser: 1,
MaxLocalTracesPerUser: 2,
MaxSpansPerTrace: 3,
MaxBytesPerTrace: 3,
IngestionBurstSize: 4,
IngestionRateSpans: 5,
},
Expand All @@ -82,22 +82,22 @@ func TestOverrides(t *testing.T) {
"user1": {
MaxGlobalTracesPerUser: 6,
MaxLocalTracesPerUser: 7,
MaxSpansPerTrace: 8,
MaxBytesPerTrace: 8,
IngestionBurstSize: 9,
IngestionRateSpans: 10,
},
"*": {
MaxGlobalTracesPerUser: 11,
MaxLocalTracesPerUser: 12,
MaxSpansPerTrace: 13,
MaxBytesPerTrace: 13,
IngestionBurstSize: 14,
IngestionRateSpans: 15,
},
},
},
expectedMaxGlobalTraces: map[string]int{"user1": 6, "user2": 11},
expectedMaxLocalTraces: map[string]int{"user1": 7, "user2": 12},
expectedMaxSpansPerTrace: map[string]int{"user1": 8, "user2": 13},
expectedMaxBytesPerTrace: map[string]int{"user1": 8, "user2": 13},
expectedIngestionBurstSpans: map[string]int{"user1": 9, "user2": 14},
expectedIngestionRateSpans: map[string]int{"user1": 10, "user2": 15},
},
Expand Down
2 changes: 1 addition & 1 deletion operations/jsonnet/microservices/config.libsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
max_traces_per_user: 100000,
ingestion_rate_limit: 150000,
ingestion_burst_size: 150000,
max_spans_per_trace: 200e3,
max_bytes_per_trace: 300e5, // ~30MB
},
},
},
Expand Down
33 changes: 33 additions & 0 deletions pkg/util/test/req.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,36 @@ func MakeTraceWithSpanCount(requests int, spansEach int, traceID []byte) *tempop

return trace
}

// Note that this fn will generate a request with size **close to** maxBytes
func MakeRequestWithByteLimit(maxBytes int, traceID []byte) *tempopb.PushRequest {
if len(traceID) == 0 {
traceID = make([]byte, 16)
rand.Read(traceID)
}

req := &tempopb.PushRequest{
Batch: &v1_trace.ResourceSpans{},
}

ils := &v1_trace.InstrumentationLibrarySpans{
InstrumentationLibrary: &v1_common.InstrumentationLibrary{
Name: "super library",
Version: "0.0.1",
},
}
req.Batch.InstrumentationLibrarySpans = append(req.Batch.InstrumentationLibrarySpans, ils)

for req.Size() < maxBytes {
sampleSpan := v1_trace.Span{
Name: "test",
TraceId: traceID,
SpanId: make([]byte, 8),
}
rand.Read(sampleSpan.SpanId)

ils.Spans = append(ils.Spans, &sampleSpan)
}

return req
}