Technique

Améliorer la résilience des nœuds grâce au disjoncteur à mémoire réelle

Vous devez être sûr qu'Elasticsearch traite votre trafic de recherche de manière fiable, quelle que soit la charge de votre site. Rien de plus normal. D'autant qu'Elasticsearch étant un système distribué, il est conçu de A à Z pour être résilient aux défaillances des nœuds individuels. Mieux : nous avons en fait implémenté dans Elasticsearch 7.0.0 un nouvel algorithme de coordination de cluster considérablement amélioré.

De plus, nous avons conçu les nœuds individuels Elasticsearch sans perdre de vue un seul instant la résilience. Si un nœud reçoit des requêtes trop nombreuses ou trop volumineuses, il les rejette. C'est le rôle des disjoncteurs. Ils sont placés à certains points du chemin de traitement de la requête (par exemple, à l'arrivée d'une requête réseau sur le nœud ou avant l'exécution d'une agrégation). L'idée est d'éviter une erreur de dépassement de mémoire OutOfMemoryError. Comment ? Grâce à une évaluation en amont qui permettra de savoir si une requête amènera le nœud à dépasser la limite configurée. Si c'est le cas, le nœud rejette la requête au lieu de s'interrompre. Outre les disjoncteurs gérant les aspects individuels comme les requêtes en cours d'exécution ou le disjoncteur dédié aux données de champ, Elasticsearch intègre aussi un "disjoncteur parent" qui permet d'avoir une vision d'ensemble de tous les disjoncteurs. Résultat, Elasticsearch peut rejeter les requêtes gérables par un disjoncteur donné, mais qui entraîneraient un dépassement de la limite totale autorisée pour l'ensemble des disjoncteurs du système.

Comme il est difficile d'effectuer le suivi de chaque allocation de mémoire, les disjoncteurs ne se concentrent que sur la mémoire explicitement réservée, et il s'avère parfois impossible d'évaluer l'utilisation exacte de la mémoire à l'avance. Autrement dit, les disjoncteurs ne sont qu'un mécanisme qui vise le meilleur résultat possible, et bien qu'ils assurent une certaine résilience pour éviter la surcharge d'un nœud, il peut arriver qu'un nœud s'interrompe en raison d'une erreur OutOfMemoryError. Plus la mémoire dont vous disposez est limitée, plus cela s'avère problématique, puisque la surcharge relative de la mémoire non suivie est plus importante.

Implémenter un disjoncteur plus performant (et le tester)

Et s'il était possible de connaître la quantité de mémoire exacte qu'utilise un nœud lorsqu'on réserve de la mémoire dans un disjoncteur ? On pourrait alors rejeter les requêtes en fonction de l'état réel du système à ce moment-là, plutôt qu'en fonction d'une estimation des réservations en cours sur l'ensemble des disjoncteurs. C'est exactement ce que nous avons fait avec le nouveau disjoncteur à mémoire réelle que nous avons intégré à Elasticsearch 7.0. Il s'agit d'une autre implémentation du disjoncteur parent, qui utilise une fonctionnalité de la machine virtuelle Java (JVM) pour évaluer l'utilisation actuelle de la mémoire au lieu de ne tenir compte que la mémoire qui fait actuellement l'objet d'un suivi. Bien que cette solution soit plus onéreuse qu'un simple ajout de mémoire, l'évaluation de l'utilisation de la mémoire reste une opération très économique : ainsi, nos microbenchmarks on révélé des surcharges comprises entre 400 et 900 nanosecondes. Pour tester l'efficacité du disjoncteur à mémoire réelle dans différentes situations, nous avons réalisé diverses expériences. Dans l'un de nos scénarios, nous avons exécuté un benchmark d'indexation full text avec un nœud qui ne disposait que de 256 Mo de mémoire. Alors que les précédentes versions d'Elasticsearch ne pouvaient soutenir cette charge de travail et se retrouvaient quasi immédiatement à court de mémoire, le disjoncteur à mémoire réelle rejette maintenant les requêtes, ce qui permet à Elasticsearch de soutenir la charge. Remarque : Dans de tels cas, Elasticsearch retourne une erreur. Il appartient donc aux clients d'implémenter les mécanismes d'interruption et de nouvelle tentative appropriés. Bien sûr, nous veillons à vous faciliter la tâche. Il suffit que vous utilisiez déjà les clients adaptés à l'un de nos quatre langages officiels. Les clients.NET, Ruby, Python et Java intègrent déjà ces règles de nouvelle tentative et offrent également des extensions permettant de gérer l'indexation groupée.

