Обсуждение: Replace is_publishable_class() with relispublishable column in pg_class

Поиск
Список
Период
Сортировка

Replace is_publishable_class() with relispublishable column in pg_class

От
Dilip Kumar
Дата:
Hello hackers,

I would like to propose an improvement to the way PostgreSQL
determines if a relation is eligible for logical replication.
Currently, this check is performed dynamically via
is_publishable_class(), which relies on hard-coded OID checks and
relkind.  As noted in the existing comments atop
is_publishable_class[1], the long-term solution is to add a boolean
column to pg_class and depend on that instead of OID checks.

Motivation
========
1) The check is performed frequently in the logical decoding path
(e.g., in pgoutput_change and pgoutput_truncate). Moving this to a
cached catalog attribute in pg_class allows for a simple check.
2) As suggested by Amit Kpila [2], for the upcoming Conflict Log Table
feature, we need a clean way to exclude these internal conflict log
tables from publication. A catalog flag allows us to set this property
at relation creation rather than adding more special cases.

Proposed Changes
===============
The attached patch implements the following:
1) Catalog Update: Adds relispublishable (bool) to pg_class.
2) Creation Logic: Input parameter is added to
heap_create_with_catalog() so that caller can pass whether the
relation is publishable or not.
3) Update pgoutput and other relevant places to utilize the new
pg_class column directly.

[1]
The best
* long-term solution may be to add a "relispublishable" bool to pg_class,
* and depend on that instead of OID checks.
*/
static bool
is_publishable_class(Oid relid, Form_pg_class reltuple)
{
..
}

[2] https://www.postgresql.org/message-id/CAA4eK1K8Aqm%2BjP_EMPF8H_3UJSEExdwDCaphq6%3DunZZMdcmD0A%40mail.gmail.com

-- 
Regards,
Dilip Kumar
Google

Вложения

Re: Replace is_publishable_class() with relispublishable column in pg_class

От
Andres Freund
Дата:
Hi,

On 2025-12-16 21:19:21 +0530, Dilip Kumar wrote:
> Motivation
> ========
> 1) The check is performed frequently in the logical decoding path
> (e.g., in pgoutput_change and pgoutput_truncate). Moving this to a
> cached catalog attribute in pg_class allows for a simple check.

You could solve this more resource-efficiently by putting the information in
the relcache entry.

> 2) As suggested by Amit Kpila [2], for the upcoming Conflict Log Table
> feature, we need a clean way to exclude these internal conflict log
> tables from publication. A catalog flag allows us to set this property
> at relation creation rather than adding more special cases.

Seems like the issue here also could be addressed the same way?

Greetings,

Andres Freund



Re: Replace is_publishable_class() with relispublishable column in pg_class

От
Andres Freund
Дата:
Hi,

On 2025-12-16 11:28:11 -0500, Andres Freund wrote:
> On 2025-12-16 21:19:21 +0530, Dilip Kumar wrote:
> > 2) As suggested by Amit Kpila [2], for the upcoming Conflict Log Table
> > feature, we need a clean way to exclude these internal conflict log
> > tables from publication. A catalog flag allows us to set this property
> > at relation creation rather than adding more special cases.
> 
> Seems like the issue here also could be addressed the same way?

Actually, wouldn't a table-level property be completely inappropriate for
that?  Imagine one publication that's used for HA (or major version upgrade)
and doesn't use a conflict table, which replicates all tables (including the
conflict table of another pub/sub). And a subscription doing bi-direction
replication that *does* obviously use the conflict table. In one of those
cases you want to replicate changes to the conflict table, in the other
not. So a table / pg_class property would be inappropriate, no?

Greetings,

Andres Freund



Re: Replace is_publishable_class() with relispublishable column in pg_class

От
shveta malik
Дата:
On Tue, Dec 16, 2025 at 9:58 PM Andres Freund <andres@anarazel.de> wrote:
>
> Hi,
>
> On 2025-12-16 21:19:21 +0530, Dilip Kumar wrote:
> > Motivation
> > ========
> > 1) The check is performed frequently in the logical decoding path
> > (e.g., in pgoutput_change and pgoutput_truncate). Moving this to a
> > cached catalog attribute in pg_class allows for a simple check.
>
> You could solve this more resource-efficiently by putting the information in
> the relcache entry.
>

I feel the  proposed solution (marking in pg_class) is required not
only to improve performance by avoiding OID checks in frequently
executed paths, but also to ensure consistent behavior when publishing
tables.

The existing approach does not provide a foolproof solution. As an
example, tables in information_schema are currently not eligible for
publication; but if information_schema is dropped and recreated, those
tables become eligible because their relid no longer falls under
FirstNormalObjectId (see steps in [1]).  There may be other
existing/future scenarios with similar behavior. In my opinion, the
proposed solution is a good approach to ensure consistency in such
cases.

[1]:
****Pub****:
create publication pub1;
ALTER PUBLICATION pub1 ADD TABLE information_schema.sql_sizing;
select * from information_schema.sql_sizing where sizing_id=97;

****Sub****:
create subscription sub1 connection '...' publication pub1 with
(copy_data=false);
select * from information_schema.sql_sizing where sizing_id=97;

****Pub****:
alter table information_schema.sql_sizing replica identity full;
--this is not replicated.
UPDATE information_schema.sql_sizing set supported_value=12 where sizing_id=97;

****Sub****:
postgres=# select supported_value from information_schema.sql_sizing
where sizing_id=97;
 supported_value
-----------------
              0

~~

Then drop and recreate and try to perform the above update again, it
gets replicated:

drop schema information_schema cascade;
./psql -d postgres -f ./../../src/backend/catalog/information_schema.sql -p 5433

****Pub****:
ALTER PUBLICATION pub1 ADD TABLE information_schema.sql_sizing;
select * from information_schema.sql_sizing where sizing_id=97;
alter table information_schema.sql_sizing replica identity full;
--This is replicated
UPDATE information_schema.sql_sizing set supported_value=14 where sizing_id=97;

****Sub****:
--This shows supported_value as 14
postgres=# select supported_value from information_schema.sql_sizing
where sizing_id=97;
 supported_value
-----------------
              14

thanks
Shveta



Re: Replace is_publishable_class() with relispublishable column in pg_class

От
Dilip Kumar
Дата:
On Tue, Dec 16, 2025 at 11:15 PM Andres Freund <andres@anarazel.de> wrote:
>
> Hi,
>
> On 2025-12-16 11:28:11 -0500, Andres Freund wrote:
> > On 2025-12-16 21:19:21 +0530, Dilip Kumar wrote:
> > > 2) As suggested by Amit Kpila [2], for the upcoming Conflict Log Table
> > > feature, we need a clean way to exclude these internal conflict log
> > > tables from publication. A catalog flag allows us to set this property
> > > at relation creation rather than adding more special cases.
> >
> > Seems like the issue here also could be addressed the same way?

Yeah we can do that, in fact the current patch already does that.  The
problem is the conflict log tables are created as regular tables and
in order to identify whether any table is used as a conflict log table
we need to scan the pg_subscription.  So in order to simplify this it
seems the table level property could be a better idea considering the
comments atop is_publishable_class() which says "the long-term
solution is to add a boolean
column to pg_class and depend on that instead of OID checks.".
Another motivation I am seeing is that we can extend this solution to
the SQL level syntax whether users can create tables by marking that
as non-publishable so that even when publication is created with ALL
TABLE options such tables can be excluded.

> Actually, wouldn't a table-level property be completely inappropriate for
> that?  Imagine one publication that's used for HA (or major version upgrade)
> and doesn't use a conflict table, which replicates all tables (including the
> conflict table of another pub/sub). And a subscription doing bi-direction
> replication that *does* obviously use the conflict table. In one of those
> cases you want to replicate changes to the conflict table, in the other
> not. So a table / pg_class property would be inappropriate, no?

Not sure I completely got this point,  but let me explain the
requirement, so the idea is that the conflict log table which are
created for the subscription if the conflict log table option is set
then all the conflict occurred on the node would be inserted into this
table, so you can consider this as a log which we want to use for
better lookup instead of looking into the server logs.  So IMHO this
table stores very node specific conflict information which might not
make any sense for other nodes.

--
Regards,
Dilip Kumar
Google



Re: Replace is_publishable_class() with relispublishable column in pg_class

От
Andres Freund
Дата:
Hi,

On 2025-12-17 09:19:57 +0530, Dilip Kumar wrote:
> On Tue, Dec 16, 2025 at 11:15 PM Andres Freund <andres@anarazel.de> wrote:
> > Actually, wouldn't a table-level property be completely inappropriate for
> > that?  Imagine one publication that's used for HA (or major version upgrade)
> > and doesn't use a conflict table, which replicates all tables (including the
> > conflict table of another pub/sub). And a subscription doing bi-direction
> > replication that *does* obviously use the conflict table. In one of those
> > cases you want to replicate changes to the conflict table, in the other
> > not. So a table / pg_class property would be inappropriate, no?
> 
> Not sure I completely got this point,  but let me explain the
> requirement, so the idea is that the conflict log table which are
> created for the subscription if the conflict log table option is set
> then all the conflict occurred on the node would be inserted into this
> table, so you can consider this as a log which we want to use for
> better lookup instead of looking into the server logs.  So IMHO this
> table stores very node specific conflict information which might not
> make any sense for other nodes.

