Skip to content

Both RDS log readers carry a standing denial on every pgmon target while rolling up HEALTHY, errors 0, "no new deadlocks/plans" #2994

Closed as not planned

Description

@erikdarlingdata

Found by the first full 50-target sweep of the pgmon store (2026-09-05). #2633 fixed this exact dishonesty for one path — a denied AWS call now records PERMISSIONS with a message that ends "Nothing was read this cycle — this is NOT 'no plans were captured'." The message is there. The rollup ignores it.

What the surface says today, fleet-wide (all 50 targets, none excepted)

pg_plan_capture: rolls up HEALTHY, errors 0, note no new auto_explain plans in the RDS log window on ~136 of 167 runs — and last_error on every one of the 50 targets is an IAM denial on the monitoring host's role:

  • 49 (cluster-endpoint targets): not authorized to perform rds:DescribeDBClusters
  • 1 (target-a, the only instance-endpoint target): not authorized to perform rds:DescribeDBLogFiles

pg_deadlocks: rolls up HEALTHY, errors 0 on 48 of 50, note no new deadlocks in the RDS log window on ~1,590 of ~2,000 runs — and last_error on those 48 is permission denied for function pg_read_file (SQLSTATE 42501). (The other 2 carry a different defect that overwrites the denial — filed separately.)

get_fleet_overview on this store: total_deadlocks: 0.

Why this is a defect and not just missing grants

The denials are deterministic — the role has the action or it doesn't; the login can execute pg_read_file or it can't — so the read path fails the same way on every target. Yet no surface a reader actually consults says so:

  • errors is 0. The denial is a classified non-fatal skip, which is right for the cycle and wrong for the rollup: a skip that means "I read nothing" is indistinguishable, in every counter, from a read that found nothing.
  • The reassuring note fires anyway on the majority of runs. Whatever sub-path emits "no new deadlocks/plans in the RDS log window" reports emptiness on targets where the log demonstrably cannot be opened.
  • The fleet answer — zero deadlocks across 50 production Aurora clusters, in every window anyone has looked at — is exactly what a healthy quiet fleet would show and exactly what total blindness would show. The product's own conventions (pg_blocking's capture-count denominator, A per-database collector that fails in SOME databases reports SUCCESS with no note #2623's survivors note) exist to keep those distinguishable; the two log readers don't follow them.

What the MCP surface cannot establish, and the store query that settles it

Whether ANY cycle has ever read a log successfully. last_error proves denials happen; it cannot prove they happen every cycle. Settle it with per-status histograms and rows-ever:

SELECT collector_name, status, COUNT(*) AS n
FROM collect.collection_log
WHERE collector_name IN ('pg_deadlocks', 'pg_plan_capture')
GROUP BY collector_name, status;

plus row counts in the deadlock/plan tables. If PERMISSIONS accounts for every attempted read, this fleet has been blind since enrollment and "0 deadlocks" has never been evidence of anything.

Fix, in two halves

Product half — the rollup must be able to show blindness:

  1. A collector whose most recent read-path outcome is a denial must not band HEALTHY with errors 0. Either count classified skips distinctly (a denied counter beside errors/yields/abandoned) or band it NO_PERMISSIONS the way an all-runs-denied collector already does.
  2. The "no new X in the RDS log window" note must be unreachable on a cycle that did not open the log — that note is a claim about the log's contents.
  3. get_fleet_overview's deadlock total should carry the denominator when the underlying reader is denied: "0 (not collected on N servers)" is the honest number.

Ops half — the actual grants, recorded here so the issue closes on both:

  • The monitoring host's IAM role needs rds:DescribeDBClusters, rds:DescribeDBLogFiles, rds:DownloadDBLogFilePortion on the pgmon targets.
  • pg_read_file needs an explicit grant darling_monitor does not have — or the deadlock reader should take the same RDS API path as plan capture on managed targets and stop attempting pg_read_file there at all. The hint text the denial currently carries ("pg_monitor covers every collector here") is wrong for this function: pg_monitor does not grant pg_read_file.

Related: #2633 (the single-path fix whose vocabulary this should reuse), #2661 (where the log reader came from), #2623 (the convention violated).

Activity

  1. erikdarlingdata commented on Sep 5, 2026

    @erikdarlingdata
    OwnerAuthor

    get_collector_cost puts a number on what these denied readers cost, and it is not small. Seven days, 50 targets:

    collector runs sql_ms rows storage_ms
    pg_deadlocks 79,333 0 0 49,258,335
    pg_plan_capture 6,750 0 0 4,592,413

    pg_deadlocks is the single most expensive collector on this store by wall time — 49.3M ms ≈ 13.7 hours per week, ~621 ms per run — and it has produced zero rows in the entire window. With pg_plan_capture that is ~15 hours a week, ~2.1 hours a day, spent on two collectors whose reads are denied on all 50 targets.

    The sql_ms: 0 is not a measurement gap. Both read the RDS log through the AWS API rather than by querying the target, so their work lands in storage_ms; pg_cpu_utilization has the same shape (sql_ms: 0, 14.7M storage_ms) and does return rows, which is the control that says the field is real work and not an artifact.

    Two things this sharpens in the issue above:

    1. The rollup problem is worse than "reports HEALTHY". A collector burning the store's largest single time budget for zero output is invisible on every surface: errors: 0, status HEALTHY, a reassuring note, and a cost that only shows up if you specifically ask get_collector_cost. Nothing correlates the cost against the output.
    2. It strengthens the "blind since enrollment" reading. Zero rows across 79,333 runs and 7 days is not a quiet fleet — the same collector on a working path would have to report something over 50 clusters and a week. It does not distinguish "no deadlocks occurred" from "never read the log", but combined with the standing denial in last_error on 48 of 50, the balance is clear.

    Worth considering as a general guard rather than a one-off: a collector with a nonzero cost and a persistently zero row count is a cheap, engine-agnostic signal that something is running without collecting — it would have surfaced this weeks ago without anyone knowing to look.

  2. erikdarlingdata commented on Sep 5, 2026

    @erikdarlingdata
    OwnerAuthor

    Retracting this issue. Both halves are false, and the ops half asks for production IAM changes that are not needed — do not action it. Measured against the store rather than reasoned.

    The evidence

    pg_deadlocks    PERMISSIONS   oldest 08-27 12:34:12   newest 08-30 15:09:37
    pg_plan_capture PERMISSIONS   oldest 08-27 13:22:54   newest 08-30 11:42:32
    
    pg_deadlocks    by day   08-29: 14281 perm /     0 succ
                             08-30:  9005 perm /  5250 succ   <- cutover
                             08-31:     0 perm / 14271 succ   ... every day since
    pg_plan_capture by day   08-29:  1200 perm /     0 succ
                             08-30:   600 perm /   600 succ   <- same cutover
                             08-31:     0 perm /  1200 succ   ... every day since
    

    Both readers cut over on 2026-08-30 and neither has produced a single PERMISSIONS row since — six days and ~14,270 successful deadlock reads a day, across all 50 targets.

    What that means for each claim

    • "Both RDS log readers are denied on every target while rolling up HEALTHY" — false. The readers work. no new deadlocks in the RDS log window is an honest empty, not blindness.
    • "The fleet's 0 deadlocks has no evidential basis" — false, for the reason above.
    • The ops half (rds:DescribeDBClusters / DescribeDBLogFiles / DownloadDBLogFilePortion on the host role, plus a pg_read_file grant) — unnecessary. No IAM denial of any kind is current on that store. The only ongoing PERMISSIONS rows are missing extensions (pg_wait_sampling, pg_buffer_usage, pg_predicate_stats, pg_kernel_stats, pg_index_bloat) and an Aurora-unsupported source (pg_write_stats / pg_stat_get_wal, 0A000), all correctly classified non-fatal skips.
    • My cost comment above — the measurement (49.3M ms, zero rows, 7 days) is real; the framing as evidence of blindness is wrong. A log-polling collector that finds nothing is doing its job.
    • The "nonzero cost + persistently zero rows" guard I proposed there — withdrawn. It would fire on pg_deadlocks today and be wrong, flagging an honest empty as a fault. That is the failure target_has_user_databases exists to prevent elsewhere in this codebase. Adding a third term (a denial actually present in the window) makes it safe and also makes it redundant with reading the errors.

    How I got it wrong, because the cause is the one thing worth keeping

    I read last_error as a current condition. It is a single retained slot with no timestamp, and it was holding a six-day-old permission denied for function pg_read_file (SQLSTATE 42501) from the in-database deadlock route — a code path the collector no longer takes.

    The two routes are mutually exclusive: no new deadlocks in the RDS log window is emitted only at DarlingCollectorRunner.cs:638 (the RDS-API route), while pg_read_file lives in PgDeadlocksCollector.cs:83 (the in-database route). So on any run where that note fires, pg_read_file was never called — the 42501 beside it can only be a fossil.

    Nothing in get_collection_health distinguishes that from a live fault: no timestamp, no indication the emitting path is retired, sitting next to a HEALTHY status and tens of thousands of successful runs.

    That is the real defect, and it is filed separately. It is also what #2996 scoped out to this issue, so the ground is covered rather than lost.

    Closing as not-a-defect. Credit for the route-exclusivity catch to the #2996 lane; the dilution explanation for the near-identical apparent denial rates (15.9% vs 14.3% — one cause, one window, diluted differently because pg_deadlocks runs ~12x more often) came from the reviewer on #3002.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions