Re: Follow-up review items for update_deleted

From: Nisha Moond <nisha(dot)moond412(at)gmail(dot)com>
To: "Zhijie Hou (Fujitsu)" <houzj(dot)fnst(at)fujitsu(dot)com>
Cc: shveta malik <shveta(dot)malik(at)gmail(dot)com>, pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>, Amit Kapila <amit(dot)kapila16(at)gmail(dot)com>
Subject: Re: Follow-up review items for update_deleted
Date: 2026-09-04 07:26:57
Message-ID: CABdArM4tH9TPfE92UgkAKv87bPZv3JicrcR4pmCOdzPjFTorAQ@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Fri, Sep 4, 2026 at 7:42 AM Zhijie Hou (Fujitsu)
<houzj(dot)fnst(at)fujitsu(dot)com> wrote:
>
> Apart from above comments, I also addressed comments from Kuroda-San[1] and
> Nisha[2].
>

Thanks for the updated patch.

I was testing/reviewing the retention stop-resume path when two dbs
(say db1 and db2) are involved. I see a race condition between the
launcher and the worker that leads to the same assert.
When a sub resumes retention on db2, update_retention_status(true)
sets subretentionactive = true and calls ApplyLauncherWakeup() before
the worker restarts itself through apply_worker_exit().

Now if the launcher runs its next cycle while the old worker's entry
is still in_use(say we hold it just before apply_worker_exit), then in
that cycle, launcher will -
- add the db2 to retained_dbids.
- fire the reset because db2 is not yet in the set.
- Finds w != NULL (still old worker), so
compute_min_nonremovable_xid() reads the "InvalidTransactionId" left
by the earlier stop and returns without contributing to the result.

Now the situation is that update_conflict_slot_xmin() advances the
slot to the horizon of db1 (which can be way ahead of db2's horizon).
When eventually a new worker starts for db2's subscription with a
valid oldest_xmin based on db2's horizon, in the next launcher cycle,
since db2 is already in the retained_dbids list, it will not fire the
reset again. The slot.xmin stays advanced, and when the db2 worker
reaches get_candidate_xid() with its old xmin, it hits the same
assertion.

TRAP: failed Assert("TransactionIdPrecedesOrEquals(MyLogicalRepWorker->oldest_nonremovable_xid,
oldest_running_xid)"), File: "worker.c", Line: 4536, PID: 60377
0 postgres 0x0000000100ac5144
ExceptionalCondition + 216
1 postgres 0x00000001007979c4
get_candidate_xid + 196
2 postgres 0x00000001007978ac
process_rdt_phase_transition + 128
3 postgres 0x00000001007973bc
maybe_advance_nonremovable_xid + 56
4 postgres 0x0000000100791b6c
LogicalRepApplyLoop + 1456
5 postgres 0x00000001007914f0 start_apply + 120
...

Attached is a TAP test that reproduces this issue using an injection
point. The patch applies on top of v2-0001.

--
Thanks,
Nisha

Attachment Content-Type Size
v2-0002-Reproducer-TAP-test.patch application/octet-stream 8.9 KB

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Ashutosh Bapat 2026-09-04 07:34:01 Re: Vertex/Edge label and view
Previous Message solai v 2026-09-04 07:15:19 Re: Introducing find_all_inheritors_ordered()