Skip to content

feat(monitoring): Give each server its own storage alert rule - #7444

Open
huzan-kazi wants to merge 1 commit into
developfrom
feat-per-server-storage-alert-rules
Open

feat(monitoring): Give each server its own storage alert rule#7444
huzan-kazi wants to merge 1 commit into
developfrom
feat-per-server-storage-alert-rules

Conversation

@huzan-kazi

Copy link
Copy Markdown
Contributor

Why

#7405 gave teams a storage alert threshold, and split the disk alert into one Prometheus rule per distinct threshold — generated in memory by the shared rule. Servers that picked the same threshold shared a rule, and none of the split was visible anywhere: nothing in Desk to inspect, disable, or reason about for a single server.

Press already has a convention for this. The autoscale triggers create a real Prometheus Alert Rule document per server, named Auto Scale Up Trigger - f929-mumbai.frappe.cloud. Storage alerts should look the same.

What

A rule document per overriding server, named <base rule> - <server>:

Disk Space Low                          instance!~"f-0003|f-0002|f-0001"  > 90
Disk Space Low - f-0001.fc.dev          instance=~"f-0001"                > 80
Disk Space Low - f-0002.fc.dev          instance=~"f-0002"                > 80
Disk Space Low - f-0003.fc.dev          instance=~"f-0003"                > 75

Each inherits severity, for, group timings, labels, annotations and press_job_type from the shared rule, with its own threshold baked into the expression. Only servers that override the default get one; everyone else stays on the shared rule, which keeps {{ instances }} and now renders only the exclusion — so an overriding server never alerts twice.

How

sync_storage_alert_rules() upserts the documents and deletes the ones no longer needed. It runs from the Server/Database Server doc event as before, and additionally from the shared rule's own on_update, so editing the shared expression propagates instead of leaving the per-server rules stale.

Reactions still resolve: react_for_instance looks the firing alertname up as a Prometheus Alert Rule document, and each per-server document carries the inherited press_job_type. There's a test pinning that, since it's the part that quietly breaks if the naming drifts.

A frappe.flags.syncing_storage_alert_rules guard stops each child save from firing its own push to the monitor server; the shared rule pushes once for the whole sync.

Review notes

  • Migration: servers that already set a threshold get their documents the first time the Disk Space Low rule is saved after deploy, via on_update. No patch, but nothing happens until someone saves that rule.
  • Archived servers: servers_by_storage_alert_threshold() filters on status = "Active", so a stale rule is cleaned up on the next sync — but archiving doesn't itself trigger one, since the threshold didn't change. The leftover rule matches an instance Prometheus no longer scrapes, so it never fires. Left alone rather than adding a second doc event; happy to hook status if reviewers prefer.
  • Scaling: one document per overriding server rather than one per distinct threshold. The rules file grows with the number of teams that override, not with the number of servers.
  • Tests: 16 alert-rule (7 new), plus the 54 server tests unchanged and passing.

🤖 Generated with Claude Code

Storage thresholds were split into one rule per distinct threshold,
generated in memory by the shared rule. Servers sharing a threshold
shared a rule, and none of it was visible in Desk to inspect or disable
for a single server.

Follow the convention the autoscale triggers already set: a real
Prometheus Alert Rule document per overriding server, named
`<base rule> - <server>`, inheriting severity, timings, labels and press
job type from the shared rule. Reactions still resolve, because
react_for_instance looks the firing alertname up as a rule document.

The shared rule keeps `{{ instances }}` and now renders only the
exclusion, so an overriding server never alerts twice. Syncing also runs
on the shared rule's on_update, so editing its expression propagates
instead of leaving the per-server rules stale.

Follow-up to #7405.
@greptile-apps

greptile-apps Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 2/5

The PR is unsafe to merge until stale-rule cleanup, initial backfill, and ownership-safe deletion are addressed.

Reviews (1) · Last reviewed commit: "feat(monitoring): Give each server its o..."


