Rails 7.1 expands its support for ActiveRecord asynchronous queries
ActiveRecord async queries are a way to execute Active Record queries in parallel, which can improve the performance of your Rails application. This is especially useful for slow or complex queries or for applications that need to handle a lot of concurrent requests.
Asynchronous queries are executed in a separate thread, meaning they don't block the main thread of your Rails application. This allows your application to continue responding to requests while the queries are running in the background.
Before Rails 7.1
The load_async
method was added to Rails in version 7.0.
load_async
allows you to schedule an ActiveRecord query to be executed
in a background thread.
This can be useful for speeding up requests that involve multiple database queries,
especially if those queries are expensive.
For example,
the following code will load the user
relation object asynchronously:
User.load_async
You can then call the loaded?
method on the relation object
to check if the query has finished executing.
Once the query has finished executing,
you can access the result as usual.
For example, the following code will print the first names of all the users to the console:
User.load_async.each do |user|
puts user.first_name
end
Before Rails 7, there were a few ways to load queries asynchronously. One common approach was to use a gem like concurrent-ruby or async. These gems provide various tools for asynchronous programming, including thread pools and promises.
Another approach was to use a library like
Dalliance.
Dalliance
is a Ruby client for the Memcached distributed cache.
It can be used to cache the results of database queries,
improving performance by reducing the number of queries that need to be executed.
Finally,
it was also possible to implement asynchronous query loading yourself.
This involved using Ruby's Thread
class to create a new thread for each query.
The main thread would then wait for all the threads to finish before continuing.
However,
all of these approaches had their own drawbacks.
For example,
using concurrent-ruby
or
async
requires developers to have a good understanding of asynchronous programming.
Using Dalliance
required the database to be configured to support Memcached.
And implementing asynchronous query loading yourself could be error-prone.
The option to load aggregate queries like count
,
sum
,
minimum
,
maximum
,
etc,
was missing before Rails 7.1.
In Rails 7.1
Rails 7.1 expands its support for ActiveRecord asynchronous queries. If your Rails application has a lot of users, you can load their count in an async manner by using the below query
# Synchronous count
count = User.count
# Asynchronous count
promise = User.async_count
=> #<ActiveRecord::Promise status=pending>
promise.value
=> 1928324
The async_count
method returns a promise.
You must execute the method value
on the promise
and
fetch the required result.
This enhancement addresses the need for more efficient handling of not-so-fast queries,
mainly focusing on aggregates (such as count, sum, etc.)
and
all methods returning a single record
or
anything other than a Relation
.
The new API includes the following asynchronous methods:
async_count
async_sum
async_minimum
async_maximum
async_average
async_pluck
async_pick
async_ids
async_find_by_sql
async_count_by_sql
Benefits of using asynchronous queries
There are several benefits to using asynchronous queries in your Rails application:
-
Improved performance: Asynchronous queries can improve the performance of your application by allowing multiple queries to be executed simultaneously. This is especially useful for slow or complex queries or for applications that need to handle a lot of concurrent requests.
-
Increased responsiveness: Asynchronous queries allow your application to continue responding to requests while the queries are running in the background. This can improve the overall responsiveness of your application.
-
Reduced memory usage: Asynchronous queries can reduce the memory usage of your application by executing queries in a separate thread. This can be beneficial for applications that need to handle a lot of concurrent requests or that need to process large datasets.
Best practices for using asynchronous queries
Here are some best practices for using asynchronous queries in your Rails application:
-
Use asynchronous queries for slow or complex queries or for queries that need to be executed concurrently.
-
Avoid using asynchronous queries for simple or fast queries, as this can add unnecessary overhead.
-
Asynchronous queries may not be compatible with all Active Record features, such as callbacks and transactions.
-
Test your application thoroughly when using asynchronous queries to ensure that it is working as expected.
Overall, asynchronous queries are a powerful tool that can improve the performance, responsiveness, and memory usage of your Rails application.
To know more about this feature, please refer to this PR.