RE: Conflict detection for update_deleted in logical replication
Zhijie Hou (Fujitsu) <houzj.fnst@fujitsu.com>
Attachments
- v54-0004-Re-create-the-replication-slot-if-the-conflict-r.patch (application/octet-stream) patch v54-0004
- v54-0001-Support-the-conflict-detection-for-update_delete.patch (application/octet-stream) patch v54-0001
- v54-0002-Collect-statistics-for-update_deleted-conflicts.patch (application/octet-stream) patch v54-0002
- v54-0003-Introduce-a-new-GUC-max_conflict_retention_durat.patch (application/octet-stream) patch v54-0003
On Monday, July 28, 2025 7:43 PM shveta malik <shveta.malik@gmail.com> wrote: > On Mon, Jul 28, 2025 at 4:38 PM shveta malik <shveta.malik@gmail.com> > wrote: > > > > On Fri, Jul 25, 2025 at 4:38 PM Zhijie Hou (Fujitsu) > > <houzj.fnst@fujitsu.com> wrote: > > > > > > > > > The V53-0001 also includes Shveta's comments in [1]. > > > > > > > Thanks, I have not yet completed the review, but please find a few > > comments on 001: > > > > 1) > > IsIndexUsableForFindingDeletedTuple() > > We first have: > > + /* > > + * A frozen transaction ID indicates that it must fall behind the > > + conflict > > + * detection slot.xmin. > > + */ > > + if (HeapTupleHeaderXminFrozen(index_tuple->t_data)) > > + return true; > > > > thent his: > > + index_xmin = HeapTupleHeaderGetRawXmin(index_tuple->t_data); > > > > Shall we use HeapTupleHeaderGetXmin() instead of above 2? We can check > > if xid returned by HeapTupleHeaderGetXmin() is FrozenTransactionId or > > normal one and then do further processing. I have simplified the code to avoid unnecessary checks and added generic error handling and proper resource release, which was overlooked in the previous version. > > > > > > 2) > > Both FindRecentlyDeletedTupleInfoByIndex and > > FindRecentlyDeletedTupleInfoSeq() has: > > + /* Exit early if the commit timestamp data is not available */ if > > + (!track_commit_timestamp) return false; > > > > We shall either move this check to FindDeletedTupleInLocalRel() or in > > the caller of FindDeletedTupleInLocalRel() where we check > > 'MySubscription->retaindeadtuples'. Moving to the caller of > > FindDeletedTupleInLocalRel() looks better as there is no need to call > > FindDeletedTupleInLocalRel itself if pre-conditions are not met. Moved. > > > > > > 3) > > FindRecentlyDeletedTupleInfoSeq(): > > + * during change applications. While this approach may be slow on > > + large tables, > > + * it is considered acceptable because it is only used in rare > > + conflict cases > > + * where the target row for an update cannot be found. > > + */ > > Shall we extend the last line to: > > where the target row for an update cannot be found and index scan can > > not be used. Changed. > > > > > > 4) > > catalogs.sgml: > > + If true, he detection of <xref > > + linkend="conflict-update-deleted"/> is > > he --> the > > Fixed. > > 5) > FindRecentlyDeletedTupleInfoSeq(): > > + /* Get the cutoff xmin for HeapTupleSatisfiesVacuum */ oldestxmin = > + GetOldestNonRemovableTransactionId(rel); > > Another point is which xid should be used as threshold in > HeapTupleSatisfiesVacuum() to decide if tuple is DEAD or RECENTLY-DEAD > for update_deleted case? Currently we are using > GetOldestNonRemovableTransactionId() but the xid returned here could be > older than pg_conflict_detection's xmin in presence of other logical slots which > have older effective_xmin. Shall we use pg_conflict_detection's xmin instead > as threshold or worker's oldest_nonremovable_xid? I thought both approaches are fine in terms of correctness, because even if the apply worker will use the outdated xmin to determine the recently dead tuples, user can still compare the timestamp to choose the correct resolution. OTOH, I agree that it would be better to use a stricter rule, so I changed to use the apply worker's oldest_nonremoable_xid to determine recently dead tuples. To be consistent, I also used this xid instead of conflict detection slot.xmin to determine whether an index is usable or not. This is the V54 patch set, with only patch 0001 updated to address the latest comments. Patch 0002 includes separate code for statistics but must be applied together with 0001 to pass the regression tests. This is because the current implementation assumes the number of conflict type in statistics matches the general ConflictType enum; otherwise, it may access an invalid memory area during stat collection. Best Regards, Hou zj
Commits
-
Fix intermittent BF failures in 035_conflicts.
- 0f42206531b3 19 (unreleased) landed
-
Resume conflict-relevant data retention automatically.
- 0d48d393d465 19 (unreleased) landed
-
Fix intermittent test failure introduced in 6456c6e2c4.
- 01d793698f59 19 (unreleased) landed
-
Fix Coverity issue reported in commit a850be2fe.
- 5ac3c1ac22cb 19 (unreleased) landed
-
Add test to prevent premature removal of conflict-relevant data.
- 6456c6e2c4ad 19 (unreleased) landed
-
Post-commit review fixes for 228c370868.
- 1f7e9ba3ac4e 19 (unreleased) landed
-
Add max_retention_duration option to subscriptions.
- a850be2fe653 19 (unreleased) landed
-
Detect and report update_deleted conflicts.
- fd5a1a0c3e56 19 (unreleased) landed
-
Preserve conflict-relevant data during logical replication.
- 228c37086855 19 (unreleased) landed
-
Integrate FullTransactionIds deeper into two-phase code
- 62a17a92833d 19 (unreleased) cited
-
Improve checks for GUC recovery_target_timeline
- fd7d7b719137 19 (unreleased) cited
-
Prevent excessive delays before launching new logrep workers.
- fd519419c948 18.0 cited
-
Keep WAL segments by slot's last saved restart LSN
- ca307d5cec90 18.0 cited
-
Rework some code handling pg_subscription data in psql and pg_dump
- 08691ea958c2 18.0 cited
-
Use generateClonedIndexStmt to propagate CREATE INDEX to partitions.
- 68dfecbef210 18.0 cited