Skip to content

Commit a35678d

Browse files
committed
[processor/memory_limiter] Update config validation errors
- Fix names of the config fields that are validated in the error messages - Move the validation from start to the initialization phrase
1 parent b0f618e commit a35678d

File tree

6 files changed

+128
-130
lines changed

6 files changed

+128
-130
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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: processor/memory_limiter
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Update config validation errors
11+
12+
# One or more tracking issues or pull requests related to the change
13+
issues: []
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+
- Fix names of the config fields that are validated in the error messages
20+
- Move the validation from start to the initialization phrase
21+
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: [user]

processor/memorylimiterprocessor/config.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,20 @@ var _ component.Config = (*Config)(nil)
3939

4040
// Validate checks if the processor configuration is valid
4141
func (cfg *Config) Validate() error {
42+
if cfg.CheckInterval <= 0 {
43+
return errCheckIntervalOutOfRange
44+
}
45+
if cfg.MemoryLimitMiB == 0 && cfg.MemoryLimitPercentage == 0 {
46+
return errLimitOutOfRange
47+
}
48+
if cfg.MemoryLimitPercentage < 0 || cfg.MemoryLimitPercentage > 100 || cfg.MemorySpikePercentage < 0 || cfg.MemorySpikePercentage > 100 {
49+
return errPercentageLimitOutOfRange
50+
}
51+
if cfg.MemoryLimitMiB > 0 && cfg.MemoryLimitMiB <= cfg.MemorySpikeLimitMiB {
52+
return errMemSpikeLimitOutOfRange
53+
}
54+
if cfg.MemoryLimitPercentage > 0 && cfg.MemoryLimitPercentage <= cfg.MemorySpikePercentage {
55+
return errMemSpikePercentageLimitOutOfRange
56+
}
4257
return nil
4358
}

processor/memorylimiterprocessor/config_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,71 @@ func TestUnmarshalConfig(t *testing.T) {
3636
MemorySpikeLimitMiB: 500,
3737
}, cfg)
3838
}
39+
40+
func TestConfigValidate(t *testing.T) {
41+
tests := []struct {
42+
name string
43+
cfg *Config
44+
err error
45+
}{
46+
{
47+
name: "valid",
48+
cfg: func() *Config {
49+
cfg := createDefaultConfig().(*Config)
50+
cfg.MemoryLimitMiB = 5722
51+
cfg.MemorySpikeLimitMiB = 1907
52+
cfg.CheckInterval = 100 * time.Millisecond
53+
return cfg
54+
}(),
55+
err: nil,
56+
},
57+
{
58+
name: "zero check interval",
59+
cfg: &Config{
60+
CheckInterval: 0,
61+
},
62+
err: errCheckIntervalOutOfRange,
63+
},
64+
{
65+
name: "unset memory limit",
66+
cfg: &Config{
67+
CheckInterval: 1 * time.Second,
68+
MemoryLimitMiB: 0,
69+
MemoryLimitPercentage: 0,
70+
},
71+
err: errLimitOutOfRange,
72+
},
73+
{
74+
name: "invalid memory spike limit",
75+
cfg: &Config{
76+
CheckInterval: 1 * time.Second,
77+
MemoryLimitMiB: 10,
78+
MemorySpikeLimitMiB: 15,
79+
},
80+
err: errMemSpikeLimitOutOfRange,
81+
},
82+
{
83+
name: "invalid memory percentage limit",
84+
cfg: &Config{
85+
CheckInterval: 1 * time.Second,
86+
MemoryLimitPercentage: 101,
87+
},
88+
err: errPercentageLimitOutOfRange,
89+
},
90+
{
91+
name: "invalid memory spike percentage limit",
92+
cfg: &Config{
93+
CheckInterval: 1 * time.Second,
94+
MemoryLimitPercentage: 50,
95+
MemorySpikePercentage: 60,
96+
},
97+
err: errMemSpikePercentageLimitOutOfRange,
98+
},
99+
}
100+
for _, tt := range tests {
101+
t.Run(tt.name, func(t *testing.T) {
102+
err := tt.cfg.Validate()
103+
assert.Equal(t, tt.err, err)
104+
})
105+
}
106+
}

