惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog RSS Feed
B
Blog
N
Netflix TechBlog - Medium
量子位
月光博客
月光博客
博客园_首页
博客园 - Franky
酷 壳 – CoolShell
酷 壳 – CoolShell
Last Week in AI
Last Week in AI
T
The Blog of Author Tim Ferriss
Hugging Face - Blog
Hugging Face - Blog
雷峰网
雷峰网
M
MIT News - Artificial intelligence
J
Java Code Geeks
大猫的无限游戏
大猫的无限游戏
D
DataBreaches.Net
腾讯CDC
Engineering at Meta
Engineering at Meta
云风的 BLOG
云风的 BLOG
L
LangChain Blog
GbyAI
GbyAI
IT之家
IT之家
Y
Y Combinator Blog
人人都是产品经理
人人都是产品经理

Blog — PlanetScale

Keeping a Postgres queue healthy — PlanetScale Patterns for Postgres Traffic Control — PlanetScale Graceful degradation in Postgres — PlanetScale High memory usage in Postgres is good, actually — PlanetScale Stripe Projects partnership: Provision PlanetScale Postgres and MySQL databases from the Stripe CLI — PlanetScale Enhanced tagging in Postgres Query Insights — PlanetScale Behind the scenes: How Database Traffic Control works — PlanetScale Introducing Database Traffic Control — PlanetScale Scaling Postgres connections with PgBouncer — PlanetScale Drizzle joins PlanetScale — PlanetScale Video Conferencing with Postgres — PlanetScale Faster PlanetScale Postgres connections with Cloudflare Hyperdrive — PlanetScale Introducing the PlanetScale MCP server — PlanetScale Database Transactions — PlanetScale Automating our changelog with Cursor commands — PlanetScale Postgres 18 is now available — PlanetScale Using MotherDuck with PlanetScale — PlanetScale $50 PlanetScale Metal is GA for Postgres — PlanetScale AI-Powered Postgres index suggestions — PlanetScale $5 PlanetScale is live — PlanetScale Announcing Vitess 23 — PlanetScale $50 PlanetScale Metal — PlanetScale Report on our investigation of the 2025-10-20 incident in AWS us-east-1 — PlanetScale $5 PlanetScale — PlanetScale Benchmarking Postgres 17 vs 18 — PlanetScale Larger than RAM Vector Indexes for Relational Databases — PlanetScale Partnering with Cloudflare to bring you the fastest globally distributed applications — PlanetScale Processes and Threads — PlanetScale PlanetScale for Postgres is now GA — PlanetScale Postgres High Availability with CDC — PlanetScale
Solving N+1’s with Rails `exists?` queries — PlanetScale
Mike Coutermarsh · 2023-01-11 · via Blog — PlanetScale

Mike Coutermarsh |

We recently had a performance issue in our Rails application where we had several N+1 .exists? queries on a single API endpoint. In the following query, we check to see if the "data_imports" feature is enabled for a user:

user.beta_feature.where(name: "data_imports").enabled.exists?

Output:

BetaFeature Exists? (0.6ms)  SELECT 1 AS one FROM `beta_feature` WHERE `beta_feature`.`name` = 'data_imports' AND `beta_feature`.`target_type` = 'User' AND `beta_feature`.`target_id` = 1 AND `beta_feature`.`enabled_at` IS NOT NULL LIMIT 1

This pattern initially worked well, but became an issue as we started to add more beta features. Each new beta feature added an additional query, and it started to impact the speed of the API endpoint.

Generally, in Rails applications, you can solve N+1's by using includes to preload the data. This, however, doesn't work with exists? queries. Rails will still execute the query.

Solving the N+1

Let's look at how we went about solving the N+1 problem for our Rails exists? query. On our user model, we originally had this method for checking beta features:

def beta_feature_enabled?(name)
  beta_features.where(name: name).enabled.exists?
end

This will always execute a query whether or not beta_features is already loaded.

One way to avoid the query is by preloading all the records and then checking them in memory rather than executing a query.

# New method, allows us to preload beta_features
def beta_feature_enabled?(name)
  if beta_features.loaded?
    beta_features.any? { |f| f.name == name.to_s && f.enabled? }
  else
    beta_features.where(name: name.to_s).enabled.exists?
  end
end

Now, in our controllers, if we preload beta_features using includes, it will already be loaded. Any calls to beta_feature_enabled? won't execute additional queries.

# Executes 2 queries
@users = User.all.includes(:beta_features)

You can also use this technique for reducing queries when loading a single record.

@user = User.find(params[:id])
@user.beta_features.load # preload all beta features for the user

Preloading with a scope

With the above solution, we're loading every beta_feature for every user. For our use case, this is what we want.

This could result in loading unnecessary records if your application is only checking a few though.

If that's you, here's a solution. You can set up a new association that only loads the records you need:

has_many :beta_features, as: :target, dependent: :destroy_async
PRELOADED_FLAGS = %w[dark_mode insights data_imports]
has_many :preloaded_beta_features, -> { where(name: PRELOADED_FLAGS) }, as: :target, class_name: "BetaFeature"

You can now replace beta_features with preloaded_beta_features to load in only the records you need.