@@ -38,13 +38,15 @@ struct CreateSubscriberOptions
38
38
char * socket_dir ; /* directory for Unix-domain socket, if any */
39
39
char * sub_port ; /* subscriber port number */
40
40
const char * sub_username ; /* subscriber username */
41
+ bool two_phase ; /* enable-two-phase option */
41
42
SimpleStringList database_names ; /* list of database names */
42
43
SimpleStringList pub_names ; /* list of publication names */
43
44
SimpleStringList sub_names ; /* list of subscription names */
44
45
SimpleStringList replslot_names ; /* list of replication slot names */
45
46
int recovery_timeout ; /* stop recovery after this time */
46
47
};
47
48
49
+ /* per-database publication/subscription info */
48
50
struct LogicalRepInfo
49
51
{
50
52
char * dbname ; /* database name */
@@ -58,6 +60,16 @@ struct LogicalRepInfo
58
60
bool made_publication ; /* publication was created */
59
61
};
60
62
63
+ /*
64
+ * Information shared across all the databases (or publications and
65
+ * subscriptions).
66
+ */
67
+ struct LogicalRepInfos
68
+ {
69
+ struct LogicalRepInfo * dbinfo ;
70
+ bool two_phase ; /* enable-two-phase option */
71
+ };
72
+
61
73
static void cleanup_objects_atexit (void );
62
74
static void usage ();
63
75
static char * get_base_conninfo (const char * conninfo , char * * dbname );
@@ -117,7 +129,7 @@ static bool dry_run = false;
117
129
118
130
static bool success = false;
119
131
120
- static struct LogicalRepInfo * dbinfo ;
132
+ static struct LogicalRepInfos dbinfos ;
121
133
static int num_dbs = 0 ; /* number of specified databases */
122
134
static int num_pubs = 0 ; /* number of specified publications */
123
135
static int num_subs = 0 ; /* number of specified subscriptions */
@@ -172,17 +184,17 @@ cleanup_objects_atexit(void)
172
184
173
185
for (int i = 0 ; i < num_dbs ; i ++ )
174
186
{
175
- if (dbinfo [i ].made_publication || dbinfo [i ].made_replslot )
187
+ if (dbinfos . dbinfo [i ].made_publication || dbinfos . dbinfo [i ].made_replslot )
176
188
{
177
189
PGconn * conn ;
178
190
179
- conn = connect_database (dbinfo [i ].pubconninfo , false);
191
+ conn = connect_database (dbinfos . dbinfo [i ].pubconninfo , false);
180
192
if (conn != NULL )
181
193
{
182
- if (dbinfo [i ].made_publication )
183
- drop_publication (conn , & dbinfo [i ]);
184
- if (dbinfo [i ].made_replslot )
185
- drop_replication_slot (conn , & dbinfo [i ], dbinfo [i ].replslotname );
194
+ if (dbinfos . dbinfo [i ].made_publication )
195
+ drop_publication (conn , & dbinfos . dbinfo [i ]);
196
+ if (dbinfos . dbinfo [i ].made_replslot )
197
+ drop_replication_slot (conn , & dbinfos . dbinfo [i ], dbinfos . dbinfo [i ].replslotname );
186
198
disconnect_database (conn , false);
187
199
}
188
200
else
@@ -192,16 +204,18 @@ cleanup_objects_atexit(void)
192
204
* that some objects were left on primary and should be
193
205
* removed before trying again.
194
206
*/
195
- if (dbinfo [i ].made_publication )
207
+ if (dbinfos . dbinfo [i ].made_publication )
196
208
{
197
209
pg_log_warning ("publication \"%s\" created in database \"%s\" on primary was left behind" ,
198
- dbinfo [i ].pubname , dbinfo [i ].dbname );
210
+ dbinfos .dbinfo [i ].pubname ,
211
+ dbinfos .dbinfo [i ].dbname );
199
212
pg_log_warning_hint ("Drop this publication before trying again." );
200
213
}
201
- if (dbinfo [i ].made_replslot )
214
+ if (dbinfos . dbinfo [i ].made_replslot )
202
215
{
203
216
pg_log_warning ("replication slot \"%s\" created in database \"%s\" on primary was left behind" ,
204
- dbinfo [i ].replslotname , dbinfo [i ].dbname );
217
+ dbinfos .dbinfo [i ].replslotname ,
218
+ dbinfos .dbinfo [i ].dbname );
205
219
pg_log_warning_hint ("Drop this replication slot soon to avoid retention of WAL files." );
206
220
}
207
221
}
@@ -227,6 +241,7 @@ usage(void)
227
241
printf (_ (" -P, --publisher-server=CONNSTR publisher connection string\n" ));
228
242
printf (_ (" -s, --socketdir=DIR socket directory to use (default current dir.)\n" ));
229
243
printf (_ (" -t, --recovery-timeout=SECS seconds to wait for recovery to end\n" ));
244
+ printf (_ (" -T, --enable-two-phase enable two-phase commit for all subscriptions\n" ));
230
245
printf (_ (" -U, --subscriber-username=NAME user name for subscriber connection\n" ));
231
246
printf (_ (" -v, --verbose output verbose messages\n" ));
232
247
printf (_ (" --config-file=FILENAME use specified main server configuration\n"
@@ -479,9 +494,10 @@ store_pub_sub_info(const struct CreateSubscriberOptions *opt,
479
494
dbinfo [i ].pubname ? dbinfo [i ].pubname : "(auto)" ,
480
495
dbinfo [i ].replslotname ? dbinfo [i ].replslotname : "(auto)" ,
481
496
dbinfo [i ].pubconninfo );
482
- pg_log_debug ("subscriber(%d): subscription: %s ; connection string: %s" , i ,
497
+ pg_log_debug ("subscriber(%d): subscription: %s ; connection string: %s, two_phase: %s " , i ,
483
498
dbinfo [i ].subname ? dbinfo [i ].subname : "(auto)" ,
484
- dbinfo [i ].subconninfo );
499
+ dbinfo [i ].subconninfo ,
500
+ dbinfos .two_phase ? "true" : "false" );
485
501
486
502
if (num_pubs > 0 )
487
503
pubcell = pubcell -> next ;
@@ -938,11 +954,12 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
938
954
failed = true;
939
955
}
940
956
941
- if (max_prepared_transactions != 0 )
957
+ if (max_prepared_transactions != 0 && ! dbinfos . two_phase )
942
958
{
943
959
pg_log_warning ("two_phase option will not be enabled for replication slots" );
944
960
pg_log_warning_detail ("Subscriptions will be created with the two_phase option disabled. "
945
961
"Prepared transactions will be replicated at COMMIT PREPARED." );
962
+ pg_log_warning_hint ("You can use --enable-two-phase switch to enable two_phase." );
946
963
}
947
964
948
965
/*
@@ -1345,8 +1362,9 @@ create_logical_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo)
1345
1362
slot_name_esc = PQescapeLiteral (conn , slot_name , strlen (slot_name ));
1346
1363
1347
1364
appendPQExpBuffer (str ,
1348
- "SELECT lsn FROM pg_catalog.pg_create_logical_replication_slot(%s, 'pgoutput', false, false, false)" ,
1349
- slot_name_esc );
1365
+ "SELECT lsn FROM pg_catalog.pg_create_logical_replication_slot(%s, 'pgoutput', false, %s, false)" ,
1366
+ slot_name_esc ,
1367
+ dbinfos .two_phase ? "true" : "false" );
1350
1368
1351
1369
PQfreemem (slot_name_esc );
1352
1370
@@ -1722,8 +1740,9 @@ create_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
1722
1740
appendPQExpBuffer (str ,
1723
1741
"CREATE SUBSCRIPTION %s CONNECTION %s PUBLICATION %s "
1724
1742
"WITH (create_slot = false, enabled = false, "
1725
- "slot_name = %s, copy_data = false)" ,
1726
- subname_esc , pubconninfo_esc , pubname_esc , replslotname_esc );
1743
+ "slot_name = %s, copy_data = false, two_phase = %s)" ,
1744
+ subname_esc , pubconninfo_esc , pubname_esc , replslotname_esc ,
1745
+ dbinfos .two_phase ? "true" : "false" );
1727
1746
1728
1747
PQfreemem (pubname_esc );
1729
1748
PQfreemem (subname_esc );
@@ -1895,6 +1914,7 @@ main(int argc, char **argv)
1895
1914
{"publisher-server" , required_argument , NULL , 'P' },
1896
1915
{"socketdir" , required_argument , NULL , 's' },
1897
1916
{"recovery-timeout" , required_argument , NULL , 't' },
1917
+ {"enable-two-phase" , no_argument , NULL , 'T' },
1898
1918
{"subscriber-username" , required_argument , NULL , 'U' },
1899
1919
{"verbose" , no_argument , NULL , 'v' },
1900
1920
{"version" , no_argument , NULL , 'V' },
@@ -1950,6 +1970,7 @@ main(int argc, char **argv)
1950
1970
opt .socket_dir = NULL ;
1951
1971
opt .sub_port = DEFAULT_SUB_PORT ;
1952
1972
opt .sub_username = NULL ;
1973
+ opt .two_phase = false;
1953
1974
opt .database_names = (SimpleStringList )
1954
1975
{
1955
1976
0
@@ -1972,7 +1993,7 @@ main(int argc, char **argv)
1972
1993
1973
1994
get_restricted_token ();
1974
1995
1975
- while ((c = getopt_long (argc , argv , "d:D:np:P:s:t:U :v" ,
1996
+ while ((c = getopt_long (argc , argv , "d:D:np:P:s:t:TU :v" ,
1976
1997
long_options , & option_index )) != -1 )
1977
1998
{
1978
1999
switch (c )
@@ -2009,6 +2030,9 @@ main(int argc, char **argv)
2009
2030
case 't' :
2010
2031
opt .recovery_timeout = atoi (optarg );
2011
2032
break ;
2033
+ case 'T' :
2034
+ opt .two_phase = true;
2035
+ break ;
2012
2036
case 'U' :
2013
2037
opt .sub_username = pg_strdup (optarg );
2014
2038
break ;
@@ -2170,12 +2194,14 @@ main(int argc, char **argv)
2170
2194
/* Rudimentary check for a data directory */
2171
2195
check_data_directory (subscriber_dir );
2172
2196
2197
+ dbinfos .two_phase = opt .two_phase ;
2198
+
2173
2199
/*
2174
2200
* Store database information for publisher and subscriber. It should be
2175
2201
* called before atexit() because its return is used in the
2176
2202
* cleanup_objects_atexit().
2177
2203
*/
2178
- dbinfo = store_pub_sub_info (& opt , pub_base_conninfo , sub_base_conninfo );
2204
+ dbinfos . dbinfo = store_pub_sub_info (& opt , pub_base_conninfo , sub_base_conninfo );
2179
2205
2180
2206
/* Register a function to clean up objects in case of failure */
2181
2207
atexit (cleanup_objects_atexit );
@@ -2184,7 +2210,7 @@ main(int argc, char **argv)
2184
2210
* Check if the subscriber data directory has the same system identifier
2185
2211
* than the publisher data directory.
2186
2212
*/
2187
- pub_sysid = get_primary_sysid (dbinfo [0 ].pubconninfo );
2213
+ pub_sysid = get_primary_sysid (dbinfos . dbinfo [0 ].pubconninfo );
2188
2214
sub_sysid = get_standby_sysid (subscriber_dir );
2189
2215
if (pub_sysid != sub_sysid )
2190
2216
pg_fatal ("subscriber data directory is not a copy of the source database cluster" );
@@ -2214,10 +2240,10 @@ main(int argc, char **argv)
2214
2240
start_standby_server (& opt , true, false);
2215
2241
2216
2242
/* Check if the standby server is ready for logical replication */
2217
- check_subscriber (dbinfo );
2243
+ check_subscriber (dbinfos . dbinfo );
2218
2244
2219
2245
/* Check if the primary server is ready for logical replication */
2220
- check_publisher (dbinfo );
2246
+ check_publisher (dbinfos . dbinfo );
2221
2247
2222
2248
/*
2223
2249
* Stop the target server. The recovery process requires that the server
@@ -2230,10 +2256,10 @@ main(int argc, char **argv)
2230
2256
stop_standby_server (subscriber_dir );
2231
2257
2232
2258
/* Create the required objects for each database on publisher */
2233
- consistent_lsn = setup_publisher (dbinfo );
2259
+ consistent_lsn = setup_publisher (dbinfos . dbinfo );
2234
2260
2235
2261
/* Write the required recovery parameters */
2236
- setup_recovery (dbinfo , subscriber_dir , consistent_lsn );
2262
+ setup_recovery (dbinfos . dbinfo , subscriber_dir , consistent_lsn );
2237
2263
2238
2264
/*
2239
2265
* Start subscriber so the recovery parameters will take effect. Wait
@@ -2244,21 +2270,21 @@ main(int argc, char **argv)
2244
2270
start_standby_server (& opt , true, true);
2245
2271
2246
2272
/* Waiting the subscriber to be promoted */
2247
- wait_for_end_recovery (dbinfo [0 ].subconninfo , & opt );
2273
+ wait_for_end_recovery (dbinfos . dbinfo [0 ].subconninfo , & opt );
2248
2274
2249
2275
/*
2250
2276
* Create the subscription for each database on subscriber. It does not
2251
2277
* enable it immediately because it needs to adjust the replication start
2252
2278
* point to the LSN reported by setup_publisher(). It also cleans up
2253
2279
* publications created by this tool and replication to the standby.
2254
2280
*/
2255
- setup_subscriber (dbinfo , consistent_lsn );
2281
+ setup_subscriber (dbinfos . dbinfo , consistent_lsn );
2256
2282
2257
2283
/* Remove primary_slot_name if it exists on primary */
2258
- drop_primary_replication_slot (dbinfo , primary_slot_name );
2284
+ drop_primary_replication_slot (dbinfos . dbinfo , primary_slot_name );
2259
2285
2260
2286
/* Remove failover replication slots if they exist on subscriber */
2261
- drop_failover_replication_slots (dbinfo );
2287
+ drop_failover_replication_slots (dbinfos . dbinfo );
2262
2288
2263
2289
/* Stop the subscriber */
2264
2290
pg_log_info ("stopping the subscriber" );
0 commit comments