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 1vcsfC-00BmUv-38 for pgsql-hackers@arkaria.postgresql.org; Mon, 05 Jan 2026 22:02:59 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vcsfB-004aIE-2R for pgsql-hackers@arkaria.postgresql.org; Mon, 05 Jan 2026 22:02:58 +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 1vcsfB-004aI5-1H for pgsql-hackers@lists.postgresql.org; Mon, 05 Jan 2026 22:02:58 +0000 Received: from mail-oa1-x35.google.com ([2001:4860:4864:20::35]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1vcsf9-004l4R-2H for pgsql-hackers@lists.postgresql.org; Mon, 05 Jan 2026 22:02:57 +0000 Received: by mail-oa1-x35.google.com with SMTP id 586e51a60fabf-3ec6c10a295so210107fac.0 for ; Mon, 05 Jan 2026 14:02:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1767650573; x=1768255373; darn=lists.postgresql.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=Q+HK9/3Da0b3hFyxUVAO0o8vWMH3n2Jv1QqJcoVV+jU=; b=WcWn5c1UsiHn2vwAiBGxL7QuFQFON4ESDL/451z/1u7N+hlM2TXQ2ziSbtY0jwHkHR hSSMvP9Kv2Ou8xJCiAR0+BOAZCpLOjxnJXhuOQpFrJ6LMsH3RkQNxwXeyE48+9BQGsia yB+h6uR0DGF7f6CrsoyvkwZSD4KwMO8Is+wefv993HGNLzLJzeeKnZnAK7veaGHM2y3c e24A3p1cyNcpjlg/rLo+NQGC3cIBaiytE0kjIkYix5IzTsaatdCkYdb2BOqngISPMO41 c07Us74zqRvXAdnt1lGi9+3a9sz6SuuKO3RwSzos2UnY7wqyU0ztvVt0uEfdvSKFvqn4 ED8w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767650573; x=1768255373; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=Q+HK9/3Da0b3hFyxUVAO0o8vWMH3n2Jv1QqJcoVV+jU=; b=Tr112VmWU6VVMX/dqYAZN8RGyY0GGLJBsKms9p/btE65n+gJHGP7kPHcE6SnRzCB3a 4E8aKZTHyadHhSq2VbOYHVoQaekrIyjHqpAv4jL8o3nXv01UPg/ycecq8fekis8poFik 7W5AXc78uS8bWPoK+/hY4J9d8ZZUrau8mzSLR6TWJ+W2Y5g5jHORVtjNgcMpoYUT4WAE XcUlin8YmQJiZoRCevMg8iWLgecETQwrCWCtUGNs1f3KpJewjXSZ6GgRYL8thg8Sch+b mpzP7deC1TOVr70E2vAmRiiOQ2yBI960v3faVxvbIu9522p6G0wMolNZ7DHOvBUJDBQi qeAA== X-Forwarded-Encrypted: i=1; AJvYcCXm7S5tijCQR3N5ndHxs1K5Q9B1GOkU49wRKzicUF4s3afRM5jdquvLmopnHduY+y2kARZ+c732jmj+lgcY@lists.postgresql.org X-Gm-Message-State: AOJu0Yz7S0xswCH6hWBYGPSPESFOSEJmDWI85Z8r4ZgaNC6r1+Aua9U8 5atbT/BwfbgqXK/Ct2scZEfjNyeUoMuicqJwRz7Yx+uOtz7WxVNZvDaKbsi4iH5KGdflYgxVjK1 /fsMfH4dpN2Q/6ki00HnE6y6nvnlsAnY= X-Gm-Gg: AY/fxX6x1J1e8oJJg6jpJRsju8k+I3m9P1JrP6iFWRgTIx0pWbQWdgVKPTJYQ0vIhQs UpJr0JT/qeIEHDs52Am18NMKVaYypqXk09u2hM9+ZP0kZB2ZVM6kZvaGb7E4ehVeugWtOVxAIna +4szBNaH7s77OXrUIvWHHH5bRtYT9Z1qGS83+74bkDfCCc4qSbhV4BTpjl4wkp1MxpAaGc0B4mG RXzDTpPdYtTr0lr2p8HSiayNKidmy8kZRxW49Uop0MuSE22WpLHpBvavNyAHtcLVmhNgaYk4Ge8 eFJ1ag== X-Google-Smtp-Source: AGHT+IEJOnnKTk8s3EpVVWVaDDEsyvwvJu9QCae2vhH+pFBYWtYCO/HuDhHIm1aaUK/8mtFwvgUXwjwmtyE5Q7vD7R8= X-Received: by 2002:a05:6820:f006:b0:659:9a49:908f with SMTP id 006d021491bc7-65f479a159cmr653851eaf.14.1767650572748; Mon, 05 Jan 2026 14:02:52 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: From: Sami Imseih Date: Mon, 5 Jan 2026 16:02:37 -0600 X-Gm-Features: AQt7F2rnFrkZZJSjnpp07B10I0Beb6pmR_-_Z0bT3UaC_CzC1Nfz4r2shMvPIhw Message-ID: Subject: Re: Report oldest xmin source when autovacuum cannot remove tuples To: Shinya Kato Cc: wenhui qiu , PostgreSQL Hackers Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk > Thank you all for the review comments, and sorry for the late reply. > I will address the review comments in order. > > On Sat, Nov 15, 2025 at 9:25=E2=80=AFAM Sami Imseih = wrote: > > More importantly: > > > > 3/ As mentioned earlier in the thread, the "idle-in-transaction" > > transactions is not being reported correctly, particularly for write > > tansactions. I think that is an important missing case. The reason > > for this is the cutoff xmin is not being looked up against the current > > list of xid's, so we are not blaming the correct pid. > > > > 4/ > > Thinking about point 3 above, I began to wonder if this > > whole thing can be simplified with inspiration. Looking at the > > existing BackendXidGetPid(), I think it can. > > > > Based on BackendXidGetPid(), I tried a new routine called > > BackendXidFindCutOffReason() which can take in the cutoff xmin, > > passed in by vacuum and can walk though the proc array and > > determine the reason. We don't need to touch ComputeXidHorizons() > > to make this work, it seems to me. This comes with an additional > > walk though the procarray holding a shared lock, but I don't think > > this will be an issue. > > > > Attached is a rough sketch of BackendXidFindCutOffReason() > > For now, I just added NOTICE messages which will log with > > VACUUM (verbose) for testing. > > Thanks for the revised proposal! Your approach is clear and makes the > code easier to read. My approach is focused on correctness rather than simplicity. The current logic in ComputeXidHorizons can report the wrong PID for blocking transactions. For example: 1. start a pgbench with 5 clients for some time ( i.e. 5 minutes ) ``` pgbench -i -s50 pgbench -c5 -T300 ```` 2. start a long running transaction that consumes an XID ``` postgres=3D# begin; BEGIN postgres=3D*# SELECT txid_current(), pg_backend_pid(); txid_current | pg_backend_pid --------------+---------------- 3665231 | 266601 (1 row) ``` 3. run a vacuum ``` postgres=3D# vacuum verbose pgbench_accounts ; INFO: vacuuming "postgres.public.pgbench_accounts" INFO: finished vacuuming "postgres.public.pgbench_accounts": index scans: = 0 pages: 0 removed, 59833 remain, 59043 scanned (98.68% of total), 0 eagerly scanned tuples: 0 removed, 1045578 remain, 35425 are dead but not yet removable removable cutoff: 4301694, which was 35981 XIDs old when operation ended oldest xmin source: active transaction (pid=3D267064) frozen: 0 pages from table (0.00% of total) had 0 tuples frozen visibility map: 0 pages set all-visible, 0 pages set all-frozen (0 were all-visible) index scan bypassed: 405 pages from table (0.68% of total) have 1494 dead item identifiers avg read rate: 1320.425 MB/s, avg write rate: 2.404 MB/s buffer usage: 67745 hits, 49974 reads, 91 dirtied WAL usage: 1 records, 0 full page images, 299 bytes, 0 full page image bytes, 0 buffers full system usage: CPU: user: 0.22 s, system: 0.06 s, elapsed: 0.29 s VACUUM ``` VACUUM reports the oldest-XID source PID as 267090 , but the correct PID is 267064. This happens because the ComputeXidHorizons loop picks the first PID it encounters with the cutoff XID, even if other backends have the same XID. There=E2=80=99s no reliable way within that loop to iden= tify the actual blocking transaction. ``` postgres=3D# SELECT pid, datname, usename, state, backend_xmin, backend_xid, substr(query, 1, 20) as query FROM pg_stat_activity; pid | datname | usename | state | backend_xmin | backend_xid | query --------+----------+----------+---------------------+--------------+-------= ------+---------------------- 267064 | postgres | postgres | active | 4301694 | 4351291 | UPDATE pgbench_branc 267069 | postgres | postgres | active | 4301694 | 4351332 | UPDATE pgbench_telle 267067 | postgres | postgres | active | 4301694 | 4351299 | UPDATE pgbench_branc 267070 | postgres | postgres | active | 4301694 | 4351279 | UPDATE pgbench_branc 267068 | postgres | postgres | active | | 4351325 | UPDATE pgbench_telle 267066 | postgres | postgres | active | 4301694 | 4351327 | UPDATE pgbench_branc 267065 | postgres | postgres | active | 4301694 | 4351292 | UPDATE pgbench_branc 267077 | postgres | postgres | active | 4301694 | 4351303 | UPDATE pgbench_branc 266606 | postgres | postgres | active | 4301694 | | SELECT pid, datname, 267071 | postgres | postgres | active | | | BEGIN; 267072 | postgres | postgres | active | 4301694 | 4351300 | UPDATE pgbench_telle 267073 | postgres | postgres | active | 4301694 | 4351258 | UPDATE pgbench_branc 267075 | postgres | postgres | idle | | | END; 267074 | postgres | postgres | active | 4301694 | 4351319 | UPDATE pgbench_branc 267076 | postgres | postgres | active | | 4351248 | END; 267084 | postgres | postgres | active | 4301694 | 4351330 | UPDATE pgbench_telle 267078 | postgres | postgres | active | 4301694 | 4351260 | UPDATE pgbench_branc 267082 | postgres | postgres | active | | 4351309 | END; 267081 | postgres | postgres | active | | 4351270 | UPDATE pgbench_branc 267083 | postgres | postgres | active | | 4351313 | END; 267080 | postgres | postgres | active | 4301694 | 4351311 | UPDATE pgbench_branc 267079 | postgres | postgres | active | 4301694 | 4351318 | UPDATE pgbench_branc 267086 | postgres | postgres | active | 4301694 | 4351335 | UPDATE pgbench_branc 267085 | postgres | postgres | active | | | BEGIN; 267090 | postgres | postgres | idle in transaction | | 4301694 | SELECT txid_current( ************ ``` > - Your proposal incurs additional cost. Furthermore, the time lag > between the execution of ComputeXidHorizons() and > BackendXidFindCutOffReason() could lead to inaccurate logging. While scanning the proc array adds some overhead, it could be limited to cases where multiple VACUUMs are stuck on the same cutoff XID, but we will need to track the last cutoff-xmin to make that possible. Alternatively, this information might be better exposed in a new system view, showing the "PID", "XID," and "reason" a transaction is blocking VACU= UM. This approach is more proactive as a DBA can continuously monitor for blocking reasons and take action before it becomes an issue. -- Sami Imseih Amazon Web Services (AWS)