Skip to content

feat: added a new field in settings screen, regarding maxConnection c…#72

Open
ealvesss wants to merge 2 commits intodebba:mainfrom
ealvesss:feat/max-conn-config
Open

feat: added a new field in settings screen, regarding maxConnection c…#72
ealvesss wants to merge 2 commits intodebba:mainfrom
ealvesss:feat/max-conn-config

Conversation

@ealvesss
Copy link

@ealvesss ealvesss commented Mar 6, 2026

PR Description

feat: make connection pool size configurable via Settings

Problem

The maximum number of database connections per pool was hardcoded in pool_manager.rs (10 for MySQL/PostgreSQL, 5 for SQLite), with no way for users to adjust it.

Changes

Backend (src-tauri/src/)

  • config.rs: Added max_connections: Option to AppConfig, its merge logic in save_config, and a get_pool_max_connections() helper that reads the config file without requiring AppHandle.
  • pool_manager.rs: Replaced all hardcoded .max_connections() values in MySQL, PostgreSQL, and SQLite pool creation with crate::config::get_pool_max_connections().

Frontend (src/)

  • contexts/SettingsContext.ts: Added maxConnections to the Settings interface and DEFAULT_SETTINGS.
  • pages/Settings.tsx: Added a number input for Connection Pool Size in the Data Editor section of the General settings tab.
  • i18n/locales/{en,it,es}.json: Added translation keys maxConnections, maxConnectionsDesc, and connections.

Behavior

  • The setting takes effect when a new connection pool is created. Users must disconnect and reconnect for changes to apply to existing connections.
  • Falls back to the default value if no config is present.

keychain_utils::delete_ai_key(&provider)
}

pub const DEFAULT_MAX_CONNECTIONS: u32 = 1;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Default pool size of 1 is too conservative and will likely cause performance degradation

The original code used max_connections(10) for MySQL/PostgreSQL and max_connections(5) for SQLite. A default of 1 means all queries will be serialized — only one query can execute at a time per connection pool. This will cause noticeable slowdowns for any workload with concurrent queries (e.g., opening multiple tabs, running queries while the sidebar loads schema info).

Consider a higher default such as 5 or 10 to match the previous behavior, while still allowing users to tune it down if needed.

/// Get the configured maximum pool connections, or DEFAULT_MAX_CONNECTIONS if not set.
/// Does not require AppHandle — reads config.json directly via the paths module.
pub fn get_pool_max_connections() -> u32 {
let config_path = get_app_config_dir().join("config.json");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: get_pool_max_connections() may resolve a different config path than the rest of the app

get_app_config_dir() uses directories::ProjectDirs::from("", "", "tabularis"), while get_config_dir() uses app.path().app_config_dir() (Tauri's path resolver). On macOS, Tauri uses the bundle identifier to construct the config path (e.g., ~/Library/Application Support/com.tabularis.app/), while the directories crate uses the app name directly (e.g., ~/Library/Application Support/tabularis/). These can diverge, meaning get_pool_max_connections() may silently read from a different (possibly non-existent) config file and always return DEFAULT_MAX_CONNECTIONS.

Consider passing AppHandle to this function (like get_max_blob_size does) to ensure consistent path resolution, or at minimum add a test/log to verify the paths match.

let url = build_sqlite_url(params);
let pool = sqlx::sqlite::SqlitePoolOptions::new()
.max_connections(5) // SQLite has lower concurrency needs
.max_connections(crate::config::get_pool_max_connections())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: SQLite now uses the same pool size as MySQL/PostgreSQL, which can cause database is locked errors

SQLite is a file-based database with limited write concurrency — only one writer can hold the lock at a time. The original code intentionally used a lower max_connections(5) with the comment // SQLite has lower concurrency needs. If a user sets maxConnections to a high value (e.g., 50 or 100), SQLite will create that many connections, and concurrent writes will result in SQLITE_BUSY / database is locked errors.

Consider capping SQLite pool size independently, e.g.:

let sqlite_max = crate::config::get_pool_max_connections().min(5);
.max_connections(sqlite_max)

Or document clearly that high values are not recommended for SQLite connections.

type="number"
value={settings.maxConnections ?? DEFAULT_SETTINGS.maxConnections}
onChange={(e) =>
updateSetting(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: parseInt fallback silently resets to default when the field is cleared

parseInt(e.target.value) || DEFAULT_SETTINGS.maxConnections! will fall back to 1 whenever the user clears the input field (empty string → NaN → falsy) or types 0. While min="1" prevents submitting 0 via the browser's native validation, the onChange handler fires on every keystroke, so clearing the field mid-edit will immediately reset the value to 1, making it impossible to type a number starting by clearing the field first.

Consider using Math.max(1, parseInt(e.target.value) || 1) or only updating the setting on onBlur to avoid this UX issue. The existing resultPageSize input has the same pattern, so this is consistent with the codebase — but worth noting.

@kilo-code-bot
Copy link

kilo-code-bot bot commented Mar 6, 2026

Code Review Summary

Status: 4 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
src-tauri/src/config.rs 221 Default pool size of 1 is too conservative — will serialize all queries and likely cause performance degradation vs. the previous hardcoded 10/5
src-tauri/src/config.rs 226 get_pool_max_connections() uses directories::ProjectDirs to resolve the config path, which may differ from Tauri's app.path().app_config_dir() on macOS — could silently read the wrong (or non-existent) file and always return the default
src-tauri/src/pool_manager.rs 206 SQLite now uses the same configurable pool size as MySQL/PostgreSQL; high values will cause SQLITE_BUSY / database is locked errors since SQLite only supports one concurrent writer

SUGGESTION

File Line Issue
src/pages/Settings.tsx 993 parseInt(e.target.value) || DEFAULT_SETTINGS.maxConnections! resets to 1 on every keystroke when the field is cleared, making it hard to type a new value
Files Reviewed (6 files)
  • src-tauri/src/config.rs — 2 issues
  • src-tauri/src/pool_manager.rs — 1 issue
  • src/contexts/SettingsContext.ts — no issues
  • src/pages/Settings.tsx — 1 issue
  • src/i18n/locales/en.json — no issues
  • src/i18n/locales/es.json — no issues
  • src/i18n/locales/it.json — no issues
  • package-lock.json — skipped (generated file)

Fix these issues in Kilo Cloud

@debba
Copy link
Owner

debba commented Mar 7, 2026

@ealvesss Thanks for the contribution! The feature is well-structured overall, but there are a few issues to address before merging.

I have 2 points I would change:

  1. DEFAULT_MAX_CONNECTIONS = 1is a regression** — the previous hardcoded values were10for MySQL/PostgreSQL and5for SQLite. Defaulting to1` will serialize all queries and cause significant performance degradation for users who haven't explicitly configured the setting.

  2. SQLite needs a separate cap — SQLite only supports one concurrent writer, so high pool values will cause SQLITE_BUSY / database is locked errors. It should be capped independently (e.g. params.max_connections.map(|v| v.min(5)).unwrap_or(5)), not share the same configurable value as MySQL/PostgreSQL.

Design suggestion

Rather than a global setting in the Settings page, consider moving max_connections to the individual connection configuration (ConnectionParams). This would allow users to set:

  • max_connections: 1 for a PostgreSQL connection behind pgBouncer
  • max_connections: 10 for a direct MySQL connection
  • SQLite capped at 5 regardless

This approach is more flexible, more explicit, and makes the global AppConfig change unnecessary — which would simplify the diff considerably.

What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants