@@ -94,6 +94,9 @@ bool UseCitusDBSelectLogic = false;
9494/* informs pg_shard to use the distributed transaction manager */
9595bool UseDtmTransactions = false;
9696
97+ /* Use two phase commit for update transactions */
98+ bool DtmTwoPhaseCommit = false;
99+
97100/* logs each statement used in a distributed plan */
98101bool LogDistributedStatements = false;
99102
@@ -178,7 +181,7 @@ static ProcessUtility_hook_type PreviousProcessUtilityHook = NULL;
178181/* XTM stuff */
179182static List * connectionsWithDtmTransactions = NIL ;
180183static csn_t currentGlobalTransactionId = 0 ;
181- static int currentLocalTransactionId = 0 ;
184+ static int currentLocalTransactionId = 0 ;
182185static bool commitCallbackSet = false;
183186
184187#define TRACE (fmt , ...) fprintf(stderr, fmt, ## __VA_ARGS__)
@@ -642,7 +645,7 @@ ErrorIfQueryNotSupported(Query *queryTree)
642645 /*
643646 * if (!IsA(targetEntry->expr, Const))
644647 * {
645- * hasNonConstTargetEntryExprs = true;
648+ * hasNonConstTargetEntryExprs = true;
646649 * }
647650 */
648651
@@ -656,7 +659,7 @@ ErrorIfQueryNotSupported(Query *queryTree)
656659 * joinTree = queryTree->jointree;
657660 * if (joinTree != NULL && contain_mutable_functions(joinTree->quals))
658661 * {
659- * hasNonConstQualExprs = true;
662+ * hasNonConstQualExprs = true;
660663 * }
661664 */
662665 }
@@ -669,9 +672,9 @@ ErrorIfQueryNotSupported(Query *queryTree)
669672 /*
670673 * if (hasNonConstTargetEntryExprs || hasNonConstQualExprs)
671674 * {
672- * ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
673- * errmsg("cannot plan sharded modification containing values "
674- * "which are not constants or constant expressions")));
675+ * ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
676+ * errmsg("cannot plan sharded modification containing values "
677+ * "which are not constants or constant expressions")));
675678 * }
676679 *
677680 */
@@ -1280,8 +1283,8 @@ PgShardExecutorStart(QueryDesc *queryDesc, int eflags)
12801283 /*
12811284 * if (!UseDtmTransactions)
12821285 * {
1283- * PreventTransactionChain(topLevel, "distributed commands");
1284- * eflags |= EXEC_FLAG_SKIP_TRIGGERS;
1286+ * PreventTransactionChain(topLevel, "distributed commands");
1287+ * eflags |= EXEC_FLAG_SKIP_TRIGGERS;
12851288 * }
12861289 *
12871290 */
@@ -1492,6 +1495,8 @@ ExecuteMultipleShardSelect(DistributedPlan *distributedPlan,
14921495
14931496 ListCell * taskCell = NULL ;
14941497
1498+ DtmTwoPhaseCommit = IsTransactionBlock ();
1499+
14951500 foreach (taskCell , taskList )
14961501 {
14971502 Task * task = (Task * ) lfirst (taskCell );
@@ -1519,6 +1524,7 @@ ExecuteMultipleShardSelect(DistributedPlan *distributedPlan,
15191524
15201525 tuplestore_end (tupleStore );
15211526 }
1527+
15221528}
15231529
15241530
@@ -1893,6 +1899,7 @@ ExecuteDistributedModify(DistributedPlan *plan)
18931899
18941900 if (UseDtmTransactions )
18951901 {
1902+ DtmTwoPhaseCommit = true;
18961903 PrepareDtmTransaction (task );
18971904 }
18981905
@@ -2000,7 +2007,12 @@ PrepareDtmTransaction(Task *task)
20002007 /* already started a transaction */
20012008 continue ;
20022009 }
2003-
2010+ if (!SendCommand (connection , "BEGIN" ))
2011+ {
2012+ PurgeConnection (connection );
2013+ abortTransaction = true;
2014+ continue ;
2015+ }
20042016 if (!currentGlobalTransactionId )
20052017 {
20062018 /* Send dtm_begin_transaction to the first node */
@@ -2013,7 +2025,7 @@ PrepareDtmTransaction(Task *task)
20132025 continue ;
20142026 }
20152027 TRACE ("shard_xtm: conn#%p: Sent dtm_begin() to %s:%u -> %llu\n" ,
2016- connection , nodeName , nodePort , currentGlobalTransactionId );
2028+ connection , nodeName , nodePort , currentGlobalTransactionId );
20172029 }
20182030 else
20192031 {
@@ -2024,19 +2036,19 @@ PrepareDtmTransaction(Task *task)
20242036 continue ;
20252037 }
20262038 TRACE ("shard_xtm: conn#%p: Sent dtm_access(%llu) to %s:%u\n" ,
2027- connection , currentGlobalTransactionId , nodeName , nodePort );
2039+ connection , currentGlobalTransactionId , nodeName , nodePort );
20282040 }
20292041
20302042 newTransactions = lappend (newTransactions , connection );
20312043 }
20322044
20332045 if (abortTransaction )
20342046 {
2035- /* make sure we abort all pending transactions */
2036- connectionsWithDtmTransactions = newTransactions ;
2037-
20382047 MemoryContextSwitchTo (oldContext );
20392048
2049+ /* make sure we abort all pending transactions */
2050+ connectionsWithDtmTransactions = newTransactions ;
2051+
20402052 /*
20412053 * Since pg_shard reuses connections across transactions on the master,
20422054 * we need to abort pending transactions on the workers.
@@ -2045,40 +2057,8 @@ PrepareDtmTransaction(Task *task)
20452057 ereport (ERROR , (errmsg ("aborting distributed transaction due to failures" )));
20462058 }
20472059
2048- foreach (taskPlacementCell , task -> taskPlacementList )
2049- {
2050- ShardPlacement * taskPlacement = (ShardPlacement * ) lfirst (taskPlacementCell );
2051- char * nodeName = taskPlacement -> nodeName ;
2052- int32 nodePort = taskPlacement -> nodePort ;
2053- PGconn * connection = NULL ;
2054-
2055- connection = GetConnection (nodeName , nodePort , false);
2056- if (connection == NULL )
2057- {
2058- ereport (WARNING , (errmsg ("failed to connect to %s:%d" ,
2059- nodeName , nodePort )));
2060- abortTransaction = true;
2061- continue ;
2062- }
2063-
2064- if (list_member_ptr (connectionsWithDtmTransactions , connection ))
2065- {
2066- /* already started a transaction */
2067- continue ;
2068- }
2069-
2070- if (!SendCommand (connection , "BEGIN" ))
2071- {
2072- PurgeConnection (connection );
2073- abortTransaction = true;
2074- continue ;
2075- }
2076- TRACE ("shard_xtm: conn#%p: Sent BEGIN to %s:%u\n" , connection , nodeName , nodePort );
2077-
2078- }
2079-
20802060 connectionsWithDtmTransactions = list_union (connectionsWithDtmTransactions ,
2081- newTransactions );
2061+ newTransactions );
20822062
20832063 MemoryContextSwitchTo (oldContext );
20842064
@@ -2107,8 +2087,10 @@ SendDtmBeginTransaction(PGconn *connection)
21072087 char * resp = NULL ;
21082088 csn_t remoteTransactionId ;
21092089
2110-
2111- result = PQexec (connection , psprintf ("SELECT dtm_extend('%d.%d')" , MyProcPid , ++ currentLocalTransactionId ));
2090+
2091+ result = DtmTwoPhaseCommit
2092+ ? PQexec (connection , psprintf ("SELECT dtm_extend('%d.%d')" , MyProcPid , ++ currentLocalTransactionId ))
2093+ : PQexec (connection , "SELECT dtm_extend()" );
21122094 if (PQresultStatus (result ) != PGRES_TUPLES_OK )
21132095 {
21142096 ReportRemoteError (connection , result );
@@ -2135,7 +2117,9 @@ SendDtmJoinTransaction(PGconn *connection, csn_t TransactionId)
21352117 PGresult * result = NULL ;
21362118 bool resultOK = true;
21372119
2138- result = PQexec (connection , psprintf ("SELECT dtm_access(%llu, '%d.%d')" , TransactionId , MyProcPid , currentLocalTransactionId ));
2120+ result = PQexec (connection , DtmTwoPhaseCommit
2121+ ? psprintf ("SELECT dtm_access(%llu, '%d.%d')" , TransactionId , MyProcPid , currentLocalTransactionId )
2122+ : psprintf ("SELECT dtm_access(%llu)" , TransactionId ));
21392123 if (PQresultStatus (result ) != PGRES_TUPLES_OK )
21402124 {
21412125 ReportRemoteError (connection , result );
@@ -2170,23 +2154,28 @@ typedef bool (*DtmCommandResultHandler)(PGresult *result, void* arg);
21702154
21712155static bool RunDtmStatement (char const * sql , unsigned expectedStatus , DtmCommandResultHandler handler , void * arg )
21722156{
2173- ListCell * connectionCell = NULL ;
21742157 int querySent = 0 ;
21752158 PGresult * result = NULL ;
21762159 PGconn * connection = NULL ;
21772160 bool allOk = true;
2161+ ListCell * connectionCell = NULL ;
2162+ ListCell * nextCell = list_head (connectionsWithDtmTransactions );
2163+ ListCell * prevCell = NULL ;
21782164
2179- foreach ( connectionCell , connectionsWithDtmTransactions )
2165+ while (( connectionCell = nextCell ) != NULL )
21802166 {
2167+ nextCell = lnext (connectionCell );
21812168 connection = (PGconn * ) lfirst (connectionCell );
21822169 querySent = PQsendQuery (connection , sql );
21832170 if (!querySent )
21842171 {
21852172 ReportRemoteError (connection , NULL );
2173+ list_delete_cell (connectionsWithDtmTransactions , connectionCell , prevCell );
21862174 PurgeConnection (connection );
21872175 allOk = false;
21882176 continue ;
21892177 }
2178+ prevCell = connectionCell ;
21902179 TRACE ("shard_xtm: conn#%p: Sent %s to %s:%s\n" , connection , sql , PQhost (connection ), PQport (connection ));
21912180 }
21922181 foreach (connectionCell , connectionsWithDtmTransactions )
@@ -2199,7 +2188,7 @@ static bool RunDtmStatement(char const* sql, unsigned expectedStatus, DtmCommand
21992188 allOk = false;
22002189 }
22012190 PQclear (result );
2202- PQresultStatus ( result ); /* consume NULL result */
2191+ PQgetResult ( connection ); /* consume NULL result */
22032192 }
22042193 return allOk ;
22052194}
@@ -2234,37 +2223,46 @@ static bool DtmMaxCSN(PGresult *result, void* arg)
22342223
22352224
22362225static void
2237- FinishDtmTransaction (XactEvent event , void * arg )
2226+ FinishDtmTransaction (XactEvent event , void * arg )
22382227{
2239- if (!(event == XACT_EVENT_COMMIT || event == XACT_EVENT_ABORT )
2240- || !connectionsWithDtmTransactions )
2241- {
2242- return ;
2243- }
2244- if (event == XACT_EVENT_COMMIT )
2245- {
2246- csn_t maxCSN = 0 ;
2247-
2248- if (!RunDtmCommand (psprintf ("PREPARE TRANSACTION '%d.%d'" , MyProcPid , currentLocalTransactionId )) ||
2249- !RunDtmFunction (psprintf ("SELECT dtm_begin_prepare('%d.%d')" , MyProcPid , currentLocalTransactionId )) ||
2250- !RunDtmStatement (psprintf ("SELECT dtm_prepare('%d.%d',0)" , MyProcPid , currentLocalTransactionId ), PGRES_TUPLES_OK , DtmMaxCSN , & maxCSN ) ||
2251- !RunDtmFunction (psprintf ("SELECT dtm_end_prepare('%d.%d',%lld)" , MyProcPid , currentLocalTransactionId , maxCSN )) ||
2252- !RunDtmCommand (psprintf ("COMMIT PREPARED '%d.%d'" , MyProcPid , currentLocalTransactionId )))
2253- {
2254- RunDtmCommand (psprintf ("ROLLBACK PREPARED '%d.%d'" , MyProcPid , currentLocalTransactionId ));
2228+ if ((event == XACT_EVENT_COMMIT || event == XACT_EVENT_ABORT ) && connectionsWithDtmTransactions )
2229+ {
2230+ if (DtmTwoPhaseCommit )
2231+ {
2232+ if (event == XACT_EVENT_COMMIT )
2233+ {
2234+ csn_t maxCSN = 0 ;
2235+
2236+ if (!RunDtmCommand (psprintf ("PREPARE TRANSACTION '%d.%d'" ,
2237+ MyProcPid , currentLocalTransactionId )) ||
2238+ !RunDtmFunction (psprintf ("SELECT dtm_begin_prepare('%d.%d')" ,
2239+ MyProcPid , currentLocalTransactionId )) ||
2240+ !RunDtmStatement (psprintf ("SELECT dtm_prepare('%d.%d',0)" ,
2241+ MyProcPid , currentLocalTransactionId ), PGRES_TUPLES_OK , DtmMaxCSN , & maxCSN ) ||
2242+ !RunDtmFunction (psprintf ("SELECT dtm_end_prepare('%d.%d',%lld)" ,
2243+ MyProcPid , currentLocalTransactionId , maxCSN )) ||
2244+ !RunDtmCommand (psprintf ("COMMIT PREPARED '%d.%d'" ,
2245+ MyProcPid , currentLocalTransactionId )))
2246+ {
2247+ RunDtmCommand (psprintf ("ROLLBACK PREPARED '%d.%d'" ,
2248+ MyProcPid , currentLocalTransactionId ));
2249+ }
2250+ } else {
2251+ RunDtmCommand ("ROLLBACK" );
2252+ }
2253+ } else {
2254+ RunDtmCommand ("COMMIT" );
22552255 }
2256- } else {
2257- RunDtmCommand ("ROLLBACK" );
2256+ /*
2257+ * Calling unregister inside callback itself leads to segfault when
2258+ * there are several callbacks on the same event.
2259+ */
2260+ /*
2261+ * UnregisterXactCallback(FinishDtmTransaction, NULL);
2262+ */
2263+ connectionsWithDtmTransactions = NIL ;
2264+ currentGlobalTransactionId = 0 ;
22582265 }
2259- /*
2260- * Calling unregister inside callback itself leads to segfault when
2261- * there are several callbacks on the same event.
2262- */
2263- /*
2264- * UnregisterXactCallback(FinishDtmTransaction, NULL);
2265- */
2266- connectionsWithDtmTransactions = NIL ;
2267- currentGlobalTransactionId = 0 ;
22682266}
22692267
22702268
0 commit comments