Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ jobs:
- run: npm ci
- run: npm test

compatibility:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ruby-version: [ "3.3", "3.4" ]
rails-version: [ "8.0", "8.1" ]
env:
RAILS_VERSION: ${{ matrix.rails-version }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: false
- run: bundle lock --update && bundle install --jobs 4
- run: bundle exec rake test

postgresql:
runs-on: ubuntu-latest
services:
Expand Down Expand Up @@ -97,6 +115,7 @@ jobs:
- mysql
- static
- javascript
- compatibility
runs-on: ubuntu-latest
permissions:
contents: write
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## Unreleased

- Run compatibility CI across the span the gemspec advertises: Ruby 3.3 and 3.4
against Rails 8.0 and 8.1. The suite previously ran on one combination, so
`>= 8.0` was a claim rather than a tested guarantee. Set `RAILS_VERSION` to
pin a Rails line locally.
- Stop a synchronous lock retry from asking for a negative wait when its
deadline expires between the check and the wait, which raised
`ArgumentError: time interval must not be negative` instead of the timeout
the caller expected. Found by the new compatibility matrix.
- Add `SolidObjects::WakeUpAdapters::Postgresql`, an optional cross-process
wake-up using PostgreSQL notifications. In-process signalling cannot reach a
worker process, so reactive delivery waited out `polling_interval`. With the
Expand Down
10 changes: 10 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ source "https://rubygems.org"

gemspec

# Compatibility runs pin the Rails line so CI can verify the span the gemspec
# advertises rather than only the newest release that resolves.
rails_version = ENV["RAILS_VERSION"]
if rails_version
constraint = "~> #{rails_version}.0"
%w[actioncable actionpack actionview activerecord activesupport railties].each do |library|
gem library, constraint
end
end

group :development, :test do
gem "mysql2", ">= 0.5", require: false
gem "pg", ">= 1.5", require: false
Expand Down
3 changes: 3 additions & 0 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
a listening connection per waiting thread and release on supervisor shutdown
- Inline RBS generation/validation, Steep, Standard Ruby, Solid Queue's exact
RuboCop policy, and a warning-free Brakeman scan
- Compatibility CI across the supported span: Ruby 3.3 and 3.4 against Rails 8.0
and 8.1, pinned through `RAILS_VERSION` so the advertised range is verified
rather than assumed
- A JavaScript suite covering the state payload and batched refresh browser
modules, run in CI with Node's test runner and jsdom, with every GitHub
Actions reference pinned to a commit SHA
Expand Down
10 changes: 6 additions & 4 deletions lib/solid_objects/database_adapters/sqlite.rb
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,15 @@ def wait_before_busy_retry(attempts)
end
end

# The deadline can expire between the check above and this wait, which
# would otherwise ask for a negative interval.
# @rbs () -> void
def wait_before_retry
interval = [ LOCK_RETRY_INTERVAL, SyncDeadline.remaining ].min
return unless interval.positive?

LOCK_RETRY_MUTEX.synchronize do
LOCK_RETRY_CONDITION.wait(
LOCK_RETRY_MUTEX,
[ LOCK_RETRY_INTERVAL, SyncDeadline.remaining ].min
)
LOCK_RETRY_CONDITION.wait(LOCK_RETRY_MUTEX, interval)
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions sig/generated/lib/solid_objects/database_adapters/sqlite.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ module SolidObjects
# @rbs (Integer) -> void
def wait_before_busy_retry: (Integer) -> void

# The deadline can expire between the check above and this wait, which
# would otherwise ask for a negative interval.
# @rbs () -> void
def wait_before_retry: () -> void
end
Expand Down
9 changes: 9 additions & 0 deletions test/integration/synchronous_invocation_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,15 @@ def wait(timeout:)
"a synchronous call should read the clock once per transaction, not once per step"
end

test "a retry wait whose deadline already expired does not raise" do
skip unless SolidObjects::Record.connection.adapter_name.match?(/sqlite/i)
adapter = SolidObjects.database_adapter

SolidObjects::SyncDeadline.with(timeout: -1) do
assert_nothing_raised { adapter.send(:wait_before_retry) }
end
end

test "sync discovers the configured SQLite busy wait it has to restore" do
skip unless SolidObjects::Record.connection.adapter_name.match?(/sqlite/i)

Expand Down
Loading