From bafb424b23c7c460a711d4a7b425c55775378900 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Sun, 10 May 2026 23:23:22 +0800 Subject: [PATCH] perf: full-stack performance optimization Frontend: - Debounce queryStore tab persistence (remove flush:sync deep watcher) - Eliminate unnecessary object spreading in connectionStore error handling - Use in-place delete for completion cache invalidation - Deduplicate schema cache prefix API calls - Split echarts, reka-ui, marked, @codemirror/commands into separate chunks - Lazy-load xlsxExport and markdownTable in DataGrid - Lazy-load marked parser in UpdateDialog Backend: - Replace tokio::sync::Mutex with RwLock for AppState connections/configs (60+ concurrent reads no longer block each other) - Parallelize PostgreSQL DDL metadata queries with tokio::try_join! --- crates/dbx-core/src/connection.rs | 26 ++++++++-------- crates/dbx-core/src/mongo_ops.rs | 12 ++++---- crates/dbx-core/src/query.rs | 4 +-- crates/dbx-core/src/redis_ops.rs | 32 ++++++++++---------- crates/dbx-core/src/schema.rs | 42 ++++++++++++++------------ crates/dbx-core/src/transfer.rs | 6 ++-- src-tauri/src/commands/connection.rs | 8 ++--- src-web/src/routes/connection.rs | 20 ++++++------ src/components/grid/DataGrid.vue | 4 +-- src/components/layout/UpdateDialog.vue | 22 +++++++++----- src/stores/connectionStore.ts | 32 +++++++++++--------- src/stores/queryStore.ts | 13 +++++++- vite.config.ts | 4 +++ 13 files changed, 126 insertions(+), 99 deletions(-) diff --git a/crates/dbx-core/src/connection.rs b/crates/dbx-core/src/connection.rs index 0fcb258cb..19ed4ea09 100644 --- a/crates/dbx-core/src/connection.rs +++ b/crates/dbx-core/src/connection.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; -use tokio::sync::Mutex; +use tokio::sync::RwLock; use crate::db; use crate::db::ssh_tunnel::TunnelManager; @@ -89,8 +89,8 @@ async fn connect_oracle_pool( } pub struct AppState { - pub connections: Mutex>, - pub configs: Mutex>, + pub connections: RwLock>, + pub configs: RwLock>, pub running_queries: RunningQueries, pub tunnels: TunnelManager, pub storage: Storage, @@ -122,8 +122,8 @@ impl AppState { pub fn new_with_plugin_dir(storage: Storage, plugin_dir: PathBuf) -> Self { Self { - connections: Mutex::new(HashMap::new()), - configs: Mutex::new(HashMap::new()), + connections: RwLock::new(HashMap::new()), + configs: RwLock::new(HashMap::new()), running_queries: RunningQueries::default(), tunnels: TunnelManager::new(), storage, @@ -154,7 +154,7 @@ impl AppState { pub async fn get_or_create_pool(&self, connection_id: &str, database: Option<&str>) -> Result { let db_type = { - let configs = self.configs.lock().await; + let configs = self.configs.read().await; configs.get(connection_id).map(|c| c.db_type.clone()) }; @@ -175,7 +175,7 @@ impl AppState { } }; - let conns = self.connections.lock().await; + let conns = self.connections.read().await; if conns.contains_key(&pool_key) { if let Some(PoolKind::Oracle(pool)) = conns.get(&pool_key) { let client = pool.primary(); @@ -184,7 +184,7 @@ impl AppState { drop(conn); drop(conns); log::info!("[oracle] connection closed, reconnecting..."); - self.connections.lock().await.remove(&pool_key); + self.connections.write().await.remove(&pool_key); } else { return Ok(pool_key); } @@ -195,7 +195,7 @@ impl AppState { drop(conns); } - let configs = self.configs.lock().await; + let configs = self.configs.read().await; let config = configs.get(connection_id).ok_or("Connection config not found")?.clone(); drop(configs); @@ -292,7 +292,7 @@ impl AppState { DatabaseType::Jdbc => self.external_driver_pool("jdbc", &db_config).await?, }; - self.connections.lock().await.insert(pool_key.clone(), pool); + self.connections.write().await.insert(pool_key.clone(), pool); Ok(pool_key) } @@ -342,7 +342,7 @@ impl AppState { pub async fn reconnect_pool(&self, connection_id: &str, database: Option<&str>) -> Result { let is_single_conn = { - let configs = self.configs.lock().await; + let configs = self.configs.read().await; configs .get(connection_id) .map(|c| { @@ -360,7 +360,7 @@ impl AppState { None => connection_id.to_string(), } }; - self.connections.lock().await.remove(&pool_key); + self.connections.write().await.remove(&pool_key); self.get_or_create_pool(connection_id, database).await } } @@ -506,7 +506,7 @@ mod tests { config.host = db_path.to_string_lossy().to_string(); config.port = 0; - state.configs.lock().await.insert(config.id.clone(), config); + state.configs.write().await.insert(config.id.clone(), config); let pool_key = state.get_or_create_pool("sqlite-conn", None).await.unwrap(); assert_eq!(pool_key, "sqlite-conn"); diff --git a/crates/dbx-core/src/mongo_ops.rs b/crates/dbx-core/src/mongo_ops.rs index c970f64b0..b9ede1ac5 100644 --- a/crates/dbx-core/src/mongo_ops.rs +++ b/crates/dbx-core/src/mongo_ops.rs @@ -3,7 +3,7 @@ use crate::db::elasticsearch_driver; use crate::db::mongo_driver::{self, MongoDocumentResult}; pub async fn mongo_list_databases_core(state: &AppState, connection_id: &str) -> Result, String> { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::MongoDb(client) => mongo_driver::list_databases(client).await, PoolKind::Elasticsearch(_) => Ok(vec!["default".to_string()]), @@ -16,7 +16,7 @@ pub async fn mongo_list_collections_core( connection_id: &str, database: &str, ) -> Result, String> { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::MongoDb(client) => mongo_driver::list_collections(client, database).await, PoolKind::Elasticsearch(client) => elasticsearch_driver::list_indices(client).await, @@ -32,7 +32,7 @@ pub async fn mongo_find_documents_core( skip: u64, limit: i64, ) -> Result { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::MongoDb(client) => mongo_driver::find_documents(client, database, collection, skip, limit).await, PoolKind::Elasticsearch(client) => { @@ -51,7 +51,7 @@ pub async fn mongo_insert_document_core( collection: &str, doc_json: &str, ) -> Result { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::MongoDb(client) => mongo_driver::insert_document(client, database, collection, doc_json).await, PoolKind::Elasticsearch(client) => { @@ -71,7 +71,7 @@ pub async fn mongo_update_document_core( id: &str, doc_json: &str, ) -> Result { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::MongoDb(client) => mongo_driver::update_document(client, database, collection, id, doc_json).await, PoolKind::Elasticsearch(client) => { @@ -90,7 +90,7 @@ pub async fn mongo_delete_document_core( collection: &str, id: &str, ) -> Result { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::MongoDb(client) => mongo_driver::delete_document(client, database, collection, id).await, PoolKind::Elasticsearch(client) => { diff --git a/crates/dbx-core/src/query.rs b/crates/dbx-core/src/query.rs index e6c1672ed..e21d71ca6 100644 --- a/crates/dbx-core/src/query.rs +++ b/crates/dbx-core/src/query.rs @@ -130,7 +130,7 @@ pub async fn do_execute( schema: Option<&str>, cancel_token: Option, ) -> Result { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; let pool = connections.get(pool_key).ok_or("Connection not found")?; match pool { @@ -417,7 +417,7 @@ pub async fn execute_statements_in_transaction( // Clone the pool handle within the lock, then drop it before any async work. let path = { - let conns = state.connections.lock().await; + let conns = state.connections.read().await; conns.get(&pool_key).map(|p| match p { PoolKind::Postgres(pg) => TxPath::Pg(pg.clone()), PoolKind::Mysql(mp, _mode) => TxPath::Mysql(mp.clone(), false), diff --git a/crates/dbx-core/src/redis_ops.rs b/crates/dbx-core/src/redis_ops.rs index 71be93db7..9601e86c7 100644 --- a/crates/dbx-core/src/redis_ops.rs +++ b/crates/dbx-core/src/redis_ops.rs @@ -2,7 +2,7 @@ use crate::connection::{AppState, PoolKind}; use crate::db::redis_driver::{self, RedisScanResult, RedisValue}; pub async fn redis_list_databases_core(state: &AppState, connection_id: &str) -> Result, String> { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; let pool = connections.get(connection_id).ok_or("Connection not found")?; match pool { PoolKind::Redis(con) => { @@ -21,7 +21,7 @@ pub async fn redis_scan_keys_core( pattern: &str, count: usize, ) -> Result { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; let pool = connections.get(connection_id).ok_or("Connection not found")?; match pool { PoolKind::Redis(con) => { @@ -43,7 +43,7 @@ pub async fn redis_get_value_in_db_core( db: u32, key_raw: &str, ) -> Result { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; let pool = connections.get(connection_id).ok_or("Connection not found")?; match pool { PoolKind::Redis(con) => { @@ -74,7 +74,7 @@ pub async fn redis_set_string_in_db_core( value: &str, ttl: Option, ) -> Result<(), String> { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; let pool = connections.get(connection_id).ok_or("Connection not found")?; match pool { PoolKind::Redis(con) => { @@ -97,7 +97,7 @@ pub async fn redis_delete_key_in_db_core( db: u32, key_raw: &str, ) -> Result<(), String> { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; let pool = connections.get(connection_id).ok_or("Connection not found")?; match pool { PoolKind::Redis(con) => { @@ -128,7 +128,7 @@ pub async fn redis_hash_set_in_db_core( field: &str, value: &str, ) -> Result<(), String> { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::Redis(con) => { let mut con = con.lock().await; @@ -151,7 +151,7 @@ pub async fn redis_hash_del_in_db_core( key_raw: &str, field: &str, ) -> Result<(), String> { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::Redis(con) => { let mut con = con.lock().await; @@ -174,7 +174,7 @@ pub async fn redis_list_push_in_db_core( key_raw: &str, value: &str, ) -> Result<(), String> { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::Redis(con) => { let mut con = con.lock().await; @@ -202,7 +202,7 @@ pub async fn redis_list_remove_in_db_core( key_raw: &str, index: i64, ) -> Result<(), String> { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::Redis(con) => { let mut con = con.lock().await; @@ -225,7 +225,7 @@ pub async fn redis_set_add_in_db_core( key_raw: &str, member: &str, ) -> Result<(), String> { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::Redis(con) => { let mut con = con.lock().await; @@ -253,7 +253,7 @@ pub async fn redis_set_remove_in_db_core( key_raw: &str, member: &str, ) -> Result<(), String> { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::Redis(con) => { let mut con = con.lock().await; @@ -273,7 +273,7 @@ pub async fn redis_zadd_in_db_core( member: &str, score: f64, ) -> Result<(), String> { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::Redis(con) => { let mut con = con.lock().await; @@ -292,7 +292,7 @@ pub async fn redis_zrem_in_db_core( key_raw: &str, member: &str, ) -> Result<(), String> { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::Redis(con) => { let mut con = con.lock().await; @@ -311,7 +311,7 @@ pub async fn redis_set_ttl_in_db_core( key_raw: &str, ttl: i64, ) -> Result<(), String> { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::Redis(con) => { let mut con = con.lock().await; @@ -329,7 +329,7 @@ pub async fn redis_delete_keys_in_db_core( db: u32, key_raws: &[String], ) -> Result { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::Redis(con) => { let mut con = con.lock().await; @@ -351,7 +351,7 @@ pub async fn redis_load_more_in_db_core( cursor: u64, count: usize, ) -> Result { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; match connections.get(connection_id).ok_or("Not found")? { PoolKind::Redis(con) => { let mut con = con.lock().await; diff --git a/crates/dbx-core/src/schema.rs b/crates/dbx-core/src/schema.rs index 8477fbb8c..9349bf9d3 100644 --- a/crates/dbx-core/src/schema.rs +++ b/crates/dbx-core/src/schema.rs @@ -131,7 +131,7 @@ pub fn extract_gaussdb( pub async fn list_databases_core(state: &AppState, connection_id: &str) -> Result, String> { { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; if extract_external(&connections, connection_id).is_some() { return Ok(vec![db::DatabaseInfo { name: "main".to_string() }]); } @@ -170,7 +170,7 @@ pub async fn list_databases_core(state: &AppState, connection_id: &str) -> Resul } } - let connections = state.connections.lock().await; + let connections = state.connections.read().await; let pool = connections.get(connection_id).ok_or("Connection not found")?; match pool { @@ -192,7 +192,7 @@ pub async fn list_schemas_core(state: &AppState, connection_id: &str, database: let pool_key = state.get_or_create_pool(connection_id, Some(database)).await?; { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; if let Some(PoolKind::ExternalDriver { config, session, .. }) = connections.get(&pool_key) { let config = config.clone(); let session = session.clone(); @@ -224,7 +224,7 @@ pub async fn list_schemas_core(state: &AppState, connection_id: &str, database: } } - let connections = state.connections.lock().await; + let connections = state.connections.read().await; let pool = connections.get(&pool_key).ok_or("Pool not found")?; match pool { @@ -242,7 +242,7 @@ pub async fn list_tables_core( let pool_key = state.get_or_create_pool(connection_id, Some(database)).await?; { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; if let Some(ext_pool) = extract_external(&connections, &pool_key) { drop(connections); let cache = ext_pool.cache.clone(); @@ -296,7 +296,7 @@ pub async fn list_tables_core( } } - let connections = state.connections.lock().await; + let connections = state.connections.read().await; let pool = connections.get(&pool_key).ok_or("Pool not found")?; match pool { @@ -322,7 +322,7 @@ pub async fn list_objects_core( let pool_key = state.get_or_create_pool(connection_id, Some(database)).await?; { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; if let Some(ext_pool) = extract_external(&connections, &pool_key) { drop(connections); let cache = ext_pool.cache.clone(); @@ -376,7 +376,7 @@ pub async fn get_columns_core( let pool_key = state.get_or_create_pool(connection_id, Some(database)).await?; { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; if let Some(ext_pool) = extract_external(&connections, &pool_key) { drop(connections); let cache = ext_pool.cache.clone(); @@ -436,7 +436,7 @@ pub async fn get_columns_core( } } - let connections = state.connections.lock().await; + let connections = state.connections.read().await; let pool = connections.get(&pool_key).ok_or("Pool not found")?; match pool { @@ -463,7 +463,7 @@ pub async fn list_indexes_core( let pool_key = state.get_or_create_pool(connection_id, Some(database)).await?; { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; if let Some(client) = extract_sqlserver(&connections, &pool_key) { drop(connections); let mut client = client.lock().await; @@ -487,7 +487,7 @@ pub async fn list_indexes_core( } } - let connections = state.connections.lock().await; + let connections = state.connections.read().await; let pool = connections.get(&pool_key).ok_or("Pool not found")?; match pool { @@ -514,7 +514,7 @@ pub async fn list_foreign_keys_core( let pool_key = state.get_or_create_pool(connection_id, Some(database)).await?; { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; if let Some(client) = extract_sqlserver(&connections, &pool_key) { drop(connections); let mut client = client.lock().await; @@ -538,7 +538,7 @@ pub async fn list_foreign_keys_core( } } - let connections = state.connections.lock().await; + let connections = state.connections.read().await; let pool = connections.get(&pool_key).ok_or("Pool not found")?; match pool { @@ -565,7 +565,7 @@ pub async fn list_triggers_core( let pool_key = state.get_or_create_pool(connection_id, Some(database)).await?; { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; if let Some(client) = extract_sqlserver(&connections, &pool_key) { drop(connections); let mut client = client.lock().await; @@ -589,7 +589,7 @@ pub async fn list_triggers_core( } } - let connections = state.connections.lock().await; + let connections = state.connections.read().await; let pool = connections.get(&pool_key).ok_or("Pool not found")?; match pool { @@ -616,7 +616,7 @@ pub async fn get_table_ddl_core( let pool_key = state.get_or_create_pool(connection_id, Some(database)).await?; { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; if let Some(con) = extract_duckdb(&connections, &pool_key) { drop(connections); let tbl = table.replace('\'', "''"); @@ -666,7 +666,7 @@ pub async fn get_table_ddl_core( } } - let connections = state.connections.lock().await; + let connections = state.connections.read().await; let pool = connections.get(&pool_key).ok_or("Pool not found")?; match pool { @@ -697,9 +697,11 @@ pub async fn sqlite_ddl(pool: &sqlx::sqlite::SqlitePool, table: &str) -> Result< } pub async fn pg_ddl(pool: &sqlx::postgres::PgPool, schema: &str, table: &str) -> Result { - let columns = db::postgres::get_columns(pool, schema, table).await?; - let indexes = db::postgres::list_indexes(pool, schema, table).await?; - let fkeys = db::postgres::list_foreign_keys(pool, schema, table).await?; + let (columns, indexes, fkeys) = tokio::try_join!( + db::postgres::get_columns(pool, schema, table), + db::postgres::list_indexes(pool, schema, table), + db::postgres::list_foreign_keys(pool, schema, table), + )?; let mut ddl = format!("CREATE TABLE \"{schema}\".\"{table}\" (\n"); let col_lines: Vec = columns diff --git a/crates/dbx-core/src/transfer.rs b/crates/dbx-core/src/transfer.rs index 297a3a55a..bce5efc60 100644 --- a/crates/dbx-core/src/transfer.rs +++ b/crates/dbx-core/src/transfer.rs @@ -469,7 +469,7 @@ pub fn count_sql(table: &str, schema: &str, db_type: &DatabaseType) -> String { } pub async fn execute_on_pool(state: &AppState, pool_key: &str, sql: &str) -> Result { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; let pool = connections.get(pool_key).ok_or("Connection not found")?; match pool { @@ -585,7 +585,7 @@ pub async fn execute_on_pool(state: &AppState, pool_key: &str, sql: &str) -> Res } pub async fn get_db_type(state: &AppState, connection_id: &str) -> Result { - let configs = state.configs.lock().await; + let configs = state.configs.read().await; configs .get(connection_id) .map(|c| c.db_type.clone()) @@ -600,7 +600,7 @@ pub async fn get_columns_for_transfer( schema: &str, table: &str, ) -> Result, String> { - let connections = state.connections.lock().await; + let connections = state.connections.read().await; if let Some(PoolKind::DuckDb(con)) = connections.get(pool_key) { let con = con.clone(); diff --git a/src-tauri/src/commands/connection.rs b/src-tauri/src/commands/connection.rs index f98221f0f..a33cad381 100644 --- a/src-tauri/src/commands/connection.rs +++ b/src-tauri/src/commands/connection.rs @@ -234,15 +234,15 @@ pub async fn connect_db(state: State<'_, Arc>, config: ConnectionConfi DatabaseType::Jdbc => state.external_driver_pool("jdbc", &db_config).await?, }; - state.connections.lock().await.insert(id.clone(), pool); - state.configs.lock().await.insert(id.clone(), config); + state.connections.write().await.insert(id.clone(), pool); + state.configs.write().await.insert(id.clone(), config); Ok(id) } #[tauri::command] pub async fn disconnect_db(state: State<'_, Arc>, connection_id: String) -> Result<(), String> { - let mut conns = state.connections.lock().await; + let mut conns = state.connections.write().await; let keys_to_remove: Vec = conns.keys().filter(|k| *k == &connection_id || k.starts_with(&format!("{connection_id}:"))).cloned().collect(); for key in keys_to_remove { @@ -266,7 +266,7 @@ pub async fn disconnect_db(state: State<'_, Arc>, connection_id: Strin } } drop(conns); - state.configs.lock().await.remove(&connection_id); + state.configs.write().await.remove(&connection_id); state.tunnels.stop_tunnel(&connection_id).await; Ok(()) } diff --git a/src-web/src/routes/connection.rs b/src-web/src/routes/connection.rs index fcd5bcd99..c1bf0bbce 100644 --- a/src-web/src/routes/connection.rs +++ b/src-web/src/routes/connection.rs @@ -35,20 +35,20 @@ pub async fn test_connection( // Store config temporarily let temp_id = format!("__test_{}", uuid::Uuid::new_v4()); - app.configs.lock().await.insert(temp_id.clone(), config.clone()); + app.configs.write().await.insert(temp_id.clone(), config.clone()); // Try to connect let result = app.get_or_create_pool(&temp_id, config.database.as_deref()).await; // Clean up any pool keys created for the temporary connection, including // database-scoped keys like "__test_uuid:database". - let mut connections = app.connections.lock().await; + let mut connections = app.connections.write().await; let temp_keys: Vec = connections.keys().filter(|key| key.starts_with(&temp_id)).cloned().collect(); for key in temp_keys { connections.remove(&key); } drop(connections); - app.configs.lock().await.remove(&temp_id); + app.configs.write().await.remove(&temp_id); match result { Ok(_) => Ok(Json("Connection successful".to_string())), @@ -64,7 +64,7 @@ pub async fn connect_db( let app = &state.app; let connection_id = config.id.clone(); - app.configs.lock().await.insert(connection_id.clone(), config.clone()); + app.configs.write().await.insert(connection_id.clone(), config.clone()); app.get_or_create_pool(&connection_id, None).await.map_err(AppError)?; @@ -76,7 +76,7 @@ pub async fn disconnect_db( Json(body): Json, ) -> Result, AppError> { let app = &state.app; - let mut connections = app.connections.lock().await; + let mut connections = app.connections.write().await; let pool_prefix = format!("{}:", body.connection_id); let keys_to_remove: Vec = @@ -86,7 +86,7 @@ pub async fn disconnect_db( } drop(connections); - app.configs.lock().await.remove(&body.connection_id); + app.configs.write().await.remove(&body.connection_id); app.tunnels.stop_tunnel(&body.connection_id).await; Ok(Json(())) @@ -108,7 +108,7 @@ pub async fn load_connections(State(state): State>) -> Result item.data); + const { formatMarkdownTable } = await import("@/lib/markdownTable"); const md = formatMarkdownTable({ columns: cols, rows: visibleRows }); if (await saveFileContent(md, "export.md", "Markdown", "md")) { toast(t("grid.exported")); @@ -2030,6 +2029,7 @@ async function exportMarkdown() { async function exportXlsx() { try { + const { buildXlsxWorkbook } = await import("@/lib/xlsxExport"); const workbook = buildXlsxWorkbook({ sheetName: props.tableMeta?.tableName || "Export", columns: props.result.columns, diff --git a/src/components/layout/UpdateDialog.vue b/src/components/layout/UpdateDialog.vue index 09d0565ea..819a7c11f 100644 --- a/src/components/layout/UpdateDialog.vue +++ b/src/components/layout/UpdateDialog.vue @@ -1,7 +1,6 @@