Skip to content

Commit b603b1e

Browse files
committed
test: scope SQLite-deadlock workarounds to the still-affected surface
Local verification 2026-04-23 with SQLite 3.51.3 (rusqlite 0.39): - perima-db tests: 5/5 PASS, 0 hangs, 8s each. The upstream lock-order inversion fix in unixClose vs unixLock-from-sqlite3WalClose is sufficient for our writer + read-pool single-writer fixture. - perima-desktop integration tests: 7/12 STILL deadlock at 80s. The `_inner` test seam (run_scan_inner_with_metadata + list_files_inner + search_inner + 4 others) opens its own short-lived writer + ReadPool per call, and tests that chain multiple `_inner` calls hit a SECOND close-ordering surface that 3.51.3 does not address. Per the research doc, "fix the symptom, not the class": the bug class (multiple Connections to same file with non-deterministic drop ordering) still exists, just no longer via the upstream-patched path. Workarounds reverted (SQLite fix is sufficient): - nextest.toml: drop perima-db retries=2 override (5/5 verified clean). - nextest.toml: drop perima-desktop slow-timeout=90s override (the perceived "slow tests" were actually deadlocked under 3.51.1). Workarounds RESTORED on the still-affected surface: - 7 #[ignore] attributes on the affected commands_test.rs tests, with a tighter WHY pointing at the remaining bug class + the migration off _inner seam tracked in #119/#126. Net effect: cleaner config + targeted test-skip. Matches the research recommendation tier-1 (rusqlite bump) without false-claim cleanup.
1 parent 3fec391 commit b603b1e

2 files changed

Lines changed: 14 additions & 63 deletions

File tree

.config/nextest.toml

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,11 @@
22
# https://nexte.st/docs/configuration/
33

44
[profile.default]
5-
# Default: flag a test as "slow" after 40s and terminate after two slow
6-
# periods (80s total per test). Rationale: the slowest clean test in
7-
# perima-{db,app,cli,core,fs,hash,media} is ~14s (fts proptest). Past
8-
# 40s is a sign of lock contention or deadlock, not legitimate compute.
9-
# Per-package overrides below relax this for crates with legitimately
10-
# slow tests (perima-desktop = real Tauri/scan/thumbnail work).
5+
# Flag a test as "slow" after 40s and terminate after two slow periods
6+
# (80s total per test). Rationale: the slowest clean test in any crate
7+
# is ~14s (fts proptest in perima-db). Past 40s is a sign of lock
8+
# contention or deadlock, not legitimate compute. nextest 0.9.133 lacks
9+
# a CLI `--slow-timeout` flag, but honors this config key — keeps
10+
# `timeout NNN cargo nextest ...` wrappers from killing the whole suite
11+
# on a single hang.
1112
slow-timeout = { period = "40s", terminate-after = 2 }
12-
13-
# perima-desktop integration tests (`crates/desktop/tests/commands_test.rs`)
14-
# do real end-to-end scan + metadata + thumbnail work via the `_inner`
15-
# helpers. On macOS CI runners these can take 60-90s legitimately; per
16-
# CLAUDE.md baseline `cargo nextest run -p perima-desktop` is 120-300s
17-
# (Tauri compile + slow tests). Bumping to 90s × 2 = 180s before kill.
18-
# The deadlock-detection signal stays at 80s for every other crate.
19-
[[profile.default.overrides]]
20-
filter = 'package(perima-desktop)'
21-
slow-timeout = { period = "90s", terminate-after = 2 }
22-
23-
# perima-db tests intermittently hit the SQLite lock-order inversion
24-
# documented in GH #131 (writer + ReadPool drop race within a single
25-
# test process). Local reproduction: ~20% per-run hang on the FTS5
26-
# proptests; macOS CI also surfaced it on plain repo tests like
27-
# `tag_repo::tests::upsert_tag_inserts_new`. cargo nextest's
28-
# process-per-test isolation eliminates the cross-test class but not
29-
# the within-test class. Retry up to 2 times → effective per-test
30-
# failure rate <1% (0.2³ if independent). Removal pending GH #131
31-
# production fix or migration off the per-test writer+pool fixture.
32-
[[profile.default.overrides]]
33-
filter = 'package(perima-db)'
34-
retries = 2

crates/desktop/tests/commands_test.rs

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,8 @@ fn write_tiny_png(path: &Path, fill: [u8; 3]) {
7272
}
7373

7474
/// Scan three fixture files and assert `files_seen=3, files_new=3, files_errored=0`.
75-
///
76-
/// WHY ignored on CI: same `SQLite` lock-order deadlock as
77-
/// `list_files_after_scan`. See GH #131.
7875
#[tokio::test]
79-
#[ignore = "GH #131 — SQLite lock-order inversion deadlocks under concurrent Connection drops"]
76+
#[ignore = "GH #131 — desktop _inner test seam still deadlocks under SQLite 3.51.3 (separate close-ordering surface, not the upstream-fixed lock-order inversion). Tracked for migration off _inner seam in #119/#126."]
8077
async fn scan_indexes_files() {
8178
let fixture_dir = tempfile::tempdir().expect("tempdir for fixtures");
8279
let data_dir = tempfile::tempdir().expect("tempdir for data");
@@ -114,17 +111,8 @@ async fn scan_indexes_files() {
114111
}
115112

116113
/// After a successful scan, `list_files_inner` must return all 3 records.
117-
///
118-
/// WHY ignored on CI: hits the `SQLite` lock-order inversion in `unixClose` vs
119-
/// `unixLock`-from-`sqlite3WalClose` (GH #131). Test creates writer + read
120-
/// pool against a single DB; at end-of-test the writer thread + pool drop
121-
/// concurrently and the lock-order cycle deadlocks. Tracked locally via gdb
122-
/// backtrace 2026-04-23. Run manually with `cargo nextest run --run-ignored
123-
/// only -p perima-desktop` if you have a kernel + libc combination immune
124-
/// to the upstream race. Removal pending #131 production fix or migration
125-
/// off the `_inner` test seam (#119/#126).
126114
#[tokio::test]
127-
#[ignore = "GH #131 — SQLite lock-order inversion deadlocks under concurrent Connection drops"]
115+
#[ignore = "GH #131 — desktop _inner test seam still deadlocks under SQLite 3.51.3 (separate close-ordering surface, not the upstream-fixed lock-order inversion). Tracked for migration off _inner seam in #119/#126."]
128116
async fn list_files_after_scan() {
129117
let fixture_dir = tempfile::tempdir().expect("tempdir for fixtures");
130118
let data_dir = tempfile::tempdir().expect("tempdir for data");
@@ -149,11 +137,8 @@ async fn list_files_after_scan() {
149137
/// After inserting metadata for a scanned file, the
150138
/// `list_files_with_metadata_inner` helper must return at least one row
151139
/// with metadata fields populated from the stored record.
152-
///
153-
/// WHY ignored on CI: same `SQLite` lock-order deadlock as
154-
/// `list_files_after_scan` above. See GH #131.
155140
#[tokio::test]
156-
#[ignore = "GH #131 — SQLite lock-order inversion deadlocks under concurrent Connection drops"]
141+
#[ignore = "GH #131 — desktop _inner test seam still deadlocks under SQLite 3.51.3 (separate close-ordering surface, not the upstream-fixed lock-order inversion). Tracked for migration off _inner seam in #119/#126."]
157142
async fn list_files_with_metadata_returns_rows() {
158143
let fixture_dir = tempfile::tempdir().expect("tempdir for fixtures");
159144
let data_dir = tempfile::tempdir().expect("tempdir for data");
@@ -235,11 +220,8 @@ async fn list_files_with_metadata_returns_rows() {
235220
}
236221

237222
/// After a successful scan, `list_volumes_inner` must return at least one volume.
238-
///
239-
/// WHY ignored on CI: same `SQLite` lock-order deadlock as
240-
/// `list_files_after_scan`. See GH #131.
241223
#[tokio::test]
242-
#[ignore = "GH #131 — SQLite lock-order inversion deadlocks under concurrent Connection drops"]
224+
#[ignore = "GH #131 — desktop _inner test seam still deadlocks under SQLite 3.51.3 (separate close-ordering surface, not the upstream-fixed lock-order inversion). Tracked for migration off _inner seam in #119/#126."]
243225
async fn list_volumes_after_scan() {
244226
let fixture_dir = tempfile::tempdir().expect("tempdir for fixtures");
245227
let data_dir = tempfile::tempdir().expect("tempdir for data");
@@ -261,11 +243,8 @@ async fn list_volumes_after_scan() {
261243
/// PNG files must produce `file_metadata` rows AND WebP thumbnails on
262244
/// disk under `<data_dir>/thumbnails/` — the same subtree the Tauri
263245
/// asset-protocol scope exposes.
264-
///
265-
/// WHY ignored on CI: same `SQLite` lock-order deadlock as
266-
/// `list_files_after_scan` above. See GH #131.
267246
#[tokio::test]
268-
#[ignore = "GH #131 — SQLite lock-order inversion deadlocks under concurrent Connection drops"]
247+
#[ignore = "GH #131 — desktop _inner test seam still deadlocks under SQLite 3.51.3 (separate close-ordering surface, not the upstream-fixed lock-order inversion). Tracked for migration off _inner seam in #119/#126."]
269248
async fn desktop_scan_populates_metadata_and_thumbnails() {
270249
let fixture_dir = tempfile::tempdir().expect("tempdir for fixtures");
271250
let data_dir = tempfile::tempdir().expect("tempdir for data");
@@ -366,11 +345,8 @@ async fn desktop_scan_populates_metadata_and_thumbnails() {
366345

367346
/// Exercises the four tag `_inner` helpers end-to-end:
368347
/// attach → list-with-tags → list-tags → detach → verify empty.
369-
///
370-
/// WHY ignored on CI: same `SQLite` lock-order deadlock as
371-
/// `list_files_after_scan`. See GH #131.
372348
#[tokio::test]
373-
#[ignore = "GH #131 — SQLite lock-order inversion deadlocks under concurrent Connection drops"]
349+
#[ignore = "GH #131 — desktop _inner test seam still deadlocks under SQLite 3.51.3 (separate close-ordering surface, not the upstream-fixed lock-order inversion). Tracked for migration off _inner seam in #119/#126."]
374350
async fn list_files_with_tags_returns_tagged_rows() {
375351
let td = tempfile::tempdir().expect("tempdir");
376352
let data_dir = td.path().join("data");
@@ -506,11 +482,8 @@ fn thumbnail_root_matches_asset_protocol_scope() {
506482
/// via the `SearchRepository` trait, and asserts the query returns the
507483
/// seeded filename. Exercises the inner helper end-to-end without
508484
/// constructing `tauri::State`.
509-
///
510-
/// WHY ignored on CI: same `SQLite` lock-order deadlock as
511-
/// `list_files_after_scan`. See GH #131.
512485
#[tokio::test]
513-
#[ignore = "GH #131 — SQLite lock-order inversion deadlocks under concurrent Connection drops"]
486+
#[ignore = "GH #131 — desktop _inner test seam still deadlocks under SQLite 3.51.3 (separate close-ordering surface, not the upstream-fixed lock-order inversion). Tracked for migration off _inner seam in #119/#126."]
514487
async fn search_returns_hit_after_scan_and_rebuild() {
515488
let td = tempfile::tempdir().expect("tempdir");
516489
let data_dir = td.path().join("data");

0 commit comments

Comments
 (0)