Skip validation for foreign key creation commands

For certaion purposes, we drop and recreate the foreign
keys. As we acquire exclusive locks on the tables in between
drop and re-create, we can safely skip validation phase of
the foreign keys. The reason is purely being performance as
foreign key validation could take a long value.
pull/4480/head
Onder Kalaci 2021-01-14 21:08:26 +03:00
parent ae0b92233d
commit c35e22d75d
7 changed files with 85 additions and 10 deletions

View File

@ -482,6 +482,10 @@ ConvertTable(TableConversionState *con)
if (con->conversionType == UNDISTRIBUTE_TABLE && con->cascadeViaForeignKeys &&
(TableReferencing(con->relationId) || TableReferenced(con->relationId)))
{
/*
* Acquire ExclusiveLock as UndistributeTable does in order to
* make sure that no modifications happen on the relations.
*/
CascadeOperationForConnectedRelations(con->relationId, ExclusiveLock,
CASCADE_FKEY_UNDISTRIBUTE_TABLE);

View File

@ -44,7 +44,8 @@ static char * GetDropFkeyCascadeCommand(Oid foreignKeyId);
static void ExecuteCascadeOperationForRelationIdList(List *relationIdList,
CascadeOperationType
cascadeOperationType);
static void ExecuteForeignKeyCreateCommand(const char *commandString,
bool skip_validation);
/*
* CascadeOperationForConnectedRelations executes citus table function specified
@ -106,7 +107,8 @@ CascadeOperationForConnectedRelations(Oid relationId, LOCKMODE lockMode,
cascadeOperationType);
/* now recreate foreign keys on tables */
ExecuteAndLogDDLCommandList(fKeyCreationCommands);
bool skip_validation = true;
ExecuteForeignKeyCreateCommandList(fKeyCreationCommands, skip_validation);
}
@ -426,3 +428,55 @@ ExecuteAndLogDDLCommand(const char *commandString)
CitusProcessUtility(parseTree, commandString, PROCESS_UTILITY_TOPLEVEL,
NULL, None_Receiver, NULL);
}
/*
* ExecuteForeignKeyCreateCommandList takes a list of foreign key creation ddl commands
* and calls ExecuteAndLogForeignKeyCreateCommand function for each of them.
*/
void
ExecuteForeignKeyCreateCommandList(List *ddlCommandList, bool skip_validation)
{
char *ddlCommand = NULL;
foreach_ptr(ddlCommand, ddlCommandList)
{
ExecuteForeignKeyCreateCommand(ddlCommand, skip_validation);
}
}
/*
* ExecuteForeignKeyCreateCommand takes a foreign key creation command
* and logs it in DEBUG4 log level.
*
* Then, parses, sets skip_validation flag to considering the input and
* executes the command via CitusProcessUtility.
*/
static void
ExecuteForeignKeyCreateCommand(const char *commandString, bool skip_validation)
{
ereport(DEBUG4, (errmsg("executing foreign key create command \"%s\"",
commandString)));
Node *parseTree = ParseTreeNode(commandString);
/*
* We might have thrown an error if IsA(parseTree, AlterTableStmt),
* but that doesn't seem to provide any benefits, so assertion is
* fine for this case.
*/
Assert(IsA(parseTree, AlterTableStmt));
if (skip_validation && IsA(parseTree, AlterTableStmt))
{
parseTree =
SkipForeignKeyValidationIfConstraintIsFkey((AlterTableStmt *) parseTree,
true);
ereport(DEBUG4, (errmsg("skipping validation for foreign key create "
"command \"%s\"", commandString)));
}
CitusProcessUtility(parseTree, commandString, PROCESS_UTILITY_TOPLEVEL,
NULL, None_Receiver, NULL);
}

View File

