Introducción a Eager Loading Associations

Eager loading es una técnica utilizada en Rails para cargar en memoria asociaciones de Active Record de manera anticipada, evitando el problema de N + 1 queries. Este problema ocurre cuando, al cargar una colección de objetos, cada uno de ellos hace una consulta adicional para cargar sus asociaciones. Esto puede llevar a un número excesivo de consultas a la base de datos, lo que afecta el rendimiento de la aplicación.

Problema de N + 1 Queries

Supongamos que tenemos los modelos: `Author` y `Book`, donde un autor puede tener varios libros. Si se quisiera mostrar un listado de libros, junto con su autor, podríamos hacer lo siguiente:

books = Book.limit(10)
books.each do |book|
puts book.author.last_name
end

Esto generará una consulta para obtener todos los libros y luego, para cada libro, se ejecutará una consulta adicional para obtener el autor. Si hay 10 libros, se ejecutarán 11 consultas (1 para los obtener todos los libros y 10 consultas para obtener los autores), lo que se conoce como el problema de N + 1 queries.

SELECT * FROM books LIMIT 10

SELECT * FROM authors WHERE id = 1
SELECT * FROM authors WHERE id = 2
SELECT * FROM authors WHERE id = 3
SELECT * FROM authors WHERE id = 4
SELECT * FROM authors WHERE id = 5
SELECT * FROM authors WHERE id = 6
SELECT * FROM authors WHERE id = 7
SELECT * FROM authors WHERE id = 8
SELECT * FROM authors WHERE id = 9
SELECT * FROM authors WHERE id = 10

Diferentes formas de utilizar Eager Loading

Rails ofrece tres métodos para realizar eager loading: includes, eager_load y preload. Cada uno tiene sus propias características y casos de uso.

includes

El método includes es el más común y permite cargar las asociaciones de manera anticipada, optimizando las consultas en función de cómo se usan los datos posteriormente. Rails decide si usar una consulta separada o un LEFT OUTER JOIN basado en la consulta y cómo se utilizan los datos.

Ejemplo:

books = Book.includes(:author).limit(10)
books.each do |book|
puts book.author.last_name
end

Si solo se accede a los datos de los autores, Rails realizará una segunda consulta. Pero si se aplica alguna condición o filtrado a través de las asociaciones, podría usar LEFT OUTER JOIN.

SELECT * FROM books LIMIT 10

SELECT authors.* FROM authors
WHERE (authors.id IN (1,2,3,4,5,6,7,8,9,10))

eager_load

El método eager_load fuerza a Rails a usar un LEFT OUTER JOIN para cargar las asociaciones. Esto es útil cuando necesitamos filtrar u ordenar en base a las asociaciones cargadas.

Ejemplo:

books = Book.eager_load(:author).limit(10)
books.each do |book|
puts book.author.last_name
end

En este caso, se realizará una única consulta con un LEFT OUTER JOIN, en lugar de una consulta para cargar los libros y otra consulta para cargar los autores.

SELECT "books"."id" AS t0_r0, "books"."title" AS t0_r1, ... FROM "books"
LEFT OUTER JOIN "authors" ON "authors"."id" = "books"."author_id"
LIMIT 10

preload

El método preload es similar a includes, pero siempre realiza consultas separadas para cargar las asociaciones. Esto es útil cuando no necesitamos filtros o condiciones en las asociaciones.

Ejemplo:

books = Book.preload(:author).limit(10)
books.each do |book|
puts book.author.last_name
end

Aquí, se ejecutarán dos consultas: una para los libros y otra para los autores, independientemente de cómo usemos los datos.

SELECT books.* FROM books LIMIT 10

SELECT authors.* FROM authors
WHERE authors.id IN (1,2,3,4,5,6,7,8,9,10)

Cómo detectar N + 1 queries?

Existen algunas gemas que nos permiten detectar automáticamente si hay consultas que tienen el problema de N + 1 queries. Incluso, algunas de ellas nos pueden advertir cuando estamos haciendo eager loading innecesarios.
Entre las gemas más conocidas tenemos:

  • Bullet: es una de las gemas más utilizadas para detectar y prevenir N + 1 queries. Proporciona advertencias cuando detecta consultas innecesarias y puede sugerir el uso de eager loading.
  • Skylight: es una herramienta de monitoreo de rendimiento que también puede ayudarte a detectar problemas de N + 1 queries. Proporciona análisis de rendimiento en tiempo real y te alerta sobre problemas potenciales.
  • Rack Mini Profiler: es otra herramienta que puede ayudarte a identificar consultas lentas y N + 1 queries. Proporciona un panel en la interfaz de usuario que muestra información sobre las consultas realizadas.

Conclusión

Eager loading es una herramienta poderosa en Ruby on Rails que nos ayuda a optimizar el rendimiento de las aplicaciones al reducir la cantidad de consultas a la base de datos. Al utilizar alguno de los métodos mencionados anteriormente, se puede mejorar la eficiencia de tus consultas y evitar el problema de N + 1 queries. Entender y aplicar eager loading correctamente puede tener un impacto significativo en la experiencia del usuario y en el rendimiento general de la aplicación.