| From: | Vaibhav Dalvi <vaibhav(dot)dalvi(at)enterprisedb(dot)com> |
|---|---|
| To: | PostgreSQL Hackers <pgsql-hackers(at)postgresql(dot)org>, Amit Kapila <amit(dot)kapila(at)enterprisedb(dot)com>, Fujii Masao <masao(dot)fujii(at)gmail(dot)com> |
| Cc: | Ashutosh Sharma <ashu(dot)coek88(at)gmail(dot)com> |
| Subject: | Re: walreceiver: stale hot_standby_feedback xmin on primary when wal_receiver_status_interval = 0 (PG16 and later) |
| Date: | 2026-10-19 07:20:54 |
| Message-ID: | CA+vB=AFnGbk54yiVair8=387AkFuKiC1NouaM2wdfUOjGSd4WA@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi,
I added a CommitFest entry: https://commitfest.postgresql.org/patch/7480.
CFBot requested a rebase, so I rebased it, and here's the revised patch.
Regards,
Vaibhav
On Mon, Oct 19, 2026 at 9:43 AM Vaibhav Dalvi <
vaibhav(dot)dalvi(at)enterprisedb(dot)com> wrote:
> Hi,
>
> I am planning to add a CommitFest entry for this bug. Please find the
> patch attached for master.
>
> Could you please review it?
>
> Regards,
> Vaibhav
>
>
> On Thu, Oct 15, 2026 at 4:55 PM Vaibhav Dalvi <
> vaibhav(dot)dalvi(at)enterprisedb(dot)com> wrote:
>
>> Hi Hackers,
>>
>> When hot_standby_feedback is on and wal_receiver_status_interval is 0,
>> the primary keeps the xmin sent at connection time and never releases
>> it, so VACUUM on the primary is held back. I see this on PG16, 17, 18,
>> 19 and master. PG14 and PG15 are not affected (reason at the end).
>>
>> *Cause:* XLogWalRcvSendHSFeedback() decides "feedback is disabled" in two
>> different ways. The wakeup time and the early return treat it as
>> disabled when
>> the interval is <= 0 or hot_standby_feedback is off, but the code that
>> picks the
>> xmin checks only hot_standby_feedback. So the first message at connect
>> (immed = true)
>> carries a real xmin, and then no more messages are sent because the
>> wakeup is infinity.
>>
>> The same happens when the interval is changed from a positive value to 0
>> and the configuration is reloaded. The feedback sent on reload carries
>> a fresh valid xmin, and then nothing follows.
>>
>> *Steps to reproduce:*
>>
>> initdb -D pg20_primary
>> echo "port = 5525" >> pg20_primary/postgresql.conf
>> pg_ctl -D pg20_primary -l primary.log -w start
>>
>> pg_basebackup -p 5525 -D pg20_standby -R -X stream
>> cat >> pg20_standby/postgresql.conf <<EOF
>> port = 5526
>> hot_standby_feedback = on
>> wal_receiver_status_interval = 0
>> EOF
>> pg_ctl -D pg20_standby -l standby.log -w start
>>
>> primary="psql -p 5525 -d postgres -X"
>> $primary -c "select backend_xmin, age(backend_xmin)
>> from pg_stat_replication"
>> backend_xmin | age
>> --------------+-----
>> 664 | 0
>>
>> $primary -c "create table hsf_t with (autovacuum_enabled = off)
>> as select g a from generate_series(1,100000) g"
>> for i in $(seq 2000); do echo "select txid_current();"; done |
>> $primary -Atq > /dev/null
>> $primary -c "delete from hsf_t"
>> $primary -c "checkpoint"
>> sleep 6
>>
>> $primary -c "select backend_xmin, age(backend_xmin) from
>> pg_stat_replication"
>> backend_xmin | age
>> --------------+------
>> 664 | 2013
>>
>> $primary -c "vacuum (verbose, index_cleanup off) hsf_t"
>> tuples: 0 removed, 100000 remain, 100000 are dead but not yet removable
>> removable cutoff: 664, which was 2013 XIDs old when operation ended
>>
>> The xmin does not move. As a control, set the interval to 1 on the
>> standby and reload:
>>
>> psql -p 5526 -d postgres -X -c "alter system set
>> wal_receiver_status_interval = 1" -c "select pg_reload_conf()"
>> sleep 3
>>
>> Now backend_xmin is 2677 (age 0), and the next VACUUM removes all the
>> 100000 tuples.
>>
>> *Fix:* compute the xmin only when feedback is really enabled.
>>
>> - if (hot_standby_feedback)
>> + if (hot_standby_feedback && wal_receiver_status_interval > 0)
>>
>> With interval 0, the walreceiver now sends InvalidTransactionId, so the
>> primary releases the xmin. This keeps "0 disables the status updates",
>> as the documentation says. Please let me know if people agree with it.
>>
>> The patches also add a TAP test
>> (0NN_hot_standby_feedback_status_interval.pl
>> in src/test/recovery). It uses a physical slot, so the xmin kept by the
>> primary is visible in pg_replication_slots.xmin. It checks that a
>> positive
>> interval sends a valid xmin (control), and that interval 0 leaves no
>> xmin, both on reload and on connect. The zero-interval cases look at the
>> DEBUG2 feedback message in the standby log, so there is no timing race,
>> and then poll until the slot xmin is NULL. It fails without the fix and
>> passes with it.
>>
>> The test makes the walreceiver reconnect before checking the connect-time
>> feedback. No feedback is sent while Hot Standby is not yet active, and
>> with feedback disabled it is not retried. So an old slot xmin may stay if
>> the walreceiver connects too early. That is a separate issue, and I will
>> start a new thread for it.
>>
>> I am attaching 4 patches. The code change is the same in all. Only the
>> test file number and the meson.build context differ per branch:
>>
>>
>> v1-walreceiver-Do-not-leave-stale-xmin-on-primary-when-status-interval-is-zero-Master.patch
>>
>> v1-walreceiver-Do-not-leave-stale-xmin-on-primary-when-status-interval-is-zero-PG19.patch
>>
>> v1-walreceiver-Do-not-leave-stale-xmin-on-primary-when-status-interval-is-zero-PG18.patch
>>
>> v1-walreceiver-Do-not-leave-stale-xmin-on-primary-when-status-interval-is-zero-PG16-17.patch
>>
>>
>> Why PG14 and PG15 are not affected: I ran the same steps there, and the
>> xmin keeps moving (733 to 2735 on PG14, 734 to 2736 on PG15). These
>> versions have no wakeup time array. Their "at most once per interval"
>> check is
>>
>> if (!TimestampDifferenceExceeds(sendTime, now,
>> wal_receiver_status_interval * 1000))
>> return;
>>
>> and with interval 0 it is always true, so feedback with a fresh xmin
>> goes out on every call. The infinite wakeup was added in PG16. (Note
>> that in PG14 and PG15 an interval of 0 does not really disable the
>> feedback, but I did not change that.)
>>
>> Thanks and regards,
>> Vaibhav Dalvi
>>
>>
>>
>>
| Attachment | Content-Type | Size |
|---|---|---|
| v2-walreceiver-Do-not-leave-stale-xmin-on-primary-when-status-interval-is-zero-Master.patch | application/octet-stream | 8.8 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Chao Li | 2026-10-19 07:30:45 | Re: Fix stale allocChunkLimit when recycling AllocSet |
| Previous Message | Peter Eisentraut | 2026-10-19 07:20:47 | Re: Use C11 alignas instead of palloc/malloc for alignment |