Thread

  1. Re-read subscription state after lock in AlterSubscription

    Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-07-02T12:08:06Z

    Hi hackers,
    
    while playing with the new ALTER SUBSCRIPTION parameter added in a5918fddf10,
    I realized that the subscription is not re-read once we acquire the lock in
    AlterSubscription().
    
    This pre-existing issue is now more visible after a5918fddf10:
    
    1/ two concurrent ALTER SUBSCRIPTION SET (conflict_log_destination = 'table')
    could result in the second session attempting to create an already-existing
    conflict log table, producing a confusing "relation already exists" error:
    
    ERROR:  relation "pg_conflict_log_24614" already exists
    
    It's confusing because ALTER SUBSCRIPTION SET (conflict_log_destination = 'table')
    would not report an error if the conflict table already exists (and no concurrent
    ALTER is running).
    
    2/ a concurrent DROP followed by the ALTER would emit a NOTICE about creating the
    conflict log table before failing with "referenced subscription was concurrently
    dropped". That sounds like a weird messaging:
    
    NOTICE:  created conflict log table "pg_conflict.pg_conflict_log_24620" for subscription "mysub"
    ERROR:  referenced subscription was concurrently dropped
    
    The attached fixes it by:
    
    - Re-reading the subscription tuple after LockSharedObject() and refreshing the
      Subscription struct.
    - Moving the local variable assignments to after the re-read.
    - Re-checking the password_required privilege restriction after the re-read.
    
    Remarks:
    
    1/ not re-checking password_required after the re-read would still produce a
    "tuple concurrently updated" error, but re-checking it allows us to display a
    better error message.
    
    2/ the ownership check is intentionally not re-done after the lock because
    AlterSubscriptionOwner() does not take AccessExclusiveLock on the subscription
    object: it only takes RowExclusiveLock on the pg_subscription catalog table.
    This means ownership can change regardless of our lock, making a re-check after
    lock acquisition pointless. The existing "tuple concurrently updated" error from
    CatalogTupleUpdate() already provides a protection if ownership changes
    concurrently.
    
    3/ the "privileges" checks are still also done before the lock acquisition because
    we don't want to lock an object we don't have privileges on.
    
    Regards,
    
    -- 
    Bertrand Drouvot
    PostgreSQL Contributors Team
    RDS Open Source Databases
    Amazon Web Services: https://aws.amazon.com
    
  2. Re: Re-read subscription state after lock in AlterSubscription

    Dilip Kumar <dilipbalaut@gmail.com> — 2026-07-02T12:27:09Z

    On Thu, Jul 2, 2026 at 5:38 PM Bertrand Drouvot
    <bertranddrouvot.pg@gmail.com> wrote:
    >
    > Hi hackers,
    >
    > while playing with the new ALTER SUBSCRIPTION parameter added in a5918fddf10,
    > I realized that the subscription is not re-read once we acquire the lock in
    > AlterSubscription().
    >
    > This pre-existing issue is now more visible after a5918fddf10:
    >
    > 1/ two concurrent ALTER SUBSCRIPTION SET (conflict_log_destination = 'table')
    > could result in the second session attempting to create an already-existing
    > conflict log table, producing a confusing "relation already exists" error:
    >
    > ERROR:  relation "pg_conflict_log_24614" already exists
    >
    > It's confusing because ALTER SUBSCRIPTION SET (conflict_log_destination = 'table')
    > would not report an error if the conflict table already exists (and no concurrent
    > ALTER is running).
    >
    > 2/ a concurrent DROP followed by the ALTER would emit a NOTICE about creating the
    > conflict log table before failing with "referenced subscription was concurrently
    > dropped". That sounds like a weird messaging:
    >
    > NOTICE:  created conflict log table "pg_conflict.pg_conflict_log_24620" for subscription "mysub"
    > ERROR:  referenced subscription was concurrently dropped
    >
    > The attached fixes it by:
    >
    > - Re-reading the subscription tuple after LockSharedObject() and refreshing the
    >   Subscription struct.
    > - Moving the local variable assignments to after the re-read.
    > - Re-checking the password_required privilege restriction after the re-read.
    >
    > Remarks:
    >
    > 1/ not re-checking password_required after the re-read would still produce a
    > "tuple concurrently updated" error, but re-checking it allows us to display a
    > better error message.
    >
    > 2/ the ownership check is intentionally not re-done after the lock because
    > AlterSubscriptionOwner() does not take AccessExclusiveLock on the subscription
    > object: it only takes RowExclusiveLock on the pg_subscription catalog table.
    > This means ownership can change regardless of our lock, making a re-check after
    > lock acquisition pointless. The existing "tuple concurrently updated" error from
    > CatalogTupleUpdate() already provides a protection if ownership changes
    > concurrently.
    >
    > 3/ the "privileges" checks are still also done before the lock acquisition because
    > we don't want to lock an object we don't have privileges on.
    >
    
    Thanks Bertrand, yeah this seems like a valid issue, and I agree we
    need to reread the subscription after acquiring the object lock.
    
    -- 
    Regards,
    Dilip Kumar
    Google
    
    
    
    
  3. RE: Re-read subscription state after lock in AlterSubscription

    Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com> — 2026-07-02T12:48:53Z

    Dear Bertrand,
    
    Good catch. Current code allows that old `sub` value is retained, so it sounds
    reasonable fix even for me.
    
    BTW, the issue that GetSubscription() is called before the LockSharedObject() looks
    the existing issues even on REL_13_STABLE. So does it mean that there were no
    cases that concurrent altering can be the unexpected state? At least,
    "retain_dead_tuples" can avoid the issue because the launcher manages the
    conflict slot.
    
    Best regards,
    Hayato Kuroda
    FUJITSU LIMITED
    
    
  4. Re: Re-read subscription state after lock in AlterSubscription

    Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-07-02T13:20:45Z

    Hi Kuroda-san,
    
    On Thu, Jul 02, 2026 at 12:48:53PM +0000, Hayato Kuroda (Fujitsu) wrote:
    > Dear Bertrand,
    > 
    > Good catch. Current code allows that old `sub` value is retained, so it sounds
    > reasonable fix even for me.
    > 
    > BTW, the issue that GetSubscription() is called before the LockSharedObject() looks
    > the existing issues even on REL_13_STABLE. So does it mean that there were no
    > cases that concurrent altering can be the unexpected state?
    
    Yeah, but I think they would produce "tuple concurrently updated" error (due to 
    CatalogTupleUpdate) so that invalid information could not be used.
    
    Regards,
    
    -- 
    Bertrand Drouvot
    PostgreSQL Contributors Team
    RDS Open Source Databases
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  5. RE: Re-read subscription state after lock in AlterSubscription

    Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com> — 2026-07-03T03:13:08Z

    Dear Bertrand,
    
    > Yeah, but I think they would produce "tuple concurrently updated" error (due to
    > CatalogTupleUpdate) so that invalid information could not be used.
    
    I confirmed with PG14 that tuple concurrently updated ERROR can be raised when
    ALTER SUBSCRIPTION DISABLE happens concurrently:
    
    ```
    postgres=# ALTER SUBSCRIPTION sub DISABLE ;
    ERROR:  tuple concurrently updated
    ```
    
    It might be harmless but I think the correct ERROR should be reported: the patch
    should be backpatched. Thought?
    
    Best regards,
    Hayato Kuroda
    FUJITSU LIMITED
    
    
    
    
    
  6. Re: Re-read subscription state after lock in AlterSubscription

    Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-07-03T04:19:00Z

    Hi Kuroda-san,
    
    On Fri, Jul 03, 2026 at 03:13:08AM +0000, Hayato Kuroda (Fujitsu) wrote:
    > Dear Bertrand,
    > 
    > > Yeah, but I think they would produce "tuple concurrently updated" error (due to
    > > CatalogTupleUpdate) so that invalid information could not be used.
    > 
    > I confirmed with PG14 that tuple concurrently updated ERROR can be raised when
    > ALTER SUBSCRIPTION DISABLE happens concurrently:
    > 
    > ```
    > postgres=# ALTER SUBSCRIPTION sub DISABLE ;
    > ERROR:  tuple concurrently updated
    > ```
    
    Yeah, reproducible by using a breakpoint just before acquiring the lock for example.
    
    > It might be harmless but I think the correct ERROR should be reported: the patch
    > should be backpatched. Thought?
    
    I'm not sure about the back patch part as it would only improve error messages
    in a rare race condition (and there is no risk of invalid data being used).
    
    Since a5918fddf10, that's a different story because a table creation is now
    involved.
    
    Regards,
    
    -- 
    Bertrand Drouvot
    PostgreSQL Contributors Team
    RDS Open Source Databases
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  7. Re: Re-read subscription state after lock in AlterSubscription

    Dilip Kumar <dilipbalaut@gmail.com> — 2026-07-03T04:50:32Z

    On Fri, Jul 3, 2026 at 9:49 AM Bertrand Drouvot
    <bertranddrouvot.pg@gmail.com> wrote:
    >
    > Hi Kuroda-san,
    >
    > On Fri, Jul 03, 2026 at 03:13:08AM +0000, Hayato Kuroda (Fujitsu) wrote:
    > > Dear Bertrand,
    > >
    > > > Yeah, but I think they would produce "tuple concurrently updated" error (due to
    > > > CatalogTupleUpdate) so that invalid information could not be used.
    > >
    > > I confirmed with PG14 that tuple concurrently updated ERROR can be raised when
    > > ALTER SUBSCRIPTION DISABLE happens concurrently:
    > >
    > > ```
    > > postgres=# ALTER SUBSCRIPTION sub DISABLE ;
    > > ERROR:  tuple concurrently updated
    > > ```
    >
    > Yeah, reproducible by using a breakpoint just before acquiring the lock for example.
    >
    > > It might be harmless but I think the correct ERROR should be reported: the patch
    > > should be backpatched. Thought?
    >
    > I'm not sure about the back patch part as it would only improve error messages
    > in a rare race condition (and there is no risk of invalid data being used).
    
    Patch LGTM. IMHO we can backpatch this as it is a small change and
    also fixes the bug, without this fix a non-superuser executing  ALTER
    SUBSCRIPTION could bypass the password_required=false restriction if a
    concurrent transaction
    updated that flag.  However, we could argue that this is a corner case
    and can be skipped but given the patch's simplicity, I recommend
    backpatching.
    
    -- 
    Regards,
    Dilip Kumar
    Google
    
    
    
    
  8. Re: Re-read subscription state after lock in AlterSubscription

    Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-07-03T05:52:44Z

    Hi,
    
    On Fri, Jul 03, 2026 at 10:20:32AM +0530, Dilip Kumar wrote:
    > On Fri, Jul 3, 2026 at 9:49 AM Bertrand Drouvot
    > <bertranddrouvot.pg@gmail.com> wrote:
    > >
    > > Hi Kuroda-san,
    > >
    > > On Fri, Jul 03, 2026 at 03:13:08AM +0000, Hayato Kuroda (Fujitsu) wrote:
    > > > Dear Bertrand,
    > > >
    > > > > Yeah, but I think they would produce "tuple concurrently updated" error (due to
    > > > > CatalogTupleUpdate) so that invalid information could not be used.
    > > >
    > > > I confirmed with PG14 that tuple concurrently updated ERROR can be raised when
    > > > ALTER SUBSCRIPTION DISABLE happens concurrently:
    > > >
    > > > ```
    > > > postgres=# ALTER SUBSCRIPTION sub DISABLE ;
    > > > ERROR:  tuple concurrently updated
    > > > ```
    > >
    > > Yeah, reproducible by using a breakpoint just before acquiring the lock for example.
    > >
    > > > It might be harmless but I think the correct ERROR should be reported: the patch
    > > > should be backpatched. Thought?
    > >
    > > I'm not sure about the back patch part as it would only improve error messages
    > > in a rare race condition (and there is no risk of invalid data being used).
    > 
    > Patch LGTM.
    
    Thanks for looking at it!
    
    > IMHO we can backpatch this as it is a small change and
    > also fixes the bug, without this fix a non-superuser executing  ALTER
    > SUBSCRIPTION could bypass the password_required=false restriction if a
    > concurrent transaction
    > updated that flag.
    
    I don't think that's right. I just tested it with a breakpoint that way:
    
    ALTER SUBSCRIPTION mysub SET (password_required = true);
    ALTER SUBSCRIPTION mysub OWNER TO nonsuperuser;
    
    gdb breakpoint at subscriptioncmds.c:1714 on session 1 (nonsuperuser)
    
    session 1 (as nonsuperuser): start ALTER SUBSCRIPTION mysub SET (binary = true);
    session 1 is paused by the breakpoint
    session 2 (as superuser): ALTER SUBSCRIPTION mysub SET (password_required = false); 
    continue session 1, gives:
    
    postgres=> ALTER SUBSCRIPTION mysub SET (binary = true);
    ERROR:  tuple concurrently updated
    
    So it's also "protected" by this error.
    
    > but given the patch's simplicity, I recommend
    > backpatching.
    
    That's right but that would only improve error messages. That said, looking closer,
    they are elog() ones, so "not expected" to occur so yeah backpatch does make sense.
    
    That said, what about also fixing DropSubscription() like in the 0002 attached?
    (that would also produce those elog() messages in case of concurrent DROP or ALTER).
    
    Regards,
    
    -- 
    Bertrand Drouvot
    PostgreSQL Contributors Team
    RDS Open Source Databases
    Amazon Web Services: https://aws.amazon.com
    
  9. RE: Re-read subscription state after lock in AlterSubscription

    Zhijie Hou (Fujitsu) <houzj.fnst@fujitsu.com> — 2026-07-03T08:08:13Z

    On Friday, July 3, 2026 1:53 PM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote:
    > 
    > > but given the patch's simplicity, I recommend backpatching.
    > 
    > That's right but that would only improve error messages. That said, looking
    > closer, they are elog() ones, so "not expected" to occur so yeah backpatch
    > does make sense.
    
    +1 for backpatching, even if it's rare, the "ERROR: tuple concurrently updated"
    message seems confusing to me.
    
    > 
    > That said, what about also fixing DropSubscription() like in the 0002 attached?
    > (that would also produce those elog() messages in case of concurrent DROP or
    > ALTER).
    
    For the patch, I'm not sure if we must repeat the checks twice. Could we
    simply move the original checks to after we take the lock? At least, the
    GetSubscription() call and the password check can be moved there and old codes
    can be deleted.
    
    BTW, this may not be strictly related, but I think it's not safe to do the
    ownership check before locking the subscription as well. If the subscription is
    concurrently dropped, a "tuple concurrently updated" error can still occur.
    
    (Thanks to Kuroda-San for discussing this with me off-list.)
    
    Best Regards,
    Hou zj
    
    
  10. Re: Re-read subscription state after lock in AlterSubscription

    Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-07-03T09:03:24Z

    Hi,
    
    On Fri, Jul 03, 2026 at 08:08:13AM +0000, Zhijie Hou (Fujitsu) wrote:
    > On Friday, July 3, 2026 1:53 PM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote:
    > > 
    > > That said, what about also fixing DropSubscription() like in the 0002 attached?
    > > (that would also produce those elog() messages in case of concurrent DROP or
    > > ALTER).
    > 
    > For the patch, I'm not sure if we must repeat the checks twice.
    
    Thanks for looking at it!
    
    > Could we
    > simply move the original checks to after we take the lock? At least, the
    > GetSubscription() call and the password check can be moved there and old codes
    > can be deleted.
    
    I'm not sure which checks you refer to. The ones that are keep before the lock
    acquisition are because we don't want to lock an object we don't have privileges
    on (see remark 3 in [1]).
    
    > BTW, this may not be strictly related, but I think it's not safe to do the
    > ownership check before locking the subscription as well. If the subscription is
    > concurrently dropped, a "tuple concurrently updated" error can still occur.
    
    That's right, I explained why in remark number 2 in [1]:
    
    "
    the ownership check is intentionally not re-done after the lock because
    AlterSubscriptionOwner() does not take AccessExclusiveLock on the subscription
    object: it only takes RowExclusiveLock on the pg_subscription catalog table.
    This means ownership can change regardless of our lock, making a re-check after
    lock acquisition pointless. The existing "tuple concurrently updated" error from
    CatalogTupleUpdate() already provides a protection if ownership changes
    concurrently.
    "
    
    Does that make sense?
    
    [1]: https://postgr.es/m/akZUpiDa1UfmzYxL%40bdtpg
    
    Regards,
    
    -- 
    Bertrand Drouvot
    PostgreSQL Contributors Team
    RDS Open Source Databases
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  11. RE: Re-read subscription state after lock in AlterSubscription

    Zhijie Hou (Fujitsu) <houzj.fnst@fujitsu.com> — 2026-07-03T09:56:13Z

    On Friday, July 3, 2026 5:03 PM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote:
    > On Fri, Jul 03, 2026 at 08:08:13AM +0000, Zhijie Hou (Fujitsu) wrote:
    > > On Friday, July 3, 2026 1:53 PM Bertrand Drouvot
    > <bertranddrouvot.pg@gmail.com> wrote:
    > > >
    > > > That said, what about also fixing DropSubscription() like in the 0002
    > attached?
    > > > (that would also produce those elog() messages in case of concurrent
    > > > DROP or ALTER).
    > >
    > > For the patch, I'm not sure if we must repeat the checks twice.
    > 
    > Thanks for looking at it!
    > 
    > > Could we
    > > simply move the original checks to after we take the lock? At least,
    > > the
    > > GetSubscription() call and the password check can be moved there and
    > > old codes can be deleted.
    > 
    > I'm not sure which checks you refer to. The ones that are keep before the lock
    > acquisition are because we don't want to lock an object we don't have
    > privileges on (see remark 3 in [1]).
    
    I was referring to the password_required check and the GetSubscription() call.
    
    I think failing the password_required check does not necessarily mean we do not
    have the permission to lock the subscription, It seems to me we only need to
    disallow changing the subscription data in this case. In
    DropSubscription, we take a lock on the subscription regardless of
    password_required.
    
    Best Regards,
    Hou zj
    
    
    
    
  12. Re: Re-read subscription state after lock in AlterSubscription

    Amit Kapila <amit.kapila16@gmail.com> — 2026-07-03T10:15:34Z

    On Fri, Jul 3, 2026 at 1:38 PM Zhijie Hou (Fujitsu)
    <houzj.fnst@fujitsu.com> wrote:
    >
    > On Friday, July 3, 2026 1:53 PM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote:
    > >
    > > > but given the patch's simplicity, I recommend backpatching.
    > >
    > > That's right but that would only improve error messages. That said, looking
    > > closer, they are elog() ones, so "not expected" to occur so yeah backpatch
    > > does make sense.
    >
    > +1 for backpatching, even if it's rare, the "ERROR: tuple concurrently updated"
    > message seems confusing to me.
    >
    
    I also think backpatching makes sense. BTW, I have a comment:
    + heap_freetuple(tup);
    + tup = SearchSysCacheCopy2(SUBSCRIPTIONNAME, ObjectIdGetDatum(MyDatabaseId),
    +   CStringGetDatum(stmt->subname));
    
    heap_freetuple() could be done before acquiring the lock, is there a
    reason to keep it after lock?
    
    > >
    > > That said, what about also fixing DropSubscription() like in the 0002 attached?
    > > (that would also produce those elog() messages in case of concurrent DROP or
    > > ALTER).
    >
    > For the patch, I'm not sure if we must repeat the checks twice. Could we
    > simply move the original checks to after we take the lock? At least, the
    > GetSubscription() call and the password check can be moved there and old codes
    > can be deleted.
    >
    
    Isn't the same true for the AlterSubscription() case as well? Also, I
    noticed that AlterPublication() does the same trick but it uses
    PUBLICATIONOID cacheid, so shouldn't we use SUBSCRIPTIONOID cacheid
    here as well? I think this is to prevent the case where the same name
    pub/sub is recreated after lock.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  13. Re: Re-read subscription state after lock in AlterSubscription

    Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-07-03T15:39:21Z

    Hi,
    
    On Fri, Jul 03, 2026 at 03:45:34PM +0530, Amit Kapila wrote:
    > On Fri, Jul 3, 2026 at 1:38 PM Zhijie Hou (Fujitsu)
    > <houzj.fnst@fujitsu.com> wrote:
    > >
    > > On Friday, July 3, 2026 1:53 PM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote:
    > > >
    > > > > but given the patch's simplicity, I recommend backpatching.
    > > >
    > > > That's right but that would only improve error messages. That said, looking
    > > > closer, they are elog() ones, so "not expected" to occur so yeah backpatch
    > > > does make sense.
    > >
    > > +1 for backpatching, even if it's rare, the "ERROR: tuple concurrently updated"
    > > message seems confusing to me.
    > >
    > 
    > I also think backpatching makes sense. BTW, I have a comment:
    
    Thanks for looking at it!
    
    > + heap_freetuple(tup);
    > + tup = SearchSysCacheCopy2(SUBSCRIPTIONNAME, ObjectIdGetDatum(MyDatabaseId),
    > +   CStringGetDatum(stmt->subname));
    > 
    > heap_freetuple() could be done before acquiring the lock, is there a
    > reason to keep it after lock?
    
    No particular reason, could be done before. Done in 0001 attached.
    
    > 
    > > >
    > > > That said, what about also fixing DropSubscription() like in the 0002 attached?
    > > > (that would also produce those elog() messages in case of concurrent DROP or
    > > > ALTER).
    > >
    > > For the patch, I'm not sure if we must repeat the checks twice. Could we
    > > simply move the original checks to after we take the lock? At least, the
    > > GetSubscription() call and the password check can be moved there and old codes
    > > can be deleted.
    > >
    > 
    > Isn't the same true for the AlterSubscription() case as well?
    
    I think there is no need to lock if we are later going to disallow changing the
    subscription data due to the password_required/superuser check.
    
    That said moving it as suggested by Hou-san, does simplify the code and the lock
    is not held for long, so done that way in 0001.
    
    > Also, I
    > noticed that AlterPublication() does the same trick but it uses
    > PUBLICATIONOID cacheid, so shouldn't we use SUBSCRIPTIONOID cacheid
    > here as well? I think this is to prevent the case where the same name
    > pub/sub is recreated after lock.
    
    Oh right and I did it that way in 0001 and 0002.
    
    But while doing this and looking closely, I'm not sure AlterPublication() does
    it right. Indeed, in theory, the OID could have been re-used too (between the
    time we did the name resolution and the time we lock the publication). I think
    what is needed is something similar to RangeVarGetRelidExtended(), means do the
    name resolution, acl check (ownership) and lock acquisition, all in unison.
    
    That's what 0003 is trying to achieve for the subscription and 0004 for the 
    publication.
    
    What do you think?
    
    Regards,
    
    -- 
    Bertrand Drouvot
    PostgreSQL Contributors Team
    RDS Open Source Databases
    Amazon Web Services: https://aws.amazon.com
    
  14. Re: Re-read subscription state after lock in AlterSubscription

    Dilip Kumar <dilipbalaut@gmail.com> — 2026-07-04T08:00:08Z

    On Fri, Jul 3, 2026 at 9:09 PM Bertrand Drouvot
    <bertranddrouvot.pg@gmail.com> wrote:
    >
    > Hi,
    >
    > On Fri, Jul 03, 2026 at 03:45:34PM +0530, Amit Kapila wrote:
    > > On Fri, Jul 3, 2026 at 1:38 PM Zhijie Hou (Fujitsu)
    > > <houzj.fnst@fujitsu.com> wrote:
    > > >
    > > > On Friday, July 3, 2026 1:53 PM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote:
    > > > >
    > > > > > but given the patch's simplicity, I recommend backpatching.
    > > > >
    > > > > That's right but that would only improve error messages. That said, looking
    > > > > closer, they are elog() ones, so "not expected" to occur so yeah backpatch
    > > > > does make sense.
    > > >
    > > > +1 for backpatching, even if it's rare, the "ERROR: tuple concurrently updated"
    > > > message seems confusing to me.
    > > >
    > >
    > > I also think backpatching makes sense. BTW, I have a comment:
    >
    > Thanks for looking at it!
    >
    > > + heap_freetuple(tup);
    > > + tup = SearchSysCacheCopy2(SUBSCRIPTIONNAME, ObjectIdGetDatum(MyDatabaseId),
    > > +   CStringGetDatum(stmt->subname));
    > >
    > > heap_freetuple() could be done before acquiring the lock, is there a
    > > reason to keep it after lock?
    >
    > No particular reason, could be done before. Done in 0001 attached.
    >
    > >
    > > > >
    > > > > That said, what about also fixing DropSubscription() like in the 0002 attached?
    > > > > (that would also produce those elog() messages in case of concurrent DROP or
    > > > > ALTER).
    > > >
    > > > For the patch, I'm not sure if we must repeat the checks twice. Could we
    > > > simply move the original checks to after we take the lock? At least, the
    > > > GetSubscription() call and the password check can be moved there and old codes
    > > > can be deleted.
    > > >
    > >
    > > Isn't the same true for the AlterSubscription() case as well?
    >
    > I think there is no need to lock if we are later going to disallow changing the
    > subscription data due to the password_required/superuser check.
    >
    > That said moving it as suggested by Hou-san, does simplify the code and the lock
    > is not held for long, so done that way in 0001.
    >
    > > Also, I
    > > noticed that AlterPublication() does the same trick but it uses
    > > PUBLICATIONOID cacheid, so shouldn't we use SUBSCRIPTIONOID cacheid
    > > here as well? I think this is to prevent the case where the same name
    > > pub/sub is recreated after lock.
    >
    > Oh right and I did it that way in 0001 and 0002.
    >
    > But while doing this and looking closely, I'm not sure AlterPublication() does
    > it right. Indeed, in theory, the OID could have been re-used too (between the
    > time we did the name resolution and the time we lock the publication). I think
    > what is needed is something similar to RangeVarGetRelidExtended(), means do the
    > name resolution, acl check (ownership) and lock acquisition, all in unison.
    >
    > That's what 0003 is trying to achieve for the subscription and 0004 for the
    > publication.
    >
    > What do you think?
    >
    0003:
    
    It looks like the implementation of DROP SUBSCRIPTION IF EXISTS has a
    concurrent drop race condition in DropSubscription(). Currently, if
    stmt->missing_ok is true, the initial lookup safely handles a missing
    subscription. However, once a subscription is found and the code
    enters the drop loop, a second internal lookup/refetch happens. If a
    concurrent transaction drops the subscription after our initial check
    but before this internal refetch, the code throws an error.
    Essentially, the loop completely ignores the missing_ok flag during
    the refetch phase.  Am I missing something?
    
    -- 
    Regards,
    Dilip Kumar
    Google
    
    
    
    
  15. Re: Re-read subscription state after lock in AlterSubscription

    Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-07-04T08:19:46Z

    Hi,
    
    On Sat, Jul 04, 2026 at 01:30:08PM +0530, Dilip Kumar wrote:
    > On Fri, Jul 3, 2026 at 9:09 PM Bertrand Drouvot
    > <bertranddrouvot.pg@gmail.com> wrote:
    > >
    > > But while doing this and looking closely, I'm not sure AlterPublication() does
    > > it right. Indeed, in theory, the OID could have been re-used too (between the
    > > time we did the name resolution and the time we lock the publication). I think
    > > what is needed is something similar to RangeVarGetRelidExtended(), means do the
    > > name resolution, acl check (ownership) and lock acquisition, all in unison.
    > >
    > > That's what 0003 is trying to achieve for the subscription and 0004 for the
    > > publication.
    > >
    > > What do you think?
    > >
    > 0003:
    > 
    > It looks like the implementation of DROP SUBSCRIPTION IF EXISTS has a
    > concurrent drop race condition in DropSubscription(). Currently, if
    > stmt->missing_ok is true, the initial lookup safely handles a missing
    > subscription. However, once a subscription is found and the code
    > enters the drop loop, a second internal lookup/refetch happens. If a
    > concurrent transaction drops the subscription after our initial check
    > but before this internal refetch, the code throws an error.
    > Essentially, the loop completely ignores the missing_ok flag during
    > the refetch phase.
    
    Good catch, will fix, thanks! 
    
    Regards,
    
    -- 
    Bertrand Drouvot
    PostgreSQL Contributors Team
    RDS Open Source Databases
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  16. RE: Re-read subscription state after lock in AlterSubscription

    Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com> — 2026-07-06T02:43:20Z

    Dear Bertrand,
    
    Thanks for updating the patch. I found one issue:
    
    ```
    	/* DROP hook for the subscription being removed */
    	InvokeObjectDropHook(SubscriptionRelationId, subid, 0);
    
    ```
    
    I think the reporting should be after the loop, otherwise the wrong subid can be
    reported. Am I missing something?
    
    Best regards,
    Hayato Kuroda
    FUJITSU LIMITED
    
    
  17. Re: Re-read subscription state after lock in AlterSubscription

    Amit Kapila <amit.kapila16@gmail.com> — 2026-07-06T04:54:26Z

    On Fri, Jul 3, 2026 at 9:09 PM Bertrand Drouvot
    <bertranddrouvot.pg@gmail.com> wrote:
    >
    > On Fri, Jul 03, 2026 at 03:45:34PM +0530, Amit Kapila wrote:
    >
    > But while doing this and looking closely, I'm not sure AlterPublication() does
    > it right. Indeed, in theory, the OID could have been re-used too (between the
    > time we did the name resolution and the time we lock the publication). I think
    > what is needed is something similar to RangeVarGetRelidExtended(), means do the
    > name resolution, acl check (ownership) and lock acquisition, all in unison.
    >
    
    It seems RangeVarGetRelidExtended() also doesn't do the additional
    invalidation handling if the caller already has an appropriate lock,
    see comments [1]. Apart from that also, I am not sure it is a good
    ideal to add this additional handling in Pub/Sub DDLs as in worst case
    scenario even if the OID is re-used the user will face "tuple
    concurrently updated" or similar ERRORs, it won't do anything wrong.
    So for such rare cases, it doesn't seem worth adding this additional
    re-checking machinery. Based on the same theory, I am thinking again
    whether it is worth backpatching these patches? I mean these fall into
    the category of improving user facing messages during Pub/Sub DDLs, so
    isn't it okay to just push this work in HEAD?
    
    [1]:
    /*
     * If no lock requested, we assume the caller knows what they're
     * doing.  They should have already acquired a heavyweight lock on
     * this relation earlier in the processing of this same statement, so
     * it wouldn't be appropriate to AcceptInvalidationMessages() here, as
     * that might pull the rug out from under them.
     */
    if (lockmode == NoLock)
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  18. Re: Re-read subscription state after lock in AlterSubscription

    Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-07-06T05:01:00Z

    Hi Kuroda-san,
    
    On Mon, Jul 06, 2026 at 02:43:20AM +0000, Hayato Kuroda (Fujitsu) wrote:
    > Dear Bertrand,
    > 
    > Thanks for updating the patch. I found one issue:
    > 
    > ```
    > 	/* DROP hook for the subscription being removed */
    > 	InvokeObjectDropHook(SubscriptionRelationId, subid, 0);
    > 
    > ```
    > 
    > I think the reporting should be after the loop, otherwise the wrong subid can be
    > reported.
    
    Yeah, and I think this is an existing behavior not related to the patch. Currently,
    InvokeObjectDropHook() is called before we lock the subscription. I think that
    makes more sense to do it after the lock is acquired, so this is now changed in
    0002.
    
    Also addressing Dilip's comment in the attached.
    
    Regards,
    
    -- 
    Bertrand Drouvot
    PostgreSQL Contributors Team
    RDS Open Source Databases
    Amazon Web Services: https://aws.amazon.com
    
  19. Re: Re-read subscription state after lock in AlterSubscription

    Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-07-06T05:43:28Z

    Hi,
    
    On Mon, Jul 06, 2026 at 10:24:26AM +0530, Amit Kapila wrote:
    > On Fri, Jul 3, 2026 at 9:09 PM Bertrand Drouvot
    > <bertranddrouvot.pg@gmail.com> wrote:
    > >
    > > On Fri, Jul 03, 2026 at 03:45:34PM +0530, Amit Kapila wrote:
    > >
    > > But while doing this and looking closely, I'm not sure AlterPublication() does
    > > it right. Indeed, in theory, the OID could have been re-used too (between the
    > > time we did the name resolution and the time we lock the publication). I think
    > > what is needed is something similar to RangeVarGetRelidExtended(), means do the
    > > name resolution, acl check (ownership) and lock acquisition, all in unison.
    > >
    > 
    > It seems RangeVarGetRelidExtended() also doesn't do the additional
    > invalidation handling if the caller already has an appropriate lock,
    > see comments [1].
    
    From what I can see, the NoLock callers of RangeVarGetRelidExtended(), are for
    callers that don't modify objects (they are "read only" callers). The only
    exception is nextval() but there is an XXX that mentions it.
    
    Here we modify the subscription or publication, so I don't think we are in the
    NoLock spirit of RangeVarGetRelidExtended().
    
    > Apart from that also, I am not sure it is a good
    > ideal to add this additional handling in Pub/Sub DDLs as in worst case
    > scenario even if the OID is re-used the user will face "tuple
    > concurrently updated" or similar ERRORs, it won't do anything wrong.
    
    That's probably right before a5918fddf10, but with conflict_log_destination='table'
    we now perform creating/dropping a table based on the stale data before it ever
    reaches CatalogTupleUpdate().
    
    Also, even prior a5918fddf10 I believe there might be situations that could not
    produce the tuple concurrently updated" but corrupt the tuple (say if vacuum had 
    the time to clean up the tuple and the same ctid is reused). Probably extremely
    rare scenario, though.
    
    > So for such rare cases, it doesn't seem worth adding this additional
    > re-checking machinery. Based on the same theory, I am thinking again
    > whether it is worth backpatching these patches? I mean these fall into
    > the category of improving user facing messages during Pub/Sub DDLs, so
    > isn't it okay to just push this work in HEAD?
    
    Those are not ereport() but elog() messages so not "expected" to happen.
    
    Based on the above, I'm thinking that backpatching 0001 and 0002 and keep
    0003 and 0004 only for HEAD could make sense.
    
    What do you think?
    
    Regards,
    
    -- 
    Bertrand Drouvot
    PostgreSQL Contributors Team
    RDS Open Source Databases
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  20. Re: Re-read subscription state after lock in AlterSubscription

    Amit Kapila <amit.kapila16@gmail.com> — 2026-07-06T09:37:24Z

    On Mon, Jul 6, 2026 at 11:13 AM Bertrand Drouvot
    <bertranddrouvot.pg@gmail.com> wrote:
    >
    > On Mon, Jul 06, 2026 at 10:24:26AM +0530, Amit Kapila wrote:
    > > On Fri, Jul 3, 2026 at 9:09 PM Bertrand Drouvot
    > > <bertranddrouvot.pg@gmail.com> wrote:
    > > >
    > > > On Fri, Jul 03, 2026 at 03:45:34PM +0530, Amit Kapila wrote:
    > > >
    > > > But while doing this and looking closely, I'm not sure AlterPublication() does
    > > > it right. Indeed, in theory, the OID could have been re-used too (between the
    > > > time we did the name resolution and the time we lock the publication). I think
    > > > what is needed is something similar to RangeVarGetRelidExtended(), means do the
    > > > name resolution, acl check (ownership) and lock acquisition, all in unison.
    > > >
    > >
    > > It seems RangeVarGetRelidExtended() also doesn't do the additional
    > > invalidation handling if the caller already has an appropriate lock,
    > > see comments [1].
    >
    > From what I can see, the NoLock callers of RangeVarGetRelidExtended(), are for
    > callers that don't modify objects (they are "read only" callers). The only
    > exception is nextval() but there is an XXX that mentions it.
    >
    > Here we modify the subscription or publication, so I don't think we are in the
    > NoLock spirit of RangeVarGetRelidExtended().
    >
    
    IIUC, here the risk is that during the first read and before we take
    the Lock, if the same OID is reused for a different subscription then
    we may end up modifying an unintended subscription. I think that is a
    theoretical risk rather than a practical one. We already note similar
    risk at other places, like see comments atop GetNewOidWithIndex(Since
    the OID is not immediately inserted into the table, there is a race
    condition here; but a problem could occur only if someone else managed
    to cycle through 2^32 OIDs and generate the same OID before we finish
    inserting our row. This seems unlikely to be a problem.). Similarly
    comments atop GetNewRelFileNumber( As with GetNewOidWithIndex(), there
    is some theoretical risk of a race) made a note of similar risk. I
    feel we should note this in comments rather than trying to add
    additional code to handle it.
    
    As per my understanding the loop exists in RangeVarGetRelidExtended()
    because relation lookup follows the name, and no lock can pin a
    name->OID binding, so the binding can be rebound by concurrent DDL
    between lookup and lock. Concretely, our lock protects relation X's
    OID, but it can't stop someone renaming X away and handing X's old
    name to a different relation Y. The lockable thing (the OID) and the
    thing that changes (the name binding) are different objects. The loop
    detects exactly this: acquiring the lock runs
    AcceptInvalidationMessages(), and if any invalidations arrived while
    we waited (inval_count == SharedInvalidMessageCounter), it re-resolves
    the name. If the name now maps to a different OID than the one we
    locked, it releases the old lock and locks the new OID. It repeats
    until the name resolves to the same OID across a lock acquisition —
    i.e. until the binding is stable while locked. OTOH, the subscription
    path follows the locked OID instead, so it needs only a single
    re-read.
    
    With Regards,
    Amit Kapila.
    
    
    
    
  21. Re: Re-read subscription state after lock in AlterSubscription

    Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-07-06T14:53:39Z

    Hi,
    
    On Mon, Jul 06, 2026 at 03:07:24PM +0530, Amit Kapila wrote:
    > On Mon, Jul 6, 2026 at 11:13 AM Bertrand Drouvot
    > <bertranddrouvot.pg@gmail.com> wrote:
    > >
    > > > It seems RangeVarGetRelidExtended() also doesn't do the additional
    > > > invalidation handling if the caller already has an appropriate lock,
    > > > see comments [1].
    > >
    > > From what I can see, the NoLock callers of RangeVarGetRelidExtended(), are for
    > > callers that don't modify objects (they are "read only" callers). The only
    > > exception is nextval() but there is an XXX that mentions it.
    > >
    > > Here we modify the subscription or publication, so I don't think we are in the
    > > NoLock spirit of RangeVarGetRelidExtended().
    > >
    > 
    > IIUC, here the risk is that during the first read and before we take
    > the Lock, if the same OID is reused for a different subscription then
    > we may end up modifying an unintended subscription. I think that is a
    > theoretical risk rather than a practical one.
    
    I agree OID reuse itself is theoretical.
    
    > As per my understanding the loop exists in RangeVarGetRelidExtended()
    > because relation lookup follows the name, and no lock can pin a
    > name->OID binding, so the binding can be rebound by concurrent DDL
    > between lookup and lock. Concretely, our lock protects relation X's
    > OID, but it can't stop someone renaming X away and handing X's old
    > name to a different relation Y.
    
    Not sure it's only about renaming. The commit message of 4240e429d0c mentions
    "This was particularly problematic in the case where a table had been dropped
    and recreated". b3ad5d02c9c also used the same logic and reasoning "avoids
    needlessly failing when the object of interest is concurrently dropped and
    recreated".
    
    Also in 4240e429d0c: "there's nothing at all here to guard against similar race
    conditions for non-relations": I think that subscriptions and publications are
    among those non-relations cases.
    
    > The lockable thing (the OID) and the
    > thing that changes (the name binding) are different objects. The loop
    > detects exactly this: acquiring the lock runs
    > AcceptInvalidationMessages(), and if any invalidations arrived while
    > we waited (inval_count == SharedInvalidMessageCounter), it re-resolves
    > the name. If the name now maps to a different OID than the one we
    > locked, it releases the old lock and locks the new OID. It repeats
    > until the name resolves to the same OID across a lock acquisition —
    > i.e. until the binding is stable while locked. OTOH, the subscription
    > path follows the locked OID instead, so it needs only a single
    > re-read.
    
    I think that's for example what RemoveRelations() was doing before 4240e429d0c
    and what get_object_address() was doing before b3ad5d02c9c:
    
    1/ Resolve name to OID
    2/ Lock by OID
    3/ Check if it still exists
    4/ If gone then elog(ERROR..
    
    but has been changed in b3ad5d02c9c with a retry loop.
    
    Also looking at get_object_address(), I can see that it handles publications and
    subscriptions:
    
    case OBJECT_PUBLICATION:
    case OBJECT_SUBSCRIPTION:
      address = get_object_address_unqualified(objtype,
                                               castNode(String, object), missing_ok);
    
    and that DROP PUBLICATION goes through it, so that it already benefits from the
    retry loop in get_object_address().
    
    DROP SUBSCRIPTION however has its own dedicated code path and does not go through
    get_object_address(): 0003 adds the retry loop for it. And if DROP already uses
    the retry loop then ALTER should probably use it too (also done in 0003 and 0004).
    
    Regards,
    
    -- 
    Bertrand Drouvot
    PostgreSQL Contributors Team
    RDS Open Source Databases
    Amazon Web Services: https://aws.amazon.com