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 1vioeV-00GCf7-1H for pgsql-hackers@arkaria.postgresql.org; Thu, 22 Jan 2026 06:58:48 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vioeU-00Bf0o-12 for pgsql-hackers@arkaria.postgresql.org; Thu, 22 Jan 2026 06:58:46 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vioeT-00Bf0g-2B for pgsql-hackers@lists.postgresql.org; Thu, 22 Jan 2026 06:58:46 +0000 Received: from mail-ed1-x535.google.com ([2a00:1450:4864:20::535]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1vioeQ-001j7a-2e for pgsql-hackers@lists.postgresql.org; Thu, 22 Jan 2026 06:58:45 +0000 Received: by mail-ed1-x535.google.com with SMTP id 4fb4d7f45d1cf-658034ce0e3so1182000a12.3 for ; Wed, 21 Jan 2026 22:58:43 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1769065121; cv=none; d=google.com; s=arc-20240605; b=aIKMfLYgzstQQFc9hiJerG0Jcz21mVxEil0mr58l8O8dm1suryvmheWUdmD9L3W8GX 8kue41LmOrMS+THg+BT8v5JbwlpLVKL1deeTMhCO5skyzfNiPNN56cJF2e9X2TKuRRsX aRMEMMvhbyAuKYRgzRt0VXWQmhoNSFSI9OQ2sgvk7E6ssTvPCxayX6PYklVbRK2/zOjm KZ668MhdeOhlF6PBunJLneQFN/K8MqP3M407OaGuYnglSFyflnK5TIdpEKwLk9LkKUYb DxVJYWfdNxFVrcmW2VupsStLKCzUNr48rhXfoADEuXGv3xDBpOvCSF68uGOUjZjBZKFx FWJw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=MXGE9wnSwLfTc/cX5IXheN9k9KvVMMP607xpk0MJwks=; fh=An7ah4mgYVhSUJhN9riKmrD0/wMLR6RW2+GF78t23Qc=; b=NKdu/kjPuUDrhZCQqXy3AjtfE64sBJjGfL3FzayS604YZBuOmhO/lOmL/SU7DjGc+e 4vPu34taiOVxbJwKDu2/KGCCWLFXRSWMyXTSUV4HojBjFwPXf3JBtXf5a6s2giCyWhS1 PsiCeoX+l7nOr3VqijN6QqWuKVTtNreeBQzNhyOkAfjUbKtBdagxa+Me22mFSOKV+tbG 8qkr1xomfoAqJO6zi0EfjwGKoJGMM1yM0YTuCIQoDzO2F3yBbrcwe7YSlSgv9QM38wX8 y2LFKto2XghwfchznqcdvoMCbigjqTQ8+/qe5Kenp2vM70hh9DUqgM4ag6jHta5EP19z oyog==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769065121; x=1769669921; darn=lists.postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=MXGE9wnSwLfTc/cX5IXheN9k9KvVMMP607xpk0MJwks=; b=fr7rk4ByqQkSQuSTnWsH6+IDVmqqlHcEKrbgpvAETr06WXlH0qqreBHhuAjqLFmHzm mZLwvNYCgZ6u8w35vmHB+oMEWm9tThANeCRrHVo7JU1wWUp3QGDtAwMQ1EerqGnFaQwu bFeT8qU1VMQ2boVMpEpVvw/R6fkRhLW6lwnGIOvRAAKn/W7JqAqVK5A54074Kk/pw4Zb LmoS7nwTtf/RkDwEQ/FUaYEnl2OxggIbKRJYOjADGIVEFoIslrkQs0C6VDPB8rTFMPzZ OvhuvzWMReyoMVQiaoLqo3WIHUmP0WKoT9k6MPOIAi/MXM4TsJBHuB5JoiJgv12ToxiI urHw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769065121; x=1769669921; h=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=MXGE9wnSwLfTc/cX5IXheN9k9KvVMMP607xpk0MJwks=; b=t974XIOC6muLRjNPBTqIn03c8uGmp1au2Ta2kAMXjpHgqk+rDNwxHY9dUxe9+Vl1Y3 uZAd2LmcvROEcATA5yqRQ1qgLV7BSPMw2MwO3tRe8MhuxIbMwbbgfuKkJFiO9vEwwEsk XwSNeEudCKRqPcR8siDe3iCw+lxSPx3z8j6FSvEkwGB68RDcJJZs1I5BCWwcHOwTHLn3 t2qBQn1AE9C99oKoDrgQt2LHgbmFczcUXIAJcHyXUtoxX9cuao/Esv3BT/IaWImaH2JA Z4v59A+aVK8bJS4HkHixbyVfq3HOCDrGPfgEFh2+Vcohb7ed4p79/HKo6FFyZa/6p9P2 KA3A== X-Forwarded-Encrypted: i=1; AJvYcCXxOVWkL9rAmY8oC45S0DAljEKBLn64jT26O4CxBJhczCb8vaf/OGVUPhKgR/71qhmyvC4S2UVSXCRpK0k3@lists.postgresql.org X-Gm-Message-State: AOJu0Yw9H36Y9uO0mvYVzteWQiYmcgPCI4ubalqLxXWsx0KRKWerBQ0Q 3L2qY6Y4yn+UuvY8Y0ryRdIW0g66KM/ViirUKPDK+SSY1b01IJprqgBZWP09v57j+UdY2XdBG3r u1b1inB58InOrMLZk9NrmoLJzptOqfRo= X-Gm-Gg: AZuq6aKsE3IVCNsPIJnRe4lAcQ/f9cHnxTW5iDG1hdoMC0gM1l3wyb1F94aK/xuYL7k ZrYCt5C3tsrTPD4Rb3tAVXQhrywx1IWOsGCbwDHWyNOlPsL3nfnbXoEIrD3uJQDCiqYSIbcvZar JqmP9jB8yOd84pO2wic5awUNlOb4N2suB/VeFQGvzBCMtPfA7jXZ/UQdbk+gKV7edOS5gxQXjtI nN05Wt2kxkLgbb9ab6HpGT8hbqazjtaLIYPzqJ8KsSyuXDJg0hrtgXMT6Z1FnyxAJXPaA== X-Received: by 2002:a05:6402:3485:b0:658:1552:5122 with SMTP id 4fb4d7f45d1cf-65815525490mr3509153a12.17.1769065121005; Wed, 21 Jan 2026 22:58:41 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: From: wenhui qiu Date: Thu, 22 Jan 2026 14:58:29 +0800 X-Gm-Features: AZwV_QjcW6kWKceTmBG7trqihI8FMMjadaBGz3bNOw8sk6pjAQIEeSI8nFevkJU Message-ID: Subject: Re: Report oldest xmin source when autovacuum cannot remove tuples To: Sami Imseih Cc: Shinya Kato , PostgreSQL Hackers Content-Type: multipart/alternative; boundary="0000000000001f45dc0648f492af" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --0000000000001f45dc0648f492af Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi Sami > Alternatively, this information might be better exposed in a new system > view, showing the "PID", "XID," and "reason" a transaction is blocking VACUUM. > This approach is more proactive as a DBA can continuously monitor for > blocking reasons and take action before it becomes an issue. Yes ,If this approach is acceptable, then as a reference for the standby conflict-handling and query replay mechanism, we could consider introducing a GUC parameter to terminate PID that blocks VACUUM freeze when the relation age is approaching the vacuum_failsafe_age threshold. Thanks On Tue, Jan 6, 2026 at 6:02=E2=80=AFAM Sami Imseih wr= ote: > > 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 curren= t > > > 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 id= entify > 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 > VACUUM. > 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) > --0000000000001f45dc0648f492af Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi=C2=A0Sami=C2=A0
> Alternatively, this informatio= n might be better exposed in a new system
> view, showing the "P= ID", "XID," and "reason" a transaction is blocking= VACUUM.
> This approach is more proactive as a DBA can continuously = monitor for
> blocking reasons and take action before it becomes an i= ssue.
Yes ,If this approach is acceptable, then as a referenc= e for the standby conflict-handling and query replay mechanism, we could co= nsider introducing a GUC parameter to terminate PID=C2=A0that blocks VACUUM= freeze when the relation age is approaching the vacuum_failsafe_age thresh= old.


