Problema muy lento de Sidekiq con cola grande debido a números masivos de notificaciones de usuario no leídas

Gracias @Falco

Sencillamente no entiendo cómo el rendimiento puede oscilar entre completar ~11 millones y ~300 mil trabajos en un día en el transcurso de ~1 semana, manteniendo la misma configuración. Una diferencia de velocidad de ~35x en términos de trabajos por segundo.

En cuanto al uso de la CPU, ha vuelto a bajar a ~15-20%, que es lo habitual. Procesando trabajos a la misma velocidad (lenta).

Solo para aclarar/confirmar, me refería a asignar (no añadir) algunos Sidekiqs exclusivamente para procesar la cola de baja prioridad, ya que parecía que las tareas de baja prioridad se podían procesar a una velocidad mucho mayor y posiblemente no sufrían los mismos cuellos de botella. Especulaba que esto podría explicar por qué los trabajos por segundo pueden variar tan drásticamente (es decir, tareas de baja prioridad ‘fáciles’ atascadas detrás del retraso de la cola predeterminada).

Para aclarar: ¿crees que el rendimiento de PostgreSQL está causando la finalización lenta de los trabajos o solo el evento de alto uso de CPU que noté ayer (que ahora ha vuelto a la normalidad)?

Esto está todo en SSD, ¿verdad?

Sí, correcto @Stephen: SSDs NVMe en RAID 1.

Actualización: Intenté eliminar la cola de baja prioridad y la cola predeterminada varias veces, sin impacto en la velocidad, ya que la cola predeterminada vuelve a crecer inmediatamente. Luego intenté eliminar la cola predeterminada y activar el modo de solo lectura. Esto provocó un aumento drástico en las tareas por segundo, procesando la cola de baja prioridad a una velocidad de unas 100 veces más rápida.

Edición: Parece que incluso con solo una cola de baja prioridad grande, la velocidad de procesamiento sigue siendo lenta. Si configuro Discourse en modo de solo lectura y luego vacío tanto la cola de baja prioridad como la predeterminada, el procesamiento de tareas posteriores parece mantenerse extremadamente rápido, vaciando las tareas programadas y las colas hasta que desactivo el modo de solo lectura. :yuno:

Mi siguiente paso sería averiguar exactamente qué proceso está causando el problema, entrando en la aplicación de Discourse y ejecutando htop o top para ver el uso de CPU más alto.

Suena como si PostgreSQL fuera el cuello de botella. Podrías configurar Prometheus para rastrear su rendimiento y ver si tiene acceso suficiente a la memoria RAM.

Gracias por tu aporte @pfaffman :slight_smile: Creo que db_shared_buffers y db_work_mem en app.yml son los únicos controles para el acceso a la memoria RAM de PostgreSQL, ¿verdad?

He hecho algunas pruebas ajustando los valores tanto hacia arriba como hacia abajo. Los ajustes actuales en app.yml son:
db_shared_buffers: “32768MB”
db_work_mem: “128MB”

Con una memoria RAM total del sistema de 128 GB.

También he intentado modificar max_connections en /var/discourse/shared/standalone/postgres_data/postgresql.conf y luego reconstruir Discourse. Probé valores superiores al predeterminado (100), desde 200 hasta 500. Actualmente está configurado en 300. No estoy seguro de si modificarlo allí realmente cambia el valor máximo de conexiones.

Veo lo siguiente en /var/discourse/templates/postgres.template.yml:

db_synchronous_commit: “off”
db_shared_buffers: “256MB”
db_work_mem: “10MB”
db_default_text_search_config: “pg_catalog.english”
db_name: discourse
db_user: discourse
db_wal_level: minimal
db_max_wal_senders: 0
db_checkpoint_segments: 6
db_logging_collector: off
db_log_min_duration_statement: 100

Gracias @bartv, siguiendo tu sugerencia he estado observando desde dentro de la aplicación Discourse mediante top. Veo bastantes procesos postmaster ejecutados por el usuario postgres, con un uso de CPU variable. Las capturas de pantalla representan periodos de tiempo prolongados con estadísticas de uso similares.

Usando ~95% de los 32 núcleos:

Usando ~20%, menor uso de CPU del postmaster.

Usando ~6% de CPU, mientras el modo de solo lectura estaba activo.

¿Qué tamaño tiene tu base de datos? ¿Cuántos usuarios tienes? ¿Cuántas publicaciones nuevas hay por día?

Lo primero que debes hacer es ejecutar VACUUM ANALYZE; desde la consola de PostgreSQL.

Esto puede tardar un tiempo en ejecutarse; es posible que quieras detener Sidekiq temporalmente para reducir la carga mientras se realiza.

Si eso no ayuda, deberíamos habilitar pg_stat_statements y luego verificar qué consultas están consumiendo una gran cantidad de CPU.

@pfaffman

  • La carpeta /var/discourse/shared/standalone/postgres_data tiene 170 GB
  • 61,7 mil usuarios activos en los últimos 30 días (no estoy seguro del total absoluto)
  • Aproximadamente de 50 mil a 80 mil nuevas publicaciones por día

Oh. Eso no es trivial.

Deberías informarte sobre el ajuste de PostgreSQL. Ese nivel de rendimiento está un poco por encima del autoalojamiento típico que se ve aquí.

Yo probaría asignando a PostgreSQL aproximadamente 3/4 de la RAM. Ciertamente, separaría los contenedores de datos y de la web. Pero es posible que necesites una configuración de PostgreSQL más compleja para obtener el rendimiento que necesitas.

EDITO: Pero no tengo experiencia con bases de datos mucho más grandes, ¡así que mira abajo! :wink:

Trabajamos con bases de datos mucho más grandes con mucha menos memoria RAM y sin un uso de CPU tan elevado.

La información de pg_stat_statements probablemente nos permitirá identificar el problema.

Muchas gracias por la ayuda, chicos.

Así que intenté ejecutar VACUUM ANALYZE; — por desgracia, sin éxito. Los comandos utilizados a continuación, para referencia:

cd /var/discourse/
./launcher enter app
sudo -u postgres psql
\c discourse
VACUUM ANALYZE;

Intenté habilitar pg_stat_statements; los pasos realizados a continuación:

Añadí/modifiqué las líneas siguientes aquí: /var/discourse/shared/standalone/postgres_data/postgresql.conf

shared_preload_libraries = ‘pg_stat_statements’ # (el cambio requiere reinicio)
pg_stat_statements.max = 10000
pg_stat_statements.track = all
pg_stat_statements.track_utility = on
pg_stat_statements.save = off

Luego reconstruí Discourse y ejecuté:

./launcher enter app
sudo -u postgres psql
\c discourse
CREATE EXTENSION pg_stat_statements;

He intentado realizar consultas, pero obtengo este error:

ERROR: pg_stat_statements debe cargarse mediante shared_preload_libraries

Mi suposición es que mis ediciones en el archivo postgresql.conf (/var/discourse/shared/standalone/postgres_data/postgresql.conf) no están funcionando (reconstruí Discourse después de editarlas). ¿Es posible realizar estas ediciones a través del archivo app.yml? ¿O ves algo que haya hecho mal?

Reconstruir Discourse borrará esos cambios. Reiniciar el contenedor probablemente hará el truco. (algo como sv restart postgres dentro del contenedor también podría funcionar).

Gracias, intenté reiniciar el contenedor:

./launcher stop app
./launcher start app

Aún obtengo el mismo error al intentar realizar la consulta:

ERROR: pg_stat_statements debe cargarse mediante shared_preload_libraries

Los cambios que realicé anteriormente siguen persistiendo en el archivo, incluso después de una reconstrucción:

/var/discourse/shared/standalone/postgres_data/postgresql.conf

Sospecho que este no es el archivo donde debería estar haciendo estas ediciones :face_with_monocle:

Quizás sea porque solo estoy usando app.yml (y mail-receiver.yml) en la carpeta de contenedores y no he implementado el uso de data.yml.

Sin mirarlo realmente, es probable que sea /etc/postgres dentro del contenedor. También es posible que necesites instalar la biblioteca que esté requiriendo.

¡Gracias, esto ayudó mucho, al principio. :man_cartwheeling:. El problema parecía resuelto y la cola de trabajos avanzaba muy rápido solo aumentando las conexiones máximas en postgresql.conf. Desafortunadamente, volvió a ralentizarse después de aproximadamente un día.

A continuación, se muestran los pasos, por si son útiles para otros que deseen aumentar el max_connections de PostgreSQL.

docker ps

Obtén el ID del contenedor, por ejemplo aaabbbccc123, y reemplázalo en los comandos siguientes:

Copia el archivo postgresql.conf desde dentro del contenedor Docker al sistema de archivos local:

docker cp aaabbbccc123:/etc/postgresql/10/main/postgresql.conf /srv

Edita la configuración:

nano /srv/postgresql.conf

Cópialo de nuevo al contenedor Docker:

docker cp /srv/postgresql.conf aaabbbccc123:/etc/postgresql/10/main/postgresql.conf

cd /var/discourse
./launcher stop app
./launcher start app

Elimina el archivo sobrante (opcional):

rm /srv/postgresql.conf

@supermathie I believe I’ve successfully enabled pg_stat_statements :grinning:

I tried using this query:

SELECT
(total_time / 1000 / 60) as total,
(total_time/calls) as avg,
query
FROM pg_stat_statements
ORDER BY 1 DESC
LIMIT 100;

From this guide: The most useful Postgres extension: pg_stat_statements

I can’t really read the result though, I think I’ve done something wrong.

total | avg | query
1671.1110420745 | 374.736186677194 | SELECT COUNT(*) FROM ( +
| | SELECT $1 FROM +
| | notifications n +
| | LEFT JOIN topics t ON t.id = n.topic_id +
| | WHERE t.deleted_at IS NULL AND +
| | n.notification_type <> $2 AND +
| | n.user_id = $3 AND +
| | n.id > $4 AND

Ahora que sabes lo que quieres hacer, puedes realizar estos cambios con una sección de reemplazo en tu app.yml.

También podrías simplemente ejecutar ./launcher enter app y editar los archivos directamente. Ten en cuenta, sin embargo, que al reconstruir, esos cambios no estarán presentes en el nuevo contenedor.