Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Darling/PerformanceMonitor.Darling.Service/DarlingWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9763,8 +9763,10 @@ await DarlingObservability.LogCollectionAsync(
}
catch (PgLogTimezoneUnsupportedException ex)
{
/* #2993: this target's log_timezone is not UTC, so the deadlock reports in its server log are
stamped in local time and occurred_at cannot be filled from them.
/* #2993: this target's log_timezone is not UTC, so the lines in its server log are stamped in
local time and cannot be stored against UTC. Both readers that assemble log entries through
PgLogEntryAssembler (pg_log_events and pg_deadlocks) land here, which is why the message names
the log, not deadlocks.

PERMISSIONS for the same reason the PostgresException FeatureDisabled arm below is: none of
the store's five statuses means "this target is configured in a way this source cannot be
Expand Down
6 changes: 4 additions & 2 deletions Lite.Tests/PgDeadlockLogParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -734,8 +734,10 @@ public void RefusesAReportWhoseLogPrefixIsNotUtc_NamingTheSetting()
Assert.Contains("EST", ex.Message, StringComparison.Ordinal);

/* The sentence that stops the row being read as an empty log. A refusal whose message does not say
so is the same silent nothing one layer over. */
Assert.Contains("NOT 'no deadlocks were detected'", ex.Message, StringComparison.Ordinal);
so is the same silent nothing one layer over. It names the log, not deadlocks: pg_log_events
throws this same message through PgLogEntryAssembler. */
Assert.Contains("this is NOT an empty log", ex.Message, StringComparison.Ordinal);
Assert.DoesNotContain("deadlock", ex.Message, StringComparison.OrdinalIgnoreCase);
}

/// <summary>
Expand Down
25 changes: 16 additions & 9 deletions PerformanceMonitor.Collectors/PgLogTimezoneUnsupportedException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ namespace PerformanceMonitor.Collectors;
/// The PostgreSQL server log is timestamped in a zone that is not UTC, so the deadlock reports in it cannot
/// be stored (#2993).
///
/// <para><b>Log events throw it too, not only deadlocks.</b> <see cref="PgLogEntryAssembler"/> is the one
/// throw site, and both <see cref="PgDeadlockLogParser"/> (pg_deadlocks) and <see cref="PgLogEventClassifier"/>
/// (pg_log_events) read through it, so the message names the log rather than either reader's rows. It said
/// "deadlock reports" and "NOT 'no deadlocks were detected'" until 2026-09-23, when DARLING01 showed that
/// sentence on a pg_log_events row.</para>
///
/// <para><b>Why this refuses rather than converting.</b> The log prefix carries the zone as an
/// ABBREVIATION, and abbreviations are ambiguous: <c>CST</c> is US Central, China Standard and Cuba
/// Standard — three offsets and two hemispheres of daylight saving. There is no correct conversion to make
Expand Down Expand Up @@ -55,14 +61,15 @@ private static string BuildMessage(string? observedZone)
{
var zone = string.IsNullOrWhiteSpace(observedZone) ? "(none)" : observedZone.Trim();

return $"The PostgreSQL server log on this target is timestamped '{zone}', which is not a "
+ $"zero-offset zone: {SettingName} is not UTC, so every timestamp in its deadlock reports is "
+ "local time. Nothing was stored this cycle, and this is NOT 'no deadlocks were detected'. "
+ "The reports are refused rather than shifted because a log-prefix zone is an ABBREVIATION "
+ "and abbreviations are ambiguous — CST alone is three different zones — so there is no "
+ "conversion to apply that would not be a guess, and a guess here produces plausible rows "
+ $"stamped at the wrong moment. Set {SettingName} = 'UTC' on the target (on managed "
+ "PostgreSQL that is a parameter-group change, and it takes effect on reload rather than "
+ "needing a restart); the next collection cycle stores normally.";
return "The collector stored nothing from the PostgreSQL server log on this target this cycle, "
+ "and this is NOT an empty log. "
+ $"The log is timestamped '{zone}', which is not a zero-offset zone. So {SettingName} is not "
+ "UTC, and every timestamp in the log is local time. "
+ "The collector refuses these lines instead of converting them. The zone in a log prefix is an "
+ "abbreviation, and one abbreviation can name several zones. For example, CST names three. "
+ "A conversion is a guess, and a guess stores rows that look correct but have the wrong time. "
+ $"Set {SettingName} = 'UTC' on the target. On managed PostgreSQL, change it in the parameter "
+ "group. The change takes effect on a reload, so no restart is necessary. The next collection "
+ "cycle then stores normally.";
}
}
Loading