Pitchfork utilise-t-il trop de mémoire ?

Est-ce que quelqu’un d’autre constate le même problème ? C’est un forum de taille et de trafic assez modestes. Il y a quatre processus de travail pitchfork, et il semble que l’un d’entre eux traite la grande majorité des requêtes entrantes et utilise plus de mémoire que les autres. Il y a beaucoup d’espace d’échange (swap) utilisé, et en relançant délibérément pitchfork, cela diminue considérablement. J’en conclus donc que les processus pitchfork ont actuellement beaucoup de pages froides actuellement échangées vers le swap.

Cela se produit après 20 jours de fonctionnement, sur une machine équipée de 4 Go de RAM et de 4 Go de swap.

Il ne serait pas bon de se retrouver à court de swap. Il ne serait pas non plus bon de voir tout cet espace d’échange être réintégré en mémoire, ce qui pourrait se produire lorsque pitchfork effectuera finalement une collecte des ordures (garbage collect).

Est-ce que quelqu’un d’autre observe quelque chose de similaire ? N’hésitez pas à partager la configuration de votre machine !

# ps aux | sort -n -k 4 | egrep pitchf\|MEM |tail
root     2167652  0.0  0.0   5912  1792 pts/0    S+   20:07   0:00 grep -E --color=auto pitchf|MEM
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
1000       15094  0.0  0.2 497544 10988 ?        Sl   Jul28   0:17 pitchfork monitor - 
1000       15704  0.0  2.5 6799124 97836 ?       Sl   Jul28   1:00 pitchfork (gen:0) service - ready
1000       15097  0.0  4.8 6791252 189724 ?      Sl   Jul28   5:26 pitchfork (gen:0) mold - ready
1000       15959  0.0  8.1 6815508 319708 ?      Sl   Jul28  12:05 pitchfork (gen:0) worker[3] - requests: 6684, waiting
1000       15900  0.0  9.2 6864596 360696 ?      Sl   Jul28  17:43 pitchfork (gen:0) worker[2] - requests: 14756, waiting
1000       15795  0.1 10.3 6884756 405420 ?      Sl   Jul28  48:36 pitchfork (gen:0) worker[1] - requests: 97007, waiting
1000       15734  2.1 12.8 11299708 500120 ?     Sl   Jul28 637:50 pitchfork (gen:0) worker[0] - requests: 2229848, waiting

# free
               total        used        free      shared  buff/cache   available
Mem:         3904968     1874636       98840      678812     1931492     1160916
Swap:        4194288     1226600     2967688


# vmstat 5 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0 1226600 115820  65288 1866284    1    1    20    38    0    3  3  1 97  0  0
 0  0 1226600 113208  65296 1866296    0    0     0    14  411  478  1  1 98  0  0
 0  0 1226600 112956  65304 1866336    0    0     1    23  438  528  2  1 97  0  0
 0  0 1226600 112704  65308 1866360    0    0     1    31  518  603  5  1 94  0  0
 0  0 1226600 112704  65308 1866364    0    0     0    11  347  387  1  1 98  0  0

La sortie de vmstat indique qu’il n’y a actuellement aucune pression sur la mémoire - aucune activité de pagination. Ma préoccupation est qu’il y a un danger, et non que mon installation fonctionne mal actuellement.

cpu0

Pour finir sur une note plus sérieuse, celui qui fait tout le travail n’a que 100 Mo de RSS de plus que la moyenne ? Ça me semble normal, ou est-ce que je rate quelque chose ?

J’aimerais voir les statistiques d’un forum plus actif. Il semble que le nombre cumulé de requêtes soit important, et le mien est loin d’être très sollicité.

Oui, les différences de RSS sont relativement modestes, mais passer de 300 Mo à 500 Mo représente tout de même une différence significative en proportion.

Plus frappant est l’utilisation du swap. Avant le recyclage, j’observe :

# free
               total        used        free      shared  buff/cache   available
Mem:         3904968     1888140       77308      679004     1939520     1147220
Swap:        4194288     1226344     2967944

et après :

# free
               total        used        free      shared  buff/cache   available
Mem:         3904968     1486412     1422380      640744      996176     1587908
Swap:        4194288      309892     3884396

À mon avis, le recyclage a libéré environ 1,3 Go (différence entre la mémoire utilisée et le swap).

Je ne pense pas avoir vu autant de swap nécessaire en utilisant unicorn : d’où le titre.

Comme je l’ai dit, je serais intéressé de voir les chiffres d’autres instances.

Il est normal qu’un seul processus pitchfork supporte la majeure partie de la charge — les autres ne sont sollicités que lorsque le premier est occupé. Il est également normal que la mémoire augmente avec le temps. Bien qu’elle devrait finir par atteindre un état stable, une fois que tout est « réchauffé », comme les caches et le JIT Ruby.

Voici un graphique issu de l’un de nos clusters de production, sur une période de 7 jours. Il suit le processus ayant la plus forte consommation mémoire. Les lignes vertes en pointillés indiquent les déploiements (c’est-à-dire les redémarrages complets de pitchfork) :

On peut donc voir qu’il augmente après le démarrage initial, mais se stabilise ensuite juste en dessous de 1,4 Go. Ce cluster sert des centaines de sites, donc la comparaison en termes de chiffres n’est peut-être pas la plus pertinente… mais j’espère que la visualisation de la croissance est utile.

Regarder les valeurs RSS par processus ne donne pas toute l’histoire. Pitchfork est un « serveur web à fourchette » (forking web server). Il démarre donc le processus « moule » (mold), puis tous les travailleurs (workers) sont créés par fourchetage (fork) à partir de celui-ci, avec un modèle mémoire en écriture-copie (copy-on-write). Ainsi, bien que le RSS puisse afficher 500 Mo pour worker[0], environ 200 Mo de cette quantité sont partagés avec le moule et tous les autres travailleurs.

Dans un avenir proche, nous espérons pouvoir activer la fonctionnalité de « reforking » de Pitchfork. Elle tue périodiquement les travailleurs et les recrée par fourchetage à partir d’un travailleur déjà réchauffé, afin qu’ils partagent le maximum de mémoire possible au fil du temps. Il faudra toutefois effectuer certains travaux pour s’assurer que tout notre code est compatible avec ce type de modèle :crossed_fingers:

Belle visualisation, merci. Je ne dirais pas que c’est stabilisé, mais le taux de croissance n’est plus aussi élevé qu’avant.

Le reforking périodique semble être une excellente chose à introduire — si c’est possible, merci de le signaler ici une fois que ce sera en place.

Pouvez-vous en dire quelque chose sur les lignes vertes ? Je suis intéressé par le moment (s’il y en a un) où les pitchforks effectuent une collecte des ordures, ce qui la provoque et quel est son effet pendant qu’elle est en cours.

Les lignes vertes correspondent aux déploiements de mises à jour. En gros : ./launcher rebuild app.

Ruby (et JS, que nous exécutons au sein des processus Ruby via MiniRacer/v8) effectue une collecte des ordures en continu pendant son exécution, et en réponse aux signaux du système d’exploitation. Je ne pense donc pas qu’il y ait un grand avantage à faire quoi que ce soit manuellement.

Merci. Je serais curieux de voir comment les choses évoluent à long terme, ce qui ne sera pas évident sur un serveur mis à jour relativement souvent. Dans mon cas, j’ai attendu 20 jours, puis j’ai choisi de relancer le serveur à l’aide de pitchfork, ce qui m’a donné certaines indications, mais qui a également remis le compteur à zéro.