Ingeniería

Mejora la resistencia de nodos con el interruptor de circuito de memoria real

Quieres confiar en que Elasticsearch está gestionando tu tráfico de búsqueda con seguridad, incluso cuando se encuentra bajo carga significativa. Como Elasticsearch es un sistema distribuido, está diseñado desde cero para ser resistente a fallas de nodos individuales. De hecho, hemos implementado un nuevo algoritmo de coordinación de cluster ampliamente mejorado en Elasticsearch 7.0.0.

Además, los nodos individuales en Elasticsearch se crean teniendo en cuenta la resistencia. Si envías demasiadas solicitudes a un nodo o tus solicitudes son demasiado grandes, el nodo las rechazará. Lo último se logra mediante interruptores de circuito. Están colocados en ciertos puntos de la ruta de gestión de solicitudes, por ejemplo, cuando la solicitud de una red ingresa al nodo o antes de que se ejecute una agregación. La idea clave es evitar el error de falta de memoria OutOfMemoryError al estimar por adelantado si una solicitud presionará al nodo por encima de su límite configurado y luego rechazará la solicitud en lugar de fallar. Además de los interruptores de circuito para aspectos individuales como todas las solicitudes en proceso o el interruptor de circuito de datos de campo, Elasticsearch también tiene un “interruptor de circuito general” que proporciona una vista global de todos los interruptores de circuito. Esto permite que Elasticsearch rechace solicitudes que se encuentran dentro del presupuesto de cualquier interruptor de circuito individual, pero que fuercen al sistema sobre su límite total en todos los interruptores de circuito.

Es poco práctico registrar cada asignación, por lo que los interruptores de circuito solo pueden registrar la memoria que está reservada explícitamente y en ocasiones no es posible estimar el uso de memoria exacta por adelantado. Esto significa que los interruptores de circuito son solo un mecanismo de mejor esfuerzo y, mientras que proporcionan algo de resistencia contra la sobrecarga de un nodo, igualmente es posible que los nodos se mueran con un error OutOfMemoryError. Mientras más pequeña sea tu heap, más problemático será, ya que la sobrecarga relativa de la memoria sin registrar es más grande.

Construcción (y prueba) de un mejor interruptor de circuito

¿Qué sucedería si fuera posible conocer exactamente cuánta memoria usa un nodo cuando hacemos una reserva en un interruptor de circuito? En ese caso, podríamos rechazar solicitudes de acuerdo con el estado actual del sistema en ese momento con base en las reservas actuales en los interruptores de circuito. Hemos hecho exactamente eso con el nuevo interruptor de circuito de memoria real en Elasticsearch 7.0. Es una implementación alternativa del interruptor de circuito general que usa una [funcionalidad en la JVM para medir el uso actual de memoria] (https://docs.oracle.com/en/java/javase/11/docs/api/java.management/java/lang/management/MemoryPoolMXBean.html#getUsage()), en lugar de solo contar la memoria registrada actual. Mientras que esto es más costoso que solo agregar algunos números, medir el uso de la memoria sigue siendo una operación muy barata: en las microevaluaciones, hemos observado sobrecargas de entre 400 y 900 nanosegundos. Llevamos a cabo una variedad de experimentos para probar la efectividad del interruptor de circuito de memoria real en diferentes condiciones. En un escenario, ejecutamos una evaluación de indexación de texto completo contra un nodo configurado con solo 256 MB de heap. Mientras que las versiones anteriores de Elasticsearch no podían resistir esa carga de trabajo y casi inmediatamente se quedan sin memoria, el interruptor de circuito de memoria real resiste y Elasticsearch puede sostener la carga. Ten en cuenta que Elasticsearch mostrará una respuesta de error en tales casos y que depende de los clientes implementar la interrupción apropiada y volver a probar los mecanismos. Por supuesto, lo facilitamos siempre que ya estés usado uno de nuestros clientes de lenguaje oficial. Los clientes de .NET, Ruby, Python y Java ya implementan estas políticas de reintento y ofrecen extensiones para gestionar indexaciones masivas.

En otro experimento, ejecutamos una agregación que intencionalmente producía un número exageradamente alto de depósitos en un nodo con heap de 16 GB. De manera similar, las versiones anteriores de Elasticsearch se quedaban sin memoria, pero la agregación se ejecutaba por casi media hora hasta que sucedía el error. Con el interruptor de circuito de memoria real, el nodo ha provisto en su lugar una respuesta que dependía de si habíamos permitido resultados parciales después de un poco más de un minuto o después de casi veinte minutos. Como resultado de varios experimentos, hemos definido el valor predeterminado para el nuevo interruptor de circuito en 95 % del heap totalmente disponible. Esto significa que Elasticsearch permitirá usar un 95 % de heap hasta que se active el interruptor de circuito de memoria real.

Consideremos un ejemplo en que se envía una solicitud masiva que es lo suficientemente pequeña como para superar los otros controles, pero que activará el interruptor de circuito de memoria real porque forzaría al nodo sobre su límite. Este nodo se ejecuta con heap de 128 MB configurada, lo que significa que el límite del interruptor del circuito general de 95 % es 117.5 MB. Si se envía esta solicitud, el nodo responderá con un HTTP 429 con los detalles siguientes:

{
  '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
}

Podemos ver que el interruptor de circuito también indica que esta es una falla transitoria. Los clientes pueden usar este dato como pista para reintentar la solicitud después de un tiempo. Se decide si una excepción de interruptor de circuito es permanente o transitoria con base en la memoria reservada en todos los interruptores de circuito. Cada tipo de interruptor de circuito tiene una durabilidad asociada; si la mayoría de la memoria reservada la reservan interruptores de circuito que registran el uso de la memoria transitoria, el interruptor de circuito de memoria real la trata como una condición transitoria y de lo contrario como permanente.

Resumen

Mientras que todavía es posible en ciertos escenarios que un nodo Elasticsearch se quede sin memoria, el nuevo interruptor de circuito de memoria real en Elasticsearch mejora ampliamente la resistencia de los nodos individuales al ejercer contrapresión con base en el uso de memoria medida real en lugar de solo tener en cuenta la memoria registrada por los interruptores del circuito. En nuestros experimentos, Elasticsearch pudo soportar cargas en este momento que estaban fuera de alcance en versiones previas. También hará que los clusters de producción sean mucho más resistentes a los picos de carga de trabajo. Para probar el nuevo interruptor de circuito de memoria real, descarga el último lanzamiento beta de 7.0, pruébalo y envíanos tus comentarios.

_ Kiran Raja Bahadur SRK nos brindó la imagen al principio de la publicación en virtud de la licencia CC BY-NC-ND 2.0 ([fuente original] (https://www.flickr.com/photos/srkkiran/5881217931/))._