Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Ruby on Rails executing `SELECT 1 AS one` queries?

I'm getting into a project in Ruby on Rails currently, which I don't really have any experience in. Now, we're running into some performance problems that I think are related to executing too many queries.

We have a Service model that we set in our controller as follows:

@services = Service.includes(:points).with_points

The with_points scope is defined in the service as such:

scope :with_points, -> { joins(:points).where.not(points: []).distinct }

(I think the where clause is not needed here, but that's probably not relevant to the question.)

Then in the view, we're using the fetched services, with linked points, as follows:

<% @services.each do |s| %>
    <div class="col-xs-12 col-sm-6 col-lg-4 serviceDiv" data-rating="<%= s.service_ratings %>" >
        <% if s.service_ratings == "A" %>
            <% grade = "rating-a" %>
                <!-- etc. -->

Now, as far as I've seen when researching around, this is a relatively normal pattern when trying to list all rows from a table. However, when I look at the logs, it looks like a separate query is executed for every service?

# This query is what I'd expect:
SQL (1.6ms)  SELECT DISTINCT "services"."id" # etc, etc
# But then we also get one of these for every service
Service Exists (1.5ms)  SELECT  1 AS one FROM "services" WHERE "services"."name" = $1 AND ("services"."id" != $2) LIMIT $3
# And quite a few of these for every service:
CACHE Service Exists (0.0ms)  SELECT  1 AS one FROM "services" WHERE "services"."name" = $1 AND ("services"."id" != $2) LIMIT $3

Now, my hunch is that those "Service Exists" lines are bad news. What are they, and where are they coming from? Is there anything else that's relevant that I'm missing here?

like image 793
Vincent Avatar asked May 23 '18 08:05

Vincent


People also ask

What is eager loading in Ruby?

Eager loading solves this problem by creating a left outer join on the table, returning all of the data in a single query. Adding an eager load is as simple as adding an :includes statement to the controller.

What is N 1 query problem Rails?

The n+1 query problem is one of the most common scalability bottlenecks. It involves fetching a list of resources from a database that includes other associated resources within them. This means that we might have to query for the associated resources separately.

What are N 1 queries?

What is the N+1 query problem. The N+1 query problem happens when the data access framework executed N additional SQL statements to fetch the same data that could have been retrieved when executing the primary SQL query. The larger the value of N, the more queries will be executed, the larger the performance impact.


1 Answers

After @MarekLipka's pointers in the comments on this question, I managed to find the source of the problem. Not sure how many people will run into this setup in the future, but I'll share it just in case.

The clue was in our accessing s.service_ratings which was not, in fact, a column in the database. ActiveRecord Query Trace pointed to the source of all those queries being a reference to s.service_ratings in the view, which was a red flag.

service_ratings was actually a method (assuming I'm using the correct terminology here) on the Service model that, apart from returning a value based on a calculation on several of the model's properties, also called self.update_attributes to actually store that value in the database. This meant that every time we retrieved this model's data in order to display it, we also ran another query to save that value to the database - often redundantly.

In other words, the solution for us now is to either run the calculation at some other point of time and store it in the database once, or recalculate it every time we need it and not store it at all.

like image 163
Vincent Avatar answered Oct 03 '22 03:10

Vincent