Skip to content

Move solid_queue and solid_cable to released versions - #3107

Merged
flavorjones merged 1 commit into
mainfrom
solid-queue-cache-released
Sep 7, 2026
Merged

Move solid_queue and solid_cable to released versions#3107
flavorjones merged 1 commit into
mainfrom
solid-queue-cache-released

Conversation

@flavorjones

@flavorjones flavorjones commented Sep 7, 2026

Copy link
Copy Markdown
Member

Moves solid_queue and solid_cable off their edge git pins and onto rubygems releases: solid_queue a8c89374 (1.4.0, 2026-05-30) → 1.7.0, and solid_cable c968ba77 (4.0.0, 2026-06-26) → 4.0.2. Supersedes #3061 and #3057. solid_cache was already on a release (~> 1.0, 1.0.10) and is untouched.

Detail

Both pins existed to pick up a fix ahead of its release, and both fixes have since shipped:

  • solid_queue was pinned in 46509095 for rails/solid_queue#747, the optional job argument to the adapter's stopping?. That commit is the pinned SHA itself, and it is an ancestor of v1.7.0.
  • solid_cable was pinned in e1892a23 because released 4.0.0 called @server on the subscription adapter after Rails edge removed it. The pubsub_executor shim is in v4.0.2, a descendant of the pinned SHA.

The Gemfile drops the github: sources for gem "solid_queue", "~> 1.7" and gem "solid_cable", "~> 4.0". Both lockfiles were resolved with bin/bundle-both update --conservative; no other gem moved. PLATFORMS is unchanged.

Solid Queue 1.7.0 adds batches, which need two new tables and a solid_queue_jobs.batch_id column. Per UPGRADING.md the migration is optional until 2.0: until it runs, Batch.migrated? is false, batch_id is never written, and each dispatcher logs one deprecation warning at boot. This PR adds the migration anyway so we are not carrying that warning:

  • db/queue_migrate/20260907161841_add_batches_to_solid_queue.rb, from bin/rails solid_queue:update. Every statement is if_not_exists: true, so it is a no-op on a database that loaded the new schema. db/queue_migrate/ did not exist before; every database*.yml already points the queue database's migrations_paths at it.
  • db/queue_schema.rb, re-dumped from the SaaS development MySQL queue database after running the migration, so fresh installs get the tables. The schema version moves from 1 to the migration timestamp as a side effect of the dump.

Deploy order does not matter: old code ignores the extra column, new code tolerates its absence.

Additional information

Upgrade analysis for both gems, per commit, is in 37signals-hq under upgrade-analysis/fizzy-20260907-solid_queue_a8c89374..v1.7.0.md (65 commits: 1 definite, 6 likely, 25 unlikely, 33 no impact) and upgrade-analysis/fizzy-20260907-solid_cable_c968ba77..v4.0.2.md (3 commits, none affecting fizzy). The batches schema is the only mitigation. Behavior changes in range that need no code change:

  • Recurring schedules now default to the Rails time zone rather than the system zone (rails/solid_queue@546864f1). Fizzy sets no config.time_zone and no TZ in the container, so every schedule in config/recurring.yml fires at the same UTC time as before.
  • Recurring enqueue failures, previously only logged, are now reported through Rails.error and will reach Sentry with source: "application.solid_queue" (rails/solid_queue@dfc36448).
  • signal_all no longer increments a semaphore past its limit (rails/solid_queue@2124a51d). This affects Storage::ReconcileJob and Storage::MaterializeJob, which use limits_concurrency. The fix does not repair rows already over the limit, so SolidQueue::Semaphore.where("value > limit") in a production console after deploy is worth a look.
  • The override audit found nothing in config/, lib/rails_ext/, saas/lib/yabeda/solid_queue.rb, or saas/lib/fizzy/saas/engine.rb that depends on anything the range changed.

