feat: add per-connection configurable connect timeout

Add a `connect_timeout_secs` field to ConnectionConfig that allows users
to configure the database connection timeout per connection (1-300s,
default 5s). Previously all connections used a hardcoded 5-second timeout.

- Add connect_timeout_secs field with serde default and clamp logic
- Add parse_connect_timeout_with_fallback to honor URL params over config
- Pass user-configured timeout to all drivers (MySQL, Postgres, Redis,
  MongoDB, ClickHouse, SQL Server, Elasticsearch) and TCP probe
- Add UI input in connection dialog SSH tab
- Add i18n keys for en/zh-CN/es
This commit is contained in:
田振洲 committed 2026-05-28 14:24:54 +08:00
1 parent ed10edb5f7
commit c03b57c179
19 files changed
+186 -94

No files matched your search

+60 -36
View File
@@ -219,33 +219,40 @@ pub async fn test_connection(state: State<'_, Arc<AppState>>, config: Connection
let probe_result = probe_connection_endpoint(&config, &host, port).await;
let url = connection_url_for_endpoint(&config, &host, port);
let target = redacted_connection_url_for_endpoint(&config, &host, port);
let connect_timeout = std::time::Duration::from_secs(config.effective_connect_timeout_secs());
log::info!("[test_connection] db_type={:?} target={}", config.db_type, target);
let result = match probe_result {
Err(e) => Err(e),
Ok(()) => match config.db_type {
DatabaseType::Mysql if config.needs_bare_mysql() => match db::mysql::connect_bare(&url).await {
Ok(pool) => {
let _ = pool.disconnect().await;
Ok("Connection successful".to_string())
DatabaseType::Mysql if config.needs_bare_mysql() => {
match db::mysql::connect_bare(&url, connect_timeout).await {
Ok(pool) => {
let _ = pool.disconnect().await;
Ok("Connection successful".to_string())
}
Err(e) => Err(e),
}
Err(e) => Err(e),
},
DatabaseType::Mysql => match db::mysql::connect_with_ca_cert(&url, Some(&config.ca_cert_path)).await {
Ok(pool) => {
let _ = pool.disconnect().await;
Ok("Connection successful".to_string())
}
DatabaseType::Mysql => {
match db::mysql::connect_with_ca_cert(&url, Some(&config.ca_cert_path), connect_timeout).await {
Ok(pool) => {
let _ = pool.disconnect().await;
Ok("Connection successful".to_string())
}
Err(e) => Err(e),
}
Err(e) => Err(e),
},
DatabaseType::Doris | DatabaseType::StarRocks => match db::mysql::connect_bare(&url).await {
Ok(pool) => {
let _ = pool.disconnect().await;
Ok("Connection successful".to_string())
}
DatabaseType::Doris | DatabaseType::StarRocks => {
match db::mysql::connect_bare(&url, connect_timeout).await {
Ok(pool) => {
let _ = pool.disconnect().await;
Ok("Connection successful".to_string())
}
Err(e) => Err(e),
}
Err(e) => Err(e),
},
}
DatabaseType::Postgres | DatabaseType::Redshift | DatabaseType::Gaussdb | DatabaseType::OpenGauss => {
match db::postgres::connect(&url).await {
match db::postgres::connect(&url, connect_timeout).await {
Ok(pool) => {
pool.close();
Ok("Connection successful".to_string())
@@ -264,7 +271,7 @@ pub async fn test_connection(state: State<'_, Arc<AppState>>, config: Connection
} else if config.uses_redis_sentinel() {
db::redis_driver::connect_sentinel(&config).await?
} else {
db::redis_driver::connect(&url).await?
db::redis_driver::connect(&url, connect_timeout).await?
};
drop(con);
Ok("Connection successful".to_string())
@@ -279,8 +286,8 @@ pub async fn test_connection(state: State<'_, Arc<AppState>>, config: Connection
}
}
DatabaseType::MongoDb => {
let native_err = match db::mongo_driver::connect(&url).await {
Ok(client) => match db::mongo_driver::test_connection(&client).await {
let native_err = match db::mongo_driver::connect(&url, connect_timeout).await {
Ok(client) => match db::mongo_driver::test_connection(&client, connect_timeout).await {
Ok(()) => return Ok("Connection successful".to_string()),
Err(e) => e,
},
@@ -307,22 +314,35 @@ pub async fn test_connection(state: State<'_, Arc<AppState>>, config: Connection
username,
password,
Some(&config.ca_cert_path),
connect_timeout,
)?;
db::clickhouse_driver::test_connection(&client).await.map(|_| "Connection successful".to_string())
}
DatabaseType::SqlServer => {
db::sqlserver::connect(&host, port, &config.username, &config.password, config.database.as_deref())
db::clickhouse_driver::test_connection(&client, connect_timeout)
.await
.map(|_| "Connection successful".to_string())
}
DatabaseType::SqlServer => {
db::sqlserver::connect(
&host,
port,
&config.username,
&config.password,
config.database.as_deref(),
connect_timeout,
)
.await
.map(|_| "Connection successful".to_string())
}
DatabaseType::Elasticsearch => {
let client = db::elasticsearch_driver::EsClient::new(
&url,
Some(&config.username),
Some(&config.password),
config.ssl,
connect_timeout,
);
db::elasticsearch_driver::test_connection(&client).await.map(|_| "Connection successful".to_string())
db::elasticsearch_driver::test_connection(&client, connect_timeout)
.await
.map(|_| "Connection successful".to_string())
}
db_type if database_capabilities::is_agent_type(&db_type) => {
test_agent_connection(state.inner(), &config, &host, port).await
@@ -362,20 +382,21 @@ pub async fn connect_db(state: State<'_, Arc<AppState>>, config: ConnectionConfi
let (host, port) = state.connection_host_port(&id, &db_config).await?;
probe_connection_endpoint(&db_config, &host, port).await?;
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).await?, MysqlMode::Bare)
PoolKind::Mysql(db::mysql::connect_bare(&url, connect_timeout).await?, MysqlMode::Bare)
}
DatabaseType::Mysql => PoolKind::Mysql(
db::mysql::connect_with_ca_cert(&url, Some(&db_config.ca_cert_path)).await?,
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).await?, MysqlMode::Bare)
PoolKind::Mysql(db::mysql::connect_bare(&url, connect_timeout).await?, MysqlMode::Bare)
}
DatabaseType::Postgres | DatabaseType::Redshift | DatabaseType::Gaussdb | DatabaseType::OpenGauss => {
PoolKind::Postgres(db::postgres::connect(&url).await?)
PoolKind::Postgres(db::postgres::connect(&url, connect_timeout).await?)
}
DatabaseType::Sqlite => PoolKind::Sqlite(db::sqlite::connect_path(&expand_tilde(&db_config.host)).await?),
DatabaseType::Redis => {
@@ -389,7 +410,7 @@ pub async fn connect_db(state: State<'_, Arc<AppState>>, config: ConnectionConfi
)))
} else {
PoolKind::Redis(db::redis_driver::RedisConnection::Direct(tokio::sync::Mutex::new(
db::redis_driver::connect(&url).await?,
db::redis_driver::connect(&url, connect_timeout).await?,
)))
};
con
@@ -405,8 +426,8 @@ pub async fn connect_db(state: State<'_, Arc<AppState>>, config: ConnectionConfi
PoolKind::DuckDb(con)
}
DatabaseType::MongoDb => {
let native_err = match db::mongo_driver::connect(&url).await {
Ok(client) => match db::mongo_driver::test_connection(&client).await {
let native_err = match db::mongo_driver::connect(&url, connect_timeout).await {
Ok(client) => match db::mongo_driver::test_connection(&client, connect_timeout).await {
Ok(()) => {
state.configs.write().await.insert(id.clone(), config);
state.connections.write().await.insert(id.clone(), PoolKind::MongoDb(client));
@@ -435,8 +456,9 @@ pub async fn connect_db(state: State<'_, Arc<AppState>>, config: ConnectionConfi
username,
password,
Some(&db_config.ca_cert_path),
connect_timeout,
)?;
db::clickhouse_driver::test_connection(&client).await?;
db::clickhouse_driver::test_connection(&client, connect_timeout).await?;
PoolKind::ClickHouse(client)
}
DatabaseType::SqlServer => {
@@ -446,6 +468,7 @@ pub async fn connect_db(state: State<'_, Arc<AppState>>, config: ConnectionConfi
&db_config.username,
&db_config.password,
db_config.database.as_deref(),
connect_timeout,
)
.await?;
PoolKind::SqlServer(std::sync::Arc::new(tokio::sync::Mutex::new(client)))
@@ -456,8 +479,9 @@ pub async fn connect_db(state: State<'_, Arc<AppState>>, config: ConnectionConfi
Some(&db_config.username),
Some(&db_config.password),
db_config.ssl,
connect_timeout,
);
db::elasticsearch_driver::test_connection(&client).await?;
db::elasticsearch_driver::test_connection(&client, connect_timeout).await?;
PoolKind::Elasticsearch(client)
}
db_type if database_capabilities::is_agent_type(&db_type) => {