@ -138,6 +138,10 @@ CreateCitusLocalTable(Oid relationId, bool cascadeViaForeignKeys)
bool tableHasExternalForeignKeys = TableHasExternalForeignKeys(relationId);
if (tableHasExternalForeignKeys && cascadeViaForeignKeys)
{
/*
* By acquiring AccessExclusiveLock, make sure that no modifications happen
* on the relations.
*/
CascadeOperationForConnectedRelations(relationId, lockMode,
CASCADE_FKEY_CREATE_CITUS_LOCAL_TABLE);

View File

@ -466,8 +466,14 @@ CreateDistributedTable(Oid relationId, Var *distributionColumn, char distributio
}
}
/* now recreate foreign keys that we dropped beforehand */
ExecuteAndLogDDLCommandList(fKeyCreationCommandsRelationInvolved);
/*
* Now recreate foreign keys that we dropped beforehand. As modifications are not
* allowed on the relations that are involved in the foreign key relationship,
* we can skip the validation of the foreign keys.
*/
bool skip_validation = true;
ExecuteForeignKeyCreateCommandList(fKeyCreationCommandsRelationInvolved,
skip_validation);
}

View File

@ -236,7 +236,10 @@ PostprocessCreateTableStmtForeignKeys(CreateStmt *createStatement)
List *relationFKeyCreationCommands =
GetForeignConstraintCommandsInternal(relationId, nonDistTableFKeysFlag);
DropRelationForeignKeys(relationId, nonDistTableFKeysFlag);
ExecuteAndLogDDLCommandList(relationFKeyCreationCommands);
bool skip_validation = true;
ExecuteForeignKeyCreateCommandList(relationFKeyCreationCommands,
skip_validation);
}
}
@ -1103,7 +1106,8 @@ PreprocessAlterTableSchemaStmt(Node *node, const char *queryString,
* ALTER TABLE ... ADD FOREIGN KEY command to skip the validation step.
*/
Node *
SkipForeignKeyValidationIfConstraintIsFkey(AlterTableStmt *alterTableStatement)
SkipForeignKeyValidationIfConstraintIsFkey(AlterTableStmt *alterTableStatement,
bool processLocalRelation)
{
/* first check whether a distributed relation is affected */
if (alterTableStatement->relation == NULL)
@ -1118,8 +1122,7 @@ SkipForeignKeyValidationIfConstraintIsFkey(AlterTableStmt *alterTableStatement)
return (Node *) alterTableStatement;
}
bool isCitusRelation = IsCitusTable(leftRelationId);
if (!isCitusRelation)
if (!IsCitusTable(leftRelationId) && !processLocalRelation)
{
return (Node *) alterTableStatement;
}

View File

@ -416,7 +416,8 @@ multi_ProcessUtility(PlannedStmt *pstmt,
* Note validation is done on the shard level when DDL propagation
* is enabled. The following eagerly executes some tasks on workers.
*/
parsetree = SkipForeignKeyValidationIfConstraintIsFkey(alterTableStmt);
parsetree =
SkipForeignKeyValidationIfConstraintIsFkey(alterTableStmt, false);
}
}
}

View File

@ -368,7 +368,8 @@ extern List * PreprocessAlterTableMoveAllStmt(Node *node, const char *queryStrin
ProcessUtilityContext processUtilityContext);
extern List * PreprocessAlterTableSchemaStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext);
extern Node * SkipForeignKeyValidationIfConstraintIsFkey(AlterTableStmt *alterTableStmt);
extern Node * SkipForeignKeyValidationIfConstraintIsFkey(AlterTableStmt *alterTableStmt,
bool processLocalRelation);
extern bool IsAlterTableRenameStmt(RenameStmt *renameStmt);
extern void ErrorIfAlterDropsPartitionColumn(AlterTableStmt *alterTableStatement);
extern void PostprocessAlterTableStmt(AlterTableStmt *pStmt);
@ -481,6 +482,8 @@ extern void ErrorIfAnyPartitionRelationInvolvedInNonInheritedFKey(List *relation
extern void DropRelationForeignKeys(Oid relationId, int flags);
extern void ExecuteAndLogDDLCommandList(List *ddlCommandList);
extern void ExecuteAndLogDDLCommand(const char *commandString);
extern void ExecuteForeignKeyCreateCommandList(List *ddlCommandList,
bool skip_validation);
/* create_citus_local_table.c */
extern void CreateCitusLocalTable(Oid relationId, bool cascadeViaForeignKeys);