Skip to content

Commit a9ea332

Browse files
committed
fix: Avoid creating movetables in case of non-empty target tables
Signed-off-by: Noble Mittal <noblemittal@outlook.com>
1 parent 95f2e3e commit a9ea332

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

go/vt/vtctl/workflow/server.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,6 +1395,29 @@ func (s *Server) moveTablesCreate(ctx context.Context, req *vtctldatapb.MoveTabl
13951395
}
13961396
s.Logger().Infof("Found tables to move: %s", strings.Join(tables, ","))
13971397

1398+
// Check if any table being moved is already non-empty in the target keyspace.
1399+
// Skip this check for multi-tenant migrations.
1400+
if req.GetWorkflowOptions().GetTenantId() == "" {
1401+
targetKeyspaceTables, err := getTablesInKeyspace(ctx, sourceTopo, s.tmc, targetKeyspace)
1402+
if err != nil {
1403+
return nil, err
1404+
}
1405+
1406+
var alreadyExistingTables []string
1407+
for _, t := range targetKeyspaceTables {
1408+
if slices.Contains(tables, t) {
1409+
alreadyExistingTables = append(alreadyExistingTables, t)
1410+
}
1411+
}
1412+
1413+
if len(alreadyExistingTables) > 0 {
1414+
err = validateEmptyTables(ctx, sourceTopo, s.tmc, targetKeyspace, alreadyExistingTables)
1415+
if err != nil {
1416+
return nil, err
1417+
}
1418+
}
1419+
}
1420+
13981421
if !vschema.Sharded {
13991422
// Save the original in case we need to restore it for a late failure
14001423
// in the defer().

go/vt/vtctl/workflow/utils.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,3 +1011,56 @@ func applyTargetShards(ts *trafficSwitcher, targetShards []string) error {
10111011
}
10121012
return nil
10131013
}
1014+
1015+
// validateEmptyTables checks if all specified tables in the keyspace are empty across all shards.
1016+
// It queries each shard's primary tablet and if any non-empty table is found, it returns an error
1017+
// containing a list of non-empty tables.
1018+
func validateEmptyTables(ctx context.Context, ts *topo.Server, tmc tmclient.TabletManagerClient, keyspace string, tables []string) error {
1019+
shards, err := ts.GetServingShards(ctx, keyspace)
1020+
if err != nil {
1021+
return err
1022+
}
1023+
if len(shards) == 0 {
1024+
return fmt.Errorf("keyspace %s has no shards", keyspace)
1025+
}
1026+
1027+
isFaultyTable := map[string]bool{}
1028+
for _, shard := range shards {
1029+
primary := shard.PrimaryAlias
1030+
if primary == nil {
1031+
return fmt.Errorf("shard does not have a primary: %v", shard.ShardName())
1032+
}
1033+
1034+
ti, err := ts.GetTablet(ctx, primary)
1035+
if err != nil {
1036+
return err
1037+
}
1038+
1039+
var selectQueries []string
1040+
for _, t := range tables {
1041+
selectQueries = append(selectQueries, fmt.Sprintf("(select '%s' from %s limit 1)", t, t))
1042+
}
1043+
query := strings.Join(selectQueries, "union all")
1044+
1045+
res, err := tmc.ExecuteFetchAsDba(ctx, ti.Tablet, true, &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{
1046+
Query: []byte(query),
1047+
MaxRows: uint64(len(tables)),
1048+
})
1049+
if err != nil {
1050+
return err
1051+
}
1052+
for _, row := range res.Rows {
1053+
isFaultyTable[string(row.Values)] = true
1054+
}
1055+
}
1056+
1057+
var faultyTables []string
1058+
for table := range isFaultyTable {
1059+
faultyTables = append(faultyTables, table)
1060+
}
1061+
1062+
if len(faultyTables) > 0 {
1063+
return fmt.Errorf("target keyspace contains following non-empty table(s): %s", strings.Join(faultyTables, ", "))
1064+
}
1065+
return nil
1066+
}

0 commit comments

Comments
 (0)