Skip to content

feat(limit): drop resolved alerts without an admitted firing counterpart - #5381

Open
siavashs wants to merge 3 commits into
prometheus:mainfrom
siavashs:feat/limit-resolved-alerts
Open

feat(limit): drop resolved alerts without an admitted firing counterpart#5381
siavashs wants to merge 3 commits into
prometheus:mainfrom
siavashs:feat/limit-resolved-alerts

Conversation

@siavashs

Copy link
Copy Markdown
Contributor

Rework the per-alertname alert limit so resolved notifications no longer consume limit slots, and change how they are handled.

  • limit: add Bucket.Remove to free a tracked slot by value/fingerprint.
  • store: apply the per-alert limit only to firing alerts. A resolved notification is forwarded only if its firing counterpart was previously admitted (its fingerprint is still tracked); removing it frees the slot the firing alert was holding. Resolves with no admitted firing are dropped as noise, since nothing downstream ever received a firing for them from us.
  • provider/mem: add a state label (firing/resolved) to alertmanager_alerts_limited_total so dropped firing alerts and dropped resolved notifications can be distinguished.
  • docs: document the new resolved handling and metric label in alertmanager.md and configuration.md.
  • tests: add Bucket.Remove coverage (presence return, slot freeing, double remove, heap eviction-order integrity, concurrency) and store coverage for the drop-if-unseen resolved semantics.

Pull Request Checklist

Please check all the applicable boxes.

  • Please list all open issue(s) discussed with maintainers related to this change
    • Fixes #
  • Is this a new Receiver integration?
  • Is this a bugfix?
    • I have added tests that can reproduce the bug which pass with this bugfix applied
  • Is this a new feature?
    • I have added tests that test the new feature's functionality
  • Does this change affect performance?
    • I have provided benchmarks comparison that shows performance is improved or is not degraded
      • You can use benchstat to compare benchmarks
    • I have added new benchmarks if required or requested by maintainers
  • Is this a breaking change?
    • My changes do not break the existing cluster messages
    • My changes do not break the existing api
  • I have added/updated the required documentation
  • I have signed-off my commits
  • I will follow best practices for contributing to this project

Which user-facing changes does this PR introduce?

[CHANGE] limit: `alertmanager_alerts_limited_total` now has a `state` label (`firing`/`resolved`) to distinguish dropped firing alerts from dropped resolved notifications. Dashboards/alerts using this metric must be updated.
[CHANGE] limit: With `--alerts.per-alertname-limit` set, a resolved notification is only forwarded if its firing counterpart was previously admitted (which frees its slot); resolved notifications with no admitted firing alert are now dropped.

@siavashs
siavashs requested a review from a team as a code owner July 13, 2026 10:00
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: e80dbd33-76f1-46d8-b547-d654f905728e

📥 Commits

Reviewing files that changed from the base of the PR and between 0680192 and d1d7f39.

📒 Files selected for processing (6)
  • docs/alertmanager.md
  • docs/configuration.md
  • provider/mem/mem.go
  • provider/mem/mem_test.go
  • store/store.go
  • store/store_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/alertmanager.md
  • docs/configuration.md

Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The change adds removal support to per-alertname limit buckets, handles resolved alerts only when their firing alerts were admitted, migrates provider and store APIs to alert.Alert, labels limited-alert metrics by state, and updates tests and documentation.

Changes

Alert limits and type migration

Layer / File(s) Summary
Bucket removal primitive
limit/bucket.go, limit/bucket_test.go
Bucket.Remove deletes admitted values from the index and heap. Tests cover repeated, absent, capacity-freeing, eviction-order, and concurrent removal.
Alert type contract migration
provider/provider.go, provider/mem/mem.go, store/store.go, provider/mem/mem_test.go, store/store_test.go
Provider and store APIs, callbacks, internal state, and tests now use alert.Alert instead of types.Alert.
Resolved-alert limit handling
store/store.go, provider/mem/mem.go, store/store_test.go, docs/alertmanager.md, docs/configuration.md
Resolved alerts remove an admitted firing fingerprint and free its slot. Unmatched resolutions return a wrapped ErrLimited. Limited-alert metrics include a state label, and documentation describes the behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AlertInput
  participant store.Alerts
  participant limit.Bucket
  participant provider.mem
  AlertInput->>store.Alerts: Submit alert.Alert
  store.Alerts->>limit.Bucket: Admit firing or remove resolved fingerprint
  limit.Bucket-->>store.Alerts: Return limit result
  store.Alerts-->>provider.mem: Return stored alert or limitError
  provider.mem->>provider.mem: Record state-labeled limit metric
Loading

Merge Risk: ⚪ Minimal · up to d1d7f

The change limits firing alerts, forwards only matching resolutions, frees admitted slots, and labels limited-alert metrics by state. The supplied tests cover these behaviors, so no actionable merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.05% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 21 functions across 6 files. (2 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the limit-related behavior change: resolved alerts without an admitted firing counterpart are dropped. It is concise and follows the repository's area-prefixed title forma…
Description check ✅ Passed The description explains the behavior changes, implementation areas, tests, documentation, metric changes, checklist status, and release notes. It is complete and aligned with the pull request objecti…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 19.05% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 21 functions across 6 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread provider/mem/mem.go Outdated
Comment thread store/store.go
Comment thread limit/bucket.go
// If the bucket is full, oldest expired item is evicted based on priority and the new value is added.
// Otherwise the new value is ignored and the method returns false.
func (b *Bucket[V]) Upsert(value V, priority time.Time) (ok bool) {
if b.capacity < 1 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we need this check? Would there be a point for a capacity 0 bucket? Should we fail at NewBucket instead in that case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The bucket limiter was added as a generic implementation, zero here means drop all, we can change it in future to allow all.

@siavashs
siavashs requested a review from ultrotter July 21, 2026 09:25
@siavashs
siavashs force-pushed the feat/limit-resolved-alerts branch from d71ba20 to 74c0127 Compare August 17, 2026 09:17
Rework the per-alertname alert limit so resolved notifications no longer
consume limit slots, and change how they are handled.

- limit: add Bucket.Remove to free a tracked slot by value/fingerprint.
- store: apply the per-alert limit only to firing alerts. A resolved
  notification is forwarded only if its firing counterpart was previously
  admitted (its fingerprint is still tracked); removing it frees the slot
  the firing alert was holding. Resolves with no admitted firing are
  dropped as noise, since nothing downstream ever received a firing for
  them from us.
- provider/mem: add a `state` label (firing/resolved) to
  alertmanager_alerts_limited_total so dropped firing alerts and dropped
  resolved notifications can be distinguished.
- docs: document the new resolved handling and metric label in
  alertmanager.md and configuration.md.
- tests: add Bucket.Remove coverage (presence return, slot freeing,
  double remove, heap eviction-order integrity, concurrency) and store
  coverage for the drop-if-unseen resolved semantics.

Signed-off-by: Siavash Safi <siavash@cloudflare.com>
@siavashs
siavashs force-pushed the feat/limit-resolved-alerts branch from 74c0127 to 4d4ecc3 Compare August 17, 2026 11:37
Comment thread docs/alertmanager.md Outdated
Comment thread provider/mem/mem.go Outdated
Comment thread store/store.go
Resolve the release-note and alert-limit documentation conflicts against current main.

Signed-off-by: Siavash Safi <siavash@cloudflare.com>
Carry the store limit decision in the returned error so metric labels do not depend on a later time check. Use alert terminology for incoming resolved alerts.

Signed-off-by: Siavash Safi <siavash@cloudflare.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants