Fix startup failure from fd exhaustion when launched via Finder#500
Conversation
WAL mode (added in #496) made each pooled connection hold ~3 file descriptors, and the pools eagerly opened all 150 connections at startup — exceeding the 256 open-file soft limit launchd gives GUI apps, so the blobs DB failed to open unless launched from a shell. - Size pools for in-flight queries (20/10) with small min_idle - Remove the Mutex around the pool; r2d2 is internally synchronized, and blocking inside get() while holding it froze all DB access whenever the pool was exhausted - Raise the open-file soft limit at startup on macOS/Linux Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Greptile SummaryThis PR fixes a startup crash on macOS GUI launches by addressing two root causes: over-sized connection pools (150 connections at startup × ~3 fds each) that blew past launchd's 256-fd soft limit, and a misplaced
Confidence Score: 5/5Safe to merge — the changes are well-scoped, directly address the reported crash, and remove a genuine synchronization anti-pattern without introducing new risks. All three changes are correct: the pool-size reduction directly caps fd usage at startup, the Mutex removal is sound because r2d2 Pool is already internally thread-safe (Arc-backed), and the rlimit guard is appropriately conditioned on macOS/Linux with proper error handling. The min_idle values ensure the pool opens only 3 connections at startup (~9 fds) rather than 150, well within the raised 10240 limit. No new panics, races, or resource leaks are introduced. No files require special attention.
|
| Filename | Overview |
|---|---|
| crates/yaak-models/src/lib.rs | Reduces main pool max_size 100→20 and blob pool 50→10, adds min_idle so startup eagerly opens only 2+1 connections instead of 150; the sized-down pools directly fix the fd exhaustion. |
| crates/yaak-models/src/query_manager.rs | Drops the Arc<Mutex> wrapper; pool.get() calls are now lock-free as r2d2 handles its own synchronization, eliminating the serialization bottleneck. |
| crates/yaak-models/src/blob_manager.rs | Same Mutex removal as query_manager; BlobManager now stores Pool directly and Pool::clone() is a cheap Arc handle copy. |
| crates-tauri/yaak-app-client/src/lib.rs | Calls rlimit::increase_nofile_limit(10240) before any pool is opened; correctly guarded by #[cfg] and uses eprintln! since the logger is not initialized yet. |
| crates-tauri/yaak-app-client/Cargo.toml | Adds rlimit = 0.11 as a target-specific dependency for macOS and Linux only; matches the #[cfg] gate in lib.rs. |
| Cargo.lock | Adds rlimit 0.11.0 lockfile entry with libc dependency; libc bumped from 0.2.172 to 0.2.186 as a transitive update. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[run - app startup] --> B[raise open-file limit to 10240 on macOS/Linux]
B --> C[tauri Builder init]
C --> D[init_standalone called]
D --> E[Main Pool: max=20 min_idle=2]
D --> F[Blob Pool: max=10 min_idle=1]
E --> G[migrate_db]
F --> H[migrate_blob_db]
G --> I[QueryManager holds Pool directly]
H --> J[BlobManager holds Pool directly]
I --> K[pool.get on each request]
J --> L[pool.get returns BlobContext]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[run - app startup] --> B[raise open-file limit to 10240 on macOS/Linux]
B --> C[tauri Builder init]
C --> D[init_standalone called]
D --> E[Main Pool: max=20 min_idle=2]
D --> F[Blob Pool: max=10 min_idle=1]
E --> G[migrate_db]
F --> H[migrate_blob_db]
G --> I[QueryManager holds Pool directly]
H --> J[BlobManager holds Pool directly]
I --> K[pool.get on each request]
J --> L[pool.get returns BlobContext]
Reviews (3): Last reviewed commit: "Remove Mutex around blob pool as well" | Re-trigger Greptile
Summary
Since #496 enabled WAL, each pooled SQLite connection holds ~3 file descriptors, and the pools eagerly opened all 150 connections at startup — blowing past the 256 open-file soft limit launchd gives GUI apps. The blobs DB then failed to open ("timed out waiting for connection: unable to open database file") unless the app was launched from a shell with a higher ulimit.
Fixes:
min_idleso startup opens 3 connections instead of 150Mutexaround the pool — r2d2 is internally synchronized, and blocking insideget()while holding the lock froze all DB access whenever the pool was exhaustedrlimitcrateSubmission
CONTRIBUTING.md.Related