Actually skip constraint validation on shards after shard move (#6640)

DESCRIPTION: Fix foreign key validation skip at the end of shard move

In eadc88a we started completely skipping foreign key constraint
validation at the end of a non blocking shard move, instead of only for
foreign keys to reference tables. However, it turns out that this didn't
work at all because of a hard to notice bug: By resetting the
SkipConstraintValidation flag at the end of our utility hook, we
actually make the SET command that sets it a no-op.

This fixes that bug by removing the code that resets it. This is fine
because #6543 removed the only place where we set the flag in C code. So
the resetting of the flag has no purpose anymore. This PR also adds a
regression test, because it turned out we didn't have any otherwise we
would have caught that the feature was completely broken.

It also moves the constraint validation skipping to the utility hook.
The reason is that #6550 showed us that this is the better place to skip
it, because it will also skip the planning phase and not just the
execution.
pull/6652/head
Jelte Fennema 2023-01-27 13:08:05 +01:00 committed by GitHub
parent 8870f0f90b
commit 81dcddd1ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 151 additions and 22 deletions

View File

@ -1311,19 +1311,6 @@ IsTableTypeIncluded(Oid relationId, int flags)
}
/*
* EnableSkippingConstraintValidation is simply a C interface for setting the following:
* SET LOCAL citus.skip_constraint_validation TO on;
*/
void
EnableSkippingConstraintValidation()
{
set_config_option("citus.skip_constraint_validation", "true",
PGC_SUSET, PGC_S_SESSION,
GUC_ACTION_LOCAL, true, 0, false);
}
/*
* RelationInvolvedInAnyNonInheritedForeignKeys returns true if relation involved
* in a foreign key that is not inherited from its parent relation.

View File

@ -389,7 +389,6 @@ ProcessUtilityInternal(PlannedStmt *pstmt,
Node *parsetree = pstmt->utilityStmt;
List *ddlJobs = NIL;
DistOpsValidationState distOpsValidationState = HasNoneValidObject;
bool oldSkipConstraintsValidationValue = SkipConstraintValidation;
if (IsA(parsetree, ExplainStmt) &&
IsA(((ExplainStmt *) parsetree)->query, Query))
@ -634,6 +633,32 @@ ProcessUtilityInternal(PlannedStmt *pstmt,
}
}
/*
* If we've explicitly set the citus.skip_constraint_validation GUC, then
* we skip validation of any added constraints.
*/
if (IsA(parsetree, AlterTableStmt) && SkipConstraintValidation)
{
AlterTableStmt *alterTableStmt = (AlterTableStmt *) parsetree;
AlterTableCmd *command = NULL;
foreach_ptr(command, alterTableStmt->cmds)
{
AlterTableType alterTableType = command->subtype;
/*
* XXX: In theory we could probably use this GUC to skip validation
* of VALIDATE CONSTRAINT and ALTER CONSTRAINT too. But currently
* this is not needed, so we make its behaviour only apply to ADD
* CONSTRAINT.
*/
if (alterTableType == AT_AddConstraint)
{
Constraint *constraint = (Constraint *) command->def;
constraint->skip_validation = true;
}
}
}
/* inform the user about potential caveats */
if (IsA(parsetree, CreatedbStmt))
{
@ -915,8 +940,6 @@ ProcessUtilityInternal(PlannedStmt *pstmt,
*/
CitusHasBeenLoaded(); /* lgtm[cpp/return-value-ignored] */
}
SkipConstraintValidation = oldSkipConstraintsValidationValue;
}

View File

@ -861,11 +861,6 @@ AlterTableConstraintCheck(QueryDesc *queryDesc)
return false;
}
if (SkipConstraintValidation)
{
return true;
}
/*
* While an ALTER TABLE is in progress, we might do SELECTs on some
* catalog tables too. For example, when dropping a column, citus_drop_trigger()

View File

@ -287,7 +287,6 @@ extern bool TableHasExternalForeignKeys(Oid relationId);
extern List * GetForeignKeyOids(Oid relationId, int flags);
extern Oid GetReferencedTableId(Oid foreignKeyId);
extern Oid GetReferencingTableId(Oid foreignKeyId);
extern void EnableSkippingConstraintValidation(void);
extern bool RelationInvolvedInAnyNonInheritedForeignKeys(Oid relationId);

View File

@ -7,3 +7,4 @@ test: isolation_cluster_management
test: isolation_logical_replication_single_shard_commands_on_mx
test: isolation_logical_replication_multi_shard_commands_on_mx
test: isolation_logical_replication_skip_fk_validation

View File

@ -0,0 +1,52 @@
Parsed test spec with 3 sessions
starting permutation: s1-start-session-level-connection s3-acquire-advisory-lock s2-move-placement s1-start-session-level-connection s1-insert-violation-into-shard s3-release-advisory-lock
step s1-start-session-level-connection:
SELECT start_session_level_connection_to_node('localhost', 57638);
start_session_level_connection_to_node
---------------------------------------------------------------------
(1 row)
step s3-acquire-advisory-lock:
SELECT pg_advisory_lock(44000, 55152);
pg_advisory_lock
---------------------------------------------------------------------
(1 row)
step s2-move-placement:
SELECT master_move_shard_placement((SELECT * FROM selected_shard_for_test_table), 'localhost', 57637, 'localhost', 57638);
<waiting ...>
step s1-start-session-level-connection:
SELECT start_session_level_connection_to_node('localhost', 57638);
start_session_level_connection_to_node
---------------------------------------------------------------------
(1 row)
step s1-insert-violation-into-shard:
SELECT run_commands_on_session_level_connection_to_node(format('INSERT INTO t1_%s VALUES (-1, -1)', (SELECT * FROM selected_shard_for_test_table)));
run_commands_on_session_level_connection_to_node
---------------------------------------------------------------------
(1 row)
step s3-release-advisory-lock:
SELECT pg_advisory_unlock(44000, 55152);
pg_advisory_unlock
---------------------------------------------------------------------
t
(1 row)
step s2-move-placement: <... completed>
master_move_shard_placement
---------------------------------------------------------------------
(1 row)

View File

@ -0,0 +1,72 @@
#include "isolation_mx_common.include.spec"
setup
{
SET citus.shard_count to 1;
SET citus.shard_replication_factor to 1;
CREATE TABLE t1 (id int PRIMARY KEY, value int);
SELECT create_distributed_table('t1', 'id');
CREATE TABLE t2 (id int PRIMARY KEY, value int);
SELECT create_distributed_table('t2', 'id');
CREATE TABLE r (id int PRIMARY KEY, value int);
SELECT create_reference_table('r');
SELECT get_shard_id_for_distribution_column('t1', 5) INTO selected_shard_for_test_table;
}
setup {
ALTER TABLE t1 ADD CONSTRAINT t1_t2_fkey FOREIGN KEY (id) REFERENCES t2(id);
}
setup {
ALTER TABLE t1 ADD CONSTRAINT t1_r_fkey FOREIGN KEY (value) REFERENCES r(id);
}
teardown
{
DROP TABLE t1;
DROP TABLE t2;
DROP TABLE r;
DROP TABLE selected_shard_for_test_table;
}
session "s1"
step "s1-start-session-level-connection"
{
SELECT start_session_level_connection_to_node('localhost', 57638);
}
// This inserts a foreign key violation directly into the shard on the target
// worker. Since we're not validating the foreign key on the new shard on
// purpose we expect no errors.
step "s1-insert-violation-into-shard"
{
SELECT run_commands_on_session_level_connection_to_node(format('INSERT INTO t1_%s VALUES (-1, -1)', (SELECT * FROM selected_shard_for_test_table)));
}
session "s2"
step "s2-move-placement"
{
SELECT master_move_shard_placement((SELECT * FROM selected_shard_for_test_table), 'localhost', 57637, 'localhost', 57638);
}
session "s3"
// this advisory lock with (almost) random values are only used
// for testing purposes. For details, check Citus' logical replication
// source code
step "s3-acquire-advisory-lock"
{
SELECT pg_advisory_lock(44000, 55152);
}
step "s3-release-advisory-lock"
{
SELECT pg_advisory_unlock(44000, 55152);
}
permutation "s1-start-session-level-connection" "s3-acquire-advisory-lock" "s2-move-placement" "s1-start-session-level-connection" "s1-insert-violation-into-shard" "s3-release-advisory-lock"