Subject: Re: BUG #19360: Bug Report: Logical Replication initial sync fails with "conflict=update_origin_differs" PG12 toPG18

From: Dilip Kumar <[email-masked]>
Date: Fri, 9 Jan 2026 10:16:04 +0530

On Fri, Jan 9, 2026 at 4:17 AM Masahiko Sawada <[email-masked]> wrote:
>
> On Mon, Dec 29, 2025 at 10:55 PM Amit Kapila <[email-masked]> wrote:
> >
> > On Mon, Dec 29, 2025 at 4:26 PM vignesh C <[email-masked]> wrote:
> > >
> > > On Mon, 22 Dec 2025 at 19:00, PG Bug reporting form
> > > <[email-masked]> wrote:
> > > >
> > >
> > > This can occur in the following scenario: commit timestamp tracking is
> > > enabled on the subscriber; the same table exists on both publisher and
> > > subscriber; a publication is created on the publisher with initial
> > > data; and a subscription is created on the subscriber with origin =
> > > none. During the initial table synchronization, the row is inserted
> > > using a tablesync replication origin, which is dropped once
> > > synchronization completes. If the row is updated on the publisher
> > > after the initial sync, the apply worker attempts to update a row that
> > > was inserted using a different replication origin(tablesync origin),
> > > resulting in an origin mismatch.
> > >
> > > The conflict is logged and logical replication continues normally. No
> > > crash occurs, and the log entry is informational rather than
> > > indicative of a failure.
> > >
> >
> > I agree with this analysis.
> >
> > > These messages can be safely ignored for now.
> > >
> > > We are currently evaluating possible improvements to handle this
> > > scenario more gracefully and to avoid reporting these conflicts in the
> > > future.
> > >
> >
> > One idea to safely ignore these LOGs is we could modify the state
> > management in the catalog pg_subscription_rel to store originID. When
> > a tablesync worker completes, instead of just deleting the origin and
> > setting the relation state to ready, it could record the origin_id it
> > used into pg_subscription_rel.  When the apply worker encounters an
> > origin mismatch, it checks pg_subscription_rel for that specific
> > table. If the "old" origin ID matches the one recorded during the sync
> > phase, the worker knows the row is "ours" and suppresses the log. Now,
> > as the origin ID could be reused, we could additionally store local
> > timestamp along with originId in pg_subscription_rel. Then, we can
> > suppress the log if: row_origin_id == srsuboriginid AND
> > row_commit_time <= srsubsynctime.
>
> It sounds very costly. IIUC we would need these checks for every first
> update to tuples loaded via initial table sync. Can we somehow share
> the apply worker's origin with tablesync workers so that they can
> refer to the same origin ID? Or can we invent special origin IDs
> (e.g., > 0x00FF) that are the same as the normal origin ID except for
> being ignored by the conflict detection system?

How will this distinguish between the initial sync is done from the
publisher node we are getting the update vs the initial sync is done
from some other node?  Can we always ignore conflict checking for
initial synced data or do we just want to ignore if the  initial sync
is done from the same node?

-- 
Regards,
Dilip Kumar
Google

---

From: Masahiko Sawada <[email-masked]>
Date: Fri, 9 Jan 2026 16:56:32 -0800

> is done from the same node?

