mirror of https://github.com/citusdata/citus.git
Re-acquire metadata locks in RouterExecutorStart
parent
1e6d1ef67e
commit
275378aa45
|
@ -57,6 +57,7 @@ multi_ExecutorStart(QueryDesc *queryDesc, int eflags)
|
||||||
executorType = JobExecutorType(multiPlan);
|
executorType = JobExecutorType(multiPlan);
|
||||||
if (executorType == MULTI_EXECUTOR_ROUTER)
|
if (executorType == MULTI_EXECUTOR_ROUTER)
|
||||||
{
|
{
|
||||||
|
List *taskList = workerJob->taskList;
|
||||||
TupleDesc tupleDescriptor = ExecCleanTypeFromTL(
|
TupleDesc tupleDescriptor = ExecCleanTypeFromTL(
|
||||||
planStatement->planTree->targetlist, false);
|
planStatement->planTree->targetlist, false);
|
||||||
List *dependendJobList PG_USED_FOR_ASSERTS_ONLY = workerJob->dependedJobList;
|
List *dependendJobList PG_USED_FOR_ASSERTS_ONLY = workerJob->dependedJobList;
|
||||||
|
@ -68,7 +69,7 @@ multi_ExecutorStart(QueryDesc *queryDesc, int eflags)
|
||||||
queryDesc->tupDesc = tupleDescriptor;
|
queryDesc->tupDesc = tupleDescriptor;
|
||||||
|
|
||||||
/* drop into the router executor */
|
/* drop into the router executor */
|
||||||
RouterExecutorStart(queryDesc, eflags);
|
RouterExecutorStart(queryDesc, eflags, taskList);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "distributed/citus_ruleutils.h"
|
#include "distributed/citus_ruleutils.h"
|
||||||
#include "distributed/commit_protocol.h"
|
#include "distributed/commit_protocol.h"
|
||||||
#include "distributed/connection_cache.h"
|
#include "distributed/connection_cache.h"
|
||||||
|
#include "distributed/listutils.h"
|
||||||
#include "distributed/master_metadata_utility.h"
|
#include "distributed/master_metadata_utility.h"
|
||||||
#include "distributed/metadata_cache.h"
|
#include "distributed/metadata_cache.h"
|
||||||
#include "distributed/multi_executor.h"
|
#include "distributed/multi_executor.h"
|
||||||
|
@ -90,6 +91,7 @@ static void InitTransactionStateForTask(Task *task);
|
||||||
static HTAB * CreateXactParticipantHash(void);
|
static HTAB * CreateXactParticipantHash(void);
|
||||||
|
|
||||||
/* functions needed during run phase */
|
/* functions needed during run phase */
|
||||||
|
static void ReacquireMetadataLocks(List *taskList);
|
||||||
static bool ExecuteSingleTask(QueryDesc *queryDesc, Task *task,
|
static bool ExecuteSingleTask(QueryDesc *queryDesc, Task *task,
|
||||||
bool isModificationQuery, bool expectResults);
|
bool isModificationQuery, bool expectResults);
|
||||||
static void ExecuteMultipleTasks(QueryDesc *queryDesc, List *taskList,
|
static void ExecuteMultipleTasks(QueryDesc *queryDesc, List *taskList,
|
||||||
|
@ -133,11 +135,23 @@ static void MarkRemainingInactivePlacements(void);
|
||||||
* execution.
|
* execution.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
RouterExecutorStart(QueryDesc *queryDesc, int eflags)
|
RouterExecutorStart(QueryDesc *queryDesc, int eflags, List *taskList)
|
||||||
{
|
{
|
||||||
EState *executorState = NULL;
|
EState *executorState = NULL;
|
||||||
CmdType commandType = queryDesc->operation;
|
CmdType commandType = queryDesc->operation;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are executing a prepared statement, then we may not yet have obtained
|
||||||
|
* the metadata locks in this transaction. To prevent a concurrent shard copy,
|
||||||
|
* we re-obtain them here or error out if a shard copy has already started.
|
||||||
|
*
|
||||||
|
* If a shard copy finishes in between fetching a plan from cache and
|
||||||
|
* re-acquiring the locks, then we might still run a stale plan, which could
|
||||||
|
* cause shard placements to diverge. To minimize this window, we take the
|
||||||
|
* locks as early as possible.
|
||||||
|
*/
|
||||||
|
ReacquireMetadataLocks(taskList);
|
||||||
|
|
||||||
/* disallow triggers during distributed modify commands */
|
/* disallow triggers during distributed modify commands */
|
||||||
if (commandType != CMD_SELECT)
|
if (commandType != CMD_SELECT)
|
||||||
{
|
{
|
||||||
|
@ -163,6 +177,63 @@ RouterExecutorStart(QueryDesc *queryDesc, int eflags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReacquireMetadataLocks re-acquires the metadata locks that are normally
|
||||||
|
* acquired during planning.
|
||||||
|
*
|
||||||
|
* If we are executing a prepared statement, then planning might have
|
||||||
|
* happened in a separate transaction and advisory locks are no longer
|
||||||
|
* held. If a shard is currently being repaired/copied/moved, then
|
||||||
|
* obtaining the locks will fail and this function throws an error to
|
||||||
|
* prevent executing a stale plan.
|
||||||
|
*
|
||||||
|
* If we are executing a non-prepared statement or planning happened in
|
||||||
|
* the same transaction, then we already have the locks and obtain them
|
||||||
|
* again here. Since we always release these locks at the end of the
|
||||||
|
* transaction, this is effectively a no-op.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ReacquireMetadataLocks(List *taskList)
|
||||||
|
{
|
||||||
|
ListCell *taskCell = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: to avoid the overhead of additional sorting, we assume tasks
|
||||||
|
* to be already sorted by shard ID such that deadlocks are avoided.
|
||||||
|
* This is true for INSERT/SELECT, which is the only multi-shard
|
||||||
|
* command right now.
|
||||||
|
*/
|
||||||
|
|
||||||
|
foreach(taskCell, taskList)
|
||||||
|
{
|
||||||
|
Task *task = (Task *) lfirst(taskCell);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only obtain metadata locks for modifications to allow reads to
|
||||||
|
* proceed during shard copy.
|
||||||
|
*/
|
||||||
|
if (task->taskType == MODIFY_TASK &&
|
||||||
|
!TryLockShardDistributionMetadata(task->anchorShardId, ShareLock))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We could error out immediately to give quick feedback to the
|
||||||
|
* client, but this might complicate flow control and our default
|
||||||
|
* behaviour during shard copy is to block.
|
||||||
|
*
|
||||||
|
* Block until the lock becomes available such that the next command
|
||||||
|
* will likely succeed and use the serialization failure error code
|
||||||
|
* to signal to the client that it should retry the current command.
|
||||||
|
*/
|
||||||
|
LockShardDistributionMetadata(task->anchorShardId, ShareLock);
|
||||||
|
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
|
||||||
|
errmsg("prepared modifications cannot be executed on "
|
||||||
|
"a shard while it is being copied")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* InitTransactionStateForTask is called during executor start with the first
|
* InitTransactionStateForTask is called during executor start with the first
|
||||||
* modifying (INSERT/UPDATE/DELETE) task during a transaction. It creates the
|
* modifying (INSERT/UPDATE/DELETE) task during a transaction. It creates the
|
||||||
|
|
|
@ -45,6 +45,28 @@ LockShardDistributionMetadata(int64 shardId, LOCKMODE lockMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TryLockShardDistributionMetadata tries to grab a lock for distribution
|
||||||
|
* metadata related to the specified shard, returning false if the lock
|
||||||
|
* is currently taken. Any locks acquired using this method are released
|
||||||
|
* at transaction end.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
TryLockShardDistributionMetadata(int64 shardId, LOCKMODE lockMode)
|
||||||
|
{
|
||||||
|
LOCKTAG tag;
|
||||||
|
const bool sessionLock = false;
|
||||||
|
const bool dontWait = true;
|
||||||
|
bool lockAcquired = false;
|
||||||
|
|
||||||
|
SET_LOCKTAG_SHARD_METADATA_RESOURCE(tag, MyDatabaseId, shardId);
|
||||||
|
|
||||||
|
lockAcquired = LockAcquire(&tag, lockMode, sessionLock, dontWait);
|
||||||
|
|
||||||
|
return lockAcquired;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* LockRelationDistributionMetadata returns after getting a the lock used for a
|
* LockRelationDistributionMetadata returns after getting a the lock used for a
|
||||||
* relation's distribution metadata, blocking if required. Only ExclusiveLock
|
* relation's distribution metadata, blocking if required. Only ExclusiveLock
|
||||||
|
|
|
@ -33,7 +33,7 @@ typedef struct XactShardConnSet
|
||||||
extern bool AllModificationsCommutative;
|
extern bool AllModificationsCommutative;
|
||||||
|
|
||||||
|
|
||||||
extern void RouterExecutorStart(QueryDesc *queryDesc, int eflags);
|
extern void RouterExecutorStart(QueryDesc *queryDesc, int eflags, List *taskList);
|
||||||
extern void RouterExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count);
|
extern void RouterExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count);
|
||||||
extern void RouterExecutorFinish(QueryDesc *queryDesc);
|
extern void RouterExecutorFinish(QueryDesc *queryDesc);
|
||||||
extern void RouterExecutorEnd(QueryDesc *queryDesc);
|
extern void RouterExecutorEnd(QueryDesc *queryDesc);
|
||||||
|
|
|
@ -65,6 +65,7 @@ typedef enum AdvisoryLocktagClass
|
||||||
|
|
||||||
/* Lock shard/relation metadata for safe modifications */
|
/* Lock shard/relation metadata for safe modifications */
|
||||||
extern void LockShardDistributionMetadata(int64 shardId, LOCKMODE lockMode);
|
extern void LockShardDistributionMetadata(int64 shardId, LOCKMODE lockMode);
|
||||||
|
extern bool TryLockShardDistributionMetadata(int64 shardId, LOCKMODE lockMode);
|
||||||
extern void LockRelationDistributionMetadata(Oid relationId, LOCKMODE lockMode);
|
extern void LockRelationDistributionMetadata(Oid relationId, LOCKMODE lockMode);
|
||||||
|
|
||||||
/* Lock shard data, for DML commands or remote fetches */
|
/* Lock shard data, for DML commands or remote fetches */
|
||||||
|
|
Loading…
Reference in New Issue