processor/memorylimiterprocessor/factory_test.go

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,38 +31,25 @@ func TestCreateProcessor(t *testing.T) {
3131

3232
cfg := factory.CreateDefaultConfig()
3333

34-
// This processor can't be created with the default config.
35-
tp, err := factory.CreateTracesProcessor(context.Background(), processortest.NewNopCreateSettings(), cfg, consumertest.NewNop())
36-
assert.Nil(t, tp)
37-
assert.Error(t, err, "created processor with invalid settings")
38-
39-
mp, err := factory.CreateMetricsProcessor(context.Background(), processortest.NewNopCreateSettings(), cfg, consumertest.NewNop())
40-
assert.Nil(t, mp)
41-
assert.Error(t, err, "created processor with invalid settings")
42-
43-
lp, err := factory.CreateLogsProcessor(context.Background(), processortest.NewNopCreateSettings(), cfg, consumertest.NewNop())
44-
assert.Nil(t, lp)
45-
assert.Error(t, err, "created processor with invalid settings")
46-
4734
// Create processor with a valid config.
4835
pCfg := cfg.(*Config)
4936
pCfg.MemoryLimitMiB = 5722
5037
pCfg.MemorySpikeLimitMiB = 1907
5138
pCfg.CheckInterval = 100 * time.Millisecond
5239

53-
tp, err = factory.CreateTracesProcessor(context.Background(), processortest.NewNopCreateSettings(), cfg, consumertest.NewNop())
40+
tp, err := factory.CreateTracesProcessor(context.Background(), processortest.NewNopCreateSettings(), cfg, consumertest.NewNop())
5441
assert.NoError(t, err)
5542
assert.NotNil(t, tp)
5643
// test if we can shutdown a monitoring routine that has not started
5744
assert.ErrorIs(t, tp.Shutdown(context.Background()), errShutdownNotStarted)
5845
assert.NoError(t, tp.Start(context.Background(), componenttest.NewNopHost()))
5946

60-
mp, err = factory.CreateMetricsProcessor(context.Background(), processortest.NewNopCreateSettings(), cfg, consumertest.NewNop())
47+
mp, err := factory.CreateMetricsProcessor(context.Background(), processortest.NewNopCreateSettings(), cfg, consumertest.NewNop())
6148
assert.NoError(t, err)
6249
assert.NotNil(t, mp)
6350
assert.NoError(t, mp.Start(context.Background(), componenttest.NewNopHost()))
6451

65-
lp, err = factory.CreateLogsProcessor(context.Background(), processortest.NewNopCreateSettings(), cfg, consumertest.NewNop())
52+
lp, err := factory.CreateLogsProcessor(context.Background(), processortest.NewNopCreateSettings(), cfg, consumertest.NewNop())
6653
assert.NoError(t, err)
6754
assert.NotNil(t, lp)
6855
assert.NoError(t, lp.Start(context.Background(), componenttest.NewNopHost()))

processor/memorylimiterprocessor/memorylimiter.go

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,16 @@ var (
3434

3535
// Construction errors
3636

37-
errCheckIntervalOutOfRange = errors.New(
38-
"checkInterval must be greater than zero")
37+
errCheckIntervalOutOfRange = errors.New("check_interval must be greater than zero")
3938

40-
errLimitOutOfRange = errors.New(
41-
"memAllocLimit or memoryLimitPercentage must be greater than zero")
39+
errLimitOutOfRange = errors.New("limit_mib or limit_percentage must be greater than zero")
4240

43-
errMemSpikeLimitOutOfRange = errors.New(
44-
"memSpikeLimit must be smaller than memAllocLimit")
41+
errMemSpikeLimitOutOfRange = errors.New("spike_limit_mib must be smaller than limit_mib")
42+
43+
errMemSpikePercentageLimitOutOfRange = errors.New("spike_limit_percentage must be smaller than limit_percentage")
4544

4645
errPercentageLimitOutOfRange = errors.New(
47-
"memoryLimitPercentage and memorySpikePercentage must be greater than zero and less than or equal to hundred",
48-
)
46+
"limit_percentage and spike_limit_percentage must be greater than zero and less than or equal to hundred")
4947

5048
errShutdownNotStarted = errors.New("no existing monitoring routine is running")
5149
)
@@ -86,13 +84,6 @@ const minGCIntervalWhenSoftLimited = 10 * time.Second
8684

