N+1 Detection

N+1 Query Detection

Tindra automatically detects N+1 query patterns in your transaction traces and opens them as issues, so they show up alongside your errors without any manual instrumentation.

What is an N+1 query?

An N+1 query happens when your code runs one query to fetch a list, then runs another query for each item in that list. A page that loads 50 orders and queries the database once per order makes 51 queries instead of 1 or 2. It is one of the most common performance problems in ORM-heavy applications and easy to miss in development where tables are small.

How detection works

Tindra analyzes the db.query spans inside every ingested transaction. When the same query pattern appears more than a threshold number of times in a single transaction, Tindra opens an issue automatically.

The issue includes:

  • The repeated query pattern (parameters normalized)
  • How many times it ran in the triggering transaction
  • A link to the transaction trace so you can see exactly where in the waterfall the queries are fired
  • The endpoint or job that triggered it

N+1 issues are deduplicated like any other issue. The first occurrence opens it; subsequent occurrences add to the event count.

Requirements

N+1 detection runs on the server side, so it works with any SDK as long as DB query spans are being sent. That means performance monitoring must be enabled with a sample rate greater than zero.

SENTRY_TRACES_SAMPLE_RATE=0.1

Most SDKs instrument database queries automatically when tracing is on. No extra configuration is needed.

Finding N+1 issues

N+1 issues appear in the Issues list like any other issue. Filter by type n+1 to see only N+1 detections, or look for issues with the title format:

N+1 Query: SELECT * FROM "orders" WHERE "user_id" = ?

Click through to the issue to see the full trace and the exact query being repeated.

Fixing an N+1

The fix is usually eager loading. In Laravel:

// Before: N+1
$orders = Order::all();
foreach ($orders as $order) {
    echo $order->user->name; // one query per order
}

// After: 2 queries total
$orders = Order::with('user')->get();
foreach ($orders as $order) {
    echo $order->user->name;
}

Once fixed and deployed, new transactions will no longer trigger the detection and the issue will stay resolved.