Skip to content

Fix the Rails 7.1 unrestorable busy wait test - #81

Merged
cardmagic merged 2 commits into
mainfrom
fix/rails-71-busy-wait-test
Oct 3, 2026
Merged

cardmagic merged 2 commits into
mainfrom
fix/rails-71-busy-wait-test

Conversation

@cardmagic

@cardmagic cardmagic commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Summary

test_sync_leaves_an_unrestorable_busy_wait_alone failed on Rails 7.1 when it ran before any other test had touched its connection. CI run 35874660739 failed this way, and seed 11785 fails every time on main.

The test checks that a sync call leaves alone a busy wait that the adapter cannot read back. It stubs configured_busy_handler_timeout to nil, so the adapter reads PRAGMA busy_timeout. The test assumed that its connection already had a Ruby busy handler, which PRAGMA busy_timeout reads as 0:

Connection PRAGMA busy_timeout
New, Rails 7.1.6 5000 (SQLite's own wait)
New, Rails 8.1.3.1 0 (Ruby busy handler)
After busy_handler_timeout= 0

On a new Rails 7.1 connection, the adapter correctly took the restorable path and put back SQLite's own wait. That wait does not release the Ruby VM lock, so the releaser thread in write_while_write_lock_is_briefly_held could not release the write lock. The write raised SQLite3::BusyException after the full 5 s.

The test now installs the Ruby busy handler on its connection and asserts that PRAGMA busy_timeout reads 0. It checks the unrestorable path on every Rails version and in every test order. Only the test changes; the adapter behaved correctly.

CI job time limits

The first push run of this PR (36888691442) did not fail a test. Its javascript job hung in npx playwright install --with-deps chromium: apt-get update printed its fifth index download at 16:01 and then nothing, until GitHub cancelled the job at its 360-minute default at 22:01. The same step on the same commit took about 3 minutes in the pull_request run. Over the last 60 runs, this step took a median of 25 s.

445cf27 gives every job timeout-minutes: 15, as solid-objects-js does. In the last 25 successful runs, the longest job (javascript) took 491 s. A hang now fails in 15 minutes instead of 6 hours. actionlint passes.

Compatibility

Test and CI configuration only. No runtime, API, or migration effect. Not applicable to solid-objects-js, which has no Rails connection or Ruby VM lock.

Validation

  • Before the fix, Rails 7.1.6, test alone: ActiveRecord::StatementInvalid: SQLite3::BusyException: database is locked at write_while_write_lock_is_briefly_held after about 6 s.
  • Before the fix, Rails 7.1.6, bundle exec rake test TESTOPTS="--seed=11785": 797 runs, 1 error (this test, BusyException).
  • After the fix, Rails 7.1.6: the test passes alone. Seed 11785 and random seeds 38098 and 37363 each ran 797 tests with 0 failures/errors and 28 skips.
  • Reversal: removing return nil unless pragma_timeout.positive? from Sqlite#restorable_busy_wait makes the adapter overwrite the handler. The changed test then fails with SQLite3::BusyException on Rails 7.1.6 and 8.1.3.1.
  • Rails 8.1.3.1, bundle exec rake: 797 runs, 2,707 assertions, 0 failures/errors, 28 skips. Standard, RuboCop, RBS validation, Steep, and Brakeman passed.

Rails 7.1 runs used RAILS_VERSION=7.1 bundle lock --update --local, as the CI compatibility job does.

The unrestorable busy wait test assumed that its connection already had
a Ruby busy handler, which PRAGMA busy_timeout reads as 0. Rails 7.2
and later install that handler on each new connection, and an earlier
sync call installs it too. A new Rails 7.1 connection instead uses
SQLite's own busy_timeout, which PRAGMA reads as 5000.

When the test ran first on Rails 7.1, the adapter correctly took the
restorable path and put back SQLite's own wait. That wait holds the
Ruby VM lock, so the releaser thread could not release the write lock,
and the write raised SQLite3::BusyException after 5 s. Seed 11785
failed every time; CI run 35874660739 failed the same way.

Install the handler in the test and assert that PRAGMA reads 0, so the
test checks the unrestorable path on every Rails version and order.
@greptile-apps

greptile-apps Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Low risk] Fixes a test for Rails 7.1 compatibility.

The PR appears safe to merge.

Summary

The PR makes the SQLite synchronous-invocation test install a Ruby busy handler explicitly, then confirms that PRAGMA busy_timeout is zero before exercising the unrestorable-wait path. It changes only the test.

Reviews (1) · Last reviewed commit: "test: install the busy handler the test ..."

No job had a time limit. In run 36888691442, apt-get update inside
"npx playwright install --with-deps chromium" stopped after its fifth
index download and printed nothing for 6 hours, until GitHub cancelled
the javascript job at its 360-minute default. The same step on the
same commit took about 3 minutes in the pull_request run.

Give every job a 15-minute limit, as solid-objects-js does. In the last
25 successful runs, the longest job took 491 s, so the limit leaves
room for a slow runner and stops a hang in 15 minutes instead of 6
hours.
@cardmagic
cardmagic merged commit 4e53160 into main Oct 3, 2026
40 checks passed
@cardmagic
cardmagic deleted the fix/rails-71-busy-wait-test branch October 3, 2026 15:30
cardmagic added a commit that referenced this pull request Oct 3, 2026
…bility

Bring in the Rails 7.1 busy wait test fix and the CI job timeout from #81.
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