From a333c9f16c70d9e9191d898784c304e39f9f3c59 Mon Sep 17 00:00:00 2001 From: Onder Kalaci Date: Fri, 11 Aug 2017 11:04:46 +0300 Subject: [PATCH] Add infrastructure for distributed deadlock detection This commit adds all the necessary pieces to do the distributed deadlock detection. Each distributed transaction is already assigned with distributed transaction ids introduced with 3369f3486f8dd33b7cd45eef47f48b1e6e304da2. The dependency among the distributed transactions are gathered with 80ea233ec1c5fb1510d9a075b32034c8bbf1f321. With this commit, we implement a DFS (depth first seach) on the dependency graph and search for cycles. Finding a cycle reveals a distributed deadlock. Once we find the deadlock, we examine the path that the cycle exists and cancel the youngest distributed transaction. Note that, we're not yet enabling the deadlock detection by default with this commit. --- src/backend/distributed/Makefile | 4 +- .../distributed/citus--7.0-12--7.0-13.sql | 1 + .../distributed/citus--7.0-13--7.0-14.sql | 13 + src/backend/distributed/citus.control | 2 +- .../distributed/transaction/backend_data.c | 68 ++++ .../distributed_deadlock_detection.c | 331 ++++++++++++++++++ .../distributed/transaction/lock_graph.c | 6 +- src/include/distributed/backend_data.h | 3 + .../distributed_deadlock_detection.h | 13 +- src/include/distributed/lock_graph.h | 2 + src/test/regress/expected/multi_extension.out | 1 + .../regress/expected/multi_mx_explain.out | 72 ++-- src/test/regress/sql/multi_extension.sql | 1 + src/test/regress/sql/multi_mx_explain.sql | 3 + 14 files changed, 477 insertions(+), 43 deletions(-) create mode 100644 src/backend/distributed/citus--7.0-13--7.0-14.sql diff --git a/src/backend/distributed/Makefile b/src/backend/distributed/Makefile index 453c74af2..e9e5687b5 100644 --- a/src/backend/distributed/Makefile +++ b/src/backend/distributed/Makefile @@ -11,7 +11,7 @@ EXTVERSIONS = 5.0 5.0-1 5.0-2 \ 6.0-1 6.0-2 6.0-3 6.0-4 6.0-5 6.0-6 6.0-7 6.0-8 6.0-9 6.0-10 6.0-11 6.0-12 6.0-13 6.0-14 6.0-15 6.0-16 6.0-17 6.0-18 \ 6.1-1 6.1-2 6.1-3 6.1-4 6.1-5 6.1-6 6.1-7 6.1-8 6.1-9 6.1-10 6.1-11 6.1-12 6.1-13 6.1-14 6.1-15 6.1-16 6.1-17 \ 6.2-1 6.2-2 6.2-3 6.2-4 \ - 7.0-1 7.0-2 7.0-3 7.0-4 7.0-5 7.0-6 7.0-7 7.0-8 7.0-9 7.0-10 7.0-11 7.0-12 7.0-13 + 7.0-1 7.0-2 7.0-3 7.0-4 7.0-5 7.0-6 7.0-7 7.0-8 7.0-9 7.0-10 7.0-11 7.0-12 7.0-13 7.0-14 # All citus--*.sql files in the source directory DATA = $(patsubst $(citus_abs_srcdir)/%.sql,%.sql,$(wildcard $(citus_abs_srcdir)/$(EXTENSION)--*--*.sql)) @@ -165,6 +165,8 @@ $(EXTENSION)--7.0-12.sql: $(EXTENSION)--7.0-11.sql $(EXTENSION)--7.0-11--7.0-12. cat $^ > $@ $(EXTENSION)--7.0-13.sql: $(EXTENSION)--7.0-12.sql $(EXTENSION)--7.0-12--7.0-13.sql cat $^ > $@ +$(EXTENSION)--7.0-14.sql: $(EXTENSION)--7.0-13.sql $(EXTENSION)--7.0-13--7.0-14.sql + cat $^ > $@ NO_PGXS = 1 diff --git a/src/backend/distributed/citus--7.0-12--7.0-13.sql b/src/backend/distributed/citus--7.0-12--7.0-13.sql index f568a54d0..9f6dc2b41 100644 --- a/src/backend/distributed/citus--7.0-12--7.0-13.sql +++ b/src/backend/distributed/citus--7.0-12--7.0-13.sql @@ -44,3 +44,4 @@ COMMENT ON FUNCTION citus_drop_trigger() IS 'perform checks and actions at the end of DROP actions'; RESET search_path; + diff --git a/src/backend/distributed/citus--7.0-13--7.0-14.sql b/src/backend/distributed/citus--7.0-13--7.0-14.sql new file mode 100644 index 000000000..7caf68995 --- /dev/null +++ b/src/backend/distributed/citus--7.0-13--7.0-14.sql @@ -0,0 +1,13 @@ +/* citus--7.0-13--7.0-14.sql */ + +SET search_path = 'pg_catalog'; + +CREATE OR REPLACE FUNCTION check_distributed_deadlocks() +RETURNS BOOL +LANGUAGE 'c' STRICT +AS $$MODULE_PATHNAME$$, $$check_distributed_deadlocks$$; +COMMENT ON FUNCTION check_distributed_deadlocks() +IS 'does a distributed deadlock check, if a deadlock found cancels one of the participating backends and returns true '; + +RESET search_path; + diff --git a/src/backend/distributed/citus.control b/src/backend/distributed/citus.control index 89004f75f..3709d0cdd 100644 --- a/src/backend/distributed/citus.control +++ b/src/backend/distributed/citus.control @@ -1,6 +1,6 @@ # Citus extension comment = 'Citus distributed database' -default_version = '7.0-13' +default_version = '7.0-14' module_pathname = '$libdir/citus' relocatable = false schema = pg_catalog diff --git a/src/backend/distributed/transaction/backend_data.c b/src/backend/distributed/transaction/backend_data.c index b1ec86013..a1705cf4d 100644 --- a/src/backend/distributed/transaction/backend_data.c +++ b/src/backend/distributed/transaction/backend_data.c @@ -19,6 +19,7 @@ #include "datatype/timestamp.h" #include "distributed/backend_data.h" #include "distributed/listutils.h" +#include "distributed/lock_graph.h" #include "distributed/metadata_cache.h" #include "distributed/transaction_identifier.h" #include "nodes/execnodes.h" @@ -566,3 +567,70 @@ GetBackendDataForProc(PGPROC *proc, BackendData *result) SpinLockRelease(&backendData->mutex); } + + +/* + * CancelTransactionDueToDeadlock cancels the input proc and also marks the backend + * data with this information. + */ +void +CancelTransactionDueToDeadlock(PGPROC *proc) +{ + BackendData *backendData = &backendManagementShmemData->backends[proc->pgprocno]; + + /* backend might not have used citus yet and thus not initialized backend data */ + if (!backendData) + { + return; + } + + SpinLockAcquire(&backendData->mutex); + + /* send a SIGINT only if the process is still in a distributed transaction */ + if (backendData->transactionId.transactionNumber != 0) + { + backendData->cancelledDueToDeadlock = true; + SpinLockRelease(&backendData->mutex); + + if (kill(proc->pid, SIGINT) != 0) + { + ereport(WARNING, + (errmsg("attempted to cancel this backend (pid: %d) to resolve a " + "distributed deadlock but the backend could not " + "be cancelled", proc->pid))); + } + } + else + { + SpinLockRelease(&backendData->mutex); + } +} + + +/* + * MyBackendGotCancelledDueToDeadlock returns whether the current distributed + * transaction was cancelled due to a deadlock. If the backend is not in a + * distributed transaction, the function returns false. + */ +bool +MyBackendGotCancelledDueToDeadlock(void) +{ + bool cancelledDueToDeadlock = false; + + /* backend might not have used citus yet and thus not initialized backend data */ + if (!MyBackendData) + { + return false; + } + + SpinLockAcquire(&MyBackendData->mutex); + + if (IsInDistributedTransaction(MyBackendData)) + { + cancelledDueToDeadlock = MyBackendData->cancelledDueToDeadlock; + } + + SpinLockRelease(&MyBackendData->mutex); + + return cancelledDueToDeadlock; +} diff --git a/src/backend/distributed/transaction/distributed_deadlock_detection.c b/src/backend/distributed/transaction/distributed_deadlock_detection.c index fc11e3b77..2d61fd1b6 100644 --- a/src/backend/distributed/transaction/distributed_deadlock_detection.c +++ b/src/backend/distributed/transaction/distributed_deadlock_detection.c @@ -25,6 +25,31 @@ #include "utils/timestamp.h" +/* used only for finding the deadlock cycle path */ +typedef struct QueuedTransactionNode +{ + TransactionNode *transactionNode; + + int currentStackDepth; +} QueuedTransactionNode; + + +/* GUC, determining whether debug messages for deadlock detection sent to LOG */ +bool LogDistributedDeadlockDetection = false; + + +static bool CheckDeadlockForTransactionNode(TransactionNode *startingTransactionNode, + TransactionNode **transactionNodeStack, + List **deadlockPath); +static void PrependOutgoingNodesToQueue(TransactionNode *queuedTransactionNode, + int currentStackDepth, + List **toBeVisitedNodes); +static void BuildDeadlockPathList(QueuedTransactionNode *cycledTransactionNode, + TransactionNode **transactionNodeStack, + List **deadlockPath); +static void ResetVisitedFields(HTAB *adjacencyList); +static void AssociateDistributedTransactionWithBackendProc(TransactionNode * + transactionNode); static TransactionNode * GetOrCreateTransactionNode(HTAB *adjacencyList, DistributedTransactionId * transactionId); @@ -32,6 +57,311 @@ static uint32 DistributedTransactionIdHash(const void *key, Size keysize); static int DistributedTransactionIdCompare(const void *a, const void *b, Size keysize); +PG_FUNCTION_INFO_V1(check_distributed_deadlocks); + + +/* + * check_distributed_deadlocks is the external API for manually + * checking for distributed deadlocks. For the details, see + * CheckForDistributedDeadlocks(). + */ +Datum +check_distributed_deadlocks(PG_FUNCTION_ARGS) +{ + bool deadlockFound = CheckForDistributedDeadlocks(); + + return BoolGetDatum(deadlockFound); +} + + +/* + * CheckForDistributedDeadlocks is the entry point for detecing + * distributed deadlocks. + * + * In plain words, the function first builds a wait graph by + * adding the wait edges from the local node and then adding the + * remote wait edges to form a global wait graph. Later, the wait + * graph is converted into another graph representation (adjacency + * lists) for more efficient searches. Finally, a DFS is done on + * the adjacency lists. Finding a cycle in the graph unveils a + * distributed deadlock. Upon finding a deadlock, the youngest + * participant backend is cancelled. + * + * The complexity of the algorithm is O(N) for each distributed + * transaction that's checked for deadlocks. Note that there exists + * 0 to MaxBackends number of transactions. + * + * The function returns true if a deadlock is found. Otherwise, returns + * false. + */ +bool +CheckForDistributedDeadlocks(void) +{ + WaitGraph *waitGraph = BuildGlobalWaitGraph(); + HTAB *adjacencyLists = BuildAdjacencyListsForWaitGraph(waitGraph); + HASH_SEQ_STATUS status; + TransactionNode *transactionNode = NULL; + int edgeCount = waitGraph->edgeCount; + int localGroupId = GetLocalGroupId(); + + /* + * We iterate on transaction nodes and search for deadlocks where the + * starting node is the given transaction node. + */ + hash_seq_init(&status, adjacencyLists); + while ((transactionNode = (TransactionNode *) hash_seq_search(&status)) != 0) + { + bool deadlockFound = false; + List *deadlockPath = NIL; + TransactionNode *transactionNodeStack[edgeCount]; + + /* we're only interested in finding deadlocks originating from this node */ + if (transactionNode->transactionId.initiatorNodeIdentifier != localGroupId) + { + continue; + } + + ResetVisitedFields(adjacencyLists); + + deadlockFound = CheckDeadlockForTransactionNode(transactionNode, + transactionNodeStack, + &deadlockPath); + if (deadlockFound) + { + TransactionNode *youngestTransaction = transactionNode; + ListCell *participantTransactionCell = NULL; + + /* there should be at least two transactions to get into a deadlock */ + Assert(list_length(deadlockPath) > 1); + + /* + * We search for the youngest participant for two reasons + * (i) predictable results (ii) cancel the youngest transaction + * (i.e., if a DDL continues for 1 hour and deadlocks with a + * SELECT continues for 10 msec, we prefer to cancel the SELECT). + * + * We're also searching for the youngest transactions initiated by + * this node. + */ + foreach(participantTransactionCell, deadlockPath) + { + TransactionNode *currentNode = + (TransactionNode *) lfirst(participantTransactionCell); + + TimestampTz youngestTimestamp = + youngestTransaction->transactionId.timestamp; + TimestampTz currentTimestamp = currentNode->transactionId.timestamp; + + AssociateDistributedTransactionWithBackendProc(currentNode); + + if (currentNode->transactionId.initiatorNodeIdentifier == + GetLocalGroupId() && + timestamptz_cmp_internal(currentTimestamp, youngestTimestamp) == 1) + { + youngestTransaction = currentNode; + } + } + + /* we should find the backend */ + Assert(youngestTransaction->initiatorProc != NULL); + + CancelTransactionDueToDeadlock(youngestTransaction->initiatorProc); + + hash_seq_term(&status); + + return true; + } + } + + return false; +} + + +/* + * CheckDeadlockForDistributedTransaction does a DFS starting with the given + * transaction node and checks for a cycle (i.e., the node can be reached again + * while traversing the graph). + * + * Finding a cycle indicates a distributed deadlock and the function returns + * true on that case. Also, the deadlockPath is filled with the transaction + * nodes that form the cycle. + */ +static bool +CheckDeadlockForTransactionNode(TransactionNode *startingTransactionNode, + TransactionNode **transactionNodeStack, + List **deadlockPath) +{ + List *toBeVisitedNodes = NIL; + int currentStackDepth = 0; + + /* + * We keep transactionNodeStack to keep track of the deadlock paths. At this point, + * adjust the depth of the starting node and set the stack's first element with + * the starting node. + */ + transactionNodeStack[currentStackDepth] = startingTransactionNode; + + PrependOutgoingNodesToQueue(startingTransactionNode, currentStackDepth, + &toBeVisitedNodes); + + /* traverse the graph and search for the deadlocks */ + while (toBeVisitedNodes != NIL) + { + QueuedTransactionNode *queuedTransactionNode = + (QueuedTransactionNode *) linitial(toBeVisitedNodes); + TransactionNode *currentTransactionNode = queuedTransactionNode->transactionNode; + + toBeVisitedNodes = list_delete_first(toBeVisitedNodes); + + /* cycle found, let the caller know about the cycle */ + if (currentTransactionNode == startingTransactionNode) + { + BuildDeadlockPathList(queuedTransactionNode, transactionNodeStack, + deadlockPath); + + return true; + } + + /* don't need to revisit the node again */ + if (currentTransactionNode->transactionVisited) + { + continue; + } + + currentTransactionNode->transactionVisited = true; + + /* set the stack's corresponding element with the current node */ + currentStackDepth = queuedTransactionNode->currentStackDepth; + transactionNodeStack[currentStackDepth] = currentTransactionNode; + + PrependOutgoingNodesToQueue(currentTransactionNode, currentStackDepth, + &toBeVisitedNodes); + } + + return false; +} + + +/* + * PrependOutgoingNodesToQueue prepends the waiters of the input transaction nodes to the + * toBeVisitedNodes. + */ +static void +PrependOutgoingNodesToQueue(TransactionNode *transactionNode, int currentStackDepth, + List **toBeVisitedNodes) +{ + ListCell *currentWaitForCell = NULL; + + /* as we traverse outgoing edges, increment the depth */ + currentStackDepth++; + + /* prepend to the list to continue depth-first search */ + foreach(currentWaitForCell, transactionNode->waitsFor) + { + TransactionNode *waitForTransaction = + (TransactionNode *) lfirst(currentWaitForCell); + QueuedTransactionNode *queuedNode = palloc0(sizeof(QueuedTransactionNode)); + + queuedNode->transactionNode = waitForTransaction; + queuedNode->currentStackDepth = currentStackDepth; + + *toBeVisitedNodes = lappend(*toBeVisitedNodes, queuedNode); + } +} + + +/* + * BuildDeadlockPathList fills deadlockPath with a list of transactions involved + * in a distributed deadlock (i.e. a cycle in the graph). + */ +static void +BuildDeadlockPathList(QueuedTransactionNode *cycledTransactionNode, + TransactionNode **transactionNodeStack, + List **deadlockPath) +{ + int deadlockStackDepth = cycledTransactionNode->currentStackDepth; + int stackIndex = 0; + + *deadlockPath = NIL; + + for (stackIndex = 0; stackIndex < deadlockStackDepth; stackIndex++) + { + *deadlockPath = lappend(*deadlockPath, transactionNodeStack[stackIndex]); + } +} + + +/* + * ResetVisitedFields goes over all the elements of the input adjacency list + * and sets transactionVisited to false. + */ +static void +ResetVisitedFields(HTAB *adjacencyList) +{ + HASH_SEQ_STATUS status; + TransactionNode *resetNode = NULL; + + /* reset all visited fields */ + hash_seq_init(&status, adjacencyList); + + while ((resetNode = (TransactionNode *) hash_seq_search(&status)) != 0) + { + resetNode->transactionVisited = false; + } +} + + +/* + * AssociateDistributedTransactionWithBackendProc gets a transaction node + * and searches the corresponding backend. Once found, transactionNodes' + * initiatorProc is set to it. + * + * The function goes over all the backends, checks for the backend with + * the same transaction number as the given transaction node. + */ +static void +AssociateDistributedTransactionWithBackendProc(TransactionNode *transactionNode) +{ + int backendIndex = 0; + + for (backendIndex = 0; backendIndex < MaxBackends; ++backendIndex) + { + PGPROC *currentProc = &ProcGlobal->allProcs[backendIndex]; + BackendData currentBackendData; + DistributedTransactionId *currentTransactionId = NULL; + + /* we're not interested in processes that are not active or waiting on a lock */ + if (currentProc->pid <= 0) + { + continue; + } + + GetBackendDataForProc(currentProc, ¤tBackendData); + + /* we're only interested in distribtued transactions */ + if (!IsInDistributedTransaction(¤tBackendData)) + { + continue; + } + + currentTransactionId = ¤tBackendData.transactionId; + + if (currentTransactionId->transactionNumber != + transactionNode->transactionId.transactionNumber) + { + continue; + } + + /* at the point we should only have transactions initiated by this node */ + Assert(currentTransactionId->initiatorNodeIdentifier == GetLocalGroupId()); + + transactionNode->initiatorProc = currentProc; + + break; + } +} + + /* * BuildAdjacencyListsForWaitGraph converts the input wait graph to * an adjacency list for further processing. @@ -121,6 +451,7 @@ GetOrCreateTransactionNode(HTAB *adjacencyList, DistributedTransactionId *transa if (!found) { transactionNode->waitsFor = NIL; + transactionNode->initiatorProc = NULL; } return transactionNode; diff --git a/src/backend/distributed/transaction/lock_graph.c b/src/backend/distributed/transaction/lock_graph.c index d3f2d51d8..2e5961976 100644 --- a/src/backend/distributed/transaction/lock_graph.c +++ b/src/backend/distributed/transaction/lock_graph.c @@ -56,10 +56,8 @@ static void AddEdgesForWaitQueue(WaitGraph *waitGraph, PGPROC *waitingProc, static void AddWaitEdge(WaitGraph *waitGraph, PGPROC *waitingProc, PGPROC *blockingProc, PROCStack *remaining); static WaitEdge * AllocWaitEdge(WaitGraph *waitGraph); -static bool IsProcessWaitingForLock(PGPROC *proc); static bool IsSameLockGroup(PGPROC *leftProc, PGPROC *rightProc); static bool IsConflictingLockMask(int holdMask, int conflictMask); -static bool IsInDistributedTransaction(BackendData *backendData); PG_FUNCTION_INFO_V1(dump_local_wait_edges); @@ -710,7 +708,7 @@ AllocWaitEdge(WaitGraph *waitGraph) /* * IsProcessWaitingForLock returns whether a given process is waiting for a lock. */ -static bool +bool IsProcessWaitingForLock(PGPROC *proc) { return proc->waitStatus == STATUS_WAITING; @@ -750,7 +748,7 @@ IsConflictingLockMask(int holdMask, int conflictMask) * IsInDistributedTransaction returns whether the given backend is in a * distributed transaction. */ -static bool +bool IsInDistributedTransaction(BackendData *backendData) { return backendData->transactionId.transactionNumber != 0; diff --git a/src/include/distributed/backend_data.h b/src/include/distributed/backend_data.h index d6d984a15..2231885ef 100644 --- a/src/include/distributed/backend_data.h +++ b/src/include/distributed/backend_data.h @@ -29,6 +29,7 @@ typedef struct BackendData { Oid databaseId; slock_t mutex; + bool cancelledDueToDeadlock; DistributedTransactionId transactionId; } BackendData; @@ -40,5 +41,7 @@ extern void UnlockBackendSharedMemory(void); extern void UnSetDistributedTransactionId(void); extern void AssignDistributedTransactionId(void); extern void GetBackendDataForProc(PGPROC *proc, BackendData *result); +extern void CancelTransactionDueToDeadlock(PGPROC *proc); +extern bool MyBackendGotCancelledDueToDeadlock(void); #endif /* BACKEND_DATA_H */ diff --git a/src/include/distributed/distributed_deadlock_detection.h b/src/include/distributed/distributed_deadlock_detection.h index f00c98317..a17b2a8d1 100644 --- a/src/include/distributed/distributed_deadlock_detection.h +++ b/src/include/distributed/distributed_deadlock_detection.h @@ -26,10 +26,21 @@ typedef struct TransactionNode /* list of TransactionNode that this distributed transaction is waiting for */ List *waitsFor; + + /* backend that is on the initiator node */ + PGPROC *initiatorProc; + + bool transactionVisited; } TransactionNode; -HTAB * BuildAdjacencyListsForWaitGraph(WaitGraph *waitGraph); +/* GUC, determining whether debug messages for deadlock detection sent to LOG */ +extern bool LogDistributedDeadlockDetection; + + +extern bool CheckForDistributedDeadlocks(void); +extern HTAB * BuildAdjacencyListsForWaitGraph(WaitGraph *waitGraph); +extern char * WaitsForToString(List *waitsFor); #endif /* DISTRIBUTED_DEADLOCK_DETECTION_H */ diff --git a/src/include/distributed/lock_graph.h b/src/include/distributed/lock_graph.h index 5084e38bc..c2adfc62b 100644 --- a/src/include/distributed/lock_graph.h +++ b/src/include/distributed/lock_graph.h @@ -55,6 +55,8 @@ typedef struct WaitGraph extern WaitGraph * BuildGlobalWaitGraph(void); +extern bool IsProcessWaitingForLock(PGPROC *proc); +extern bool IsInDistributedTransaction(BackendData *backendData); #endif /* LOCK_GRAPH_H */ diff --git a/src/test/regress/expected/multi_extension.out b/src/test/regress/expected/multi_extension.out index 140b3fd2b..369c83bf5 100644 --- a/src/test/regress/expected/multi_extension.out +++ b/src/test/regress/expected/multi_extension.out @@ -123,6 +123,7 @@ ALTER EXTENSION citus UPDATE TO '7.0-10'; ALTER EXTENSION citus UPDATE TO '7.0-11'; ALTER EXTENSION citus UPDATE TO '7.0-12'; ALTER EXTENSION citus UPDATE TO '7.0-13'; +ALTER EXTENSION citus UPDATE TO '7.0-14'; -- show running version SHOW citus.version; citus.version diff --git a/src/test/regress/expected/multi_mx_explain.out b/src/test/regress/expected/multi_mx_explain.out index 51e42f86c..a3b2de9ec 100644 --- a/src/test/regress/expected/multi_mx_explain.out +++ b/src/test/regress/expected/multi_mx_explain.out @@ -352,6 +352,8 @@ Custom Scan (Citus Router) -> Index Scan using lineitem_mx_pkey_1220052 on lineitem_mx_1220052 lineitem_mx Index Cond: (l_orderkey = 1) Filter: (l_partkey = 0) +-- make the outputs more consistent +VACUUM ANALYZE lineitem_mx; -- Test single-shard SELECT EXPLAIN (COSTS FALSE) SELECT l_quantity FROM lineitem_mx WHERE l_orderkey = 5; @@ -360,10 +362,8 @@ Custom Scan (Citus Router) Tasks Shown: All -> Task Node: host=localhost port=57638 dbname=regression - -> Bitmap Heap Scan on lineitem_mx_1220055 lineitem_mx - Recheck Cond: (l_orderkey = 5) - -> Bitmap Index Scan on lineitem_mx_pkey_1220055 - Index Cond: (l_orderkey = 5) + -> Index Scan using lineitem_mx_pkey_1220055 on lineitem_mx_1220055 lineitem_mx + Index Cond: (l_orderkey = 5) SELECT true AS valid FROM explain_xml($$ SELECT l_quantity FROM lineitem_mx WHERE l_orderkey = 5$$); t @@ -391,68 +391,68 @@ Aggregate -> Task Node: host=localhost port=57637 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220052 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220052 on lineitem_mx_1220052 lineitem_mx + Index Cond: (l_orderkey > 9030) -> Task Node: host=localhost port=57638 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220053 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220053 on lineitem_mx_1220053 lineitem_mx + Index Cond: (l_orderkey > 9030) -> Task Node: host=localhost port=57637 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220054 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220054 on lineitem_mx_1220054 lineitem_mx + Index Cond: (l_orderkey > 9030) -> Task Node: host=localhost port=57638 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220055 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220055 on lineitem_mx_1220055 lineitem_mx + Index Cond: (l_orderkey > 9030) -> Task Node: host=localhost port=57637 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220056 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220056 on lineitem_mx_1220056 lineitem_mx + Index Cond: (l_orderkey > 9030) -> Task Node: host=localhost port=57638 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220057 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220057 on lineitem_mx_1220057 lineitem_mx + Index Cond: (l_orderkey > 9030) -> Task Node: host=localhost port=57637 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220058 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220058 on lineitem_mx_1220058 lineitem_mx + Index Cond: (l_orderkey > 9030) -> Task Node: host=localhost port=57638 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220059 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220059 on lineitem_mx_1220059 lineitem_mx + Index Cond: (l_orderkey > 9030) -> Task Node: host=localhost port=57637 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220060 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220060 on lineitem_mx_1220060 lineitem_mx + Index Cond: (l_orderkey > 9030) -> Task Node: host=localhost port=57638 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220061 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220061 on lineitem_mx_1220061 lineitem_mx + Index Cond: (l_orderkey > 9030) -> Task Node: host=localhost port=57637 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220062 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220062 on lineitem_mx_1220062 lineitem_mx + Index Cond: (l_orderkey > 9030) -> Task Node: host=localhost port=57638 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220063 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220063 on lineitem_mx_1220063 lineitem_mx + Index Cond: (l_orderkey > 9030) -> Task Node: host=localhost port=57637 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220064 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220064 on lineitem_mx_1220064 lineitem_mx + Index Cond: (l_orderkey > 9030) -> Task Node: host=localhost port=57638 dbname=regression -> Aggregate @@ -461,13 +461,13 @@ Aggregate -> Task Node: host=localhost port=57637 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220066 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220066 on lineitem_mx_1220066 lineitem_mx + Index Cond: (l_orderkey > 9030) -> Task Node: host=localhost port=57638 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220067 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220067 on lineitem_mx_1220067 lineitem_mx + Index Cond: (l_orderkey > 9030) SELECT true AS valid FROM explain_xml($$ SELECT avg(l_linenumber) FROM lineitem_mx WHERE l_orderkey > 9030$$); t @@ -486,8 +486,8 @@ Aggregate -> Task Node: host=localhost port=57637 dbname=regression -> Aggregate - -> Seq Scan on lineitem_mx_1220052 lineitem_mx - Filter: (l_orderkey > 9030) + -> Index Only Scan using lineitem_mx_pkey_1220052 on lineitem_mx_1220052 lineitem_mx + Index Cond: (l_orderkey > 9030) -- Test re-partition join SET citus.large_table_shard_count TO 1; EXPLAIN (COSTS FALSE) diff --git a/src/test/regress/sql/multi_extension.sql b/src/test/regress/sql/multi_extension.sql index d748f6c8d..f796d022e 100644 --- a/src/test/regress/sql/multi_extension.sql +++ b/src/test/regress/sql/multi_extension.sql @@ -123,6 +123,7 @@ ALTER EXTENSION citus UPDATE TO '7.0-10'; ALTER EXTENSION citus UPDATE TO '7.0-11'; ALTER EXTENSION citus UPDATE TO '7.0-12'; ALTER EXTENSION citus UPDATE TO '7.0-13'; +ALTER EXTENSION citus UPDATE TO '7.0-14'; -- show running version SHOW citus.version; diff --git a/src/test/regress/sql/multi_mx_explain.sql b/src/test/regress/sql/multi_mx_explain.sql index 408a0b7b4..a7bab99c2 100644 --- a/src/test/regress/sql/multi_mx_explain.sql +++ b/src/test/regress/sql/multi_mx_explain.sql @@ -127,6 +127,9 @@ EXPLAIN (COSTS FALSE) DELETE FROM lineitem_mx WHERE l_orderkey = 1 AND l_partkey = 0; +-- make the outputs more consistent +VACUUM ANALYZE lineitem_mx; + -- Test single-shard SELECT EXPLAIN (COSTS FALSE) SELECT l_quantity FROM lineitem_mx WHERE l_orderkey = 5;