Avoiding N+1 queries on a model with memoization
Ruby on RailsDuring the Scribbles request cycle, there are multiple checks to see if a domain exists on a given blog, and if so, tweak the URL of certain links and also posts.
The problem I had is that when I render out a post, in a list, it would check if the blog had a valid domain to it. And because I am silly, I didn't think much about it until I've seen the database query where it would just do this check many times — obviously.
Here was my initial take on checking if I a blog has_valid_hostname?
:
def has_valid_hostname? domains.any? && domains.first.is_active? end
First it checks if I have any domains associated to the blog, and if so
grab the first one and see if it's active. The active check is automated
and starts with false
when you first set up the domain, and then it
will check in the background if it's pointing to Scribbles, and your
blog.
I could perhaps use a counter cache here, but don't want to complicate things.
Also notice that I use domains.first
— the way I designed it is that I
will allow multiple domains, and it will grab the one that is set as
default
, however that will be for another day and I'll add a scope for
this. Right now that works and is what I want.
I probably could do without the domains.any?
and just optionally chain
the last query...
Anyway, this was getting called many times in the same request, which
was stupid. Each post list item would call this method, not to mention
multiple times just for setting extra bits in the head
component of
the blog.
So what's the best way to solve that? Enter memoization. Basically store the value once in the same request and then I can re-use it within that request. Here it is:
def has_valid_hostname? @has_valid_hostname ||= domains.any? && domains.first.is_active? end
Basically now I set @has_valid_hostname
once only if it doesn't exist.
Which basically just means once.
Instead of querying the database 3 million times... ok, more like around 30 times, it does it once in the request.
A nice simple result — and I have no idea why I didn't think of that earlier.