Skip to content

fix(observability): a degraded CNPG replica is invisible until it wedges the merge queueΒ #2978

Closed

Description

@devantler

πŸ€– Generated by the Agentic Engineer

Evidence

A degraded CNPG replica is currently invisible to alerting until it wedges the platform merge queue β€”
and then it is only visible as a Flux failure, 20 minutes at a time.

Measured on prod today (2026-08-05 ~17:1xZ):

Cluster desired ready Ready condition
backstage/backstage-db 2 2 True
observability/coroot-db 3 3 True
wedding-app/wedding-db 3 3 True
umami/umami-db 3 2 False since 12:03:42Z (5h 09m)

Nothing alerted on that 5-hour degradation. What eventually surfaced it was the apps Kustomization
health check timing out and evicting every PR from the merge queue (see the parent issue).

Why the current coverage misses it

docs/dr/alerting.md states the design explicitly: the four top-level Kustomizations wait: true on
their children, "so a failed controller / app / HelmRelease surfaces here as its parent going
NotReady". So today the only signal for a degraded database is the merge-queue gate.

That has two consequences:

  1. The alert fires ~20 minutes late, as a Flux error naming a Kustomization rather than a database.
  2. It cannot be fixed by relaxing the gate β€” parent issue direction (1) β€” because relaxing the gate
    removes the only coverage that exists. The alert must therefore land first.

Coroot's auto-inspections cover crashloops, so today's umami-db-13 (58 restarts) would be caught by
that path β€” but the 07-14 coroot-db case was a pod that stayed Running, never Ready, with zero
restarts
, which no Coroot inspection covers. The parent issue recorded "nothing alerts on it".

What to build

A scan-safe synthetic check that alerts on a CNPG Cluster that has been degraded past a grace
period, independent of any Flux gate β€” same shape as the existing cluster-heartbeat and
coroot-alert-autosuppressor CronJobs, posting to the ${alertmanager_webhook_url} Slack webhook
already used by Coroot and flux-notifications.

Detection: status.readyInstances < spec.instances, or the Ready condition not True.

Grace period keyed on the Ready condition's lastTransitionTime. This was verified rather than
assumed: sampled 45s apart, every cluster's lastTransitionTime was byte-identical, so CNPG does
not rewrite it each reconcile and it is a genuine transition timestamp. (This is the opposite of
Crossplane's Synced condition, which does rewrite it every ~60s and would make a grace period
silently unreachable.)

Acceptance criteria

  • A degraded cluster past the grace period produces exactly one Slack alert naming the namespace,
    cluster, and ready/desired counts.
  • The three currently-healthy clusters produce no alert (negative control).
  • A cluster inside its grace period produces no alert, so a routine rolling restart or switchover
    does not page.
  • The check does not depend on any Flux Kustomization's health gate.
  • No new always-on pod (CronJob, matching the existing scan-safe pattern).

Rough size

S–M. Prerequisite for the parent's direction (1); relaxing the gate is a separate follow-up and must
not land before this.

Activity

  1. added theissue type on Aug 5, 2026
  2. self-assigned this
    on Aug 5, 2026
  3. coderabbitai commented on Aug 5, 2026

    @coderabbitai
    Contributor
    πŸ”— Related PRs

    #2425 - fix(cnpg): move platform Postgres clusters to the longhorn-wffc StorageClass [merged]
    #2445 - feat(kubescape): route runtime-detection alerts to Headlamp, Slack, and Coroot [merged]
    #2635 - fix(deploy): reboot stale nodes so containerd adopts refreshed GHCR auth [merged]
    #2793 - feat(cilium): activate homogeneous device selection [merged]
    #2962 - fix(deploy): make rollout-gate suppression of machine-config sync visible and bounded [merged]


    πŸ“ Issue Planner

    Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    πŸ§ͺ Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    πŸ’¬ Have feedback or questions? Drop into our discord!

  4. moved this to πŸƒπŸ»β€β™‚οΈ In Progress in 🌊 Project Boardon Aug 5, 2026
  5. moved this from πŸƒπŸ»β€β™‚οΈ In Progress to πŸ‘€ In Review in 🌊 Project Boardon Aug 5, 2026
  6. moved this from πŸ‘€ In Review to βœ… Done in 🌊 Project Boardon Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Fields

Priority

None yet

Effort

None yet

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions