Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wfb17-005WtR-0W for pgsql-general@arkaria.postgresql.org; Fri, 03 Jul 2026 10:21:10 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wfb16-006vKf-0H for pgsql-general@arkaria.postgresql.org; Fri, 03 Jul 2026 10:21:04 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wfb15-006vKX-2D for pgsql-general@lists.postgresql.org; Fri, 03 Jul 2026 10:21:03 +0000 Received: from mout01.posteo.de ([185.67.36.65]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1wfb12-00000001Rcz-1YqV for pgsql-general@lists.postgresql.org; Fri, 03 Jul 2026 10:21:03 +0000 Received: from submission (posteo.de [185.67.36.169]) by mout01.posteo.de (Postfix) with ESMTPS id C0EC1240027 for ; Fri, 3 Jul 2026 12:20:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=posteo.de; s=1984.8680eb; t=1783074057; bh=Ib4FIbzH7+Ty90rvADcWcmqhnrCTjLtVH8m7C8yggSg=; h=Message-ID:Subject:From:To:Date:Content-Type: Content-Transfer-Encoding:MIME-Version:From; b=l7RoSS740VwQjC/+/p/weSkuwCA5ECXIgkL8yguPFrtf12jar83uhRmwnBx7vQVpP 713g/vvYCv0XbLusg4Ec6UWZHP6QHHPoUxCwzpu/E19X4XrBHfCpPS3tj9g534291S nfR3+jy3B329rBwSONiPQwYEHXKf3HIrAKW82BJ8YxZzzKZxmSea82w3wMd0vVvNq4 JD34aXJKJ4Fncn+fT0MH1cZAdTg3HGE0zgbDxEyXH5ZQQEMwZ2bxDhbpxeKDNIWSMO 7YUFHrsGm3hS6ejmGKj+xS5F/QnqMuFOQCpGHcUnfUyrWP2BsO+uNVtB1AxJgTxzoz BDqBXgPAT/qOA== Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4gs8sj2Vj9z6tx7 for ; Fri, 3 Jul 2026 12:20:57 +0200 (CEST) Message-ID: Subject: last_analyze/last_vacuum in pg_stat_all_tables (PG 17.10) From: Thomas Michael Engelke To: "pgsql-general@lists.postgresql.org" Date: Fri, 03 Jul 2026 10:20:57 +0000 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Good morning, for monitoring purposes we use check_pgactivity (https://github.com/OPMDG/check_pgactivity) to check a number of different metrics. One of those checks the last analyze timestamp of either kind, another the last vacuum timestamp of either kind, and alerts when either of those becomes older than a certain age that can be specified as a parameter to these checks. This is the SQL that runs to find these values: $PG_VERSION_91 =3D> qq{ SELECT pg_is_in_recovery()::int AS is_in_recovery, current_database(), schemaname, a.relname, CASE WHEN NOT pg_is_in_recovery() THEN coalesce(max( coalesce(extract(epoch FROM current_timestamp - greatest(last_${type}, last_auto${type})), 'infinity'::float)), 'NaN'::float) ELSE 'NaN'::float END, CASE WHEN NOT pg_is_in_recovery() THEN coalesce(sum(${type}_count), 0) ELSE 0 END AS ${type}_count, CASE WHEN NOT pg_is_in_recovery() THEN coalesce(sum(auto${type}_count), 0) ELSE 0 END AS auto${type}_count, CASE WHEN NOT pg_is_in_recovery() THEN ( SELECT md5(tup_inserted::text||tup_updated::text||tup_deleted::text||'$c_limit $w_limit' ) FROM pg_catalog.pg_stat_database WHERE datname =3D current_database() ) ELSE '' END FROM pg_stat_all_tables a JOIN pg_class b on a.relid =3D b.oid WHERE schemaname NOT LIKE 'pg_temp_%' AND schemaname NOT LIKE 'pg_toast_temp_%' AND pg_relation_size(relid) >=3D $table_min_size AND schemaname NOT LIKE 'information_schema' AND (('${type}' =3D 'analyze' AND schemaname NOT LIKE 'pg_toast') -- TOAST tables aren't ANALYZEd OR ('${type}' =3D 'vacuum')) AND (('${type}' =3D 'analyze' AND a.relname NOT LIKE 'pg_statistic') -- pg_statistic never gets ANALYZEd OR ('${type}' =3D 'vacuum')) AND relkind <> 'p' -- partitioned table do not have last_* information GROUP BY schemaname, a.relname } {type} gets replaced by either analyze or vacuum. What happens *sometimes* on servers is that the age of the oldest maintenance increases to infinity, as the fields of last_analyze and last_vacuum are emptied (NULL value). However, looking into the monitoring system I can see that there must have been values in these fields before. I can see the values coming ouf of that check 2 hours ago, the values were not infinity. Are there any circumstances under which these fields get emptied? From reading the documentation any value should never be replaced by a NULL value: last_vacuum timestamp with time zone: Last time at which this table was manually vacuumed (not counting VACUUM FULL) last_autovacuum timestamp with time zone: Last time at which this table was vacuumed by the autovacuum daemon last_analyze timestamp with time zone: Last time at which this table was manually analyzed last_autoanalyze timestamp with time zone: Last time at which this table was analyzed by the autovacuum daemon I can probably work around that by using either vacuum against the database or vacuumdb on the system, but I'd like to understand if there are other factors that influence these fields, and maybe the monitoring agent needs to change in that regard.