Fix data inconsistency between publisher and subscriber.
authorAmit Kapila <akapila@postgresql.org>
Thu, 16 Jun 2022 03:15:07 +0000 (08:45 +0530)
committerAmit Kapila <akapila@postgresql.org>
Thu, 16 Jun 2022 03:15:07 +0000 (08:45 +0530)
We were not updating the partition map cache in the subscriber even when
the corresponding remote rel is changed. Due to this data was getting
incorrectly replicated for partition tables after the publisher has
changed the table schema.

Fix it by resetting the required entries in the partition map cache after
receiving a new relation mapping from the publisher.

Reported-by: Shi Yu
Author: Shi Yu, Hou Zhijie
Reviewed-by: Amit Langote, Amit Kapila
Backpatch-through: 13, where it was introduced
Discussion: https://postgr.es/m/OSZPR01MB6310F46CD425A967E4AEF736FDA49@OSZPR01MB6310.jpnprd01.prod.outlook.com

src/backend/replication/logical/relation.c
src/backend/replication/logical/worker.c
src/include/replication/logicalrelation.h
src/test/subscription/t/013_partition.pl

index 9c9ec144d8bcc9750a50734e0fd44d6722568f46..34c55c04e308309f65cc127c7459a7a8f7f9fec4 100644 (file)
@@ -486,6 +486,40 @@ logicalrep_partmap_invalidate_cb(Datum arg, Oid reloid)
    }
 }
 
+/*
+ * Reset the entries in the partition map that refer to remoterel.
+ *
+ * Called when new relation mapping is sent by the publisher to update our
+ * expected view of incoming data from said publisher.
+ *
+ * Note that we don't update the remoterel information in the entry here,
+ * we will update the information in logicalrep_partition_open to avoid
+ * unnecessary work.
+ */
+void
+logicalrep_partmap_reset_relmap(LogicalRepRelation *remoterel)
+{
+   HASH_SEQ_STATUS status;
+   LogicalRepPartMapEntry *part_entry;
+   LogicalRepRelMapEntry *entry;
+
+   if (LogicalRepPartMap == NULL)
+       return;
+
+   hash_seq_init(&status, LogicalRepPartMap);
+   while ((part_entry = (LogicalRepPartMapEntry *) hash_seq_search(&status)) != NULL)
+   {
+       entry = &part_entry->relmapentry;
+
+       if (entry->remoterel.remoteid != remoterel->remoteid)
+           continue;
+
+       logicalrep_relmap_free_entry(entry);
+
+       memset(entry, 0, sizeof(LogicalRepRelMapEntry));
+   }
+}
+
 /*
  * Initialize the partition map cache.
  */
index fc210a9e7b947554fd2706add36d58f105bf515a..607f719fd61a86c3fa8071b95cb02deae8ee0e1a 100644 (file)
@@ -1562,6 +1562,9 @@ apply_handle_relation(StringInfo s)
 
    rel = logicalrep_read_rel(s);
    logicalrep_relmap_update(rel);
+
+   /* Also reset all entries in the partition map that refer to remoterel. */
+   logicalrep_partmap_reset_relmap(rel);
 }
 
 /*
index 7bf8cd22bd49d86034a00cfc14d20f7bdb888ddb..78cd7e77f541029fc29fcf862a0af9c05f6fd4e6 100644 (file)
@@ -38,6 +38,7 @@ typedef struct LogicalRepRelMapEntry
 } LogicalRepRelMapEntry;
 
 extern void logicalrep_relmap_update(LogicalRepRelation *remoterel);
+extern void logicalrep_partmap_reset_relmap(LogicalRepRelation *remoterel);
 
 extern LogicalRepRelMapEntry *logicalrep_rel_open(LogicalRepRelId remoteid,
                                                  LOCKMODE lockmode);
index 06f9215018490f7095b665dcde850d91c584dd9a..69f4009a14a595b8294eca7dd4e584c0dc19a811 100644 (file)
@@ -853,4 +853,19 @@ $result = $node_subscriber2->safe_psql('postgres',
    "SELECT a, b, c FROM tab5 ORDER BY 1");
 is($result, qq(3|1|), 'updates of tab5 replicated correctly after altering table on subscriber');
 
+# Test that replication into the partitioned target table continues to
+# work correctly when the published table is altered.
+$node_publisher->safe_psql(
+   'postgres', q{
+   ALTER TABLE tab5 DROP COLUMN b, ADD COLUMN c INT;
+   ALTER TABLE tab5 ADD COLUMN b INT;});
+
+$node_publisher->safe_psql('postgres', "UPDATE tab5 SET c = 1 WHERE a = 3");
+
+$node_publisher->wait_for_catchup('sub2');
+
+$result = $node_subscriber2->safe_psql('postgres',
+   "SELECT a, b, c FROM tab5 ORDER BY 1");
+is($result, qq(3||1), 'updates of tab5 replicated correctly after altering table on publisher');
+
 done_testing();