Problema muito lento no Sidekiq com fila grande devido a enorme quantidade de notificações de usuário não lidas

Obrigado, @Falco

Estou principalmente perplexo quanto a como o desempenho pode oscilar entre concluir ~11 milhões e ~300 mil jobs em um dia, dentro de ~1 semana, com a mesma configuração. Uma diferença de velocidade de ~35x em termos de jobs por segundo.

Quanto ao uso da CPU, ele voltou a ~15-20%, que é o habitual. Processando jobs na mesma velocidade (lenta).

Apenas para esclarecer/confirmar, eu quis dizer atribuir (não adicionar) alguns sidekiqs exclusivamente para processar a fila de baixa prioridade, já que parecia que as tarefas de baixa prioridade podiam ser processadas a uma taxa muito mais rápida e possivelmente não sofrem os mesmos gargalos. Eu especulava que isso poderia explicar como os jobs por segundo podem variar tão drasticamente (ou seja, tarefas ‘fáceis’ de baixa prioridade presas atrás do backlog da fila padrão).

Para esclarecer: você acha que o desempenho do PostgreSQL está causando a conclusão lenta dos jobs ou apenas o evento de alto uso de CPU que notei ontem (que agora voltou ao normal)?

Isso tudo é em SSD, certo?

Sim, correto @Stephen - RAID 1 de SSDs NVMe.

Atualização: Tentei excluir a fila de baixa prioridade e a fila padrão algumas vezes, sem impacto na velocidade, pois a fila padrão volta a crescer imediatamente. Em seguida, tentei excluir a fila padrão e ativar o modo somente leitura. Isso fez com que o número de tarefas por segundo disparasse dramaticamente, esvaziando a fila de baixa prioridade a uma velocidade cerca de 100 vezes maior.

Edição: Parece que, mesmo com apenas uma fila de baixa prioridade grande, a velocidade de processamento ainda é lenta. Se eu definir o Discourse como somente leitura e depois esvaziar tanto a fila de baixa prioridade quanto a fila padrão, o processamento subsequente de tarefas parece permanecer super rápido, esvaziando as tarefas agendadas e as filas até que eu desative o modo somente leitura. :yuno:

Meu próximo passo seria descobrir exatamente qual processo está causando o problema, acessando o aplicativo Discourse e executando o htop ou top para verificar os maiores usos de CPU.

Parece que o PostgreSQL é o gargalo. Você pode configurar o Prometheus para monitorar seu desempenho e verificar se ele tem acesso suficiente à memória RAM.

Obrigado pela sua contribuição, @pfaffman :slight_smile: Acredito que db_shared_buffers e db_work_mem no app.yml sejam os únicos controles para o acesso à RAM do PostgreSQL, certo?

Fiz alguns ajustes, tanto para cima quanto para baixo. As configurações atuais no app.yml são:
db_shared_buffers: “32768MB”
db_work_mem: “128MB”

Com uma memória RAM total do sistema de 128 GB.

Também tentei alterar max_connections em /var/discourse/shared/standalone/postgres_data/postgresql.conf e depois reconstruir o Discourse. Testei valores acima do padrão (100), de 200 a 500. Atualmente está configurado para 300. Não tenho certeza se modificá-lo ali realmente altera o valor máximo de conexões.

Vejo estas configurações em /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

Obrigado @bartv, seguindo sua sugestão, tenho monitorado de dentro do aplicativo Discourse via top. Estou vendo muitos processos postmaster executados pelo usuário postgres — a quantidade de uso de CPU varia. As capturas de tela representam períodos prolongados com estatísticas de uso semelhantes.

Usando ~95% dos 32 núcleos:

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

Usando ~6% de CPU, enquanto o modo somente leitura estava ativo.

Qual o tamanho do seu banco de dados? Quantos usuários você tem? Quantas novas postagens por dia?

A primeira coisa que você deve fazer é executar VACUUM ANALYZE; no console do PostgreSQL.

Isso pode demorar um pouco para ser executado; talvez seja interessante parar o Sidekiq temporariamente para reduzir a carga enquanto ele trabalha.

