ruby-on-railsperformancepostgresqlactiverecorddefault-scope

Effects of Rail's default_scope on performance


Can default_scope when used to not order records by ID significantly slow down a Rails application?

For example, I have a Rails (currently 3.1) app using PostgreSQL where nearly every Model has a default_scope ordering records by their name:

default_scope order('users.name')

Right now because the default_scope's order records by name rather by ID, I am worried I might be incurring a significant performance penalty when normal queries are run. For example with:

User.find(5563401)

or

@User.where('created_at = ?', 2.weeks.ago)

or

User.some_scope_sorted_best_by_id.all

In the above examples, what performance penalty might I incur by having a default_scope by name on my Model? Should I be concerned about this default_scope affecting application performance?


Solution

  • Your question is missing the point. The default scope itself is just a few microseconds of Ruby execution to cause an order by clause to be added to every SQL statement sent to PostgreSQL.

    So your question is really asking about the performance difference between unordered queries and ordered ones.

    Postgresql documentation is pretty explicit. Ordered queries on unindexed fields are much slower than unordered because (no surprise), PostgreSQL must sort the results before returning them, first creating temporary table or index to contain the result. This could easily be a factor of 4 in query time, possibly much more.

    If you introduce an index just to achieve quick ordering, you are still paying to maintain the index on every insert and update. And unless it's the primary index, sorted access still involves random seeks, which may actually be slower than creating a temporary table. This also is discussed in the Postgres docs.

    In a nutshell, NEVER add an order clause to an SQL query that doesn't need it (unless you enjoy waiting for your database).

    NB: I doubt a simple find() will have order by attached because it must return exactly one result. You can verify this very quickly by starting rails console, issuing a find, and watching the generated SQL scroll by. However, the where and all definitely will be ordered and consequently definitely be slower than needed.