Skip to content

Commit cd4b233

Browse files
committed
Invalidate pgoutput's replication-decisions cache upon schema rename.
A schema rename should cause reporting the new qualified names of tables to logical replication subscribers, but that wasn't happening. Flush the RelationSyncCache to make it happen. (If you ask me, the new test case shows that the behavior in this area is still pretty dubious, but apparently it's operating as designed.) Vignesh C Discussion: https://postgr.es/m/CALDaNm32vLRv5KdrDFeVC-CU+4Wg1daA55hMqOxDGJBzvd76-w@mail.gmail.com
1 parent 211d80c commit cd4b233

File tree

2 files changed

+84
-6
lines changed

2 files changed

+84
-6
lines changed

src/backend/replication/pgoutput/pgoutput.c

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1929,7 +1929,22 @@ init_rel_sync_cache(MemoryContext cachectx)
19291929

19301930
Assert(RelationSyncCache != NULL);
19311931

1932+
/* We must update the cache entry for a relation after a relcache flush */
19321933
CacheRegisterRelcacheCallback(rel_sync_cache_relation_cb, (Datum) 0);
1934+
1935+
/*
1936+
* Flush all cache entries after a pg_namespace change, in case it was a
1937+
* schema rename affecting a relation being replicated.
1938+
*/
1939+
CacheRegisterSyscacheCallback(NAMESPACEOID,
1940+
rel_sync_cache_publication_cb,
1941+
(Datum) 0);
1942+
1943+
/*
1944+
* Flush all cache entries after any publication changes. (We need no
1945+
* callback entry for pg_publication, because publication_invalidation_cb
1946+
* will take care of it.)
1947+
*/
19331948
CacheRegisterSyscacheCallback(PUBLICATIONRELMAP,
19341949
rel_sync_cache_publication_cb,
19351950
(Datum) 0);
@@ -2325,8 +2340,8 @@ rel_sync_cache_relation_cb(Datum arg, Oid relid)
23252340
/*
23262341
* Publication relation/schema map syscache invalidation callback
23272342
*
2328-
* Called for invalidations on pg_publication, pg_publication_rel, and
2329-
* pg_publication_namespace.
2343+
* Called for invalidations on pg_publication, pg_publication_rel,
2344+
* pg_publication_namespace, and pg_namespace.
23302345
*/
23312346
static void
23322347
rel_sync_cache_publication_cb(Datum arg, int cacheid, uint32 hashvalue)
@@ -2337,14 +2352,14 @@ rel_sync_cache_publication_cb(Datum arg, int cacheid, uint32 hashvalue)
23372352
/*
23382353
* We can get here if the plugin was used in SQL interface as the
23392354
* RelSchemaSyncCache is destroyed when the decoding finishes, but there
2340-
* is no way to unregister the relcache invalidation callback.
2355+
* is no way to unregister the invalidation callbacks.
23412356
*/
23422357
if (RelationSyncCache == NULL)
23432358
return;
23442359

23452360
/*
2346-
* There is no way to find which entry in our cache the hash belongs to so
2347-
* mark the whole cache as invalid.
2361+
* We have no easy way to identify which cache entries this invalidation
2362+
* event might have affected, so just mark them all invalid.
23482363
*/
23492364
hash_seq_init(&status, RelationSyncCache);
23502365
while ((entry = (RelationSyncEntry *) hash_seq_search(&status)) != NULL)

src/test/subscription/t/100_bugs.pl

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,10 @@
7070
pass('index predicates do not cause crash');
7171

7272
# We'll re-use these nodes below, so drop their replication state.
73-
# We don't bother to drop the tables though.
7473
$node_subscriber->safe_psql('postgres', "DROP SUBSCRIPTION sub1");
7574
$node_publisher->safe_psql('postgres', "DROP PUBLICATION pub1");
75+
# Drop the tables too.
76+
$node_publisher->safe_psql('postgres', "DROP TABLE tab1");
7677

