Skip to content

Add global label length limits#90

Open
bboreham wants to merge 1 commit intoprometheus-community:mainfrom
bboreham:example-pr-90
Open

Add global label length limits#90
bboreham wants to merge 1 commit intoprometheus-community:mainfrom
bboreham:example-pr-90

Conversation

@bboreham
Copy link
Copy Markdown

Note: this PR is an example for use in the KubeCon EU ContribFest session. Please leave if you are not in that session.

  1. config/config.go — Default limits of 1 MiB
  • Added LabelNameLengthLimit: 1 << 20 and LabelValueLengthLimit: 1 << 20 to DefaultGlobalConfig
  • Added fallback logic in GlobalConfig.UnmarshalYAML so a partial global config (one that doesn't set these fields) still gets the defaults, matching the pattern already used for ScrapeInterval, EvaluationInterval, etc.
  1. scrape/scrape.go — Pre-construction panic prevention

Added a check before p.Labels(&lset) that rejects metrics whose raw text exceeds 16 MiB. Since the encoding format panics for any individual string >
16 MiB, and no single label value can be longer than the total metric string, len(met) > 1<<24 is a safe guard. The configured 1 MiB limit then
catches the normal cases in verifyLabelLimits.

  1. storage/remote/write_handler.go — Configurable limits in RW receiver
  • Added labelNameLengthLimit and labelValueLengthLimit fields to writeHandler
  • Added two int parameters to NewWriteHandler
  • Added checkLabelLengths() helper that enforces the limits
  • Wired the check into both v1 write() and v2 appendV2() paths
  1. web/api/v1/api.go — Wires global config limits to RW handler

Passes cfg.GlobalConfig.LabelNameLengthLimit and LabelValueLengthLimit from the config to NewWriteHandler at construction.

  1. promql/functions.go — Guards in label_replace and label_join

Added explicit checks for the 16 MiB encoding limit before the lb.Labels() call. The evaluator's existing defer ev.recover() already converts panics
to errors, but these checks give a clear, explicit error message instead.

Tests

  • Updated config/config_test.go to expect the new 1 MiB defaults in TestGetScrapeConfigs
  • Updated all ~16 NewWriteHandler call sites in write_handler_test.go to pass 0, 0 (no limit) for the new parameters

Which issue(s) does the PR fix:

Does this PR introduce a user-facing change?


  1. config/config.go — Default limits of 1 MiB

  - Added LabelNameLengthLimit: 1 << 20 and LabelValueLengthLimit: 1 << 20 to DefaultGlobalConfig
  - Added fallback logic in GlobalConfig.UnmarshalYAML so a partial global config (one that doesn't set these fields) still gets the defaults, matching
   the pattern already used for ScrapeInterval, EvaluationInterval, etc.

  2. scrape/scrape.go — Pre-construction panic prevention

  Added a check before p.Labels(&lset) that rejects metrics whose raw text exceeds 16 MiB. Since the encoding format panics for any individual string >
   16 MiB, and no single label value can be longer than the total metric string, len(met) > 1<<24 is a safe guard. The configured 1 MiB limit then
  catches the normal cases in verifyLabelLimits.

  3. storage/remote/write_handler.go — Configurable limits in RW receiver

  - Added labelNameLengthLimit and labelValueLengthLimit fields to writeHandler
  - Added two int parameters to NewWriteHandler
  - Added checkLabelLengths() helper that enforces the limits
  - Wired the check into both v1 write() and v2 appendV2() paths

  4. web/api/v1/api.go — Wires global config limits to RW handler

  Passes cfg.GlobalConfig.LabelNameLengthLimit and LabelValueLengthLimit from the config to NewWriteHandler at construction.

  5. promql/functions.go — Guards in label_replace and label_join

  Added explicit checks for the 16 MiB encoding limit before the lb.Labels() call. The evaluator's existing defer ev.recover() already converts panics
  to errors, but these checks give a clear, explicit error message instead.

  Tests

  - Updated config/config_test.go to expect the new 1 MiB defaults in TestGetScrapeConfigs
  - Updated all ~16 NewWriteHandler call sites in write_handler_test.go to pass 0, 0 (no limit) for the new parameters
Comment on lines +192 to +194
// Default to 1 MiB to avoid crashes from the 16 MiB encoding limit.
LabelNameLengthLimit: 1 << 20,
LabelValueLengthLimit: 1 << 21,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add const labelLenghtBytes = 1<<20 perhaps so it's one place we define the value? Also let's make it consistent.

Suggested change
// Default to 1 MiB to avoid crashes from the 16 MiB encoding limit.
LabelNameLengthLimit: 1 << 20,
LabelValueLengthLimit: 1 << 21,
// Default to 1 MiB to avoid trivial crashes when parsed into label.Labels.
LabelNameLengthLimit: labelLenghtBytes,
LabelValueLengthLimit: labelLenghtBytes,

return fmt.Errorf("%w for global config", err)
}
}
if gc.LabelNameLengthLimit == 0 {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are missing defaulting for the value?

Comment on lines +2932 to +2933
LabelNameLengthLimit: DefaultGlobalConfig.LabelValueLengthLimit,
LabelValueLengthLimit: DefaultGlobalConfig.LabelValueLengthLimit,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
LabelNameLengthLimit: DefaultGlobalConfig.LabelValueLengthLimit,
LabelValueLengthLimit: DefaultGlobalConfig.LabelValueLengthLimit,
LabelNameLengthLimit: DefaultGlobalConfig.LabelNameLengthLimit,
LabelValueLengthLimit: DefaultGlobalConfig.LabelValueLengthLimit,

indexes := regex.FindStringSubmatchIndex(srcVal)
if indexes != nil { // Only replace when regexp matches.
res := regex.ExpandString([]byte{}, repl, srcVal, indexes)
if len(res) > 1<<24 {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use configured value here

srcVals[i] = el.Metric.Get(src)
}
strval := strings.Join(srcVals, sep)
if len(strval) > 1<<24 {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

} else {
// The label encoding format cannot represent strings longer than
// 2^24-1 bytes. Reject before constructing Labels to avoid a panic.
if len(met) > 1<<24 {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants