Cómo arreglar una consulta implícita lenta en la tabla pg_attribute en Rails

En nuestro entorno de producción, notamos picos frecuentes (~ cada 1 hora) en nuestra aplicación Rails. Al profundizar, se debe a la siguiente consulta que se ejecuta de forma acumulativa en> 1.5 s (llamada 100x) en una sola solicitud HTTP.

SELECT a.attname, format_type(a.atttypid, a.atttypmod), pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod FROM pg_attribute a LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum WHERE a.attrelid = ?::regclass AND a.attnum > ? AND NOT a.attisdropped ORDER BY a.attnum 

No tenemos un código que llame explícitamente a esa tabla, pero parece que Rails lo llama para averiguar los atributos de cada modelo. Las ” consultas SQL inesperadas a la base de datos de Postgres en Rails / Heroku ” están relacionadas.

Pero, ¿no debería ser llamado no repetitivamente por Rails?

¿Cómo aceleramos esto?

En producción, cada proceso de Rails ejecutará esa consulta una vez para cada tabla / modelo que encuentre. Eso es una vez por rails s , no por solicitud: si lo está viendo repetidamente, investigaría si sus procesos se están reiniciando con frecuencia por algún motivo.

Para eliminar por completo esas consultas de tiempo de ejecución, puede generar un archivo de caché de esquema en su servidor:

 RAILS_ENV=production rails db:schema:cache:dump 

(Rails 4: RAILS_ENV=production bin/rake db:schema:cache:dump )

Ese comando realizará las consultas de inmediato y luego escribirá sus resultados en un archivo de caché, que los procesos futuros de Rails cargarán directamente en lugar de inspeccionar la base de datos. Naturalmente, luego deberá regenerar el caché después de cualquier cambio futuro en el esquema de la base de datos.

No he experimentado este problema en ninguna aplicación de Rails que haya trabajado hasta ahora. Creo que su solución es agregar active-record-query-trace a su proyecto y verificar qué está activando esta consulta.

En el trabajo utilizo esta configuración:

 # Gemfile group :test, :development do gem "active-record-query-trace" end # config/initializers/ar_tracer.rb if ENV.has_key?("AR_TRACER") && defined? ActiveRecordQueryTrace ActiveRecordQueryTrace.enabled = true ActiveRecordQueryTrace.lines = 20 # you may want to increase if not enough end 

Luego simplemente inicie su servidor de Rails de esta manera: AR_TRACER=1 bundle exec rails s .

Como se recomienda en este comentario de problema , puede agregarlo a su Procfile :

 web: rails db:schema:cache:dump && rails s ... 

Si está en Heroku, no puede simplemente ejecutarlo usando heroku run ... porque el volcado de esquema solo se genera por servidor y el comando se ejecutará en un dinamómetro único, que desaparecerá inmediatamente después del comando se ejecuta Debe asegurarse de que se ejecute en cada servidor mientras arranca.

Aceleramos esto con un término llamado carga ansiosa. El método de registro activo de eager_load nos permite no hacer la consulta N + 1 para cada fila en su tabla. De lo contrario, los Rails no saben lo que está haciendo y el valor predeterminado es un SELECT / JOIN por separado para cada atributo que desee que no esté en la misma tabla.

¡¡¡Espero que esto ayude!!!