diff --git a/go/test/endtoend/vreplication/fk_config_test.go b/go/test/endtoend/vreplication/fk_config_test.go index deda1a1b296..97d868cb371 100644 --- a/go/test/endtoend/vreplication/fk_config_test.go +++ b/go/test/endtoend/vreplication/fk_config_test.go @@ -21,16 +21,23 @@ var ( create table parent(id int, name varchar(128), primary key(id)) engine=innodb; create table child(id int, parent_id int, name varchar(128), primary key(id), foreign key(parent_id) references parent(id) on delete cascade) engine=innodb; create view vparent as select * from parent; +create table t1(id int, name varchar(128), primary key(id)) engine=innodb; +create table t2(id int, t1id int, name varchar(128), primary key(id), foreign key(t1id) references t1(id) on delete cascade) engine=innodb; ` initialFKData = ` insert into parent values(1, 'parent1'), (2, 'parent2'); -insert into child values(1, 1, 'child11'), (2, 1, 'child21'), (3, 2, 'child32');` +insert into child values(1, 1, 'child11'), (2, 1, 'child21'), (3, 2, 'child32'); +insert into t1 values(1, 't11'), (2, 't12'); +insert into t2 values(1, 1, 't21'), (2, 1, 't22'), (3, 2, 't23'); +` initialFKSourceVSchema = ` { "tables": { "parent": {}, - "child": {} + "child": {}, + "t1": {}, + "t2": {} } } ` @@ -59,6 +66,22 @@ insert into child values(1, 1, 'child11'), (2, 1, 'child21'), (3, 2, 'child32'); "name": "reverse_bits" } ] + }, + "t1": { + "column_vindexes": [ + { + "column": "id", + "name": "reverse_bits" + } + ] + }, + "t2": { + "column_vindexes": [ + { + "column": "t1id", + "name": "reverse_bits" + } + ] } } } diff --git a/go/test/endtoend/vreplication/fk_test.go b/go/test/endtoend/vreplication/fk_test.go index a313de09488..b7a88aee655 100644 --- a/go/test/endtoend/vreplication/fk_test.go +++ b/go/test/endtoend/vreplication/fk_test.go @@ -34,6 +34,8 @@ import ( binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" ) +const testWorkflowFlavor = workflowFlavorVtctl + // TestFKWorkflow runs a MoveTables workflow with atomic copy for a db with foreign key constraints. // It inserts initial data, then simulates load. We insert both child rows with foreign keys and those without, // i.e. with foreign_key_checks=0. @@ -77,10 +79,13 @@ func TestFKWorkflow(t *testing.T) { }() go ls.simulateLoad() } + targetKeyspace := "fktarget" targetTabletId := 200 vc.AddKeyspace(t, []*Cell{cell}, targetKeyspace, shardName, initialFKTargetVSchema, "", 0, 0, targetTabletId, sourceKsOpts) + testFKCancel(t, vc) + workflowName := "fk" ksWorkflow := fmt.Sprintf("%s.%s", targetKeyspace, workflowName) @@ -92,7 +97,7 @@ func TestFKWorkflow(t *testing.T) { }, sourceKeyspace: sourceKeyspace, atomicCopy: true, - }, workflowFlavorRandom) + }, testWorkflowFlavor) mt.Create() waitForWorkflowState(t, vc, ksWorkflow, binlogdatapb.VReplicationWorkflowState_Running.String()) @@ -122,6 +127,7 @@ func TestFKWorkflow(t *testing.T) { cancel() <-ch } + mt.Complete() } func insertInitialFKData(t *testing.T) { @@ -136,6 +142,9 @@ func insertInitialFKData(t *testing.T) { log.Infof("Done inserting initial FK data") waitForRowCount(t, vtgateConn, db, "parent", 2) waitForRowCount(t, vtgateConn, db, "child", 3) + waitForRowCount(t, vtgateConn, db, "t1", 2) + waitForRowCount(t, vtgateConn, db, "t2", 3) + }) } @@ -269,3 +278,25 @@ func (ls *fkLoadSimulator) exec(query string) *sqltypes.Result { require.NotNil(t, qr) return qr } + +// testFKCancel confirms that a MoveTables with tables with foreign key constraints, such that the parent table is +// lexico-graphically before the child table, can be cancelled. In such cases the child tables (which come later in +// the sorted order) need to be dropped first. +func testFKCancel(t *testing.T, vc *VitessCluster) { + var targetKeyspace = "fktarget" + var sourceKeyspace = "fksource" + var workflowName = "wf2" + var ksWorkflow = fmt.Sprintf("%s.%s", targetKeyspace, workflowName) + mt := newMoveTables(vc, &moveTablesWorkflow{ + workflowInfo: &workflowInfo{ + vc: vc, + workflowName: workflowName, + targetKeyspace: targetKeyspace, + }, + sourceKeyspace: sourceKeyspace, + atomicCopy: true, + }, testWorkflowFlavor) + mt.Create() + waitForWorkflowState(t, vc, ksWorkflow, binlogdatapb.VReplicationWorkflowState_Running.String()) + mt.Cancel() +}