Thanks

On Tue, Jan 6, 2026 at 6:02=E2=80=AFAM Sami Imseih <samimseih@gmail.com> wrote:
> 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 <samimseih@gmail.com> wrot= e:
> > More importantly:
> >
> > 3/ As mentioned earlier in the thread, the "idle-in-transact= ion"
> > transactions is not being reported correctly, particularly for wr= ite
> > tansactions. I think that is an important missing case. The reaso= n
> > for this is the cutoff xmin is not being looked up against the cu= rrent
> > 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=C2=A0 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 ComputeXidHorizo= ns()
> > to make this work, it seems to me. This comes with an additional<= br> > > 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<= br> > 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();
=C2=A0txid_current | pg_backend_pid
--------------+----------------
=C2=A0 =C2=A0 =C2=A0 3665231 |=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0266601
(1 row)
```

3. run a vacuum
```
postgres=3D# vacuum verbose pgbench_accounts ;
INFO:=C2=A0 vacuuming "postgres.public.pgbench_accounts"
INFO:=C2=A0 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;
=C2=A0 pid=C2=A0 =C2=A0| datname=C2=A0 | usename=C2=A0 |=C2=A0 =C2=A0 =C2= =A0 =C2=A0 state=C2=A0 =C2=A0 =C2=A0 =C2=A0 | backend_xmin |
backend_xid |=C2=A0 =C2=A0 =C2=A0 =C2=A0 query
--------+----------+----------+---------------------+--------------+-------= ------+----------------------
=C2=A0267064 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 4351291 | UPDATE pgbench_branc
=C2=A0267069 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 4351332 | UPDATE pgbench_telle
=C2=A0267067 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 4351299 | UPDATE pgbench_branc
=C2=A0267070 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 4351279 | UPDATE pgbench_branc
=C2=A0267068 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |
=C2=A0 4351325 | UPDATE pgbench_telle
=C2=A0267066 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 4351327 | UPDATE pgbench_branc
=C2=A0267065 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 4351292 | UPDATE pgbench_branc
=C2=A0267077 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 4351303 | UPDATE pgbench_branc
=C2=A0266606 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | SELECT pid, datname,
=C2=A0267071 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | BEGIN;
=C2=A0267072 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 4351300 | UPDATE pgbench_telle
=C2=A0267073 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 4351258 | UPDATE pgbench_branc
=C2=A0267075 | postgres | postgres | idle=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | END;
=C2=A0267074 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 4351319 | UPDATE pgbench_branc
=C2=A0267076 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |
=C2=A0 4351248 | END;
=C2=A0267084 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 4351330 | UPDATE pgbench_telle
=C2=A0267078 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 4351260 | UPDATE pgbench_branc
=C2=A0267082 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |
=C2=A0 4351309 | END;
=C2=A0267081 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |
=C2=A0 4351270 | UPDATE pgbench_branc
=C2=A0267083 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |
=C2=A0 4351313 | END;
=C2=A0267080 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 4351311 | UPDATE pgbench_branc
=C2=A0267079 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 4351318 | UPDATE pgbench_branc
=C2=A0267086 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 4301694 |
=C2=A0 4351335 | UPDATE pgbench_branc
=C2=A0267085 | postgres | postgres | active=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | BEGIN;
=C2=A0267090 | postgres | postgres | idle in transaction |=C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |
=C2=A0 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 VACUUM.
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)
--0000000000001f45dc0648f492af--