The new bin/jobs check subcommand fails in SaaS development because config.solid_queue.connects_to is only set in config/environments/production.rb, so it looks for solid_queue_recurring_tasks in the primary database. That predates this change and does not affect production, where connects_to is set.

The test environment uses adapter: test for Action Cable, so as with e1892a23 the real solid_cable adapter is only exercised on beta. Worth a WebSocket connect after deploying there.

Checklist

  • SaaS 1845 tests, OSS 1658 tests: no failures, no errors
  • SaaS System 18 tests, OSS System 18 tests: no failures, no errors
  • bin/bundle-drift reports the lockfiles in sync; bin/bundler-audit check --update reports no vulnerabilities; bin/rubocop clean
  • PLATFORMS unchanged from main in both lockfiles

ref: https://app.fizzy.do/5986089/cards/5265

🤖 Generated with Claude Code

Copilot AI balanced review requested due to automatic review settings September 7, 2026 16:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@flavorjones

Copy link
Copy Markdown
Member Author

🤖 Upgrade Plan: solid_queue a8c89374..v1.7.0

Upgrade Plan: solid_queue a8c89374..v1.7.0 for fizzy

  • Date: 2026-09-07
  • Gem: solid_queue
  • Range: a8c89374b2b600f7306cb44059dce1cc3fc45cec..v1.7.0 (e2b53849c715b29fa2d97a939097bd1beaea4bc9), 65 commits
  • Target: fizzy (~/Work/basecamp/fizzy--solid-queue-cache-released)
  • Current state: Gemfile already says gem "solid_queue", "~> 1.7", but Gemfile.lock and Gemfile.saas.lock still pin the git revision a8c89374 (reported as 1.4.0). Both lockfiles must be regenerated.

Summary

  • Total commits: 65
  • No impact: 33 (skipped at recon)
  • Analyzed, not affected: 26
  • Requires mitigation: 1 (batches schema)
  • Behavior changes worth knowing, no code change: 5 (546864f1, dfc36448, 2124a51d, 124176af, e66e4358)
  • Override audit: no monkey-patches of SolidQueue in fizzy; all local surface is declarative config plus one metrics reader. Nothing broken, redundant, or inert.
  • Advisories: no CVE/GHSA identifiers in any in-range assessment (not evidence of absence).

Bottom line: the upgrade is safe to ship as a plain gem bump. The only real work is adopting the optional batches migration, which is not required to keep jobs running but should ship with the bump to avoid a per-process deprecation warning and to stay ahead of Solid Queue 2.0, where it becomes mandatory.

Commits Requiring Mitigation

1bcac835 + 112bb1cb: Add batch support (and refine it)

Impact: definite impact (1bcac835) / likely impact (112bb1cb)

What changed: v1.7.0 adds SolidQueue::Batch, two new tables (solid_queue_batches, solid_queue_batch_executions) and a solid_queue_jobs.batch_id column. The recon for 1bcac835 says enqueueing on an unmigrated DB raises ActiveModel::UnknownAttributeError; that was true at 1bcac835 but 112bb1cb (also in v1.7.0) made the schema optional. Verified at the v1.7.0 tag:

  • Job.attributes_from_active_job only sets batch_id if Batch.migrated? (app/models/solid_queue/job.rb:73).
  • Job::Batchable#batched? is Batch.migrated? && batch_id?.
  • Batch.migrated? checks both tables and the batch_id column exist.
  • Dispatcher::Maintenance#sweep_stalled_batches calls Batch.warn_about_pending_migrations once per dispatcher process instead of querying missing tables.
  • UPGRADING.md: "The migration is optional for now: until you run it, everything works as before, batches aside. It will become part of the required schema in Solid Queue 2.0."

