Skip to content

Fix startup failure from fd exhaustion when launched via Finder#500

Merged
gschier merged 3 commits into
mainfrom
fix-fd-exhaustion-launch
Jul 5, 2026
Merged

Fix startup failure from fd exhaustion when launched via Finder#500
gschier merged 3 commits into
mainfrom
fix-fd-exhaustion-launch

Conversation

@gschier

@gschier gschier commented Jul 5, 2026

Copy link
Copy Markdown
Member

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:

  • Size pools for concurrent in-flight queries (20 main / 10 blobs) with small min_idle so startup opens 3 connections instead of 150
  • Remove the Mutex around the pool — r2d2 is internally synchronized, and blocking inside get() while holding the lock froze all DB access whenever the pool was exhausted
  • Raise the open-file soft limit to 10240 at startup on macOS/Linux via the rlimit crate

Submission

  • This PR is a bug fix.
  • If this PR is not a bug fix, I linked the feedback item where @gschier explicitly gave me permission to work on it.
  • I have read and followed CONTRIBUTING.md.
  • I tested this change locally.
  • I added or updated tests, or tests are not reasonable for this change.
  • I added screenshots or recordings, or this change does not affect the UI.

Related

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-apps

greptile-apps Bot commented Jul 5, 2026

Copy link
Copy Markdown

Greptile Summary

This 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 Mutex around an already-thread-safe r2d2 pool that serialized all DB access.

  • Pool max_size is reduced from 100→20 (main) and 50→10 (blob), with min_idle set so startup opens only 3 connections instead of 150.
  • The redundant Arc<Mutex<Pool<...>>> wrappers are removed from both QueryManager and BlobManager; r2d2 Pool is internally Arc-backed and Send + Sync.
  • rlimit::increase_nofile_limit(10240) is called at startup on macOS/Linux as a belt-and-suspenders guard against future fd pressure.

Confidence Score: 5/5

Safe 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.

Important Files Changed

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]
Loading
%%{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]
Loading

Reviews (3): Last reviewed commit: "Remove Mutex around blob pool as well" | Re-trigger Greptile

@gschier gschier merged commit b332a0e into main Jul 5, 2026
7 checks passed
@gschier gschier deleted the fix-fd-exhaustion-launch branch July 5, 2026 15:49
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.

1 participant