Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 1462aad

Browse files
author
Amit Kapila
committed
Allow altering of two_phase option of a SUBSCRIPTION.
The two_phase option is controlled by both the publisher (as a slot option) and the subscriber (as a subscription option), so the slot option must also be modified. Changing the 'two_phase' option for a subscription from 'true' to 'false' is permitted only when there are no pending prepared transactions corresponding to that subscription. Otherwise, the changes of already prepared transactions can be replicated again along with their corresponding commit leading to duplicate data or errors. To avoid data loss, the 'two_phase' option for a subscription can only be changed from 'false' to 'true' once the initial data synchronization is completed. Therefore this is performed later by the logical replication worker. Author: Hayato Kuroda, Ajin Cherian, Amit Kapila Reviewed-by: Peter Smith, Hou Zhijie, Amit Kapila, Vitaly Davydov, Vignesh C Discussion: https://postgr.es/m/8fab8-65d74c80-1-2f28e880@39088166
1 parent 774d47b commit 1462aad

File tree

17 files changed

+452
-114
lines changed

17 files changed

+452
-114
lines changed

doc/src/sgml/protocol.sgml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2192,7 +2192,23 @@ psql "dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;"
21922192
</varlistentry>
21932193
</variablelist>
21942194

2195-
<para>The following option is supported:</para>
2195+
<para>The following options are supported:</para>
2196+
2197+
<variablelist>
2198+
<varlistentry>
2199+
<term><literal>TWO_PHASE [ <replaceable class="parameter">boolean</replaceable> ]</literal></term>
2200+
<listitem>
2201+
<para>
2202+
If true, this logical replication slot supports decoding of two-phase
2203+
commit. With this option, commands related to two-phase commit such as
2204+
<literal>PREPARE TRANSACTION</literal>, <literal>COMMIT PREPARED</literal>
2205+
and <literal>ROLLBACK PREPARED</literal> are decoded and transmitted.
2206+
The transaction will be decoded and transmitted at
2207+
<literal>PREPARE TRANSACTION</literal> time.
2208+
</para>
2209+
</listitem>
2210+
</varlistentry>
2211+
</variablelist>
21962212

21972213
<variablelist>
21982214
<varlistentry>

doc/src/sgml/ref/alter_subscription.sgml

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,9 @@ ALTER SUBSCRIPTION <replaceable class="parameter">name</replaceable> RENAME TO <
6868
<para>
6969
Commands <command>ALTER SUBSCRIPTION ... REFRESH PUBLICATION</command>,
7070
<command>ALTER SUBSCRIPTION ... {SET|ADD|DROP} PUBLICATION ...</command>
71-
with <literal>refresh</literal> option as <literal>true</literal> and
72-
<command>ALTER SUBSCRIPTION ... SET (failover = true|false)</command>
71+
with <literal>refresh</literal> option as <literal>true</literal>,
72+
<command>ALTER SUBSCRIPTION ... SET (failover = true|false)</command> and
73+
<command>ALTER SUBSCRIPTION ... SET (two_phase = false)</command>
7374
cannot be executed inside a transaction block.
7475

7576
These commands also cannot be executed when the subscription has
@@ -228,8 +229,9 @@ ALTER SUBSCRIPTION <replaceable class="parameter">name</replaceable> RENAME TO <
228229
<link linkend="sql-createsubscription-params-with-disable-on-error"><literal>disable_on_error</literal></link>,
229230
<link linkend="sql-createsubscription-params-with-password-required"><literal>password_required</literal></link>,
230231
<link linkend="sql-createsubscription-params-with-run-as-owner"><literal>run_as_owner</literal></link>,
231-
<link linkend="sql-createsubscription-params-with-origin"><literal>origin</literal></link>, and
232-
<link linkend="sql-createsubscription-params-with-failover"><literal>failover</literal></link>.
232+
<link linkend="sql-createsubscription-params-with-origin"><literal>origin</literal></link>,
233+
<link linkend="sql-createsubscription-params-with-failover"><literal>failover</literal></link>, and
234+
<link linkend="sql-createsubscription-params-with-two-phase"><literal>two_phase</literal></link>.
233235
Only a superuser can set <literal>password_required = false</literal>.
234236
</para>
235237