Se isso não ajudar, devemos habilitar o pg_stat_statements e, em seguida, verificar quais consultas estão consumindo uma quantidade enorme de CPU.

@pfaffman

  • A pasta /var/discourse/shared/standalone/postgres_data tem 170 GB
  • 61,7 mil usuários ativos nos últimos 30 dias (não tenho certeza sobre o total absoluto)
  • ~50 mil a 80 mil novas postagens por dia

Oh. Isso não é trivial.

Você deve se informar sobre o ajuste do PostgreSQL. Esse nível de desempenho está um pouco além do auto-hospedagem típica vista aqui.

Eu tentaria reservar cerca de 3/4 da RAM para o PostgreSQL. Com certeza eu separaria em containers de dados e web distintos. Mas você pode precisar de uma configuração mais complexa do PostgreSQL para obter o desempenho necessário.

EDIT: Mas eu não tenho experiência com bancos de dados muito maiores, então veja abaixo! :wink:

Lidamos com bancos de dados muito maiores usando muito menos RAM e com um uso de CPU significativamente menor.

As informações do pg_stat_statements provavelmente serão capazes de nos dizer o que está errado.

Muito obrigado pela ajuda, pessoal.

Então, tentei executar VACUUM ANALYZE; — infelizmente, não funcionou. Os comandos usados estão abaixo, para referência:

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

Tentei habilitar o pg_stat_statements. Os passos realizados estão abaixo:

Adicionei/modifiquei as linhas abaixo neste arquivo: /var/discourse/shared/standalone/postgres_data/postgresql.conf

shared_preload_libraries = ‘pg_stat_statements’ # (alteração requer reinício)
pg_stat_statements.max = 10000
pg_stat_statements.track = all
pg_stat_statements.track_utility = on
pg_stat_statements.save = off

Em seguida, recompilei o Discourse e executei:

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

Tentei executar consultas, mas recebi o seguinte erro:

ERROR: pg_stat_statements deve ser carregado via shared_preload_libraries

Minha suposição é que minhas edições no arquivo postgresql.conf (/var/discourse/shared/standalone/postgres_data/postgresql.conf) não estão funcionando (recompilei o Discourse após editar). É possível fazer essas edições através do arquivo app.yml? Ou você vê algo que eu tenha feito de errado?

Reconstruir o Discourse apagará essas alterações. Reiniciar o container provavelmente resolverá o problema. (algo como sv restart postgres dentro do container também pode funcionar).

Obrigado, tentei reiniciar o container:

./launcher stop app
./launcher start app

Ainda recebo o mesmo erro ao tentar consultar:

ERRO: pg_stat_statements deve ser carregado via shared_preload_libraries

As alterações que fiz anteriormente ainda persistem no arquivo aqui, mesmo após uma reconstrução:

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

Suspeito que este não seja o local onde devo fazer essas edições :face_with_monocle:

Talvez seja porque estou usando apenas app.yml (e mail-receiver.yml) na pasta de containers e não implementei o uso de data.yml.

Sem olhar de fato, é provável que seja /etc/postgres dentro do container. Você também pode precisar instalar a biblioteca que ele está exigindo.

Obrigado, isso ajudou muito, no início. :man_cartwheeling:. O problema parecia resolvido e a fila de jobs estava rodando muito rápido apenas aumentando as conexões máximas no postgresql.conf. Infelizmente, desacelerou novamente após cerca de um dia.

Abaixo estão os passos, caso sejam úteis para outras pessoas que queiram aumentar o max_connections do PostgreSQL.

docker ps

Obtenha o ID do contêiner, por exemplo, aaabbbccc123, e substitua nos comandos abaixo:

Copie o arquivo postgresql.conf de dentro do contêiner Docker para o sistema de arquivos local:

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

Edite a configuração:

nano /srv/postgresql.conf

Copie-o de volta para o contêiner Docker:

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

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

Exclua o arquivo residual (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

Agora que você sabe o que deseja fazer, pode aplicar essas alterações usando um bloco replace no seu app.yml.

Além disso, você pode simplesmente executar ./launcher enter app e editar os arquivos diretamente. Observe, no entanto, que ao reconstruir, essas alterações não estarão presentes no novo contêiner.