fix(mysql): retry metadata connection with default database

This commit is contained in:
t8y2 2026-06-03 02:49:59 +08:00
parent d952082442
commit bed72d2132
2 changed files with 87 additions and 15 deletions

View File

@ -90,6 +90,66 @@ pub fn database_connection_config(config: &ConnectionConfig, database: Option<&s
db_config
}
pub async fn connect_mysql_metadata_pool(
config: &ConnectionConfig,
db_config: &ConnectionConfig,
host: &str,
port: u16,
connect_timeout: std::time::Duration,
) -> Result<(db::mysql::MySqlPool, MysqlMode), String> {
let url = connection_url_for_endpoint(db_config, host, port);
if db_config.needs_bare_mysql() {
return match db::mysql::connect_bare(&url, connect_timeout).await {
Ok(pool) => Ok((pool, MysqlMode::Bare)),
Err(err) => {
let fallback_url = mysql_metadata_fallback_url(config, db_config, host, port);
if let Some(fallback_url) = fallback_url {
log::info!(
"MySQL metadata connection without a default database failed ({err}); retrying with configured default database."
);
db::mysql::connect_bare(&fallback_url, connect_timeout).await.map(|pool| (pool, MysqlMode::Bare))
} else {
Err(err)
}
}
};
}
match db::mysql::connect_with_ca_cert(&url, Some(&db_config.ca_cert_path), connect_timeout).await {
Ok(pool) => {
let mode = detect_ob_oracle_mode(config, &pool).await;
Ok((pool, mode))
}
Err(err) => {
let fallback_url = mysql_metadata_fallback_url(config, db_config, host, port);
if let Some(fallback_url) = fallback_url {
log::info!(
"MySQL metadata connection without a default database failed ({err}); retrying with configured default database."
);
let pool =
db::mysql::connect_with_ca_cert(&fallback_url, Some(&config.ca_cert_path), connect_timeout).await?;
let mode = detect_ob_oracle_mode(config, &pool).await;
Ok((pool, mode))
} else {
Err(err)
}
}
}
}
fn mysql_metadata_fallback_url(
config: &ConnectionConfig,
db_config: &ConnectionConfig,
host: &str,
port: u16,
) -> Option<String> {
if db_config.db_type != DatabaseType::Mysql || db_config.effective_database().is_some() {
return None;
}
config.effective_database()?;
Some(connection_url_for_endpoint(config, host, port))
}
impl AppState {
pub fn new(storage: Storage) -> Self {
Self::new_with_plugin_dir(storage, default_plugin_dir())
@ -210,13 +270,9 @@ impl AppState {
let url = connection_url_for_endpoint(&db_config, &host, port);
let connect_timeout = std::time::Duration::from_secs(db_config.effective_connect_timeout_secs());
let pool = match db_config.db_type {
DatabaseType::Mysql if db_config.needs_bare_mysql() => {
PoolKind::Mysql(db::mysql::connect_bare(&url, connect_timeout).await?, MysqlMode::Bare)
}
DatabaseType::Mysql => {
let pool =
db::mysql::connect_with_ca_cert(&url, Some(&db_config.ca_cert_path), connect_timeout).await?;
let mode = detect_ob_oracle_mode(&db_config, &pool).await;
let (pool, mode) =
connect_mysql_metadata_pool(&config, &db_config, &host, port, connect_timeout).await?;
PoolKind::Mysql(pool, mode)
}
DatabaseType::Doris | DatabaseType::StarRocks => {
@ -952,7 +1008,7 @@ async fn detect_ob_oracle_mode(config: &ConnectionConfig, pool: &db::mysql::MySq
mod tests {
use super::{
connection_url_for_endpoint, database_connection_config, metadata_connection_config,
redacted_connection_url_for_endpoint, uses_tcp_probe, AppState, PoolKind,
mysql_metadata_fallback_url, redacted_connection_url_for_endpoint, uses_tcp_probe, AppState, PoolKind,
};
use crate::agent_connection::{
agent_connect_params, mongo_legacy_error_with_auth_hint, oracle_alternate_connect_config,
@ -1395,6 +1451,25 @@ mod tests {
assert_eq!(metadata.db_type, DatabaseType::Mysql);
}
#[test]
fn mysql_metadata_fallback_uses_saved_default_database() {
let config = mysql_config(Some("app"));
let metadata = metadata_connection_config(&config);
assert_eq!(
mysql_metadata_fallback_url(&config, &metadata, &config.host, config.port),
Some("mysql://root:secret@127.0.0.1:3306/app?ssl-mode=preferred&charset=utf8mb4".to_string())
);
}
#[test]
fn mysql_metadata_fallback_is_unavailable_without_default_database() {
let config = mysql_config(None);
let metadata = metadata_connection_config(&config);
assert_eq!(mysql_metadata_fallback_url(&config, &metadata, &config.host, config.port), None);
}
#[test]
fn mysql_database_connection_keeps_requested_database() {
let config = mysql_config(Some("app"));

View File

@ -6,8 +6,8 @@ pub use dbx_core::agent_connection::{
oracle_auth_fallback_profiles, should_retry_oracle_with_10g_driver,
};
pub use dbx_core::connection::{
connection_url_for_endpoint, expand_tilde, metadata_connection_config, probe_connection_endpoint,
redacted_connection_url_for_endpoint, AppState, MysqlMode, PoolKind,
connect_mysql_metadata_pool, connection_url_for_endpoint, expand_tilde, metadata_connection_config,
probe_connection_endpoint, redacted_connection_url_for_endpoint, AppState, MysqlMode, PoolKind,
};
use dbx_core::database_capabilities;
use dbx_core::db;
@ -434,13 +434,10 @@ pub async fn connect_db(state: State<'_, Arc<AppState>>, config: ConnectionConfi
let connect_timeout = std::time::Duration::from_secs(db_config.effective_connect_timeout_secs());
let pool = match db_config.db_type {
DatabaseType::Mysql if db_config.needs_bare_mysql() => {
PoolKind::Mysql(db::mysql::connect_bare(&url, connect_timeout).await?, MysqlMode::Bare)
DatabaseType::Mysql => {
let (pool, mode) = connect_mysql_metadata_pool(&config, &db_config, &host, port, connect_timeout).await?;
PoolKind::Mysql(pool, mode)
}
DatabaseType::Mysql => PoolKind::Mysql(
db::mysql::connect_with_ca_cert(&url, Some(&db_config.ca_cert_path), connect_timeout).await?,
MysqlMode::Normal,
),
DatabaseType::Doris | DatabaseType::StarRocks => {
PoolKind::Mysql(db::mysql::connect_bare(&url, connect_timeout).await?, MysqlMode::Bare)
}