No commit activity in last 3 years
No release in over 3 years
There's a lot of open issues
Save unnecessary round trips to the database
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

>= 0.3.12.b1
>= 0
>= 0

Runtime

>= 3.2.11
 Project Readme

ActiveRecord::Futures

Gem Version Build Status Code Climate Coverage Status

Define future queries in ActiveRecord that will get executed in a single round trip to the database.

This gem allows to easily optimize an application using activerecord. All independent queries can be marked as futures, so that when you execute any of them at a later time, all the other ones will be executed as well, but the query of all of them will be executed in a single round trip to the database. That way, when you access the other results, they'll already be there, not needing to go to the database again.

The idea is heavily inspired from NHibernate's future queries

Installation

Add this line to your application's Gemfile:

gem 'activerecord-futures'

And then execute:

$ bundle

Or install it yourself as:

$ gem install activerecord-futures

Usage

If you're using postgresql or mysql2 currently, you have nothing more to do. The gem will automatically use the future enabled adapter and just work. If you are using a custom adapter, specify it in the config/database.yml file as you're used to.

Check the database support (below) section for more info.

Now let's see what this does, consider a model User, with a :name attribute:

# Build the queries and mark them as futures
users = User.where("name like 'John%'")
user_list = users.future # becomes a future relation, does not execute the query.
count = users.future_count # becomes a future calculation, does not execute the query.

# Execute any of the futures
count = count.value # trigger the future execution, both queries will get executed in one round trip!
#=> User Load (fetching Futures) (0.6ms)  SELECT `users`.* FROM `users` WHERE (name like 'John%');SELECT COUNT(*) FROM `users` WHERE (name like 'John%')

# Access the other results
user_list.to_a # does not execute the query, results from previous query get loaded

Any amount of futures can be prepared, and they will get executed as soon as one of them needs to be evaluated.

This makes this especially useful for pagination queries, since you can execute both count and page queries at once.

Rails

No configuration to do, things will Just Work.

Rack based apps (not Rails)

You will need to manually add the ActiveRecord::Futures::Middleware somewhere in the middleware stack:

use ActiveRecord::Futures::Middleware

This is to clear the futures that were defined and not triggered between requests.

Methods

#future method

ActiveRecord::Relation instances get a future method that futurizes a normal relation. The future gets executed whenever #to_a gets executed. Note that, as ActiveRecord does, enumerable methods get delegated to #to_a also, so things like #each, #map, #collect all trigger the future.

Calculation methods

You also get all the calculation methods provided by the ActiveRecord::Calculations module "futurized". More specifically you get:

  • future_count
  • future_average
  • future_minimum
  • future_maximum
  • future_sum
  • future_calculate
  • future_pluck

All future calculations are triggered with the #value method, except for the #future_pluck method, that returns an array, and is triggered with a #to_a method (or any other method that delegates to it).

Finder methods

Lastly, you also get finder methods futurized, which are:

  • future_find
  • future_first
  • future_last
  • future_exists?
  • future_all

As with the other future methods, those which return an array get triggered with the #to_a method, or the delegated ones, and those that return a value or a hash are triggered with the #value method. Note that the #find method returns an array or a value depending on the parameters provided, and so will the futurized version of the method.

Database support

SQlite

SQlite doesn't support multiple statement queries. ActiveRecord::Futures will fall back to normal query execution, that is, it will execute the future's query whenever the future is triggered, but it will not execute the other futures' queries.

MySQL

Multi statement queries are supported by the mysql2 gem since version 0.3.12b1, so you'll need to use that one or a newer one. Currently the adapter provided inherits the built-in one in Rails, and it also sets the MULTI_STATEMENTS flag to allow multiple queries in a single command. If you have an older version of the gem, ActiveRecord::Futures will fall back to normal query execution.

Postgres

The pg gem supports multiple statement queries by using the #send_query method and retrieving the results via #get_result.

Other databases

In general, ActiveRecord::Futures will look for a method #supports_futures? in the adapter. So any adapter that returns false when calling the method, or does not respond to it, will fall back to normal query execution. If you want to have support for ActiveRecord::Futures with your database, feel free to create a pull request with it, or create your own gem, or just create an issue.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request