Residual delta for fizzy:

  • db/queue_schema.rb lacks the batches tables and column.
  • config/database.mysql.yml, config/database.sqlite.yml and saas/config/database.yml all point the queue database at migrations_paths: db/queue_migrate, which does not exist in the repo. Fresh installs load db/queue_schema.rb; existing databases (SaaS production, self-hosters) need a migration file in that directory.
  • Fizzy does not call SolidQueue::Batch anywhere. config/recurring.yml lines 19 and 50 call SolidQueue::Job.clear_finished_in_batches, which still exists at v1.7.0 (app/models/solid_queue/job/clearable.rb:13); the new SolidQueue::Batch.clear_finished_in_batches is a separate, additional method.
  • The UUID primary-key shims (config/initializers/uuid_primary_keys.rb, uuid_framework_models.rb) only fire for binary(16)/blob(16)/uuid columns; the batches migration uses bigint, matching every other solid_queue table.

Mitigation:

  1. Run bin/rails solid_queue:update (the generator defaults to --database queue, so it writes into db/queue_migrate/). Commit the generated *_add_batches_to_solid_queue.rb; it uses if_not_exists: true throughout, so it is safe on fresh installs that already loaded the new schema.
  2. Run bin/rails db:migrate and regenerate db/queue_schema.rb (bin/rails db:schema:dump), or hand-add the two tables and the batch_id column + index to match the generator template at lib/generators/solid_queue/install/templates/db/queue_schema.rb. Diff the result against that template.
  3. Deploy the migration with or after the gem bump. Ordering is not critical: old code ignores the extra column, new code tolerates its absence. Until it runs, each dispatcher logs one SolidQueue.deprecator warning at boot.
  4. Optional: add a recurring task clear_solid_queue_finished_batches with command: "SolidQueue::Batch.clear_finished_in_batches(sleep_between_batches: 0.3)" alongside the existing job clearers, so finished batch rows are pruned once batches are in use. Not needed while nothing enqueues batches.

Behavior Changes, No Code Change Required

546864f1: Recurring schedules default to the Rails time zone instead of system local time

Impact: likely impact
Matched in: config/recurring.yml — all 11 schedules lack an explicit zone (every day at 04:02, every hour at minute 50, etc.).
Residual delta: fizzy sets no config.time_zone (Rails default UTC), and the Dockerfile sets no TZ, so containers run UTC. Both interpretations agree; fire times do not move. If a deployment ever sets TZ on the jobs hosts, schedules would previously have followed it and will now follow UTC. To pin old behavior, set config.solid_queue.time_zone = nil in config/application.rb (must be on config.solid_queue at boot; the engine initializer checks key?(:time_zone)).

dfc36448: Report rescued recurring enqueue errors to Rails.error

Impact: unlikely impact
Matched in: config/recurring.yml (has recurring tasks).
Residual delta: failed recurring enqueues, previously only logged, now reach Sentry as handled errors with source: "application.solid_queue". Treat as signal; filter on that source if unwanted.

2124a51d: Guard signal_all against incrementing semaphore beyond its limit

Impact: unlikely impact
Matched in: app/jobs/storage/reconcile_job.rb:5, app/jobs/storage/materialize_job.rb:3 (limits_concurrency to: 1).
Residual delta: bug fix. The fix stops future permit leaks but does not repair rows already over the limit. Optional one-time check in production after deploy: SolidQueue::Semaphore.where("value > limit"); delete any hits.

124176af: Honour TERM received during supervisor boot before forking workers

Impact: unlikely impact
Matched in: saas/lib/fizzy/saas/engine.rb:76 (SolidQueue.on_start running Process.warmup and starting the Yabeda exporter).
Residual delta: benefit only. A TERM arriving during that hook no longer leaves freshly forked workers claiming jobs until SIGKILL.

e66e4358: Improve DISTINCT queries on PostgreSQL via recursive CTE

Impact: unlikely impact
Matched in: config/queue.yml:4 (wildcard "*" queue).
Residual delta: none. The queue database uses trilogy (MySQL) in SaaS and MySQL or SQLite in OSS; the CTE path is PostgreSQL-only.


Override Audit

