fix(transfer): defer MySQL foreign keys until after data transfer
This commit is contained in:
1 parent
78aab1e5ed
commit
f36d854e68
6 files changed
+467
-37
No files matched your search
@@ -57,10 +57,12 @@ pub async fn start_transfer(
|
||||
.await?;
|
||||
|
||||
tokio::spawn(async move {
|
||||
// Sort tables by FK dependency so referenced tables are transferred first.
|
||||
// Sort tables by FK dependency so referenced tables are transferred first,
|
||||
// and keep the foreign key metadata fetched along the way — MySQL-family
|
||||
// targets reuse it per table below instead of re-querying it.
|
||||
// Skip for external Doris/StarRocks catalogs — the database name does not
|
||||
// exist in the default catalog and sorting is unnecessary (no FK constraints).
|
||||
let sorted_tables = {
|
||||
let (sorted_tables, known_foreign_keys) = {
|
||||
let skip_fk_sort = {
|
||||
let configs = state.configs.read().await;
|
||||
configs
|
||||
@@ -74,9 +76,9 @@ pub async fn start_transfer(
|
||||
.is_some()
|
||||
};
|
||||
if skip_fk_sort {
|
||||
request.tables.clone()
|
||||
(request.tables.clone(), std::collections::HashMap::new())
|
||||
} else {
|
||||
dbx_core::transfer::sort_tables_by_fk_dependency(
|
||||
dbx_core::transfer::sort_tables_by_fk_dependency_with_foreign_keys(
|
||||
&state,
|
||||
&request.source_connection_id,
|
||||
&request.source_database,
|
||||
@@ -87,7 +89,7 @@ pub async fn start_transfer(
|
||||
.await
|
||||
.unwrap_or_else(|e| {
|
||||
log::warn!("[transfer] failed to sort tables by FK dependency, using original order: {e}");
|
||||
request.tables.clone()
|
||||
(request.tables.clone(), std::collections::HashMap::new())
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -96,6 +98,7 @@ pub async fn start_transfer(
|
||||
log::info!("[transfer] starting transfer_id={} tables={}", transfer_id, total_tables);
|
||||
|
||||
let mut failed_tables: Vec<String> = Vec::new();
|
||||
let mut pending_fk_alters: Vec<(String, String)> = Vec::new();
|
||||
let mut last_rows_transferred = 0_u64;
|
||||
let mut last_total_rows = None;
|
||||
|
||||
@@ -185,6 +188,8 @@ pub async fn start_transfer(
|
||||
&target_db_type,
|
||||
&source_pool_key,
|
||||
&target_pool_key,
|
||||
&known_foreign_keys,
|
||||
&mut pending_fk_alters,
|
||||
|progress| {
|
||||
last_rows_transferred = progress.rows_transferred;
|
||||
last_total_rows = progress.total_rows;
|
||||
@@ -247,6 +252,25 @@ pub async fn start_transfer(
|
||||
}
|
||||
}
|
||||
|
||||
// Add any foreign keys deferred during MySQL-family table creation now that
|
||||
// every selected table exists — see transfer_table's use of
|
||||
// strip_inline_foreign_key_constraint_lines for why these can't be created
|
||||
// inline (a foreign key cycle has no valid CREATE TABLE order at all).
|
||||
let mut failed_fk_tables: Vec<String> = Vec::new();
|
||||
let mut failed_fk_count = 0usize;
|
||||
for (table, alter_sql) in &pending_fk_alters {
|
||||
if let Err(e) = dbx_core::transfer::execute_on_pool(&state, &target_pool_key, alter_sql).await {
|
||||
log::warn!("[transfer] failed to add deferred foreign key constraint for {table}: {e}");
|
||||
failed_fk_count += 1;
|
||||
failed_fk_tables.push(table.clone());
|
||||
}
|
||||
}
|
||||
if failed_fk_count > 0 {
|
||||
failed_fk_tables.sort();
|
||||
failed_fk_tables.dedup();
|
||||
failed_tables.push(format!("{} foreign key(s) on: {}", failed_fk_count, failed_fk_tables.join(", ")));
|
||||
}
|
||||
|
||||
// Transfer selected non-table objects (views, procedures, functions,
|
||||
// triggers, sequences, events) after the per-table loop. The shared
|
||||
// Core decision handles all content modes: DataOnly never
|
||||
|
||||
Reference in new issue
Block a user