Imagine you have a bi-directional replication setup between A <-> B. Then you
want to upgrade A to a new major version A'. To minimize downtime, you want to
use logical replication for that. For the logical replication A -> A', you
*would* want to logically replicate the conflict log table, because the
history of logical conflicts is actually important.

Greetings,

Andres Freund



Re: Replace is_publishable_class() with relispublishable column in pg_class

От
Amit Kapila
Дата:
On Wed, Dec 17, 2025 at 9:52 AM Andres Freund <andres@anarazel.de> wrote:
>
> On 2025-12-17 09:19:57 +0530, Dilip Kumar wrote:
> > On Tue, Dec 16, 2025 at 11:15 PM Andres Freund <andres@anarazel.de> wrote:
> > > Actually, wouldn't a table-level property be completely inappropriate for
> > > that?  Imagine one publication that's used for HA (or major version upgrade)
> > > and doesn't use a conflict table, which replicates all tables (including the
> > > conflict table of another pub/sub). And a subscription doing bi-direction
> > > replication that *does* obviously use the conflict table. In one of those
> > > cases you want to replicate changes to the conflict table, in the other
> > > not. So a table / pg_class property would be inappropriate, no?
> >
> > Not sure I completely got this point,  but let me explain the
> > requirement, so the idea is that the conflict log table which are
> > created for the subscription if the conflict log table option is set
> > then all the conflict occurred on the node would be inserted into this
> > table, so you can consider this as a log which we want to use for
> > better lookup instead of looking into the server logs.  So IMHO this
> > table stores very node specific conflict information which might not
> > make any sense for other nodes.
>
> Imagine you have a bi-directional replication setup between A <-> B. Then you
> want to upgrade A to a new major version A'. To minimize downtime, you want to
> use logical replication for that. For the logical replication A -> A', you
> *would* want to logically replicate the conflict log table, because the
> history of logical conflicts is actually important.
>

Yes, we need it for this case but still we need a way to distinguish
cases where we won't need to send conflict table changes for FOR ALL
TABLES publication (say a bi-directional replication set up). For
cases where user wants to allow conflict table changes to be
replicated, we want users to explicitly mention conflict_table in a
publication either as a publication option or explicitly as part of
FOR TABLE syntax, otherwise, its changes won't be replicated. So, to
ignore changes for other publications like FOR ALL TABLES, such a flag
(relispublishable) could be useful. The other way to identify whether
to replicate the changes in conflict table is as mentioned by Dilip
that for the first time when replicating a change for a table, we need
to scan pg_subscription to identify whether the current table is a
conflict log table and then cache it as relsync cache entry. We need
to do this because the conflict table is created as a regular table
along with subscriptions. Do you have any better ideas?

--
With Regards,
Amit Kapila.



Re: Replace is_publishable_class() with relispublishable column in pg_class

От
Peter Smith
Дата:
Hi,

Here is a completely different idea. This may solve the immediate
problem re the replication of the Conflict Log Table (CLT) at least...

0. AFAIK, anything named with a "pg_" prefix generally means that the
named thing is intended for PG internal use, right?

1. So, the CLT should be automatically named to have a "pg_" prefix,
indicating that it really is an internal table, albeit not a system
catalog. IIRC, this kind of name was a recent suggestion anyhow.

2. Then, the CREATE PUBLICATION pub1 FOR ALL TABLES should be modified
to automatically *exclude* any tables having a "pg_" prefix.

3. But, if you *really* still want to publish the CLT, then you can do it:

3a) publish it explicitly
CREATE PUBLICATION pub1 FOR TABLE pg_clt_for_sub_1234

3b) invent a new option so that FOR ALL TABLES won't exclude it:
CREATE PUBLICATION pub1 FOR ALL TABLES WITH (publish_internal_tables=true);

======
Kind Regards,
Peter Smith.
Fujitsu Australia.



Re: Replace is_publishable_class() with relispublishable column in pg_class

От
Amit Kapila
Дата:
On Wed, Dec 17, 2025 at 12:37 PM Peter Smith <smithpb2250@gmail.com> wrote:
>
> Here is a completely different idea. This may solve the immediate
> problem re the replication of the Conflict Log Table (CLT) at least...
>
> 0. AFAIK, anything named with a "pg_" prefix generally means that the
> named thing is intended for PG internal use, right?
>
> 1. So, the CLT should be automatically named to have a "pg_" prefix,
> indicating that it really is an internal table, albeit not a system
> catalog. IIRC, this kind of name was a recent suggestion anyhow.
>

We would also like to give user a provision to provide
conflict_table|conflict_log name. In general also, I am not sure if
assuming this is a good idea as there could be some other internal
table if not now then in future where we would like to allow
replication by default.

--
With Regards,
Amit Kapila.