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 && if (con->conversionType == UNDISTRIBUTE_TABLE && con->cascadeViaForeignKeys &&
(TableReferencing(con->relationId) || TableReferenced(con->relationId))) (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, CascadeOperationForConnectedRelations(con->relationId, ExclusiveLock,
CASCADE_FKEY_UNDISTRIBUTE_TABLE); CASCADE_FKEY_UNDISTRIBUTE_TABLE);

View File

@ -44,7 +44,8 @@ static char * GetDropFkeyCascadeCommand(Oid foreignKeyId);
static void ExecuteCascadeOperationForRelationIdList(List *relationIdList, static void ExecuteCascadeOperationForRelationIdList(List *relationIdList,
CascadeOperationType CascadeOperationType
cascadeOperationType); cascadeOperationType);
static void ExecuteForeignKeyCreateCommand(const char *commandString,
bool skip_validation);
/* /*
* CascadeOperationForConnectedRelations executes citus table function specified * CascadeOperationForConnectedRelations executes citus table function specified
@ -106,7 +107,8 @@ CascadeOperationForConnectedRelations(Oid relationId, LOCKMODE lockMode,
cascadeOperationType); cascadeOperationType);
/* now recreate foreign keys on tables */ /* 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, CitusProcessUtility(parseTree, commandString, PROCESS_UTILITY_TOPLEVEL,
NULL, None_Receiver, NULL); 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); bool tableHasExternalForeignKeys = TableHasExternalForeignKeys(relationId);
if (tableHasExternalForeignKeys && cascadeViaForeignKeys) if (tableHasExternalForeignKeys && cascadeViaForeignKeys)
{ {
/*
* By acquiring AccessExclusiveLock, make sure that no modifications happen
* on the relations.
*/
CascadeOperationForConnectedRelations(relationId, lockMode, CascadeOperationForConnectedRelations(relationId, lockMode,
CASCADE_FKEY_CREATE_CITUS_LOCAL_TABLE); 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 = List *relationFKeyCreationCommands =
GetForeignConstraintCommandsInternal(relationId, nonDistTableFKeysFlag); GetForeignConstraintCommandsInternal(relationId, nonDistTableFKeysFlag);
DropRelationForeignKeys(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. * ALTER TABLE ... ADD FOREIGN KEY command to skip the validation step.
*/ */
Node * Node *
SkipForeignKeyValidationIfConstraintIsFkey(AlterTableStmt *alterTableStatement) SkipForeignKeyValidationIfConstraintIsFkey(AlterTableStmt *alterTableStatement,
bool processLocalRelation)
{ {
/* first check whether a distributed relation is affected */ /* first check whether a distributed relation is affected */
if (alterTableStatement->relation == NULL) if (alterTableStatement->relation == NULL)
@ -1118,8 +1122,7 @@ SkipForeignKeyValidationIfConstraintIsFkey(AlterTableStmt *alterTableStatement)
return (Node *) alterTableStatement; return (Node *) alterTableStatement;
} }
bool isCitusRelation = IsCitusTable(leftRelationId); if (!IsCitusTable(leftRelationId) && !processLocalRelation)
if (!isCitusRelation)
{ {
return (Node *) alterTableStatement; return (Node *) alterTableStatement;
} }

View File

@ -416,7 +416,8 @@ multi_ProcessUtility(PlannedStmt *pstmt,
* Note validation is done on the shard level when DDL propagation * Note validation is done on the shard level when DDL propagation
* is enabled. The following eagerly executes some tasks on workers. * 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); ProcessUtilityContext processUtilityContext);
extern List * PreprocessAlterTableSchemaStmt(Node *node, const char *queryString, extern List * PreprocessAlterTableSchemaStmt(Node *node, const char *queryString,
ProcessUtilityContext processUtilityContext); ProcessUtilityContext processUtilityContext);
extern Node * SkipForeignKeyValidationIfConstraintIsFkey(AlterTableStmt *alterTableStmt); extern Node * SkipForeignKeyValidationIfConstraintIsFkey(AlterTableStmt *alterTableStmt,
bool processLocalRelation);
extern bool IsAlterTableRenameStmt(RenameStmt *renameStmt); extern bool IsAlterTableRenameStmt(RenameStmt *renameStmt);
extern void ErrorIfAlterDropsPartitionColumn(AlterTableStmt *alterTableStatement); extern void ErrorIfAlterDropsPartitionColumn(AlterTableStmt *alterTableStatement);
extern void PostprocessAlterTableStmt(AlterTableStmt *pStmt); extern void PostprocessAlterTableStmt(AlterTableStmt *pStmt);
@ -481,6 +482,8 @@ extern void ErrorIfAnyPartitionRelationInvolvedInNonInheritedFKey(List *relation
extern void DropRelationForeignKeys(Oid relationId, int flags); extern void DropRelationForeignKeys(Oid relationId, int flags);
extern void ExecuteAndLogDDLCommandList(List *ddlCommandList); extern void ExecuteAndLogDDLCommandList(List *ddlCommandList);
extern void ExecuteAndLogDDLCommand(const char *commandString); extern void ExecuteAndLogDDLCommand(const char *commandString);
extern void ExecuteForeignKeyCreateCommandList(List *ddlCommandList,
bool skip_validation);
/* create_citus_local_table.c */ /* create_citus_local_table.c */
extern void CreateCitusLocalTable(Oid relationId, bool cascadeViaForeignKeys); extern void CreateCitusLocalTable(Oid relationId, bool cascadeViaForeignKeys);