I imagined the former idea; always ignore conflict checking, so we
don't need to distinguish them. IOW we treat the changes via the
initial tablesync as if the changes made by the normal backend process
(who doesn't use replication origin) while using the replication
tracking ability of the replication origin.

Regards,

-- 
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com

---

From: "Zhijie Hou (Fujitsu)" <[email-masked]>
Date: Fri, 3 Apr 2026 07:24:28 +0000

On Saturday, January 10, 2026 8:57 AM Masahiko Sawada <[email-masked]> wrote:
> > > Can we somehow
> > > share the apply worker's origin with tablesync workers so that they
> > > can refer to the same origin ID? Or can we invent special origin IDs
> > > (e.g., > 0x00FF) that are the same as the normal origin ID except
> > > for being ignored by the conflict detection system?
> >
> I imagined the former idea; always ignore conflict checking, so we don't need
> to distinguish them. IOW we treat the changes via the initial tablesync as if
> the changes made by the normal backend process (who doesn't use
> replication origin) while using the replication tracking ability of the replication
> origin.

I think for changes made by backend process without setting up the origin, the
apply worker still treat that as a conflict change when applying the remote
changes as that's necessary to local vs. remote updates.

I personally prefer to let the tablesync worker share the apply worker's origin
ID while keeping a separate origin for progress tracking. Currently, the worker
first calls replorigin_session_setup() and then stores the origin ID in
replorigin_xact_state. The natural implementation is for the tablesync worker to
still set up its own origin for tracking, but assign the apply worker's origin
ID to the global state. This gives us per‑tablesync progress tracking while
ensuring that changes from both workers appear to come from the same origin.

A small patch demonstrating this approach is attached.

Best Regards,
Hou zj

---

From: "Zhijie Hou (Fujitsu)" <[email-masked]>
Date: Tue, 7 Apr 2026 08:13:38 +0000

On Friday, April 3, 2026 3:24 PM Zhijie Hou (Fujitsu) <[email-masked]> wrote:
> > > > share the apply worker's origin with tablesync workers so that
> > > > they can refer to the same origin ID? Or can we invent special
> > > > origin IDs (e.g., > 0x00FF) that are the same as the normal origin
> > > > ID except for being ignored by the conflict detection system?
> > >
> > > initial synced data or do we just want to ignore if the  initial
> > > sync is done from the same node?
> >
> replorigin_xact_state. The natural implementation is for the tablesync worker
> to still set up its own origin for tracking, but assign the apply worker's origin ID
> to the global state. This gives us per‑tablesync progress tracking while

After further analysis, I think the approach I mentioned earlier is unsafe. When
replaying the commit record during recovery, if only the main apply origin ID is
present, we cannot recover the progress status for each tablesync origin. The
idea of using a special origin ID for all tablesync origins suffers from the
same problem, e.g., progress cannot be recovered when replaying commit WAL
records.

I have been trying to find a way to fix this issue within the proposed
approaches, but I haven't been able to come up with a better solution for now.

One attempt was to continue WAL‑logging the tablesync's own origin ID, but only
store the main origin ID in the commit timestamp module. However, this also has
a problem during recovery: it cannot identify which main origin corresponds to a
given tablesync origin recorded in the commit WAL record. (One might think we
could store this top‑level relationship in the catalog, but since catalogs are
not accessible during recovery, that approach would not work.) Consequently, we
cannot restore the same origin ID in the commit timestamp module during recovery
as was present during normal commit.

The remaining idea: storing the origin ID in pg_subscription_rel and teaching
the apply worker to skip reporting origin_differs if the origin of the update
matches the one stored in pg_subscription_rel, seems worth considering, if we
cannot find an easier solution. There was a concern about performance, but since
we could cache those tablesync origins in a local hash table and consult it
during conflict detection, the performance impact might not be significant.

That said, I may have missed some points. I will continue to think about this
and try to update the patch later.

Best Regards,
Hou zj

---

From: Ajin Cherian <[email-masked]>
Date: Thu, 4 Jun 2026 21:37:30 +1000

> and try to update the patch later.

I have implemented this solution in the attached patch. The patch adds a
new field to pg_subscription_rel, srtablesyncoriginid, which stores the
tablesync worker's origin ID for each relation. These origin IDs are loaded
into a per-subscription hash table after the tablesync COPY completes, and
also when the apply worker starts, thereby avoiding repeated catalog
lookups. During conflict detection, the apply worker checks this hash table
to determine whether the conflict origin ID matches one of the saved
tablesync origin IDs for the subscription. If a match is found, the
conflict is treated as originating from the initial table synchronization
and logging is suppressed. I have not included documentation updates in
this patch. If this approach is considered acceptable, I can prepare the
corresponding documentation changes as well.

regards,
Ajin Cherian
Fujitsu Australia