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 1w22rh-000par-03 for pgsql-hackers@arkaria.postgresql.org; Mon, 16 Mar 2026 07:59:54 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w22rf-008HJS-18 for pgsql-hackers@arkaria.postgresql.org; Mon, 16 Mar 2026 07:59:52 +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 1w22rf-008HJK-04 for pgsql-hackers@lists.postgresql.org; Mon, 16 Mar 2026 07:59:51 +0000 Received: from mail-ej1-x633.google.com ([2a00:1450:4864:20::633]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w22rc-00000000OAn-4BAO for pgsql-hackers@lists.postgresql.org; Mon, 16 Mar 2026 07:59:51 +0000 Received: by mail-ej1-x633.google.com with SMTP id a640c23a62f3a-b8f9568e074so592311766b.0 for ; Mon, 16 Mar 2026 00:59:49 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1773647988; cv=none; d=google.com; s=arc-20240605; b=Sv5+/BQkEV+/Ye1cyhSkzex9x3zIg6Im3Fx2cH8hyuwq1ikWcMx8+qfU7xQKiEQtpc Fl61wNdYc+GiJfbNOckAk/N6PwMdv+9cOIBR0i0C8lG4+1CCyBYB9s08LSL1Q/CO1fLt 85pZe6nh/BH60PVGiIqTEICJZdPEz84gIDYArwAPysmAHYj+gx8O93s3D+GmWkDDk/53 VDYf5V8yFLlmlvwGOzIwwiI6RpKvr+gInYJ2JktjoqrjvALhTeZL/PmMkN4Um4pz5RfP h2aLFxxK0Xfkr0FksoR+SyEW96QkSTP06Ix20CapZVhga9/xH83p/+lB1fOPH7cDo0vb 1A9A== 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=1fdyyWt7yEvQttKqhuiYg3U0RiaG6799ZJYc1VDArPE=; fh=Eo3ATO4S6Ub7mYwgLPmGHHPJPaN7r8gKfs/UEQR2oKo=; b=aE/rJqpojF+0rdh7aMXea+WrJcFfMgoLd+ATLDVCCrhRo77xXUEl3wv1O4uSYpiLnf gu4M2RBUCWCZenrRJmmeo/e37w/47piD9mOAsu0JqMfeW1ck+lBrnr8BUDXJXhJuHlOS opvMIxy9HhKTT3nJ+uHjgTXh5jGHlQAckfhp3+HWCV34yd+n4vHgqb9UJKYG49dh+5Hf 1jjapv2HYxcu12UvZq0Yshj2QrKa3nk+hNQG7BD1yiZMUY6EjDeoXx0ghsmxUyk4J9Kz iC96v6ItNHlurPJ7pyDbOTGaGqOsnDVrKlkKorswv5XBBYTqmqw/3W1UGH2D89hiJtQb HDnA==; 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=1773647988; x=1774252788; 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=1fdyyWt7yEvQttKqhuiYg3U0RiaG6799ZJYc1VDArPE=; b=A9OxOmJL2Mgk/HtCBWbA/6Ot2FyW1+PE66jatgZQR7CTYb1hUj702YIKwIp6LiXDiy cLLfNbXROQB2Lf/24hG+/z2eHSLZTIePNTMX2VEuFnlfmOJYmM34ytQrfYLwp9hxW6Cw oMV81spnPuL4pcDZiOBHVNT+7lShD7zTaJlfueHe3DibvVyXZBd3/OYTuGdoMlNdarg3 1xZdOYSlVjuK0go2yJoLXCG2HX24/arlu1Yk+oM6fSG5zGZ/wEGquBwRelXMEBjV3cHT zWs6abht23HhS5lHfIRc1u0PUHLpZ88S7eCaF96jUZ72m2h6TS/Ty2nXiUZg0h7pxGQ4 cqyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773647988; x=1774252788; 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=1fdyyWt7yEvQttKqhuiYg3U0RiaG6799ZJYc1VDArPE=; b=Yo1zrdA0ZvnljbSK3q9vZWcwg+tgPXGiPNIFDwIcy86P7RaklLmF6J9GXl+l7SdRAr eBnfeAxcgugn/FnAifJ5u9hh4N+ESCEA+6ng9cMAaT+BTmC7ZAik2nUutgu1pYy0Sdb5 JAREdLz7CS2bt2bMU4zMQFiPhwvYQBWPkZf/Il9R/yjMeJnMmTbpssa8o88av4TaiSXA 67dv6SnH5AZ4naB7SgwrfmHwK3s6leZQdoYV3XWtpxHVeTvj9ZQne+UCWmR0GEt/BaZa Jr6laAHJPorNyw9H1AwWeWyBAn1PJyRka7Qp/U0jUBIrITGSN7GiK199G4hXIQgTtEvL iPQQ== X-Forwarded-Encrypted: i=1; AJvYcCXVqbvKxXUuoSPW/lTI2sVMjUORN2VYuPvsCZHeWhLZle6wKG8Q48PacFWhrkUKRE1S/kojUSUUithfcE/C@lists.postgresql.org X-Gm-Message-State: AOJu0YzluHeKuuvYz2BcW1yVrl659HdIrjwQLNLR8U72vZ1PGlcGa4Ej 3pRpKViEhklah7rINjuoQYZFEOWrJb7lMPmryNecnRTcToZwaDmu31VqxUOdDswUt4fWy1iXTuE F4tzyz4LegAct45w3z1IIrWvUy5FVowk= X-Gm-Gg: ATEYQzyUlypMBLFZ+uu/TkOg3u4aKLDjbN2jCDAfQ9/U6EAthzuigE3uIOV7kqyO9ft LAxFauEXHcQkkf+q2P3HKj82eA/5UCiEOgiFMypuJJN0uKMvJoFn3bzzpGZ4Qy2B9UBxB2aNKSy NwqZ4jsSn3fHgl88ElZv2wTwyhOPS7DZ1S0SMgTkr1/aE8mzXdjQF8Ju1cOc/8v0HVO8cXgZ394 jOFMgB6BpiQS/uNmSYxphwYpWxY+Y6yiioBfnI9DFuxp1CO0LB6jJHag6OY+xSZ24zZFU8/EpB5 lnidMyb5k6zcOQ32koMXntsw1eW7b8JFQ152Vg== X-Received: by 2002:a17:907:3e06:b0:b93:3c03:706e with SMTP id a640c23a62f3a-b976543019cmr662911066b.52.1773647988119; Mon, 16 Mar 2026 00:59:48 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: wenhui qiu Date: Mon, 16 Mar 2026 15:59:36 +0800 X-Gm-Features: AaiRm533cYTJj40yxejBgYITrT2lFukdO_HwJtMBCY3Glgm8mpoRd6hrsKHmNW8 Message-ID: Subject: Re: Report oldest xmin source when autovacuum cannot remove tuples To: Shinya Kato Cc: Sami Imseih , PostgreSQL Hackers Content-Type: multipart/alternative; boundary="00000000000049daba064d1f9a40" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --00000000000049daba064d1f9a40 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable HI Shinya > typedef enum XidHorizonBlockerType > { > XHB_NONE =3D 0, > XHB_ACTIVE_TRANSACTION, > XHB_IDLE_IN_TRANSACTION, > XHB_PREPARED_TRANSACTION, > XHB_XMIN_ACTIVE_TRANSACTION, > XHB_XMIN_IDLE_IN_TRANSACTION, > XHB_HOT_STANDBY_FEEDBACK, > XHB_REPLICATION_SLOT, > } Thank you for your working on this ,I have another small suggestion The priority ordering encoded in XidHorizonBlockerType determines which blocker gets reported when multiple candidates exist. In particular: ACTIVE_TRANSACTION IDLE_IN_TRANSACTION PREPARED_TRANSACTION Prepared transactions are currently ranked after idle-in-transaction sessions. Operationally, prepared transactions are often harder for DBAs to resolve than idle sessions, so it might be worth clarifying the rationale behind this ordering or reconsidering whether prepared transactions should have higher priority. > typedef enum XidHorizonBlockerType > { > XHB_NONE =3D 0, > XHB_ACTIVE_TRANSACTION, > XHB_PREPARED_TRANSACTION, > XHB_IDLE_IN_TRANSACTION, > XHB_XMIN_ACTIVE_TRANSACTION, > XHB_XMIN_IDLE_IN_TRANSACTION, > XHB_HOT_STANDBY_FEEDBACK, > XHB_REPLICATION_SLOT, > } Another one: Currently GetXidHorizonBlocker() selects only one blocker (based on the enum priority) even though multiple independent sources could hold back the xmin horizon simultaneously. For example, it is possible to have both a prepared transaction and a replication slot preventing the horizon from advancing. Have you considered reporting all detected blockers instead of just the highest-priority one? Returning only a single entry might hide other relevant blockers from the user. Thanks On Thu, Feb 5, 2026 at 12:40=E2=80=AFPM Shinya Kato wrote: > HI, > > Sorry for the late reply. I've updated the patch to follow Sami's > recommended approach. > > Overview: > - Instead of modifying ComputeXidHorizons(), this patch introduces two > new functions: GetXidHorizonBlockers() and GetXidHorizonBlocker(). > - GetXidHorizonBlockers() retrieves all potential blockers. This API > design leaves open the possibility of exposing this information > through a dynamic statistics view in the future [0]. > - GetXidHorizonBlocker() selects the highest-priority blocker from the > candidates returned by GetXidHorizonBlockers(). > - Priority is defined in the XidHorizonBlockerType enum. By > distinguishing whether the blocker matches the horizon via xid or > xmin, the appropriate blocker is selected. > > Changes addressed from review comments: > - Fixed unstable regression test (Fujii-san's and Andres's comments). > - When multiple blockers share the same horizon, the blocker with the > highest priority is now selected for output (Fujii-san's comment). > - Removed unnecessary code (Fujii-san's comment). > - Distinguished between active transactions and idle-in-transaction > sessions, and added tests for both (Sami's and Wenhui's comments). > - Added a trailing comma to the last value of the enum (Sami's comment). > - Added a new function GetXidHorizonBlockers(), modeled after > BackendXidGetPid(), instead of modifying ComputeXidHorizons() (Sami's > comment). > - Added a test for a SERIALIZABLE transaction (Sami's comment). > > Not addressed: > - Did not switch from int to pid_t for the pid type, because int is > used consistently throughout the PostgreSQL codebase for this purpose > (Sami's comment). > > Other changes: > - Changed the TAP test to use VACUUM (VERBOSE) instead of autovacuum. > > [0] > https://www.postgresql.org/message-id/CAAaqYe9Dy9sicKg3xzCQUMK3VLdEP39g9n= MGZheqtFYfNiO5Bg%40mail.gmail.com > > > > -- > Best regards, > Shinya Kato > NTT OSS Center > --00000000000049daba064d1f9a40 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
HI Shinya
> typedef enum XidHorizonBlockerType
&= gt; {
> =C2=A0 =C2=A0 XHB_NONE =3D 0,
> =C2=A0 =C2=A0 XHB_ACTIV= E_TRANSACTION,
> =C2=A0 =C2=A0 XHB_IDLE_IN_TRANSACTION,
> =C2= =A0 =C2=A0 XHB_PREPARED_TRANSACTION,
> =C2=A0 =C2=A0 XHB_XMIN_ACTIVE_= TRANSACTION,
> =C2=A0 =C2=A0 XHB_XMIN_IDLE_IN_TRANSACTION,
> = =C2=A0 =C2=A0 XHB_HOT_STANDBY_FEEDBACK,
> =C2=A0 =C2=A0 XHB_REPLICATI= ON_SLOT,
> }
Thank you for your working on this ,I have= another small suggestion=C2=A0
The priority ordering encoded in = XidHorizonBlockerType determines which blocker gets reported when multiple = candidates exist. In particular:

ACTIVE_TRANSACTION
ID= LE_IN_TRANSACTION
PREPARED_TRANSACTION

Prepared transactions are = currently ranked after idle-in-transaction sessions. Operationally, prepare= d transactions are often harder for DBAs to resolve than idle sessions, so = it might be worth clarifying the rationale behind this ordering or reconsid= ering whether prepared transactions should have higher priority.
<= div>> typedef enum XidHorizonBlockerType
> {
> =C2=A0 =C2=A0= XHB_NONE =3D 0,
> =C2=A0 =C2=A0 XHB_ACTIVE_TRANSACTION,
> =C2= =A0 =C2=A0 XHB_PREPARED_TRANSACTION,
> =C2=A0 =C2=A0 XHB_IDLE_IN_TRAN= SACTION,
> =C2=A0 =C2=A0 XHB_XMIN_ACTIVE_TRANSACTION,
> =C2=A0 = =C2=A0 XHB_XMIN_IDLE_IN_TRANSACTION,
> =C2=A0 =C2=A0 XHB_HOT_STANDBY_= FEEDBACK,
> =C2=A0 =C2=A0 XHB_REPLICATION_SLOT,
> }
A= nother one:
Currently GetXidHorizonBlocker() selects only one blocker (b= ased on the enum priority) even though multiple independent sources could h= old back the xmin horizon simultaneously. For example, it is possible to ha= ve both a prepared transaction and a replication slot preventing the horizo= n from advancing.
Have you considered reporting all detected blockers in= stead of just the highest-priority one? Returning only a single entry might= hide other relevant blockers from the user.

<= br>
Thanks

On Thu, Feb 5, 2026 at 12:4= 0=E2=80=AFPM Shinya Kato <shi= nya11.kato@gmail.com> wrote:
HI,

Sorry for the late reply. I've updated the patch to follow Sami's recommended approach.

Overview:
- Instead of modifying ComputeXidHorizons(), this patch introduces two
new functions: GetXidHorizonBlockers() and GetXidHorizonBlocker().
- GetXidHorizonBlockers() retrieves all potential blockers. This API
design leaves open the possibility of exposing this information
through a dynamic statistics view in the future [0].
- GetXidHorizonBlocker() selects the highest-priority blocker from the
candidates returned by GetXidHorizonBlockers().
- Priority is defined in the XidHorizonBlockerType enum. By
distinguishing whether the blocker matches the horizon via xid or
xmin, the appropriate blocker is selected.

Changes addressed from review comments:
- Fixed unstable regression test (Fujii-san's and Andres's comments= ).
- When multiple blockers share the same horizon, the blocker with the
highest priority is now selected for output (Fujii-san's comment).
- Removed unnecessary code (Fujii-san's comment).
- Distinguished between active transactions and idle-in-transaction
sessions, and added tests for both (Sami's and Wenhui's comments).<= br> - Added a trailing comma to the last value of the enum (Sami's comment)= .
- Added a new function GetXidHorizonBlockers(), modeled after
BackendXidGetPid(), instead of modifying ComputeXidHorizons() (Sami's comment).
- Added a test for a SERIALIZABLE transaction (Sami's comment).

Not addressed:
- Did not switch from int to pid_t for the pid type, because int is
used consistently throughout the PostgreSQL codebase for this purpose
(Sami's comment).

Other changes:
- Changed the TAP test to use VACUUM (VERBOSE) instead of autovacuum.

[0] https://www.postgresql.org/message-id/CAAaqYe9Dy9sicKg3xzCQUMK3= VLdEP39g9nMGZheqtFYfNiO5Bg%40mail.gmail.com



--
Best regards,
Shinya Kato
NTT OSS Center
--00000000000049daba064d1f9a40--