Checked and unaffected:

  • config/queue.ymlpolling_interval is explicit for both dispatchers and workers, so the generator-template default change (d093f62b) is moot. threads/processes/batch_size keys unchanged.
  • config/recurring.ymlSolidQueue::Job.clear_finished_in_batches still exists at v1.7.0 (see above).
  • config/puma.rbplugin :solid_queue; plugin interface unchanged.
  • bin/jobsSolidQueue::Cli.start(ARGV); the new check subcommand (98af3c9e) and --only-recurring (517a974d) are additive.
  • config/environments/production.rb, saas/config/environments/development.rb — only queue_adapter and config.solid_queue.connects_to, both unchanged.
  • saas/lib/fizzy/saas/engine.rbSolidQueue.on_start, signature unchanged.
  • saas/lib/yabeda/solid_queue.rb — reads FailedExecution.count, ClaimedExecution.where(process: nil).count, ScheduledExecution, RecurringTask#last_enqueued_time/previous_time. None changed. It does not read worker metadata, so 944aa72d (thread_pool_sizepool_type/pool_size) is inert; mission_control-jobs 1.1.0 does not read those keys either.
  • config/initializers/active_job.rb, config/initializers/mission_control.rb — no SolidQueue internals.
  • config/initializers/uuid_primary_keys.rb, uuid_framework_models.rb — no-op for solid_queue tables (bigint PKs).
  • lib/rails_ext/*.rb — nothing touches SolidQueue.
  • SolidQueue::Configuration#error_messages was removed (ecc170cf); fizzy never called it.
  • Ruby: fizzy is on 3.4.8; the new >= 3.2 floor (42db5672) is satisfied.

Analyzed — No App Impact

Commit Summary Impact Level
4f72ea31 Warn instead of raising when the connection pool is smaller than the thread pool unlikely impact
98af3c9e Add bin/jobs check / solid_queue:check to validate config unlikely impact
ecc170cf Refactor errors and warnings when parsing configuration (removes Configuration#error_messages) unlikely impact
e99dd403 Memoize Runnable#mode unlikely impact
0d5134b9 Avoid reloading job rows after dispatch and schedule unlikely impact
e43f2042 Fix recurring task double-enqueue race; removes RecurringTask#delay_from_now likely impact
23100e0b Support strict_loading_by_default unlikely impact
42db5672 Drop support for Ruby 3.1 unlikely impact
4f263b25 Prevent wrong release of blocked jobs and semaphore corruption; removes ClaimedExecution#unblock_next_job likely impact
40d6bd57 Lease connections in DistinctValues unlikely impact
6a2a201e Supervisor replaces forks that fail to boot unlikely impact
f8899dff Boot guards managed by processes; renames process_startup_timeoutfork_boot_timeout (never released under the old name) unlikely impact
222df7a1 Add fiber execution mode for workers; SolidQueue::Pool reorganized likely impact
1bf083f9 Block on pending executions queue in fiber pool unlikely impact
944aa72d Store only pool type and size in worker metadata (thread_pool_size removed) likely impact
5048cd78 Rely on configuration validation for worker execution options unlikely impact
50468fac Start fiber pool reactor lazily unlikely impact
5270cc89 Simplify worker initialization unlikely impact
000e8ba4 Report errors escaping the execution future unlikely impact
30b92caf Move fiber worker checks to Configuration unlikely impact
9c0d5d32 Reorganize execution pools under SolidQueue::Pool unlikely impact
6816e52b Include the error in fail_many_claimed events unlikely impact
0526401f Replace terminated forks even if releasing claimed jobs fails unlikely impact
174cb071 Protect the shutdown reap from release failures unlikely impact
fcb5f7ac Name the supervisor's stop checkpoint unlikely impact
112bb1cb Refine batch support (schema made optional) — see mitigation above likely impact

Note: bundle show --paths failed in several analysis subagents (Ruby 4.0.3 on the host vs 3.4.8 in .ruby-version), so gem-dependency directories were searched only where the bundle resolved. The patterns in question are SolidQueue internals that no fizzy dependency other than mission_control-jobs touches, and that gem was searched.

No Impact (Skipped)

33 commits assessed as "no impact" during recon (test-only changes, docs, CI, version bumps, Windows signal handling, generator-template default, --only-recurring flag, update generator scaffolding). Not analyzed against the app.

Recommended Upgrade Steps

  1. In the fizzy worktree, drop the git source for solid_queue if the Gemfile still has one, keep gem "solid_queue", "~> 1.7", and run bundle update solid_queue for both lockfiles (Gemfile.lock and, via BUNDLE_GEMFILE=Gemfile.saas or the saas rake task, Gemfile.saas.lock). Confirm both resolve to solid_queue (1.7.0) from rubygems.
  2. Run bin/rails solid_queue:update to generate db/queue_migrate/<timestamp>_add_batches_to_solid_queue.rb. Commit it.
  3. Run bin/rails db:migrate against the queue database, then dump or hand-update db/queue_schema.rb so fresh installs get the batches tables. Diff against lib/generators/solid_queue/install/templates/db/queue_schema.rb at v1.7.0.
  4. Run bin/jobs check to validate config/queue.yml and config/recurring.yml under the new configuration parser (new in this range; exits 1 on errors).
  5. Run the test suite, including test/jobs/storage/* (the limits_concurrency jobs).
  6. Deploy. Expect: one SolidQueue.deprecator warning per dispatcher only if the migration has not yet run; new Sentry events with source: "application.solid_queue" if any recurring enqueue fails; no change to recurring fire times (UTC before and after).
  7. Post-deploy, optionally run SolidQueue::Semaphore.where("value > limit") in a SaaS console and clear any rows left over from the pre-2124a51d permit leak.
  8. Optional follow-up: add a clear_solid_queue_finished_batches recurring task once batches are used.

@flavorjones

Copy link
Copy Markdown
Member Author

🤖 Upgrade Plan: solid_cable c968ba77..v4.0.2

Upgrade Plan: solid_cable c968ba77..v4.0.2 for fizzy

  • Date: 2026-09-07
  • Gem: solid_cable
  • Range: c968ba777f990194596d035e6ba50e1ee677a09c..v4.0.2 (4a41ec8b0b6ddb5dcfa4e8c9c0062d3bf40a2dc3) (3 commits)
  • Target: fizzy (/home/flavorjones/Work/basecamp/fizzy--solid-queue-cache-released)

Summary

  • Total commits: 3
  • No impact: 2 (skipped at recon)
  • Analyzed, not affected: 1
  • Requires mitigation: 0

The range is one code change plus two version bumps: the listener's reconnect
counter now resets after every successful poll, not only after a poll that
delivered a message. Nothing in fizzy patches, configures, or asserts on that
behavior. This is a strict bug fix for fizzy; no app changes are needed.

Context: why fizzy is on a git ref

fizzy commit e1892a23 ("Track solid_cable main for Rails edge Action Cable
compatibility") pinned solid_cable to github: "rails/solid_cable" at
c968ba77 because released 4.0.0 called @server.mutex / @server.event_loop,
which Rails edge removed (rails/rails 96b3add356). That commit says "Pin back
to a released version once one ships the fix."

v4.0.2 is a descendant of c968ba77 (git merge-base --is-ancestor confirms)
and still carries the @executor/pubsub_executor compatibility shim, so
returning to the rubygems release drops the git pin without losing the fix.
solid_cable 4.0.2 is published on rubygems.org.

The worktree already has an uncommitted Gemfile edit doing exactly this
(gem "solid_cable", "~> 4.0" replacing the github: line, alongside the same
change for solid_queue). Gemfile.lock and Gemfile.saas.lock still record
the GIT source at c968ba77 / solid_cable (4.0.0); they have not been
regenerated yet.

Commits Requiring Mitigation

None.

Analyzed — No App Impact

Commit Summary Impact Level
f781e4da Reset retry counter on each successful poll (rails/solid_cable#84) unlikely impact

Detection pattern from the assessment (reconnect_attempts in config/cable.yml,
app code, saas/, and every bundled gem's lib/app) matched nowhere.
config/cable.yml sets only polling_interval and message_retention, so
fizzy runs the default reconnect_attempts and gains the fix automatically.

No Impact (Skipped)

2 commits assessed as "no impact" during recon (cf0695c5, 4a41ec8b: version
bumps 4.0.0 → 4.0.1 → 4.0.2). Not analyzed against app.

Override Audit

Override surface inspected: config/initializers/*.rb, lib/ (incl.
lib/rails_ext/), saas/ (lib, app, config), config/cable.yml,
config/environments/*.rb, plus every file referencing SolidCable /
solid_cable (Gemfile, both lockfiles, db/cable_schema.rb,
saas/config/deploy.*.yml, saas/config/database.yml,
test/channels/solid_cable_adapter_test.rb).

  • No monkeypatch, subclass, or reimplementation of the adapter exists anywhere
    in the app. Nothing references reconnect_attempt, broadcast_messages, or
    the listener internals.
  • test/channels/solid_cable_adapter_test.rb guards the @server@executor
    break (stubs Listener.new, asserts subscribe/shutdown do not raise). It
    does not exercise polling or retries. It stays relevant after the upgrade: it
    is the only test that instantiates the real adapter, since config/cable.yml
    uses adapter: test in the test environment.
  • Schema (db/cable_schema.rb) and SaaS deploy/database plumbing are untouched
    by the range.

Residual delta: none. Nothing is broken, redundant, or inert.

Advisories: no CVE/GHSA identifiers in the in-range assessments. Absence of an
identifier is not evidence of absence of a security fix; the single code change
here is a retry-counter reset, not a security fix.

Recommended Upgrade Steps

  1. Keep the pending Gemfile change: gem "solid_cable", "~> 4.0" (no github:).
  2. Regenerate both lockfiles so the GIT section is replaced by the rubygems
    release with a checksum (fizzy records bundler checksums, see 2ac2c8ad):
    • bundle lock --update solid_cable (updates Gemfile.lock)
    • BUNDLE_GEMFILE=Gemfile.saas bundle lock --update solid_cable (updates
      Gemfile.saas.lock)
      Confirm both lockfiles show solid_cable (4.0.2) under GEM, not GIT.
  3. Run bin/rails test test/channels/solid_cable_adapter_test.rb, then the OSS
    and SaaS unit suites.
  4. Because the test env uses adapter: test, the real adapter is only
    exercised on beta/staging. After deploying to beta, open a WebSocket and
    confirm ApplicationCable::Connection#connect succeeds (the failure mode
    e1892a23 fixed) and that the cable listener stays alive on an idle
    channel.
  5. Commit message should reference e1892a23 and
    Reset retry counter on each successful poll rails/solid_cable#84, and note that the git pin is
    retired because 4.0.2 ships the executor compatibility fix.

No configuration, schema, or application code changes are required.

Both gems tracked edge commits for fixes that had not shipped yet:
`solid_queue` since 4650909 for rails/solid_queue#747, and
`solid_cable` since e1892a2 for the Rails edge Action Cable executor
change. Both are now released, in `solid_queue` 1.7.0 and `solid_cable`
4.0.2.

Pin both to rubygems releases. Solid Queue 1.7.0 adds optional batch
tables, so add its generated migration under `db/queue_migrate/` and
the re-dumped `db/queue_schema.rb`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@flavorjones
flavorjones force-pushed the solid-queue-cache-released branch from 6dac1ec to 83c56dd Compare September 7, 2026 17:03
@flavorjones

Copy link
Copy Markdown
Member Author

Deployed OK to staging. Looks good.

@flavorjones
flavorjones merged commit ebfb067 into main Sep 7, 2026
13 checks passed
@flavorjones
flavorjones deleted the solid-queue-cache-released branch September 7, 2026 17:16
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.

2 participants