Hi everyone,

On Wed, 4 Nov, 2026, 6:35 pm Zhijie Hou, <[email protected]> wrote:

> Hi,
>
> While working on another logical replication feature, I noticed a bug: the
> apply worker uses a raw xmin that may have been frozen when fetching tuple
> info
> for conflict detection and reporting.
>
> The reason is that, GetTupleTransactionInfo() used slot_getsysattr() with
> MinTransactionIdAttributeNumber to fetch the local tuple's xmin.
> However, heap_getsysattr() calls HeapTupleHeaderGetRawXmin(), which
> returns the raw 32-bit t_xmin from the tuple header even when the tuple
> is marked frozen (HEAP_XMIN_FROZEN).
>
> Once a tuple is frozen, its raw t_xmin is retained only for debugging
> purposes; its commit timestamp entry may no longer be valid, or the
> 32-bit XID may have wrapped around and been reassigned in a later epoch.
> Using the raw xmin of a frozen tuple could therefore trigger spurious
> update_origin_differs or delete_origin_differs conflicts, or report
> bogus transaction ID, origin, and commit timestamp details for unique
> constraint conflicts.
>
> Attaching a patch fixing this by fetching the tuple via
> ExecFetchSlotHeapTuple() and calling HeapTupleHeaderGetXmin(), returning
> InvalidTransactionId and skipping the commit timestamp lookup when the
> tuple's
> xmin is not a normal transaction ID. Also update
> errdetail_apply_conflict() to
> omit the transaction ID from unique-conflict detail messages when
> localxmin is
> invalid.
>
> --
> Best Regards,
> Zhijie Hou
>

I am ready to contribute, but I've some doubts running with regards to and
design decision.

1. Why did GetTupleTransactionInfo() use slot_getsysattr() originally? Was
it chosen to avoid full tuple conversion/detoasting overhead via
ExecFetchSlotHeapTuple(), and what is the performance trade-off of changing
it?

2. How is a "normal transaction ID" strictly defined here? Does the check
use TransactionIdIsNormal() to filter out frozen, bootstrap, and
bootstrap/subtransaction placeholder XIDs?

3. How can this bug be reliably reproduced in an automated regression test?
Is it possible to artificially freeze a tuple via vacuum or pg_freeze/SQL
commands and trigger a logical replication conflict deterministically, or
does it require specific timing with XID wraparound?

I'm kind of busy as my university exams are approaching but I'll try to
find a perfect time to review the patch.

With Regards,
Osama Abdul Qader.

>

Reply via email to