Dev & EngARTICLE

Rails moves toward Ractor support and fixes ordering in multi-key cache

The This Week in Rails bulletin details commits that make configurations shareable between Ractors, fix a connection leak, and preserve key order in fetch_multi. What this changes for whoever maintains Rails in production.

Rails moves toward Ractor support and fixes ordering in multi-key cache
Image: Bisneto Braga

The This Week in Rails newsletter, written by Vipul A M, gathers a batch of commits that matter less to marketing and more to whoever runs a real Rails application in production. Two topics lead the pack: the framework's gradual progress toward Ractor support and a handful of fixes in cache, connection, and the PostgreSQL adapter that solve concrete pain points. It's worth breaking down each one, because none of them comes with fanfare, and all of them carry a trade-off.

Ractor: real parallelism, one piece at a time

Ractor is Ruby's concurrency model that promises real parallelism (escaping the GVL, the Global VM Lock) by isolating state between actors. The long-standing problem: for an object to be shared between Ractors, it needs to be frozen and shareable. Much of Rails' internal configuration wasn't, which in practice made it impossible to run Rails under Ractors without hitting an unshareable-object error.

This week's news is that Rails made controller configuration, Action View settings, Active Record commit callbacks, and time zone configuration shareable between Ractors. In addition, Active Record's schema context no longer deadlocks when initializing attributes, and event reporters now use per-Ractor storage when running outside the main Ractor.

None of these commits, on its own, will make your app run under Ractors tomorrow. The message is directional: the framework is clearing the path, one subsystem at a time. For the Brazilian team currently scaling Rails with multi-process Puma or with fibers (Falcon), the horizon of real parallelism inside a single process remains distant, but less utopian. Anyone who wants to experiment needs to remember that third-party gems and application code also need to be Ractor-safe, which is still the exception in the ecosystem.

Connection leak in Action Controller Live

This is the kind of bug that only shows up in production under load. TypeCaster now uses with_connection, returning the connection to the pool immediately instead of relying on the executor's cleanup. The fixed symptom was a connection leak in ActionController::Live actions, those used for streaming (SSE, long responses).

The logic behind this matters: in a Live action, the request can stay open for a long time. If the connection's checkin is stuck waiting on the executor's cycle, you hold a pool connection for the entire duration of the stream, unnecessarily. In apps with a small pool and multiple clients connected to streaming endpoints, this exhausts the pool and drops new requests. Switching to with_connection closes this hole by returning the connection as soon as type casting finishes.

Order preserved in fetch_multi with local cache

This one is subtle and may have bitten people without them noticing. ActiveSupport::Cache::Strategy::LocalCache#fetch_multi used to return local hits first and misses afterward, changing the order the caller had requested. Now it returns the keys in the original order, matching Store#fetch_multi.

Why does this break code in practice? Because many people do something like:

ruby
results = Rails.cache.fetch_multi(*ids) { |id| load(id) }
results.values # expected the order of ids

If part of the ids was in the local cache (inside a request block with with_local_cache) and part wasn't, values came back shuffled, with hits first. Code that relied on positional correspondence between input and output silently produced the wrong result, the worst kind of bug. The fix aligns the behavior of the two cache layers. If you had a workaround manually reordering things, you can remove it, but check before assuming.

Adjustments to the PostgreSQL adapter

Two changes on the Postgres side. The first: the adapter now accepts the error_verbosity option in database.yml, applied to each connection's configuration.

yaml
production:
  adapter: postgresql
  error_verbosity: <%= PG::PQERRORS_TERSE %>

Controlling Postgres error verbosity is useful for reducing log noise or, conversely, gaining more detail for debugging. It's one of those fine-grained tweaks that only someone who's already been burned by a giant error log in production truly appreciates.

The second is a robustness fix: disable_referential_integrity now restores disabled triggers from within an ensure block. Previously, if something raised an exception inside the block, the triggers could remain disabled after exiting, silently leaving referential integrity turned off. It's the kind of trap you only discover once inconsistent data has already gotten in.

More honest tests and other fixes

A new configuration option helps hunt down accidental order dependencies in tests:

ruby
config.active_record.shuffle_unordered_selects = true

With it, results from SELECT queries without ORDER BY are shuffled, exposing tests that were passing by luck because the database happened to return rows in a convenient order. It's a welcome double-edged sword: it will turn green tests red, but exactly the ones that were lying. The trade-off is to turn it on in CI and face the cleanup, not right before a deploy.

Other points worth attention before bumping the version:

  • Active Storage now depends on Marcel 2, with broader MIME detection. Newly analyzed files may get canonical content types where Marcel 1 used to return aliases. Existing blobs keep their stored type, but if you compare content type strings anywhere, review this before updating.
  • Compatibility with JSON 3.0: Active Support now sends options to JSON.parse as keyword arguments, a requirement of the JSON 3.0 gem. ActiveRecord::Coders::JSON and encrypted fixtures in JSON columns were also fixed.
  • 15-minute timeout on the GitHub Actions jobs generated in new apps. Big test suite? Increase or remove the limit.
  • Removal of a redundant join when merging a has_many :through association with a scope, producing a leaner query.

What remains open

Two new guides are open for community review: the long-awaited Hotwire guide and a rewrite of the Securing Rails guide. There were 25 contributors to the codebase this week. For whoever maintains Rails in production, the useful takeaway from this bulletin isn't Ractor (still far from daily use), but the cache/connection/PostgreSQL combo: those are exactly the corners where ordering bugs, pool leaks, and silent referential integrity issues cost dearly. Reviewing content type comparisons and fetch_multi behavior before the next bump is the practical move this changelog calls for.

Translated from the Brazilian Portuguese original · Read the original