use serde::de::DeserializeOwned; use std::time::Duration; use crate::agent_manager::{AgentManager, DEFAULT_JRE_KEY}; use crate::database_capabilities; use crate::db::agent_driver::{AgentDriverClient, AgentMethod}; use crate::models::connection::DatabaseType; pub fn db_type_to_agent_key(db_type: &DatabaseType, driver_profile: Option<&str>) -> Option<&'static str> { database_capabilities::agent_key(db_type, driver_profile) } pub fn is_agent_type(db_type: &DatabaseType) -> bool { database_capabilities::is_agent_type(db_type) } pub async fn stop_daemons(manager: &AgentManager) { manager.daemons.lock().await.clear(); } pub async fn stop_daemon_by_key(manager: &AgentManager, agent_key: &str) { manager.daemons.lock().await.remove(agent_key); } pub async fn restart_daemon_by_key(manager: &AgentManager, agent_key: &str) -> Result<(), String> { manager.daemons.lock().await.remove(agent_key); let client = spawn_client_for_key(manager, agent_key).await?; manager.daemons.lock().await.insert(agent_key.to_string(), client); Ok(()) } pub async fn spawn_connection_client( manager: &AgentManager, db_type: &DatabaseType, driver_profile: Option<&str>, ) -> Result { let keys = runtime_agent_key_candidates(db_type, driver_profile) .ok_or_else(|| format!("{:?} is not an agent-driven database type", db_type))?; spawn_first_available_client(manager, &keys).await } pub async fn call_daemon( manager: &AgentManager, db_type: &DatabaseType, driver_profile: Option<&str>, method: &str, params: serde_json::Value, ) -> Result { let keys = runtime_agent_key_candidates(db_type, driver_profile) .ok_or_else(|| format!("{:?} is not an agent-driven database type", db_type))?; let key = first_installed_agent_key(manager, &keys).unwrap_or(keys[0]).to_string(); let mut daemons = manager.daemons.lock().await; if !daemons.contains_key(&key) { let client = spawn_client_for_key(manager, &key).await?; daemons.insert(key.clone(), client); } let client = daemons.get_mut(&key).unwrap(); match client.call::(method, params.clone()).await { Ok(result) => Ok(result), Err(err) => { log::warn!("[agent] daemon call failed, respawning: {err}"); daemons.remove(&key); let mut new_client = spawn_client_for_key(manager, &key).await?; let result = new_client.call::(method, params).await?; daemons.insert(key, new_client); Ok(result) } } } pub async fn call_daemon_with_timeout( manager: &AgentManager, db_type: &DatabaseType, driver_profile: Option<&str>, method: &str, params: serde_json::Value, timeout_duration: Option, ) -> Result { let keys = runtime_agent_key_candidates(db_type, driver_profile) .ok_or_else(|| format!("{:?} is not an agent-driven database type", db_type))?; let key = first_installed_agent_key(manager, &keys).unwrap_or(keys[0]).to_string(); let mut daemons = manager.daemons.lock().await; if !daemons.contains_key(&key) { let client = spawn_client_for_key(manager, &key).await?; daemons.insert(key.clone(), client); } let client = daemons.get_mut(&key).unwrap(); match client.call_with_timeout::(method, params.clone(), timeout_duration).await { Ok(result) => Ok(result), Err(err) => { log::warn!("[agent] daemon call failed, respawning: {err}"); daemons.remove(&key); let mut new_client = spawn_client_for_key(manager, &key).await?; let result = new_client.call_with_timeout::(method, params, timeout_duration).await?; daemons.insert(key, new_client); Ok(result) } } } pub async fn call_daemon_method( manager: &AgentManager, db_type: &DatabaseType, driver_profile: Option<&str>, method: AgentMethod, params: serde_json::Value, ) -> Result { call_daemon(manager, db_type, driver_profile, method.as_str(), params).await } pub async fn call_daemon_method_with_timeout( manager: &AgentManager, db_type: &DatabaseType, driver_profile: Option<&str>, method: AgentMethod, params: serde_json::Value, timeout_duration: Option, ) -> Result { call_daemon_with_timeout(manager, db_type, driver_profile, method.as_str(), params, timeout_duration).await } fn runtime_agent_key_candidates(db_type: &DatabaseType, driver_profile: Option<&str>) -> Option> { let primary = db_type_to_agent_key(db_type, driver_profile)?; if *db_type == DatabaseType::Oracle { return match driver_profile { Some("oracle-legacy") => Some(vec![primary, "oracle-legacy"]), Some("oracle-10g") => Some(vec![primary, "oracle-10g"]), _ => Some(vec![primary]), }; } Some(vec![primary]) } fn first_installed_agent_key<'a>(manager: &AgentManager, keys: &'a [&'static str]) -> Option<&'a str> { keys.iter().copied().find(|key| manager.is_driver_installed(key)) } async fn spawn_first_available_client( manager: &AgentManager, keys: &[&'static str], ) -> Result { let mut last_error = None; for key in keys { match spawn_client_for_key(manager, key).await { Ok(client) => return Ok(client), Err(err) => last_error = Some(err), } } Err(last_error.unwrap_or_else(|| "No agent driver candidates available".to_string())) } async fn spawn_client_for_key(manager: &AgentManager, key: &str) -> Result { let state = manager.load_state(); let jre_key = state.installed_drivers.get(key).map(|driver| driver.jre.as_str()).unwrap_or(DEFAULT_JRE_KEY); let launch = manager.resolve_agent_launch_spec(&state, key, jre_key)?; let mut client = AgentDriverClient::spawn(launch).await?; client.try_optional_handshake(manager.agent_app_version()).await; Ok(client) } #[cfg(test)] mod tests { use super::*; #[test] fn prestosql_does_not_use_agent_driver() { assert_eq!(runtime_agent_key_candidates(&DatabaseType::PrestoSql, None), None); } #[test] fn trino_uses_only_trino_agent_driver() { assert_eq!(runtime_agent_key_candidates(&DatabaseType::Trino, None).unwrap(), vec!["trino"]); } }