Skip to content

test(rust): stage watch-test writes and rename them into place - #1247

Merged
thekevinscott merged 1 commit into
mainfrom
claude/1246-watch-test-race
Oct 5, 2026
Merged

thekevinscott merged 1 commit into
mainfrom
claude/1246-watch-test-race

Conversation

@thekevinbot

Copy link
Copy Markdown
Collaborator

Fixes #1246

Status

Test-only change in packages/rust/tests/sdk.rs. just preflight passes (0 failing, 3 packaging skips). No e2e receipt or changelog fragment owed.

The race

fs::write opens with O_TRUNC and then writes. The watcher's per-file hook runs on the first inotify event, which can land between the truncate and the write, so read_to_string returns "" and the first Update (or Insert) carries an empty name. it_streams_watch_events had the same shape on new_item.txt; async_sdk.rs::it_streams_watch_events already avoided it by staging and renaming.

What changed

A write_watched helper in sdk.rs writes to a .tmp sibling (outside the **/*.txt glob) and fs::renames it over the target, so the watcher sees one atomic replace with the final bytes. Both it_streams_watch_events and it_streams_watch_update_events use it. No sleeps, timeouts, retries, or loosened assertions. The other writes in the file happen before watch() or feed an extractor that ignores content, so they are not exposed.

Reproduction

Serial runs never tripped it: 30 sequential runs of the prebuilt sdk test binary (filtered to the two watch tests) under stress --cpu 32 --io 8 --hdd 4 passed 30/30 (/tmp/dirsql-1246-repro.sh).

Concurrent copies did. Same binary, 10 copies at once per round under stress --cpu 32 --io 8, on a 32-core box (/tmp/dirsql-1246-rounds.sh <bin> <label> 10 <rounds>):

runs failures
before (origin/main) 30 (3 rounds x 10) 1, it_streams_watch_update_events: left: Text(""), right: Text("final") at sdk.rs:426
after (this branch) 100 (10 rounds x 10) 0

A first attempt at 30 concurrent copies produced 8 failures, all Too many open files (os error 24) from the inotify instance cap (max_user_instances=128, ~100 already in use); those are not the race and were discarded.

Manual run: cargo test -p dirsql --test sdk passes 37/37 on this branch.

fs::write truncates before it writes, so the watcher could read an empty
file and the first Update carried an empty name.
@thekevinscott
thekevinscott merged commit b5d4097 into main Oct 5, 2026
82 checks passed
@thekevinscott
thekevinscott deleted the claude/1246-watch-test-race branch October 5, 2026 00:12
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.

test(rust): it_streams_watch_update_events races fs::write's truncate-then-write

2 participants