From 38f482566cffd5ebaa2c5f836d1715dabb021e9f Mon Sep 17 00:00:00 2001
From: Ajin Cherian <ajinc@fast.au.fujitsu.com>
Date: Tue, 30 Mar 2021 00:16:35 -0400
Subject: [PATCH v2] Make sure a prepare is sent when decoder detects a
 concurrent abort.

While decoding a prepared transaction, and a concurrent abort of the transaction
being decoded is detected, the decoding is stopped. But this fix makes sure that
even if the decoding is aborted, the PREPARE is sent out. This ensures that
when the ROLLBACK PREPARED is eventually sent downstream, there is a corresponding
prepared transaction to rollback.
---
 doc/src/sgml/logicaldecoding.sgml               | 10 ++++++----
 src/backend/replication/logical/reorderbuffer.c |  7 +++++++
 2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/doc/src/sgml/logicaldecoding.sgml b/doc/src/sgml/logicaldecoding.sgml
index 80eb96d..d2f8d39 100644
--- a/doc/src/sgml/logicaldecoding.sgml
+++ b/doc/src/sgml/logicaldecoding.sgml
@@ -545,12 +545,14 @@ CREATE TABLE another_catalog_table(data text) WITH (user_catalog_table = true);
      executed within that transaction. A transaction that is prepared for
      a two-phase commit using <command>PREPARE TRANSACTION</command> will
      also be decoded if the output plugin callbacks needed for decoding
-     them are provided. It is possible that the current transaction which
+     them are provided. It is possible that the current prepared transaction which
      is being decoded is aborted concurrently via a <command>ROLLBACK PREPARED</command>
      command. In that case, the logical decoding of this transaction will
-     be aborted too. We will skip all the changes of such a transaction once
-     the abort is detected and abort the transaction when we read WAL for
-     <command>ROLLBACK PREPARED</command>.
+     be aborted too. All the changes of such a transaction is skipped once
+     the abort is detected and the <function>prepare_cb</function> callback is invoked.
+     This could result in a prepared transaction with incomplete changes.
+     This is done so that eventually when the <command>ROLLBACK PREPARED</command>
+     is decoded, there is a corresponding prepared transaction with a matching gid.
     </para>
 
     <note>
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 127f2c4..1442af1 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -2664,6 +2664,13 @@ ReorderBufferPrepare(ReorderBuffer *rb, TransactionId xid,
 
 	ReorderBufferReplay(txn, rb, xid, txn->final_lsn, txn->end_lsn,
 						txn->commit_time, txn->origin_id, txn->origin_lsn);
+
+	/*
+	 * If the transaction has been concurrently aborted, make sure we send
+	 * prepare here.
+	 */
+	if (txn->concurrent_abort)
+		rb->prepare(rb, txn, txn->final_lsn);
 }
 
 /*
-- 
1.8.3.1

