one_plus_n_detector

Detect 1+n SQL queries in your Ecto application.


Keywords
ecto, elixir, phoenix, sql
License
MIT

Documentation

OnePlusNDetector

This library helps detect 1+n SQL queries in your application. This is a common problem when working with ORMs like Ecto.

What is the 1+n problem?

When you have a parent-child relationship like a has_many, has_one or belongs_to you might load your parent records with one query and then for each record, trigger another SQL statment to load the related children.

Let's say you have the following Ecto schemas

defmodule User do
  use Ecto.Schema

  schema "users" do
    has_one :details, Details
  end
end

defmodule Details do
  use Ecto.Schema

  schema "users_details" do
    belongs_to :user, User
  end
end

You could load all the users with its respective details the following way (wrong way)

User
|> Repo.all
|> Enum.map(& %{&1 | details: Repo.get(&1.details_id)})

You can query all users and then for each record that it's returned you can query it's user details. This implementation falls into the 1+N problem because the first query returns N records and for each record you trigger an additional SQL query, having in total 1+N SQL queries issued to your DB.

The number of queries can be reduced to two by just using a preload statement.

User
|> preload(:details)
|> Repo.all

This library helps you detect these cases by triggering a warning in your query log.

Usage

Add an extra logger to Ecto repo.

config :my_app, MyApp.Repo, loggers: [
  {OnePlusNDetector, :analyze, []},
  {Ecto.LogEntry, :log, []}
]

And then configure this library's GenServer in your application

def start(_type, _args) do
  import Supervisor.Spec, warn: false

  children = [
    supervisor(MyApp.Endpoint, []),
    supervisor(MyApp.Repo, []),
    worker(OnePlusNDetector.Detector, []),
  ]

  opts = [strategy: :one_for_one, name: MyApp.Supervisor]
  Supervisor.start_link(children, opts)
end

Documentation

Documentation can be found at https://hexdocs.pm/one_plus_n_detector.

Installation

This library should be used only on development

def deps do
  [
    {:one_plus_n_detector, "~> 0.1.0", only: :dev}
  ]
end

Remember to not add :one_plus_n_detector to your applications list!

License

one_plus_n_detector is licensed under the MIT license.

See LICENSE for the full license text.