Sobre el tema del rendimiento de versiones antiguas y nuevas de un nodo

De vez en cuando, los desarrolladores que admiten aplicaciones de nodos antiguos tienen dudas sobre la necesidad de transferir la aplicación de versiones antiguas a nuevos nodos. El argumento principal a favor de permanecer en las antiguas es "No uso nuevas funciones, pero su implementación ciertamente retrasará el procesamiento en su conjunto". Además, la situación puede complicarse enormemente por la presencia de dependencias en bibliotecas que ya no son compatibles con nuevos nodos, y un cambio en la versión de un nodo se traduce automáticamente en un procesamiento significativo de la arquitectura de la aplicación. Mi artículo, espero, les ayudará a decidir sobre este tema.

Antes de comenzar, le recuerdo el principio básico del negocio de TI: ¡ FUNCIONA, NO TOQUE!

Si su aplicación funciona como debería, si hace frente completamente a las tareas y no hay una necesidad urgente de procesarla, es mejor dejar todo como está. El reciclaje puede ser un proceso muy doloroso y prolongado. Y como resultado, no obtendrá ningún beneficio tangible, aparte de una sensación de satisfacción estética, y en este momento su negocio sufrirá.

Mi artículo es para aquellos que aún lo necesitan. Describiré mi situación reciente, hablaré sobre las dificultades encontradas para resolverla y daré los resultados que finalmente recibí.

Estoy desarrollando un sistema para recopilar y procesar registros de otras aplicaciones. Cargas de trabajo establecidas: cientos de miles de registros por minuto por componente. El sistema escalaba bien horizontalmente, tenía una arquitectura modular, funcionaba con éxito durante más de un año y hacía frente a sus funciones en su conjunto. La versión utilizada del nodo es 0.10

A que te enfrentas

Naturalmente, no es falta de funcionalidad. Las nuevas características de es6 ni siquiera se consideraron un argumento. Por supuesto, hacen la vida un poco más agradable, pero nada más. Para cualquiera de nuestras tareas, la funcionalidad del antiguo nodo era suficiente. Surgieron problemas en el lugar más inesperado a medida que la funcionalidad se volvió más compleja.

Problema uno:

Uno de los componentes con cargas entrantes máximas y consumo de memoria de menos de 5 GB de repente comenzó a disminuir infernalmente. El problema no ocurrió cada vez, espontáneamente, generalmente cerca del final del pico. Si la aplicación no cayó en los tiempos de espera, el rendimiento se recuperó gradualmente en media hora o una hora. Reinicio curado de inmediato.

Después del proceso de "depuración profunda", resultó que todo el proceso comienza a ralentizarse, incluso las operaciones sincrónicas, por lo que se concluyó que el recolector de basura en sí estaba "empeorando". Qué hacer al respecto no estaba completamente claro. La única solución fue buscar en el historial de cambios en nuestro código durante varios meses y deshacer funcionalidades importantes. Por otro lado, el problema no ocurría con frecuencia, no todos los días, y el reinicio manual del sistema también parecía ser una solución totalmente aceptable (manual significa un script basado en la señal del detector de retraso). Estábamos más inclinados a la segunda opción. Y si no fuera por el segundo problema, lo más probable es que se hubiera implementado.

El segundo problema:

Otro de nuestros componentes estaba comiendo mucha memoria. Dado que este componente era en realidad un caché, su "codicia" era en principio explicable. Hasta el momento en que se requería limitarlo desde arriba a un volumen estrictamente especificado. Y luego resultó que el componente estaba ganando memoria y no tenía prisa por regalarlo. E incluso trabajando a una velocidad casi inactiva. Es decir, en el momento de la carga máxima, el administrador de memoria de nodo seleccionó la memoria máxima, e incluso con un margen, y luego la mantuvo hasta el final del siglo (reiniciando el componente, por ejemplo). Mencionaré de inmediato que, naturalmente, la opción de fuga ha sido examinada y verificada. Por desgracia, no hubo fugas, y de nuevo estábamos en un callejón sin salida.

Traté de preguntar en varios lugares de Internet cómo realizar la gestión de memoria de un nodo y cómo resolver nuestra situación. Pero en respuesta, recibí solo mucha negatividad con respecto al uso del nodo 0.10. En general, fue esta negatividad la que me llevó a la tarea de cambiar a las últimas versiones del nodo.

¿Qué estaba frenando?

1. Miedo a la pérdida de productividad.

Aquellos que trabajaron en Python recuerdan que la transición de la línea 2.xa 3.x estuvo acompañada por una pérdida sustancial de productividad. No sé cómo están las cosas en Python ahora, tal vez la situación ha mejorado. Pero es bastante lógico esperar que después de agregar todas estas nuevas características a es6, el nodo también podría hundirse sólidamente. Intenté googlear algunos puntos de referencia para comparar nuevos nodos con los antiguos, no encontré nada sensato.

2. Rendimiento JSON.parse

Como trabajamos con registros, JSON.parse ocupa la mayor parte del procesador. En un momento, lo comparamos en diferentes versiones del nodo y obtuvimos una caída de rendimiento de aproximadamente el 30%. 4.x nodo se comparó con 0.10 y 0.12.

De hecho, estas razones no fueron decisivas, ya que el procesador no era un cuello de botella. Además, para tales tareas hay escala horizontal.

3. Dependencias del paquete

Pero esto fue un obstáculo. Nuestro componente central más complejo utilizó el paquete libmysqlclient, que solo funciona bajo el nodo 0.10. Peor aún, sus llamadas síncronas. Es decir, era imposible simplemente cambiar el controlador mysql, reemplazando una llamada por otra, sin procesar profundamente la arquitectura del componente de procesamiento parcialmente sincrónico a completamente asincrónico. Además, la parte más difícil de esta tarea no fue tanto el procesamiento en sí como la forma de justificar la gestión de su necesidad y mostrar exactamente qué le daría al proyecto :)

Lo que dio

Como resultado, todavía nos mudamos del nodo 0.10 al último lts 8.11.x

1. Sin caída de rendimiento. Por el contrario, recibimos un aumento del orden del 10-15%
2. Consumo de memoria significativamente mejorado, aproximadamente 30-50%, dependiendo del componente
3. Los problemas "irresolubles" expresados ​​anteriormente se resolvieron por sí mismos
4. ¡Finalmente tuvimos la oportunidad de usar nuevas funciones de es6! Aunque por costumbre, todavía no los usamos)))

Source: https://habr.com/ru/post/es413673/


All Articles