From e192ca3300b63c18b211be9355639d52fc602b49 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Fri, 24 Apr 2026 16:39:57 -0500 Subject: [PATCH] fix(weather): analyze_all skip_recent check reads both analyze timestamps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pg_stat_user_tables tracks two timestamps — `last_autoanalyze` is only bumped by the autovacuum daemon, while `last_analyze` is bumped by manual ANALYZE statements (including the ones our Task issues). The previous query only filtered on `last_autoanalyze`, so on a re-run within the skip_recent window every manually- analyzed table came back for another ANALYZE pass. Uses `GREATEST(last_analyze, last_autoanalyze)` so fresh manual analyses count as recent. --- lib/microwaveprop/weather.ex | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/microwaveprop/weather.ex b/lib/microwaveprop/weather.ex index 5b003727..3dd58492 100644 --- a/lib/microwaveprop/weather.ex +++ b/lib/microwaveprop/weather.ex @@ -434,12 +434,18 @@ defmodule Microwaveprop.Weather do def analyze_all(opts \\ []) do skip_recent = Keyword.get(opts, :skip_recent_seconds, 6 * 3600) + # `last_autoanalyze` is only set by the autovacuum daemon; + # `last_analyze` is set by manual ANALYZE (including our Task + # below). Take the newer of the two — otherwise a re-run within + # `skip_recent` would re-analyze everything we just did. q = """ SELECT relname FROM pg_stat_user_tables WHERE schemaname = 'public' - AND (last_autoanalyze IS NULL - OR last_autoanalyze < now() - ($1 || ' seconds')::interval) + AND COALESCE( + GREATEST(last_analyze, last_autoanalyze), + 'epoch'::timestamptz + ) < now() - ($1 || ' seconds')::interval ORDER BY n_live_tup ASC """