Dans une autre expérience, nous avons exécuté une agrégation qui avait intentionnellement généré un nombre irréaliste de buckets sur un nœud disposant de 16 Go de mémoire. Ici encore, les précédentes versions d'Elasticsearch ne disposaient plus de mémoire suffisante. Mais nous avons réussi à exécuter l'agrégation pendant près d'une demi-heure avant qu'une erreur ne se produise. Avec le disjoncteur à mémoire réelle, le nœud a répondu, selon que nous ayons ou non autorisé des résultats partiels au bout d'un peu plus d'une minute ou au bout d'environ 20 minutes. Suite à de nombreuses expériences, nous avons décidé de définir la valeur par défaut du nouveau disjoncteur sur 95 % de la mémoire totale disponible. Autrement dit, Elasticsearch vous autorise à utiliser jusqu'à 95 % de la mémoire. Après quoi, le disjoncteur à mémoire réelle se déclenche.

Prenons un exemple : imaginons que vous envoyez une requête groupée de taille assez raisonnable pour satisfaire à tous les autres contrôles, mais qui déclenche le disjoncteur à mémoire réelle, car elle entraînerait le dépassement des limites du nœud. Ce nœud dispose de 128 Mo de mémoire configurée, ce qui signifie que la limite de 95 % du disjoncteur parent est égale à 117,5 Mo. Si cette requête est envoyée, le nœud retourne une erreur HTTP 429 qui se présente comme suit :

{
  'error': {
    'type': 'circuit_breaking_exception',
    'reason': '[parent] Data too large, data for [<http_request>] would be [123848638/118.1mb], which is larger than the limit of [123273216/117.5mb], real usage: [120182112/114.6mb], new bytes reserved: [3666526/3.4mb]',
    'bytes_wanted': 123848638,
    'bytes_limit': 123273216,
    'durability': 'TRANSIENT'
  },
  'status': 429
}

Nous voyons que le disjoncteur indique également qu'il s'agit d'un échec temporaire, ce qui permet aux clients de renouveler la requête ultérieurement. Le caractère permanent ou temporaire d'une exception de disjoncteur dépend de la mémoire réservée sur l'ensemble des disjoncteurs. Chaque type de disjoncteur est associé à une durabilité. Si la majeure partie de la mémoire réservée l'est par des disjoncteurs qui effectuent le suivi de l'utilisation de la mémoire temporaire, le disjoncteur à mémoire réelle traite cela comme une condition temporaire, et dans le cas contraire, comme une condition permanente.

Pour conclure

Bien que dans certains cas, un nœud Elasticsearch puisse ne plus disposer de mémoire suffisante, le nouveau disjoncteur à mémoire réelle intégré à Elasticsearch renforce considérablement la résilience des nœuds individuels. Il permet de réguler la charge en fonction de l'utilisation de mémoire réellement évaluée, au lieu de ne tenir compte que de la mémoire dont les disjoncteurs assurent le suivi. Dans les différentes expériences que nous avons menées, Elasticsearch peut maintenant soutenir des charges de travail de loin supérieures à celles des précédentes versions, tout en assurant à vos clusters de production une bien meilleure résilience aux pics de charge. Envie d'essayer le nouveau disjoncteur à mémoire réelle ? Téléchargez la dernière version bêta 7.0, lancez-vous et n'hésitez pas à nous dire ce que vous en pensez.

L'image affichée en haut de cet article est fournie par Kiran Raja Bahadur SRK sous licence Creative Commons CC BY-NC-ND 2.0 (source originale).