fix: use std:🧵:spawn for sync-to-async bridges to avoid nested block_on panic
Some checks are pending
BotServer CI/CD / build (push) Waiting to run

Root cause: new_current_thread().block_on() panics when called from within
an existing tokio runtime (including from spawn_blocking). Tokio doesn't
allow nested block_on() calls.

Fix: Use std:🧵:spawn to create a completely separate OS thread
with its own runtime, communicating via mpsc channel. This works from
any context: async, spawn_blocking, or sync.
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2026-04-03 12:12:59 -03:00
parent 21170faea9
commit b2a9c8213d

View file

@ -53,17 +53,19 @@ pub async fn get_database_url() -> Result<String> {
pub fn get_database_url_sync() -> Result<String> { pub fn get_database_url_sync() -> Result<String> {
let guard = SECRETS_MANAGER.read().map_err(|e| anyhow::anyhow!("Lock poisoned: {}", e))?; let guard = SECRETS_MANAGER.read().map_err(|e| anyhow::anyhow!("Lock poisoned: {}", e))?;
if let Some(ref manager) = *guard { if let Some(ref manager) = *guard {
if tokio::runtime::Handle::try_current().is_ok() { let manager_clone = manager.clone();
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread() let rt = tokio::runtime::Builder::new_current_thread()
.enable_all() .enable_all()
.build() .build();
.map_err(|e| anyhow::anyhow!("Failed to create runtime: {}", e))?; let result = match rt {
return rt.block_on(manager.get_database_url()); Ok(rt) => rt.block_on(manager_clone.get_database_url()),
} else { Err(e) => Err(anyhow::anyhow!("Failed to create runtime: {}", e)),
let rt = tokio::runtime::Runtime::new() };
.map_err(|e| anyhow::anyhow!("Failed to create runtime: {}", e))?; let _ = tx.send(result);
return rt.block_on(manager.get_database_url()); });
} return rx.recv().map_err(|e| anyhow::anyhow!("Channel error: {}", e))?;
} }
Err(anyhow::anyhow!( Err(anyhow::anyhow!(
@ -84,15 +86,19 @@ pub fn get_secrets_manager_sync() -> Option<SecretsManager> {
pub fn get_work_path() -> String { pub fn get_work_path() -> String {
let sm = get_secrets_manager_sync(); let sm = get_secrets_manager_sync();
if let Some(sm) = sm { if let Some(sm) = sm {
let rt = tokio::runtime::Builder::new_current_thread() let (tx, rx) = std::sync::mpsc::channel();
.enable_all() std::thread::spawn(move || {
.build() let rt = tokio::runtime::Builder::new_current_thread()
.ok(); .enable_all()
match rt { .build();
Some(rt) => rt.block_on(sm.get_value("gbo/app", "work_path")) let result = match rt {
.unwrap_or_else(|_| "./work".to_string()), Some(rt) => rt.block_on(sm.get_value("gbo/app", "work_path"))
None => "./work".to_string(), .unwrap_or_else(|_| "./work".to_string()),
} None => "./work".to_string(),
};
let _ = tx.send(result);
});
rx.recv().unwrap_or_else(|_| "./work".to_string())
} else { } else {
"./work".to_string() "./work".to_string()
} }