frappe.flags.syncing_storage_alert_rules = True
try:
for server, threshold in overrides.items():

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.

P1 Cleanup Skips Empty Overrides
Cleanup runs inside the override loop. When the last server returns to the default threshold, the loop does not run, so its obsolete per-server rule remains enabled and can produce duplicate alerts at the old threshold. Run stale-rule cleanup after the loop.

Knowledge Base Used: Monitoring and telemetry

Prompt To Fix With AI
This is a comment left during a code review.
Path: press/press/doctype/prometheus_alert_rule/prometheus_alert_rule.py
Line: 291

Comment:
**Cleanup Skips Empty Overrides**
Cleanup runs inside the override loop. When the last server returns to the default threshold, the loop does not run, so its obsolete per-server rule remains enabled and can produce duplicate alerts at the old threshold. Run stale-rule cleanup after the loop.

**Knowledge Base Used:** [Monitoring and telemetry](https://app.greptile.com/frappe/-/custom-context/knowledge-base/frappe/press/-/docs/monitoring-and-telemetry.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines 103 to +105

overrides = servers_by_storage_alert_threshold()
overridden_servers = [server for servers in overrides.values() for server in servers]

rules = [self.get_rule_for_threshold(DEFAULT_STORAGE_ALERT_THRESHOLD, overridden_servers, True)]
rules.extend(
self.get_rule_for_threshold(threshold, servers, False) for threshold, servers in overrides.items()
)
return rules
overriding_servers = list(servers_by_storage_alert_threshold())
return [

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.

P1 Existing Overrides Lose Alerts
Existing servers with custom thresholds have no generated rule documents immediately after deployment. Any alert-rule save can then publish the shared rule with those servers excluded, leaving them without storage alerts until the base rule or a server threshold is changed. Add an idempotent backfill before publishing these exclusions.

Knowledge Base Used: Monitoring and telemetry

Prompt To Fix With AI
This is a comment left during a code review.
Path: press/press/doctype/prometheus_alert_rule/prometheus_alert_rule.py
Line: 103-105

Comment:
**Existing Overrides Lose Alerts**
Existing servers with custom thresholds have no generated rule documents immediately after deployment. Any alert-rule save can then publish the shared rule with those servers excluded, leaving them without storage alerts until the base rule or a server threshold is changed. Add an idempotent backfill before publishing these exclusions.

**Knowledge Base Used:** [Monitoring and telemetry](https://app.greptile.com/frappe/-/custom-context/knowledge-base/frappe/press/-/docs/monitoring-and-telemetry.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines +327 to +330
prefix = storage_alert_rule_name(base_rule.name, "")
for name in frappe.get_all("Prometheus Alert Rule", {"name": ("like", f"{prefix}%")}, pluck="name"):
if name[len(prefix) :] not in overriding_servers:
frappe.delete_doc("Prometheus Alert Rule", name)

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.

P1 Cleanup Deletes Unrelated Rules
Alert-rule names are user-defined, but cleanup deletes every rule whose name begins with <base> - when its suffix is not an overriding server. An unrelated rule using that prefix can therefore be deleted, removing its monitoring coverage. Mark generated children explicitly and restrict cleanup to those records.

Knowledge Base Used: Operations and reliability

Prompt To Fix With AI
This is a comment left during a code review.
Path: press/press/doctype/prometheus_alert_rule/prometheus_alert_rule.py
Line: 327-330

Comment:
**Cleanup Deletes Unrelated Rules**
Alert-rule names are user-defined, but cleanup deletes every rule whose name begins with `<base> - ` when its suffix is not an overriding server. An unrelated rule using that prefix can therefore be deleted, removing its monitoring coverage. Mark generated children explicitly and restrict cleanup to those records.

**Knowledge Base Used:** [Operations and reliability](https://app.greptile.com/frappe/-/custom-context/knowledge-base/frappe/press/-/docs/operations-and-reliability.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

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.

1 participant