Обсуждение: Replace is_publishable_class() with relispublishable column in pg_class
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
Вложения
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
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
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
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
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
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.
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.
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.