@@ -159,6 +159,9 @@ static bool MtmRunUtilityStmt(PGconn* conn, char const* sql, char **errmsg);
159
159
static void MtmBroadcastUtilityStmt (char const * sql , bool ignoreError );
160
160
static void MtmProcessDDLCommand (char const * queryString , bool transactional );
161
161
162
+ static void MtmSuspendNode (void );
163
+ static void MtmResumeNode (void );
164
+
162
165
MtmState * Mtm ;
163
166
164
167
VacuumStmt * MtmVacuumStmt ;
@@ -251,6 +254,7 @@ static bool MtmIgnoreTablesWithoutPk;
251
254
static int MtmLockCount ;
252
255
static bool MtmMajorNode ;
253
256
static bool MtmBreakConnection ;
257
+ static bool MtmSuspended ;
254
258
255
259
static ExecutorStart_hook_type PreviousExecutorStartHook ;
256
260
static ExecutorFinish_hook_type PreviousExecutorFinishHook ;
@@ -272,8 +276,11 @@ static bool MtmAtExitHookRegistered = false;
272
276
* This function is called when backend is terminated because of critical error or when error is catched
273
277
* by FINALLY block
274
278
*/
275
- void MtmReleaseLock (void )
279
+ void MtmReleaseLocks (void )
276
280
{
281
+ if (MtmSuspended ) {
282
+ MtmResumeNode ();
283
+ }
277
284
if (MtmLockCount != 0 ) {
278
285
Assert (Mtm -> lastLockHolder == MyProcPid );
279
286
MtmLockCount = 0 ;
@@ -296,7 +303,7 @@ void MtmLock(LWLockMode mode)
296
303
{
297
304
timestamp_t start , stop ;
298
305
if (!MtmAtExitHookRegistered ) {
299
- atexit (MtmReleaseLock );
306
+ atexit (MtmReleaseLocks );
300
307
MtmAtExitHookRegistered = true;
301
308
}
302
309
if (mode == LW_EXCLUSIVE || MtmLockCount != 0 ) {
@@ -848,7 +855,6 @@ MtmBeginTransaction(MtmCurrentTrans* x)
848
855
MTM_ELOG (MtmBreakConnection ? FATAL : ERROR , "Multimaster node is not online: current status %s" , MtmNodeStatusMnem [Mtm -> status ]);
849
856
}
850
857
x -> containsDML = false;
851
- x -> snapshot = MtmAssignCSN ();
852
858
x -> gtid .xid = InvalidTransactionId ;
853
859
x -> gid [0 ] = '\0' ;
854
860
x -> status = TRANSACTION_STATUS_IN_PROGRESS ;
@@ -858,9 +864,14 @@ MtmBeginTransaction(MtmCurrentTrans* x)
858
864
* Allow applying of replicated transactions to avoid deadlock (to caught-up we need active transaction counter to become zero).
859
865
* Also allow user to complete explicit 2PC transactions.
860
866
*/
861
- if (x -> isDistributed && !x -> isReplicated && !x -> isTwoPhase && strcmp (application_name , MULTIMASTER_ADMIN ) != 0 ) {
867
+ if (x -> isDistributed
868
+ && (Mtm -> exclusiveLock || (!x -> isReplicated && !x -> isTwoPhase ))
869
+ && !MtmSuspended
870
+ && strcmp (application_name , MULTIMASTER_ADMIN ) != 0 )
871
+ {
862
872
MtmCheckClusterLock ();
863
873
}
874
+ x -> snapshot = MtmAssignCSN ();
864
875
865
876
MtmUnlock ();
866
877
@@ -1319,6 +1330,9 @@ MtmEndTransaction(MtmCurrentTrans* x, bool commit)
1319
1330
{
1320
1331
MTM_LOG2 ("%d: End transaction %d, prepared=%d, replicated=%d, distributed=%d, 2pc=%d, gid=%s -> %s" ,
1321
1332
MyProcPid , x -> xid , x -> isPrepared , x -> isReplicated , x -> isDistributed , x -> isTwoPhase , x -> gid , commit ? "commit" : "abort" );
1333
+ if (MtmSuspended ) {
1334
+ MtmResumeNode ();
1335
+ }
1322
1336
commit &= (x -> status != TRANSACTION_STATUS_ABORTED );
1323
1337
if (x -> isDistributed && (x -> isPrepared || x -> isReplicated ) && !x -> isTwoPhase ) {
1324
1338
MtmTransState * ts = NULL ;
@@ -2038,7 +2052,44 @@ void MtmSwitchClusterMode(MtmNodeStatus mode)
2038
2052
/* ??? Something else to do here? */
2039
2053
}
2040
2054
2055
+ /*
2056
+ * Prevent start of any new transactions at this node
2057
+ */
2058
+ static void
2059
+ MtmSuspendNode (void )
2060
+ {
2061
+ timestamp_t delay = MIN_WAIT_TIMEOUT ;
2062
+ bool insideTransaction = MtmTx .isActive ;
2063
+ Assert (!MtmSuspended );
2064
+ MtmLock (LW_EXCLUSIVE );
2065
+ if (Mtm -> exclusiveLock ) {
2066
+ elog (ERROR , "There is already pending exclusive lock" );
2067
+ }
2068
+ Mtm -> exclusiveLock = true;
2069
+ MtmSuspended = true;
2070
+ while (Mtm -> nActiveTransactions != insideTransaction ) {
2071
+ MtmUnlock ();
2072
+ MtmSleep (delay );
2073
+ if (delay * 2 <= MAX_WAIT_TIMEOUT ) {
2074
+ delay *= 2 ;
2075
+ }
2076
+ MtmLock (LW_EXCLUSIVE );
2077
+ }
2078
+ MtmUnlock ();
2079
+ }
2041
2080
2081
+ /*
2082
+ * Resume transaction processing at node (blocked by MtmSuspendNode)
2083
+ */
2084
+ static void
2085
+ MtmResumeNode (void )
2086
+ {
2087
+ MtmLock (LW_EXCLUSIVE );
2088
+ Mtm -> exclusiveLock = false;
2089
+ MtmSuspended = false;
2090
+ MtmUnlock ();
2091
+ }
2092
+
2042
2093
/*
2043
2094
* If there are recovering nodes which are catching-up WAL, check the status and prevent new transaction from commit to give
2044
2095
* WAL-sender a chance to catch-up WAL, completely synchronize replica and switch it to normal mode.
@@ -2050,7 +2101,7 @@ MtmCheckClusterLock()
2050
2101
timestamp_t delay = MIN_WAIT_TIMEOUT ;
2051
2102
while (true)
2052
2103
{
2053
- if (Mtm -> globalLockerMask | Mtm -> walSenderLockerMask ) {
2104
+ if (Mtm -> exclusiveLock || ( Mtm -> globalLockerMask | Mtm -> walSenderLockerMask ) ) {
2054
2105
/* some "almost cautch-up" wal-senders are still working. */
2055
2106
/* Do not start new transactions until them are completed. */
2056
2107
MtmUnlock ();
@@ -2474,6 +2525,7 @@ static void MtmInitialize()
2474
2525
Mtm -> reconnectMask = 0 ;
2475
2526
Mtm -> recoveredLSN = INVALID_LSN ;
2476
2527
Mtm -> nLockers = 0 ;
2528
+ Mtm -> exclusiveLock = false;
2477
2529
Mtm -> nActiveTransactions = 0 ;
2478
2530
Mtm -> votingTransactions = NULL ;
2479
2531
Mtm -> transListHead = NULL ;
@@ -4944,8 +4996,12 @@ static void MtmProcessUtility(Node *parsetree, const char *queryString,
4944
4996
}
4945
4997
break ;
4946
4998
4947
- case T_DropStmt :
4948
4999
case T_TruncateStmt :
5000
+ skipCommand = false;
5001
+ MtmSuspendNode ();
5002
+ break ;
5003
+
5004
+ case T_DropStmt :
4949
5005
{
4950
5006
DropStmt * stmt = (DropStmt * ) parsetree ;
4951
5007
if (stmt -> removeType == OBJECT_INDEX && stmt -> concurrent )
0 commit comments