Problème Sidekiq très lent avec une grande file d'attente dû à un nombre massif de notifications utilisateur non lues

Merci @Falco

Je suis surtout perplexe quant à la façon dont les performances peuvent osciller entre l’exécution d’environ 11 millions et 300 000 tâches par jour en l’espace d’une semaine, avec la même configuration. Une différence de vitesse d’environ 35 fois en termes de tâches par seconde.

Pour l’utilisation du processeur, elle est retombée à environ 15-20 %, ce qui est la normale. Le traitement des tâches se fait à la même vitesse (lente).

Pour clarifier/confirmer, je voulais dire affecter (et non ajouter) certains Sidekiqs exclusivement pour traiter la file d’attente de priorité faible, car il semblait que les tâches de priorité faible puissent être traitées beaucoup plus rapidement et ne souffrent peut-être pas des mêmes goulots d’étranglement. Je spéculais que cela pourrait expliquer pourquoi le nombre de tâches par seconde peut varier de manière si drastique (c’est-à-dire des tâches « faciles » de priorité faible bloquées derrière l’accumulation de la file d’attente par défaut).

Pour clarifier : pensez-vous que les performances de PostgreSQL sont à l’origine de la lenteur d’achèvement des tâches, ou seulement de l’événement de forte utilisation du processeur que j’ai remarqué hier (qui est maintenant revenu à la normale) ?

Tout est bien sur SSD, n’est-ce pas ?

Oui, c’est exact @Stephen - NVMe SSDs RAID 1.

Mise à jour : J’ai essayé de supprimer la file d’attente à faible priorité et la file d’attente par défaut plusieurs fois, sans aucun impact sur la vitesse, car la file d’attente par défaut repousse immédiatement. J’ai ensuite essayé de supprimer la file d’attente par défaut et d’activer le mode lecture seule. Cela a fait grimper drastiquement le nombre de tâches par seconde, vidant la file d’attente à faible priorité à une vitesse fulgurante (~100 fois plus de tâches par seconde).

Édition : Il semble que même avec une grande file d’attente à faible priorité, la vitesse de traitement reste lente. Si je passe Discourse en mode lecture seule, puis que je vide les files d’attente à faible et à priorité par défaut, le traitement des tâches qui suit reste extrêmement rapide, vidant les tâches planifiées et les files d’attente jusqu’à ce que je désactive le mode lecture seule. :yuno:

Ma prochaine étape consisterait à identifier précisément le processus qui pose problème en accédant à l’application Discourse et en exécutant htop ou top pour observer les processus les plus gourmands en CPU.

Cela ressemble effectivement à un goulot d’étranglement côté PostgreSQL. Vous pourriez configurer Prometheus pour suivre ses performances et vérifier qu’il dispose de suffisamment de RAM.

Merci pour votre retour @pfaffman :slight_smile: Je pense que db_shared_buffers et db_work_mem dans app.yml sont les seuls contrôles pour l’accès à la RAM de PostgreSQL, n’est-ce pas ?

J’ai un peu expérimenté, tant à la hausse qu’à la baisse. Les paramètres actuels dans app.yml sont :
db_shared_buffers : “32768MB”
db_work_mem : “128MB”

Avec une RAM système totale de 128 Go.

J’ai également essayé de modifier max_connections dans /var/discourse/shared/standalone/postgres_data/postgresql.conf, puis de reconstruire Discourse. J’ai testé des valeurs supérieures à la valeur par défaut (100), de 200 à 500. Actuellement réglé à 300. Je ne suis pas sûr que la modification ici change réellement la valeur maximale des connexions.

Je vois ceci dans /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

Merci @bartv, en suivant votre suggestion, j’ai surveillé depuis l’intérieur de l’application Discourse via top. Je constate un grand nombre de processus postmaster exécutés par l’utilisateur postgres, avec des niveaux d’utilisation du CPU variables. Les captures d’écran représentent des périodes prolongées avec des statistiques d’utilisation similaires.

Utilisation d’environ 95 % des 32 cœurs :

Utilisation d’environ 20 %, avec une utilisation du CPU plus faible pour postmaster.

Utilisation d’environ 6 % du CPU, tandis que le mode en lecture seule était actif.

Quelle est la taille de votre base de données ? Combien d’utilisateurs avez-vous ? Combien de nouveaux posts par jour ?

La première chose à faire est d’exécuter VACUUM ANALYZE; depuis la console PostgreSQL.

Cela peut prendre un certain temps ; vous voudrez peut-être arrêter temporairement Sidekiq pour alléger la charge pendant son exécution.

Si cela ne suffit pas, nous devrions activer pg_stat_statements puis vérifier quelles requêtes consomment une énorme quantité de CPU.

