Skip to content

Commit 4397725

Browse files
authored
[processor/memorylimiter] Add profiles support to memorylimiter processor (#12454)
#### Description Add profiles support to the memorylimiter processor. #### Link to tracking issue Fixes #12453 Signed-off-by: Israel Blancas <[email protected]>
1 parent 3c72d35 commit 4397725

File tree

12 files changed

+201
-28
lines changed

12 files changed

+201
-28
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
7+
component: memorylimiterprocessor
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add support for profiles.
11+
12+
# One or more tracking issues or pull requests related to the change
13+
issues: [12453]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# Optional: The change log or logs in which this entry should be included.
21+
# e.g. '[user]' or '[user, api]'
22+
# Include 'user' if the change is relevant to end users.
23+
# Include 'api' if there is a change to a library API.
24+
# Default: '[user]'
25+
change_logs: []

cmd/builder/internal/builder/main_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ var replaceModules = []string{
9797
"/processor/processortest",
9898
"/processor/batchprocessor",
9999
"/processor/memorylimiterprocessor",
100+
"/processor/processorhelper/xprocessorhelper",
100101
"/processor/xprocessor",
101102
"/receiver",
102103
"/receiver/nopreceiver",

cmd/otelcorecol/builder-config.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ replaces:
9999
- go.opentelemetry.io/collector/processor/batchprocessor => ../../processor/batchprocessor
100100
- go.opentelemetry.io/collector/processor/memorylimiterprocessor => ../../processor/memorylimiterprocessor
101101
- go.opentelemetry.io/collector/processor/xprocessor => ../../processor/xprocessor
102+
- go.opentelemetry.io/collector/processor/processorhelper/xprocessorhelper => ../../processor/processorhelper/xprocessorhelper
102103
- go.opentelemetry.io/collector/receiver => ../../receiver
103104
- go.opentelemetry.io/collector/receiver/nopreceiver => ../../receiver/nopreceiver
104105
- go.opentelemetry.io/collector/receiver/otlpreceiver => ../../receiver/otlpreceiver

cmd/otelcorecol/go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ require (
118118
go.opentelemetry.io/collector/pdata/testdata v0.120.0 // indirect
119119
go.opentelemetry.io/collector/pipeline v0.120.0 // indirect
120120
go.opentelemetry.io/collector/pipeline/xpipeline v0.120.0 // indirect
121+
go.opentelemetry.io/collector/processor/processorhelper/xprocessorhelper v0.120.0 // indirect
121122
go.opentelemetry.io/collector/processor/processortest v0.120.0 // indirect
122123
go.opentelemetry.io/collector/processor/xprocessor v0.120.0 // indirect
123124
go.opentelemetry.io/collector/receiver/receivertest v0.120.0 // indirect
@@ -287,6 +288,8 @@ replace go.opentelemetry.io/collector/processor/memorylimiterprocessor => ../../
287288

288289
replace go.opentelemetry.io/collector/processor/xprocessor => ../../processor/xprocessor
289290

291+
replace go.opentelemetry.io/collector/processor/processorhelper/xprocessorhelper => ../../processor/processorhelper/xprocessorhelper
292+
290293
replace go.opentelemetry.io/collector/receiver => ../../receiver
291294

292295
replace go.opentelemetry.io/collector/receiver/nopreceiver => ../../receiver/nopreceiver

processor/memorylimiterprocessor/README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
<!-- status autogenerated section -->
44
| Status | |
55
| ------------- |-----------|
6-
| Stability | [beta]: traces, metrics, logs |
6+
| Stability | [alpha]: profiles |
7+
| | [beta]: traces, metrics, logs |
78
| Distributions | [core], [contrib], [k8s] |
89
| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aopen%20label%3Aprocessor%2Fmemorylimiter%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aopen+is%3Aissue+label%3Aprocessor%2Fmemorylimiter) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aclosed%20label%3Aprocessor%2Fmemorylimiter%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aclosed+is%3Aissue+label%3Aprocessor%2Fmemorylimiter) |
910

11+
[alpha]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#alpha
1012
[beta]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#beta
1113
[core]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol
1214
[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib

processor/memorylimiterprocessor/factory.go

+32-5
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@ import (
1111

1212
"go.opentelemetry.io/collector/component"
1313
"go.opentelemetry.io/collector/consumer"
14+
"go.opentelemetry.io/collector/consumer/xconsumer"
1415
"go.opentelemetry.io/collector/internal/memorylimiter"
1516
"go.opentelemetry.io/collector/internal/telemetry"
1617
"go.opentelemetry.io/collector/internal/telemetry/componentattribute"
1718
"go.opentelemetry.io/collector/processor"
1819
"go.opentelemetry.io/collector/processor/memorylimiterprocessor/internal/metadata"
1920
"go.opentelemetry.io/collector/processor/processorhelper"
21+
"go.opentelemetry.io/collector/processor/processorhelper/xprocessorhelper"
22+
"go.opentelemetry.io/collector/processor/xprocessor"
2023
)
2124

2225
var processorCapabilities = consumer.Capabilities{MutatesData: false}
@@ -29,16 +32,17 @@ type factory struct {
2932
}
3033

3134
// NewFactory returns a new factory for the Memory Limiter processor.
32-
func NewFactory() processor.Factory {
35+
func NewFactory() xprocessor.Factory {
3336
f := &factory{
3437
memoryLimiters: map[component.Config]*memoryLimiterProcessor{},
3538
}
36-
return processor.NewFactory(
39+
return xprocessor.NewFactory(
3740
metadata.Type,
3841
createDefaultConfig,
39-
processor.WithTraces(f.createTraces, metadata.TracesStability),
40-
processor.WithMetrics(f.createMetrics, metadata.MetricsStability),
41-
processor.WithLogs(f.createLogs, metadata.LogsStability))
42+
xprocessor.WithTraces(f.createTraces, metadata.TracesStability),
43+
xprocessor.WithMetrics(f.createMetrics, metadata.MetricsStability),
44+
xprocessor.WithLogs(f.createLogs, metadata.LogsStability),
45+
xprocessor.WithProfiles(f.createProfiles, metadata.ProfilesStability))
4246
}
4347

4448
// CreateDefaultConfig creates the default configuration for processor. Notice
@@ -98,6 +102,29 @@ func (f *factory) createLogs(
98102
processorhelper.WithShutdown(memLimiter.shutdown))
99103
}
100104

105+
func (f *factory) createProfiles(
106+
ctx context.Context,
107+
set processor.Settings,
108+
cfg component.Config,
109+
nextConsumer xconsumer.Profiles,
110+
) (xprocessor.Profiles, error) {
111+
memLimiter, err := f.getMemoryLimiter(set, cfg)
112+
if err != nil {
113+
return nil, err
114+
}
115+
116+
return xprocessorhelper.NewProfiles(
117+
ctx,
118+
set,
119+
cfg,
120+
nextConsumer,
121+
memLimiter.processProfiles,
122+
xprocessorhelper.WithCapabilities(processorCapabilities),
123+
xprocessorhelper.WithStart(memLimiter.start),
124+
xprocessorhelper.WithShutdown(memLimiter.shutdown),
125+
)
126+
}
127+
101128
// getMemoryLimiter checks if we have a cached memoryLimiter with a specific config,
102129
// otherwise initialize and add one to the store.
103130
func (f *factory) getMemoryLimiter(set processor.Settings, cfg component.Config) (*memoryLimiterProcessor, error) {

processor/memorylimiterprocessor/factory_test.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,17 @@ func TestCreateProcessor(t *testing.T) {
6868
lp, err := factory.CreateLogs(context.Background(), set, cfg, consumertest.NewNop())
6969
require.NoError(t, err)
7070
assert.NotNil(t, lp)
71-
assert.NoError(t, lp.Start(context.Background(), componenttest.NewNopHost()))
71+
require.NoError(t, lp.Start(context.Background(), componenttest.NewNopHost()))
72+
73+
pp, err := factory.CreateProfiles(context.Background(), set, cfg, consumertest.NewNop())
74+
require.NoError(t, err)
75+
assert.NotNil(t, pp)
76+
assert.NoError(t, pp.Start(context.Background(), componenttest.NewNopHost()))
7277

7378
assert.NoError(t, lp.Shutdown(context.Background()))
7479
assert.NoError(t, tp.Shutdown(context.Background()))
7580
assert.NoError(t, mp.Shutdown(context.Background()))
81+
assert.NoError(t, pp.Shutdown(context.Background()))
7682
// verify that no monitoring routine is running
7783
require.ErrorIs(t, tp.Shutdown(context.Background()), memorylimiter.ErrShutdownNotStarted)
7884

processor/memorylimiterprocessor/go.mod

+9-3
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@ require (
1010
go.opentelemetry.io/collector/consumer v1.26.0
1111
go.opentelemetry.io/collector/consumer/consumererror v0.120.0
1212
go.opentelemetry.io/collector/consumer/consumertest v0.120.0
13+
go.opentelemetry.io/collector/consumer/xconsumer v0.120.0
1314
go.opentelemetry.io/collector/internal/memorylimiter v0.120.0
1415
go.opentelemetry.io/collector/internal/telemetry v0.120.0
1516
go.opentelemetry.io/collector/pdata v1.26.0
17+
go.opentelemetry.io/collector/pdata/pprofile v0.120.0
1618
go.opentelemetry.io/collector/pipeline v0.120.0
19+
go.opentelemetry.io/collector/pipeline/xpipeline v0.120.0
1720
go.opentelemetry.io/collector/processor v0.120.0
21+
go.opentelemetry.io/collector/processor/processorhelper/xprocessorhelper v0.120.0
1822
go.opentelemetry.io/collector/processor/processortest v0.120.0
23+
go.opentelemetry.io/collector/processor/xprocessor v0.120.0
1924
go.opentelemetry.io/otel v1.34.0
2025
go.opentelemetry.io/otel/metric v1.34.0
2126
go.opentelemetry.io/otel/sdk/metric v1.34.0
@@ -50,10 +55,7 @@ require (
5055
github.com/yusufpapurcu/wmi v1.2.4 // indirect
5156
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
5257
go.opentelemetry.io/collector/component/componentstatus v0.120.0 // indirect
53-
go.opentelemetry.io/collector/consumer/xconsumer v0.120.0 // indirect
54-
go.opentelemetry.io/collector/pdata/pprofile v0.120.0 // indirect
5558
go.opentelemetry.io/collector/pdata/testdata v0.120.0 // indirect
56-
go.opentelemetry.io/collector/processor/xprocessor v0.120.0 // indirect
5759
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
5860
go.uber.org/multierr v1.11.0 // indirect
5961
golang.org/x/net v0.33.0 // indirect
@@ -103,3 +105,7 @@ replace go.opentelemetry.io/collector/internal/memorylimiter => ../../internal/m
103105
replace go.opentelemetry.io/collector/internal/telemetry => ../../internal/telemetry
104106

105107
replace go.opentelemetry.io/collector/consumer/consumererror => ../../consumer/consumererror
108+
109+
replace go.opentelemetry.io/collector/processor/processorhelper/xprocessorhelper => ../processorhelper/xprocessorhelper
110+
111+
replace go.opentelemetry.io/collector/pipeline/xpipeline => ../../pipeline/xpipeline

processor/memorylimiterprocessor/internal/metadata/generated_status.go

+4-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

processor/memorylimiterprocessor/memorylimiter.go

+23-15
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import (
1010
"go.opentelemetry.io/collector/internal/memorylimiter"
1111
"go.opentelemetry.io/collector/pdata/plog"
1212
"go.opentelemetry.io/collector/pdata/pmetric"
13+
"go.opentelemetry.io/collector/pdata/pprofile"
1314
"go.opentelemetry.io/collector/pdata/ptrace"
1415
"go.opentelemetry.io/collector/pipeline"
16+
"go.opentelemetry.io/collector/pipeline/xpipeline"
1517
"go.opentelemetry.io/collector/processor"
1618
)
1719

@@ -50,11 +52,8 @@ func (p *memoryLimiterProcessor) shutdown(ctx context.Context) error {
5052
func (p *memoryLimiterProcessor) processTraces(ctx context.Context, td ptrace.Traces) (ptrace.Traces, error) {
5153
numSpans := td.SpanCount()
5254
if p.memlimiter.MustRefuse() {
53-
// TODO: actually to be 100% sure that this is "refused" and not "dropped"
54-
// it is necessary to check the pipeline to see if this is directly connected
55-
// to a receiver (ie.: a receiver is on the call stack). For now it
56-
// assumes that the pipeline is properly configured and a receiver is on the
57-
// callstack and that the receiver will correctly retry the refused data again.
55+
// TODO:
56+
// https://github.com/open-telemetry/opentelemetry-collector/issues/12463
5857
p.obsrep.refused(ctx, numSpans, pipeline.SignalTraces)
5958
return td, memorylimiter.ErrDataRefused
6059
}
@@ -68,11 +67,8 @@ func (p *memoryLimiterProcessor) processTraces(ctx context.Context, td ptrace.Tr
6867
func (p *memoryLimiterProcessor) processMetrics(ctx context.Context, md pmetric.Metrics) (pmetric.Metrics, error) {
6968
numDataPoints := md.DataPointCount()
7069
if p.memlimiter.MustRefuse() {
71-
// TODO: actually to be 100% sure that this is "refused" and not "dropped"
72-
// it is necessary to check the pipeline to see if this is directly connected
73-
// to a receiver (ie.: a receiver is on the call stack). For now it
74-
// assumes that the pipeline is properly configured and a receiver is on the
75-
// callstack.
70+
// TODO:
71+
// https://github.com/open-telemetry/opentelemetry-collector/issues/12463
7672
p.obsrep.refused(ctx, numDataPoints, pipeline.SignalMetrics)
7773
return md, memorylimiter.ErrDataRefused
7874
}
@@ -86,11 +82,8 @@ func (p *memoryLimiterProcessor) processMetrics(ctx context.Context, md pmetric.
8682
func (p *memoryLimiterProcessor) processLogs(ctx context.Context, ld plog.Logs) (plog.Logs, error) {
8783
numRecords := ld.LogRecordCount()
8884
if p.memlimiter.MustRefuse() {
89-
// TODO: actually to be 100% sure that this is "refused" and not "dropped"
90-
// it is necessary to check the pipeline to see if this is directly connected
91-
// to a receiver (ie.: a receiver is on the call stack). For now it
92-
// assumes that the pipeline is properly configured and a receiver is on the
93-
// callstack.
85+
// TODO:
86+
// https://github.com/open-telemetry/opentelemetry-collector/issues/12463
9487
p.obsrep.refused(ctx, numRecords, pipeline.SignalLogs)
9588
return ld, memorylimiter.ErrDataRefused
9689
}
@@ -100,3 +93,18 @@ func (p *memoryLimiterProcessor) processLogs(ctx context.Context, ld plog.Logs)
10093
p.obsrep.accepted(ctx, numRecords, pipeline.SignalLogs)
10194
return ld, nil
10295
}
96+
97+
func (p *memoryLimiterProcessor) processProfiles(ctx context.Context, td pprofile.Profiles) (pprofile.Profiles, error) {
98+
numProfiles := td.SampleCount()
99+
if p.memlimiter.MustRefuse() {
100+
// TODO:
101+
// https://github.com/open-telemetry/opentelemetry-collector/issues/12463
102+
p.obsrep.refused(ctx, numProfiles, xpipeline.SignalProfiles)
103+
return td, memorylimiter.ErrDataRefused
104+
}
105+
106+
// Even if the next consumer returns error record the data as accepted by
107+
// this processor.
108+
p.obsrep.accepted(ctx, numProfiles, xpipeline.SignalProfiles)
109+
return td, nil
110+
}

processor/memorylimiterprocessor/memorylimiter_test.go

+92
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@ import (
2222
"go.opentelemetry.io/collector/internal/memorylimiter/iruntime"
2323
"go.opentelemetry.io/collector/pdata/plog"
2424
"go.opentelemetry.io/collector/pdata/pmetric"
25+
"go.opentelemetry.io/collector/pdata/pprofile"
2526
"go.opentelemetry.io/collector/pdata/ptrace"
2627
"go.opentelemetry.io/collector/processor"
2728
"go.opentelemetry.io/collector/processor/memorylimiterprocessor/internal"
2829
"go.opentelemetry.io/collector/processor/memorylimiterprocessor/internal/metadata"
2930
"go.opentelemetry.io/collector/processor/memorylimiterprocessor/internal/metadatatest"
3031
"go.opentelemetry.io/collector/processor/processorhelper"
32+
"go.opentelemetry.io/collector/processor/processorhelper/xprocessorhelper"
3133
"go.opentelemetry.io/collector/processor/processortest"
3234
)
3335

@@ -419,6 +421,96 @@ func TestLogMemoryPressureResponse(t *testing.T) {
419421
})
420422
}
421423

424+
// TestProfileMemoryPressureResponse manipulates results from querying memory and
425+
// check expected side effects.
426+
func TestProfileMemoryPressureResponse(t *testing.T) {
427+
pd := pprofile.NewProfiles()
428+
ctx := context.Background()
429+
430+
tests := []struct {
431+
name string
432+
mlCfg *Config
433+
memAlloc uint64
434+
expectError bool
435+
}{
436+
{
437+
name: "Below memAllocLimit",
438+
mlCfg: &Config{
439+
CheckInterval: time.Second,
440+
MemoryLimitPercentage: 50,
441+
MemorySpikePercentage: 1,
442+
},
443+
memAlloc: 800,
444+
expectError: false,
445+
},
446+
{
447+
name: "Above memAllocLimit",
448+
mlCfg: &Config{
449+
CheckInterval: time.Second,
450+
MemoryLimitPercentage: 50,
451+
MemorySpikePercentage: 1,
452+
},
453+
memAlloc: 1800,
454+
expectError: true,
455+
},
456+
{
457+
name: "Below memSpikeLimit",
458+
mlCfg: &Config{
459+
CheckInterval: time.Second,
460+
MemoryLimitPercentage: 50,
461+
MemorySpikePercentage: 10,
462+
},
463+
memAlloc: 800,
464+
expectError: false,
465+
},
466+
{
467+
name: "Above memSpikeLimit",
468+
mlCfg: &Config{
469+
CheckInterval: time.Second,
470+
MemoryLimitPercentage: 50,
471+
MemorySpikePercentage: 11,
472+
},
473+
memAlloc: 800,
474+
expectError: true,
475+
},
476+
}
477+
for _, tt := range tests {
478+
t.Run(tt.name, func(t *testing.T) {
479+
memorylimiter.GetMemoryFn = totalMemory
480+
memorylimiter.ReadMemStatsFn = func(ms *runtime.MemStats) {
481+
ms.Alloc = tt.memAlloc
482+
}
483+
484+
ml, err := newMemoryLimiterProcessor(processortest.NewNopSettings(metadata.Type), tt.mlCfg)
485+
require.NoError(t, err)
486+
tp, err := xprocessorhelper.NewProfiles(
487+
context.Background(),
488+
processortest.NewNopSettings(metadata.Type),
489+
tt.mlCfg,
490+
consumertest.NewNop(),
491+
ml.processProfiles,
492+
xprocessorhelper.WithCapabilities(processorCapabilities),
493+
xprocessorhelper.WithStart(ml.start),
494+
xprocessorhelper.WithShutdown(ml.shutdown))
495+
require.NoError(t, err)
496+
497+
assert.NoError(t, tp.Start(ctx, &host{}))
498+
ml.memlimiter.CheckMemLimits()
499+
err = tp.ConsumeProfiles(ctx, pd)
500+
if tt.expectError {
501+
assert.Equal(t, memorylimiter.ErrDataRefused, err)
502+
} else {
503+
require.NoError(t, err)
504+
}
505+
assert.NoError(t, tp.Shutdown(ctx))
506+
})
507+
}
508+
t.Cleanup(func() {
509+
memorylimiter.GetMemoryFn = iruntime.TotalMemory
510+
memorylimiter.ReadMemStatsFn = runtime.ReadMemStats
511+
})
512+
}
513+
422514
type host struct {
423515
component.Host
424516
}

0 commit comments

Comments
 (0)