7778
$node_publisher->stop('fast');
7879
$node_subscriber->stop('fast');
@@ -307,6 +308,68 @@
307308
qq(-1|1),
308309
"update works with REPLICA IDENTITY");
309310

311+
# Clean up
312+
$node_subscriber->safe_psql('postgres', "DROP SUBSCRIPTION tap_sub");
313+
$node_publisher->safe_psql('postgres', "DROP PUBLICATION tap_pub");
314+
$node_publisher->safe_psql('postgres', "DROP TABLE tab_replidentity_index");
315+
$node_subscriber->safe_psql('postgres', "DROP TABLE tab_replidentity_index");
316+
317+
# Test schema invalidation by renaming the schema
318+
319+
# Create tables on publisher
320+
$node_publisher->safe_psql('postgres', "CREATE SCHEMA sch1");
321+
$node_publisher->safe_psql('postgres', "CREATE TABLE sch1.t1 (c1 int)");
322+
323+
# Create tables on subscriber
324+
$node_subscriber->safe_psql('postgres', "CREATE SCHEMA sch1");
325+
$node_subscriber->safe_psql('postgres', "CREATE TABLE sch1.t1 (c1 int)");
326+
$node_subscriber->safe_psql('postgres', "CREATE SCHEMA sch2");
327+
$node_subscriber->safe_psql('postgres', "CREATE TABLE sch2.t1 (c1 int)");
328+
329+
# Setup logical replication that will cover t1 under both schema names
330+
$node_publisher->safe_psql('postgres',
331+
"CREATE PUBLICATION tap_pub_sch FOR ALL TABLES");
332+
$node_subscriber->safe_psql('postgres',
333+
"CREATE SUBSCRIPTION tap_sub_sch CONNECTION '$publisher_connstr' PUBLICATION tap_pub_sch"
334+
);
335+
336+
# Wait for initial table sync to finish
337+
$node_subscriber->wait_for_subscription_sync($node_publisher, 'tap_sub_sch');
338+
339+
# Check what happens to data inserted before and after schema rename
340+
$node_publisher->safe_psql(
341+
'postgres',
342+
"begin;
343+
insert into sch1.t1 values(1);
344+
alter schema sch1 rename to sch2;
345+
create schema sch1;
346+
create table sch1.t1(c1 int);
347+
insert into sch1.t1 values(2);
348+
insert into sch2.t1 values(3);
349+
commit;");
350+
351+
$node_subscriber->wait_for_subscription_sync($node_publisher, 'tap_sub_sch');
352+
353+
# Subscriber's sch1.t1 should receive the row inserted into the new sch1.t1,
354+
# but not the row inserted into the old sch1.t1 post-rename.
355+
my $result = $node_subscriber->safe_psql('postgres', "SELECT * FROM sch1.t1");
356+
is( $result, qq(1
357+
2), 'check data in subscriber sch1.t1 after schema rename');
358+
359+
# Subscriber's sch2.t1 won't have gotten anything yet ...
360+
$result = $node_subscriber->safe_psql('postgres', "SELECT * FROM sch2.t1");
361+
is($result, '', 'no data yet in subscriber sch2.t1 after schema rename');
362+
363+
# ... but it should show up after REFRESH.
364+
$node_subscriber->safe_psql('postgres',
365+
'ALTER SUBSCRIPTION tap_sub_sch REFRESH PUBLICATION');
366+
367+
$node_subscriber->wait_for_subscription_sync($node_publisher, 'tap_sub_sch');
368+
369+
$result = $node_subscriber->safe_psql('postgres', "SELECT * FROM sch2.t1");
370+
is( $result, qq(1
371+
3), 'check data in subscriber sch2.t1 after schema rename');
372+
310373
$node_publisher->stop('fast');
311374
$node_subscriber->stop('fast');
312375

0 commit comments

Comments
 (0)