@@ -252,6 +254,32 @@ ALTER SUBSCRIPTION <replaceable class="parameter">name</replaceable> RENAME TO <
252254
<link linkend="sql-createsubscription-params-with-failover"><literal>failover</literal></link>
253255
option is enabled.
254256
</para>
257+
258+
<para>
259+
The <link linkend="sql-createsubscription-params-with-failover"><literal>failover</literal></link>
260+
and <link linkend="sql-createsubscription-params-with-two-phase"><literal>two_phase</literal></link>
261+
parameters can only be altered when the subscription is disabled.
262+
</para>
263+
264+
<para>
265+
When altering <link linkend="sql-createsubscription-params-with-two-phase"><literal>two_phase</literal></link>
266+
from <literal>true</literal> to <literal>false</literal>, the backend
267+
process reports an error if any prepared transactions done by the
268+
logical replication worker (from when <literal>two_phase</literal>
269+
parameter was still <literal>true</literal>) are found. You can resolve
270+
prepared transactions on the publisher node, or manually roll back them
271+
on the subscriber, and then try again. The transactions prepared by
272+
logical replication worker corresponding to a particular subscription have
273+
the following pattern: <quote><literal>pg_gid_%u_%u</literal></quote>
274+
(parameters: subscription <parameter>oid</parameter>, remote transaction id <parameter>xid</parameter>).
275+
To resolve such transactions manually, you need to roll back all
276+
the prepared transactions with corresponding subscription IDs in their
277+
names. Applications can check
278+
<link linkend="view-pg-prepared-xacts"><structname>pg_prepared_xacts</structname></link>
279+
to find the required prepared transactions. After the <literal>two_phase</literal>
280+
option is changed from <literal>true</literal> to <literal>false</literal>,
281+
the publisher will replicate the transactions again when they are committed.
282+
</para>
255283
</listitem>
256284
</varlistentry>
257285

src/backend/access/transam/twophase.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2681,3 +2681,82 @@ LookupGXact(const char *gid, XLogRecPtr prepare_end_lsn,
26812681
LWLockRelease(TwoPhaseStateLock);
26822682
return found;
26832683
}
2684+
2685+
/*
2686+
* TwoPhaseTransactionGid
2687+
* Form the prepared transaction GID for two_phase transactions.
2688+
*
2689+
* Return the GID in the supplied buffer.
2690+
*/
2691+
void
2692+
TwoPhaseTransactionGid(Oid subid, TransactionId xid, char *gid_res, int szgid)
2693+
{
2694+
Assert(OidIsValid(subid));
2695+
2696+
if (!TransactionIdIsValid(xid))
2697+
ereport(ERROR,
2698+
(errcode(ERRCODE_PROTOCOL_VIOLATION),
2699+
errmsg_internal("invalid two-phase transaction ID")));
2700+
2701+
snprintf(gid_res, szgid, "pg_gid_%u_%u", subid, xid);
2702+
}
2703+
2704+
/*
2705+
* IsTwoPhaseTransactionGidForSubid
2706+
* Check whether the given GID (as formed by TwoPhaseTransactionGid) is
2707+
* for the specified 'subid'.
2708+
*/
2709+
static bool
2710+
IsTwoPhaseTransactionGidForSubid(Oid subid, char *gid)
2711+
{
2712+
int ret;
2713+
Oid subid_from_gid;
2714+
TransactionId xid_from_gid;
2715+
char gid_tmp[GIDSIZE];
2716+
2717+
/* Extract the subid and xid from the given GID */
2718+
ret = sscanf(gid, "pg_gid_%u_%u", &subid_from_gid, &xid_from_gid);
2719+
2720+
/*
2721+
* Check that the given GID has expected format, and at least the subid
2722+
* matches.
2723+
*/
2724+
if (ret != 2 || subid != subid_from_gid)
2725+
return false;
2726+
2727+
/*
2728+
* Reconstruct a temporary GID based on the subid and xid extracted from
2729+
* the given GID and check whether the temporary GID and the given GID
2730+
* match.
2731+
*/
2732+
TwoPhaseTransactionGid(subid, xid_from_gid, gid_tmp, sizeof(gid_tmp));
2733+
2734+
return strcmp(gid, gid_tmp) == 0;
2735+
}
2736+
2737+
/*
2738+
* LookupGXactBySubid
2739+
* Check if the prepared transaction done by apply worker exists.
2740+
*/
2741+
bool
2742+
LookupGXactBySubid(Oid subid)
2743+
{
2744+
bool found = false;
2745+
2746+
LWLockAcquire(TwoPhaseStateLock, LW_SHARED);
2747+
for (int i = 0; i < TwoPhaseState->numPrepXacts; i++)
2748+
{
2749+
GlobalTransaction gxact = TwoPhaseState->prepXacts[i];
2750+
2751+
/* Ignore not-yet-valid GIDs. */
2752+
if (gxact->valid &&
2753+
IsTwoPhaseTransactionGidForSubid(subid, gxact->gid))
2754+
{
2755+
found = true;
2756+
break;
2757+
}
2758+
}
2759+
LWLockRelease(TwoPhaseStateLock);
2760+
2761+
return found;
2762+
}

0 commit comments

Comments
 (0)