One of the major focuses for Ruby 3 was parallelism and concurrency. A trailblazer in this space is the use of Ruby Fibers. Fibers are a concurrency mechanism that allows us to pause, loop, and resume execution while consuming far fewer context switches. It consumes less memory than threads while giving the developer control over code segment execution.
Fibers are the best solution for scalable non-blocking clients and servers. However, it has taken Rails a while to make use of it.
As Rails continues to replace the usage of threads with fibers, the performance will only get better. One area of development is ActiveRecord which until now still depended on threads, despite configuring ActiveSupport’s isolation level. An isolation level determines how database tractions are propagated to other users and systems.
Why is that important? Well for that we need to understand what ActiveRecord’s ConnectionPool is. A connection pool manages multiple ActiveRecord connections. A connection essentially performs a transaction on a database. To perform this transaction, Rails offloads the I/O operation to a thread where every thread maintains a separate DB connection (in the case of Puma). Otherwise individual conversations with the database could mix up. Now that there is a more performant alternative to threads, ActiveRecord should switch over! Sadly, it does not.
Now let’s check how a ConnectionPool converses with the database.
ActiveRecord’s ConnectionPool does not honor ActiveSupport’s isolation level!
Fortunately, this PR migrated overall ConnectionPool’s conversations to either use Threads or Fibers.
Now let’s have a look!
Great! But, what does this mean?
in the foreseeable future,
we can expect good performance improvements to Rails I/O operations.
Rails abstracts execution using
at least that’s what it is supposed to do.
as one looks around the Rails codebase,
there seems to be a lot of code
that is hard-wired to a thread-centric implementation.
So there’s a lot of work for the Rails core team,
and contribute today!