8785
// newMemoryLimiter returns a new memorylimiter processor.
8886
func newMemoryLimiter(set processor.CreateSettings, cfg *Config) (*memoryLimiter, error) {
89-
if cfg.CheckInterval <= 0 {
90-
return nil, errCheckIntervalOutOfRange
91-
}
92-
if cfg.MemoryLimitMiB == 0 && cfg.MemoryLimitPercentage == 0 {
93-
return nil, errLimitOutOfRange
94-
}
95-
9687
logger := set.Logger
9788
usageChecker, err := getMemUsageChecker(cfg, logger)
9889
if err != nil {
@@ -129,7 +120,7 @@ func getMemUsageChecker(cfg *Config, logger *zap.Logger) (*memUsageChecker, erro
129120
memAllocLimit := uint64(cfg.MemoryLimitMiB) * mibBytes
130121
memSpikeLimit := uint64(cfg.MemorySpikeLimitMiB) * mibBytes
131122
if cfg.MemoryLimitMiB != 0 {
132-
return newFixedMemUsageChecker(memAllocLimit, memSpikeLimit)
123+
return newFixedMemUsageChecker(memAllocLimit, memSpikeLimit), nil
133124
}
134125
totalMemory, err := getMemoryFn()
135126
if err != nil {
@@ -139,7 +130,8 @@ func getMemUsageChecker(cfg *Config, logger *zap.Logger) (*memUsageChecker, erro
139130
zap.Uint64("total_memory_mib", totalMemory/mibBytes),
140131
zap.Uint32("limit_percentage", cfg.MemoryLimitPercentage),
141132
zap.Uint32("spike_limit_percentage", cfg.MemorySpikePercentage))
142-
return newPercentageMemUsageChecker(totalMemory, uint64(cfg.MemoryLimitPercentage), uint64(cfg.MemorySpikePercentage))
133+
return newPercentageMemUsageChecker(totalMemory, uint64(cfg.MemoryLimitPercentage),
134+
uint64(cfg.MemorySpikePercentage)), nil
143135
}
144136

145137
func (ml *memoryLimiter) start(_ context.Context, host component.Host) error {
@@ -319,23 +311,17 @@ func (d memUsageChecker) aboveHardLimit(ms *runtime.MemStats) bool {
319311
return ms.Alloc >= d.memAllocLimit
320312
}
321313

322-
func newFixedMemUsageChecker(memAllocLimit, memSpikeLimit uint64) (*memUsageChecker, error) {
323-
if memSpikeLimit >= memAllocLimit {
324-
return nil, errMemSpikeLimitOutOfRange
325-
}
314+
func newFixedMemUsageChecker(memAllocLimit, memSpikeLimit uint64) *memUsageChecker {
326315
if memSpikeLimit == 0 {
327316
// If spike limit is unspecified use 20% of mem limit.
328317
memSpikeLimit = memAllocLimit / 5
329318
}
330319
return &memUsageChecker{
331320
memAllocLimit: memAllocLimit,
332321
memSpikeLimit: memSpikeLimit,
333-
}, nil
322+
}
334323
}
335324

336-
func newPercentageMemUsageChecker(totalMemory uint64, percentageLimit, percentageSpike uint64) (*memUsageChecker, error) {
337-
if percentageLimit > 100 || percentageLimit <= 0 || percentageSpike > 100 || percentageSpike <= 0 {
338-
return nil, errPercentageLimitOutOfRange
339-
}
325+
func newPercentageMemUsageChecker(totalMemory uint64, percentageLimit, percentageSpike uint64) *memUsageChecker {
340326
return newFixedMemUsageChecker(percentageLimit*totalMemory/100, percentageSpike*totalMemory/100)
341327
}

processor/memorylimiterprocessor/memorylimiter_test.go

Lines changed: 3 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import (
1717
"go.opentelemetry.io/collector/component"
1818
"go.opentelemetry.io/collector/component/componenttest"
1919
"go.opentelemetry.io/collector/config/configtelemetry"
20-
"go.opentelemetry.io/collector/consumer"
2120
"go.opentelemetry.io/collector/consumer/consumertest"
2221
"go.opentelemetry.io/collector/internal/iruntime"
2322
"go.opentelemetry.io/collector/pdata/plog"
@@ -29,71 +28,6 @@ import (
2928
"go.opentelemetry.io/collector/processor/processortest"
3029
)
3130

32-
func TestNew(t *testing.T) {
33-
type args struct {
34-
nextConsumer consumer.Traces
35-
checkInterval time.Duration
36-
memoryLimitMiB uint32
37-
memorySpikeLimitMiB uint32
38-
}
39-
sink := new(consumertest.TracesSink)
40-
tests := []struct {
41-
name string
42-
args args
43-
wantErr error
44-
}{
45-
{
46-
name: "zero_checkInterval",
47-
args: args{
48-
nextConsumer: sink,
49-
},
50-
wantErr: errCheckIntervalOutOfRange,
51-
},
52-
{
53-
name: "zero_memAllocLimit",
54-
args: args{
55-
nextConsumer: sink,
56-
checkInterval: 100 * time.Millisecond,
57-
},
58-
wantErr: errLimitOutOfRange,
59-
},
60-
{
61-
name: "memSpikeLimit_gt_memAllocLimit",
62-
args: args{
63-
nextConsumer: sink,
64-
checkInterval: 100 * time.Millisecond,
65-
memoryLimitMiB: 1,
66-
memorySpikeLimitMiB: 2,
67-
},
68-
wantErr: errMemSpikeLimitOutOfRange,
69-
},
70-
{
71-
name: "success",
72-
args: args{
73-
nextConsumer: sink,
74-
checkInterval: 100 * time.Millisecond,
75-
memoryLimitMiB: 1024,
76-
},
77-
},
78-
}
79-
for _, tt := range tests {
80-
t.Run(tt.name, func(t *testing.T) {
81-
cfg := createDefaultConfig().(*Config)
82-
cfg.CheckInterval = tt.args.checkInterval
83-
cfg.MemoryLimitMiB = tt.args.memoryLimitMiB
84-
cfg.MemorySpikeLimitMiB = tt.args.memorySpikeLimitMiB
85-
got, err := newMemoryLimiter(processortest.NewNopCreateSettings(), cfg)
86-
if tt.wantErr != nil {
87-
assert.ErrorIs(t, err, tt.wantErr)
88-
return
89-
}
90-
assert.NoError(t, err)
91-
assert.NoError(t, got.start(context.Background(), componenttest.NewNopHost()))
92-
assert.NoError(t, got.shutdown(context.Background()))
93-
})
94-
}
95-
}
96-
9731
// TestMetricsMemoryPressureResponse manipulates results from querying memory and
9832
// check expected side effects.
9933
func TestMetricsMemoryPressureResponse(t *testing.T) {
@@ -309,11 +243,6 @@ func TestGetDecision(t *testing.T) {
309243
memSpikeLimit: 20 * mibBytes,
310244
}, d)
311245
})
312-
t.Run("fixed_limit_error", func(t *testing.T) {
313-
d, err := getMemUsageChecker(&Config{MemoryLimitMiB: 20, MemorySpikeLimitMiB: 100}, zap.NewNop())
314-
require.Error(t, err)
315-
assert.Nil(t, d)
316-
})
317246

318247
t.Cleanup(func() {
319248
getMemoryFn = iruntime.TotalMemory
@@ -329,26 +258,12 @@ func TestGetDecision(t *testing.T) {
329258
memSpikeLimit: 10 * mibBytes,
330259
}, d)
331260
})
332-
t.Run("percentage_limit_error", func(t *testing.T) {
333-
d, err := getMemUsageChecker(&Config{MemoryLimitPercentage: 101, MemorySpikePercentage: 10}, zap.NewNop())
334-
require.Error(t, err)
335-
assert.Nil(t, d)
336-
d, err = getMemUsageChecker(&Config{MemoryLimitPercentage: 99, MemorySpikePercentage: 101}, zap.NewNop())
337-
require.Error(t, err)
338-
assert.Nil(t, d)
339-
})
340261
}
341262

342263
func TestRefuseDecision(t *testing.T) {
343-
decison1000Limit30Spike30, err := newPercentageMemUsageChecker(1000, 60, 30)
344-
require.NoError(t, err)
345-
decison1000Limit60Spike50, err := newPercentageMemUsageChecker(1000, 60, 50)
346-
require.NoError(t, err)
347-
decison1000Limit40Spike20, err := newPercentageMemUsageChecker(1000, 40, 20)
348-
require.NoError(t, err)
349-
decison1000Limit40Spike60, err := newPercentageMemUsageChecker(1000, 40, 60)
350-
require.Error(t, err)
351-
assert.Nil(t, decison1000Limit40Spike60)
264+
decison1000Limit30Spike30 := newPercentageMemUsageChecker(1000, 60, 30)
265+
decison1000Limit60Spike50 := newPercentageMemUsageChecker(1000, 60, 50)
266+
decison1000Limit40Spike20 := newPercentageMemUsageChecker(1000, 40, 20)
352267

353268
tests := []struct {
354269
name string

0 commit comments

Comments
 (0)