@pfaffman

  • Le dossier /var/discourse/shared/standalone/postgres_data fait 170 Go
  • 61 700 utilisateurs actifs au cours des 30 derniers jours (je ne suis pas certain du total absolu)
  • Environ 50 000 à 80 000 nouveaux posts par jour

Oh. C’est loin d’être trivial.

Vous devriez vous renseigner sur l’optimisation de PostgreSQL. Ce niveau de performance dépasse un peu l’auto-hébergement typique que l’on voit ici.

Je vous conseillerais d’allouer environ les 3/4 de la RAM à PostgreSQL. Je séparerais certainement les conteneurs de données et de web. Mais vous pourriez avoir besoin d’une configuration PostgreSQL plus complexe pour obtenir les performances dont vous avez besoin.

EDIT : Mais je n’ai pas beaucoup d’expérience avec des bases de données beaucoup plus volumineuses, donc voyez ci-dessous ! :wink:

Nous gérons des bases de données beaucoup plus volumineuses avec beaucoup moins de RAM et une utilisation du CPU bien moindre.

Les informations de pg_stat_statements devraient probablement nous indiquer ce qui ne va pas.

Merci beaucoup pour votre aide, les gars.

J’ai donc essayé d’exécuter VACUUM ANALYZE; — pas de chance malheureusement. Voici les commandes utilisées, pour référence :

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

J’ai tenté d’activer pg_stat_statements, les étapes étant les suivantes :

J’ai ajouté/modifié les lignes ci-dessous ici : /var/discourse/shared/standalone/postgres_data/postgresql.conf

shared_preload_libraries = ‘pg_stat_statements’ # (redémarrage requis)
pg_stat_statements.max = 10000
pg_stat_statements.track = all
pg_stat_statements.track_utility = on
pg_stat_statements.save = off

Ensuite, j’ai reconstruit Discourse et exécuté :

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

J’ai essayé d’exécuter des requêtes, mais j’obtiens cette erreur :

ERROR: pg_stat_statements doit être chargé via shared_preload_libraries

Je suppose que mes modifications apportées au fichier postgresql.conf (/var/discourse/shared/standalone/postgres_data/postgresql.conf) ne fonctionnent pas (j’ai reconstruit Discourse après les avoir effectuées). Est-il possible d’apporter ces modifications via le fichier app.yml ? Ou voyez-vous une erreur dans ce que j’ai fait ?

Reconstruire Discourse effacera ces modifications. Redémarrer le conteneur devrait probablement suffire. (quelque chose comme sv restart postgres à l’intérieur du conteneur pourrait aussi fonctionner).

Merci, j’ai essayé de redémarrer le conteneur :

./launcher stop app
./launcher start app

Je reçois toujours la même erreur lors de la tentative de requête :

ERREUR : pg_stat_statements doit être chargé via shared_preload_libraries

Les modifications que j’ai apportées précédemment persistent toujours dans le fichier ici, même après une reconstruction :

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

Je soupçonne que ce n’est pas dans ce fichier que je devrais apporter ces modifications :face_with_monocle:

Peut-être parce que j’utilise uniquement app.yml (et mail-receiver.yml) dans le dossier des conteneurs, sans avoir mis en œuvre l’utilisation de data.yml.

Sans même regarder, il s’agit probablement de /etc/postgres à l’intérieur du conteneur. Vous devrez peut-être également installer la bibliothèque dont il a besoin.

Merci, cela a beaucoup aidé, au début. :man_cartwheeling:. Le problème semblait résolu et la file d’attente des tâches fonctionnait très rapidement simplement en augmentant le nombre maximal de connexions dans postgresql.conf. Malheureusement, cela a ralenti à nouveau après environ une journée.

Voici les étapes, au cas où elles seraient utiles à d’autres personnes souhaitant augmenter le nombre maximal de connexions de PostgreSQL.

docker ps

Récupérez l’ID du conteneur, par exemple aaabbbccc123, et remplacez-le dans les commandes ci-dessous :

Copiez le fichier postgresql.conf depuis l’intérieur du conteneur Docker vers le système de fichiers local :

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

Modifiez la configuration :

nano /srv/postgresql.conf

Recopiez-le dans le conteneur Docker :

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

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

Supprimez le fichier résiduel (facultatif) :

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

Maintenant que vous savez ce que vous souhaitez faire, vous pouvez apporter ces modifications en ajoutant un bloc replace dans votre fichier app.yml.

Vous pouvez également exécuter ./launcher enter app et modifier les fichiers directement. Notez toutefois que lors de la reconstruction, ces modifications ne seront pas présentes dans le nouveau conteneur.