Los errores notorios y cómo evitarlos en el ejemplo de ClickHouse

Si está escribiendo código, prepárese para los problemas. Definitivamente lo serán, y se debe esperar de todos lados: desde su código y compilador, desde el sistema operativo y el hardware, y los usuarios a veces arrojan "sorpresas". Si escalaste el clúster a escalas cósmicas, entonces espera errores de "espacio". Especialmente cuando se trata de datos del tráfico de Internet.


Alexey Milovidov (o6CuFl2Q) hablará sobre los problemas más ridículos, desalentadores y desesperados de su experiencia en el desarrollo y soporte de ClickHouse. Veamos cómo tuvieron que depurarse y qué medidas deberían tomar los desarrolladores desde el principio, para que hubiera menos problemas.

Errores notorios


Si escribió algún código, prepárese para los problemas de inmediato.

Errores en el código. Serán requeridos. Pero supongamos que escribió el código perfecto, compilado, pero aparecerán errores en el compilador y el código no funcionará correctamente. Arreglamos el compilador, todo compilado, ejecútelo. Pero (inesperadamente) todo funciona incorrectamente, porque también hay errores en el núcleo del sistema operativo .

Si no hay errores en el sistema operativo, inevitablemente, estarán en el hardware . Incluso si escribió el código perfecto que funciona perfectamente en el hardware perfecto, aún encontrará problemas, por ejemplo, errores de configuración . Parece que hiciste todo bien, pero alguien cometió un error en el archivo de configuración y no todo vuelve a funcionar.

Cuando se hayan corregido todos los errores, los usuarios lo terminarán, porque constantemente usan su código "incorrectamente". Pero el problema definitivamente no está en los usuarios, sino en el código: escribiste algo que es difícil de usar .

Veamos estos errores con algunos ejemplos.

Errores de configuración


Eliminación de datos . El primer caso de la práctica. Afortunadamente, no es mío y no Yandex, no te preocupes.

Introductorio primero. La arquitectura de reducción de mapas de un clúster (como Hadoop) consta de varios servidores de datos (nodos de datos) que almacenan datos, y uno o más servidores maestros que conocen la ubicación de todos los datos en los servidores.

Los nodos de datos conocen la dirección del maestro y se conectan a ella. El asistente monitorea dónde y qué datos deben ubicarse, y da diferentes comandos a los nodos de datos: "Descargue los datos X, debe tener los datos Y y elimine los datos Z". ¿Qué puede salir mal?

Cuando se cargó un nuevo archivo de configuración a todos los nodos de datos, se conectaron por error al maestro desde otro clúster, y no al suyo. El maestro examinó los datos sobre los cuales se informaron los nodos de datos, decidió que los datos eran incorrectos y deberían eliminarse. El problema se notó cuando se borró la mitad de los datos.


Los errores más épicos son aquellos que conducen a la eliminación accidental de datos.
Evitar esto es muy simple.

No elimine datos . Por ejemplo, reservar en un directorio separado o eliminar con retraso. Primero, transferimos para que no sean visibles para el usuario, y si descubre que algo ha desaparecido en unos días, lo devolveremos.

No elimine datos inesperados si se desconoce la causa . Limite programáticamente el inicio de la eliminación de datos desconocidos: inesperados, con nombres extraños o si hay demasiados. El administrador notará que el servidor no se inicia y escribe algún mensaje, y lo comprenderá.

Si el programa realiza acciones destructivas, aísle las pruebas y la producción a nivel de red(iptables) Por ejemplo, eliminar archivos o enviar correos electrónicos es una acción destructiva porque “devorará” la atención de alguien. Ponles un umbral: se pueden enviar cien cartas, y por mil pon una casilla de seguridad, que se establece antes de que ocurra algo terrible.

Configuraciones . El segundo ejemplo ya es de mi práctica.

Una buena compañía de alguna manera tenía un extraño clúster de ClickHouse. Lo extraño era que las réplicas no se sincronizaban. Cuando se reinició el servidor, no se inició y apareció un mensaje de que todos los datos eran incorrectos: "Hay muchos datos inesperados, no comenzaré. Debemos establecer la bandera force_restore_datay resolverlo ".

Nadie pudo resolverlo en la empresa, solo pusieron la bandera. Al mismo tiempo, la mitad de los datos desaparecieron en alguna parte, lo que resultó en gráficos con lagunas. Los desarrolladores se volvieron hacia mí, pensé que algo interesante estaba sucediendo y decidí investigar. Cuando amaneció unas horas más tarde y los pájaros comenzaron a cantar fuera de la ventana, me di cuenta de que no entendía nada.

El servidor ClickHouse utiliza el servicio ZooKeeper para la coordinación. ClickHouse almacena datos y ZooKeeper determina en qué servidores deben estar los datos: almacena metadatos sobre qué datos deben estar en qué réplica. ZooKeeper también es un clúster: se replica de acuerdo con un algoritmo de consenso distribuido muy bueno, con una consistencia estricta.

Como regla general, ZooKeeper tiene 3 máquinas, a veces 5. En la configuración ClickHouse, todas las máquinas se indican a la vez, la conexión se establece con una máquina aleatoria, interactúa con ella y este servidor replica todas las solicitudes.

¿Que pasó? La compañía tenía tres servidores ZooKeeper. Pero no funcionaron como un grupo de tres nodos , sino como tres nodos independientes : tres grupos de un nodo. One ClickHouse se conecta a un servidor y escribe datos. Las réplicas quieren descargar estos datos, pero no se encuentran en ninguna parte. Al reiniciar, el servidor se conecta a otro ZooKeeper: ve que los datos con los que trabajó antes son superfluos, deben posponerse en algún lugar. No los elimina, sino que los transfiere a un directorio separado; en ClickHouse, los datos no se eliminan tan fácilmente.

Decido arreglar la configuración de ZooKeeper. Renombro todos los datos y solicito ATTACHpartes de los datos del directorio detached/unexpeted_*.

Como resultado, se restauraron todos los datos, las réplicas se sincronizaron, no hubo pérdidas, los gráficos fueron continuos. La compañía está satisfecha, agradecida, como si ya hubieran olvidado cómo todo había funcionado mal antes.

Estos fueron simples errores de configuración. Más errores estarán en el código.

Errores en el código


Escribimos código en C ++. Esto significa que ya tenemos problemas.
El siguiente ejemplo es un error real de producción en el clúster Yandex.Metrica (2015), una consecuencia del código C ++. El error fue que a veces el usuario en lugar de responder a la solicitud recibió un mensaje de error:

  • "La suma de verificación no coincide, los datos dañados" - la suma de verificación no coincide, los datos están rotos - ¡da miedo!
  • "LRUCache se volvió inconsistente. Debe haber un error en él ”: el caché se volvió inconsistente, probablemente un error en él.

El código que escribimos se informa de que hay un error allí.

"La suma de comprobación no coincide, los datos dañados ". Las sumas de verificación de los bloques de datos comprimidos se verifican antes de descomprimirse. Por lo general, este error aparece cuando los datos se rompen en el sistema de archivos. Por varias razones, algunos archivos resultan ser basura cuando se reinicia el servidor.

Pero aquí hay otro caso: leí el archivo manualmente, la suma de verificación coincide, no hay error. Una vez aparecido, el error se reproduce de manera estable a pedido repetido. Cuando el servidor se reinicia, el error desaparece por un tiempo y luego vuelve a aparecer de manera estable.

¿Quizás el asunto está en la RAM? Una situación típica es cuando los bits están latiendo en ella. Miro en dmesg(kern.log), pero no hay excepciones de comprobación de máquina: generalmente escriben cuando algo está mal con la RAM. Si el servidor hubiera superado la RAM, no solo mi programa funcionaría incorrectamente, sino que todos los demás generarían errores al azar. Sin embargo, no hay otras manifestaciones del error.

"LRUCache se volvió inconsistente. Debe haber un error en él ". Este es un claro error en el código, y estamos escribiendo en C ++, ¿quizás acceso a la memoria? Pero las pruebas bajo AddressSanitizer, ThreadSanitizer, MemorySanitizer, UndefinedBehaviorSanitizer en CI no muestran nada.

¿Quizás algunos casos de prueba no están cubiertos? Recopilo el servidor con AddressSanitizer, lo ejecuto en producción, no detecta nada. Durante algún tiempo, el error se borra restableciendo alguna caché de marcas (caché de sobres).

Una de las reglas de programación dice: si no está claro cuál es el error, mire de cerca el código, esperando encontrar algo allí. Lo hice, encontré un error, lo arreglé, no ayudó. Miro a otro lugar en el código, también hay un error. Corregido, nuevamente no ayudó. Arreglé algunos más, el código mejoró, ¡pero el error aún no desapareció!

Porque. Intentar encontrar un patrón por servidor, por tiempo, por la naturaleza de la carga, nada ayuda. Luego se dio cuenta de que el problema se manifiesta solo en uno de los grupos, y nunca en los otros. El error no se reproduce con tanta frecuencia, pero siempre aparece en un clúster después de un reinicio, y todo está limpio en el otro.

Resultó que la razón es que en el clúster "problemático" utilizaron una nueva característica: diccionarios de caché. Utilizan el asignador de memoria escrito a mano ArenaWithFreeLists . No solo escribimos en C ++, sino que también vimos algún tipo de asignadores personalizados: nos condenamos a los problemas dos veces.

ArenaWithFreeLists es una parte de la memoria en la que la memoria se asigna consecutivamente en tamaños divisibles por dos: 16, 32, 64 bytes. Si se libera memoria, forman una lista individualmente vinculada de bloques gratuitos de FreeLists.

Miremos el código.

class ArenaWithFreeLists
{
    Block * free_lists[16] {};
    static auto sizeToPreviousPowerOfTwo(size_t size)
    {
        return _bit_scan_reverse(size - 1);
    }

    char * alloc(size_t size)
    {
        const auto list_idx = findFreeListIndex(size);
        free_lists[list_idx] ->...
    }
}

Esto utiliza la función de _bit_scan_reversesubrayado al principio.
Hay una regla no escrita: "Si una función tiene un guión bajo al principio, lea la documentación una vez, y si hay dos, léala dos veces".
Escuchamos y leemos la documentación: “int _bit_scan_reverse (int a). Establezca dst en el índice del bit de ajuste más alto en un entero de 32 bits a. Si no se establecen bits en a, entonces dst no está definido ". Parece que hemos encontrado un problema.

En C ++, esta situación se considera imposible para el compilador. El compilador puede usar un comportamiento indefinido, esta "imposibilidad", como una suposición para optimizar el código.

El compilador no hace nada malo: honestamente genera instrucciones de ensamblaje bsr %edi, %eax. Pero, si el operando es cero, la instrucción tiene bsrun comportamiento indefinido no a nivel de C ++, sino a nivel de CPU. Si el registro de origen es cero, entonces el registro de destino no cambia: había algo de basura en la entrada, esta basura también permanecerá en la salida.

El resultado depende de dónde el compilador coloca esta instrucción. A veces, una función con esta instrucción está en línea, a veces no. En el segundo caso, habrá algo como este código:

bsrl %edi, %eax
retq

Luego miré un ejemplo de código similar en mi binario usando objdump.



Según los resultados, veo que a veces el registro de origen y el registro de destino son iguales. Si hubo cero, entonces el resultado también será cero: todo está bien. Pero a veces los registros son diferentes, y el resultado será basura.

¿Cómo se manifiesta este error?

  • Usamos basura como índice en la matriz FreeLists. En lugar de una matriz, vamos a alguna dirección distante y obtenemos acceso a la memoria.
  • Somos afortunados, casi todas las direcciones cercanas están llenas de datos de la caché; la estropeamos. El caché contiene compensaciones de archivos.
  • Leemos archivos en el desplazamiento incorrecto. Del desplazamiento incorrecto, obtenemos la suma del cheque. Pero no hay una suma de verificación, sino algo más: esta suma de verificación no coincidirá con los siguientes datos.
  • Recibimos el error "La suma de comprobación no coincide, los datos dañados".

Afortunadamente, no los datos están dañados, sino solo el caché en la RAM. Nos informaron de inmediato sobre el error, porque verificamos y sumamos los datos. El error fue corregido el 27 de diciembre de 2015 y fue a celebrar.

Como puede ver, el código incorrecto puede al menos repararse. Pero, ¿cómo solucionar errores en el hardware?

Bichos en hierro


Estos ni siquiera son errores, sino leyes físicas, efectos inevitables. Según las leyes físicas, el hierro es inevitablemente defectuoso.

Escritura no atómica en RAID . Por ejemplo, creamos RAID1. Se compone de dos discos duros. Esto significa que un servidor es un sistema distribuido: los datos se escriben en un disco duro y en otro. Pero, ¿qué pasa si los datos se escriben en un disco y se pierde energía mientras se graba en el segundo? Los datos en una matriz RAID1 no serán consistentes. No podremos entender qué datos son correctos, porque leeremos un byte u otro.

Puede lidiar con esto colocando el registro. Por ejemplo, en ZFS este problema está resuelto, pero más sobre eso más adelante.

putrefacción de bits en HDD y SSD. Los bits en los discos duros y en los SSD pueden salir mal así como así. Los SSD modernos, especialmente aquellos con celdas de varios niveles, están diseñados para garantizar que las celdas se deterioren constantemente. Los códigos de corrección de errores ayudan, pero a veces las células se deterioran tanto y tanto que incluso esto no se guarda. Se obtienen errores no detectados.

bit voltea en RAM (pero ¿qué pasa con ECC?). En la RAM de los servidores, los bits también están dañados. También tiene códigos de corrección de errores. Cuando se producen errores, generalmente son visibles desde los mensajes en el registro del kernel de Linux en dmesg. Cuando haya muchos errores, veremos algo como: "Se han solucionado N millones de errores con memoria". Pero los bits individuales no se notarán, y seguramente algo tendrá errores.

bit voltea a nivel de CPU y red . Hay errores a nivel de CPU, en cachés de CPU y, por supuesto, al transmitir datos a través de una red.

¿Cómo suelen manifestarse los errores de hierro? El ticket " Un znode con formato incorrecto impide que ClickHouse se inicie " llega a GitHub : los datos en el nodo ZooKeeper están dañados.

En ZooKeeper, usualmente escribimos algunos metadatos en texto plano. Hay algo mal con él: " réplica " está escrita muy extraña.



Raramente sucede que debido a un error en el código, un bit cambia. Por supuesto, podemos escribir este código: tomamos el filtro Bloom, cambiamos el bit en ciertas direcciones, calculamos las direcciones incorrectamente, cambiamos el bit incorrecto, cae en algunos datos. Eso es todo, ahora en ClickHouse no es " replica" , sino " repli b a " y en él todos los datos están equivocados. Pero generalmente, un cambio en un bit es un síntoma de problemas de hierro.

Quizás conoces el ejemplo de bitsquatting. Artyom Dinaburg hizo un experimento : hay dominios en Internet que tienen mucho tráfico, aunque los usuarios no van a estos dominios por su cuenta. Por ejemplo, dicho dominio FB-CDN.com es un CDN de Facebook.

Artyom registró un dominio similar (y muchos otros), pero cambió un poco. Por ejemplo, FA-CDN.com en lugar de FB-CDN.com. El dominio no se publicó en ninguna parte, pero el tráfico llegó a él. En ocasiones, el host FB-CDN se escribió en los encabezados HTTP y la solicitud se envió a otro host debido a errores en la RAM de los dispositivos de los usuarios. La RAM con corrección de errores no siempre ayuda. A veces incluso interfiere y conduce a vulnerabilidades (lea sobre Rowhammer, ECCploit, RAMBleed).
Conclusión: siempre verifique y sume los datos usted mismo.
Al escribir en el sistema de archivos, verifique la suma sin fallar. Al transmitir a través de la red, compruebe también el resumen, no espere que haya sumas de comprobación allí.

Más errores! ..


Métrica del clúster de producción . Los usuarios en respuesta a una solicitud a veces obtienen una excepción: "La suma de verificación no coincide: datos dañados": la suma de verificación no es correcta, los datos están dañados.



El mensaje de error muestra datos detallados: qué cantidad de cheque se esperaba, qué cantidad de cheque está realmente en estos datos, el tamaño del bloque para el que verificamos el monto del cheque y el contexto de excepción.

Cuando recibimos el paquete a través de la red de algún servidor, apareció una excepción: parece familiar. Quizás de nuevo pasando por la memoria, la condición de la carrera u otra cosa.

Esta excepción apareció en 2015. El error fue corregido, ya no apareció. En febrero de 2019, apareció de nuevo de repente. En este momento estaba en una de las conferencias, mis colegas trataron el problema. El error se reprodujo varias veces al día entre 1000 servidores con ClickHouse: no es posible recopilar estadísticas en un servidor y luego en otro. Al mismo tiempo, no hubo nuevos lanzamientos en este momento. No funcionó y resolvió el problema, pero después de unos días el error desapareció.

Se olvidaron del error y, el 15 de mayo de 2019, se repitió. Continuamos tratando con ella. Lo primero que hice fue mirar todos los registros y gráficos disponibles. Los estudió todo el día, no entendió nada, no encontró ningún patrón. Si el problema no se puede reproducir, la única opción es recopilar todos los casos, buscar patronesy adicciones Quizás el kernel de Linux no funciona correctamente con el procesador, guarda o carga incorrectamente cualquier registro.

Hipótesis y patrones


7 de los 9 servidores con E5-2683 v4 fallaron. Pero del error propenso, solo aproximadamente la mitad del E5-2683 v4 es una hipótesis vacía.

Los errores generalmente no se repiten . Además del clúster mtauxyz, donde de hecho hay datos dañados (datos incorrectos en el disco). Este es otro caso, rechazamos la hipótesis.

El error no depende del kernel de Linux : comprobado en diferentes servidores, no encontró nada. Nada interesante en kern.log, machine check exceptionsin mensajes . En gráficos de red, incluidos retransmisores, CPU, IO, Red, nada interesante. Todos los adaptadores de red en los servidores en los que se producen errores y no aparecen son los mismos.

No hay patrones . ¿Qué hacer? Continúa buscando patrones. Segundo intento.

Miro los servidores de tiempo de actividad:el tiempo de actividad es alto, los servidores funcionan de manera estable , por defecto y algo así no lo es. Siempre me alegro cuando veo que el programa se bloqueó con segfault, al menos se bloqueó. Peor aún, cuando hay un error, estropea algo, pero nadie lo nota.

Los errores se agrupan por día y ocurren dentro de un par de días. En unos 2 días, aparecen más, en algunos menos, luego de nuevo más: no es posible determinar con precisión el momento en que se producen los errores.

Algunos errores coinciden con los paquetes y el monto del cheque que esperábamos. La mayoría de los errores tienen solo dos opciones de paquete. Tuve suerte porque en el mensaje de error agregamos el valor de la suma de verificación, lo que ayudó a compilar estadísticas.

Sin patrones de servidorde donde leemos los datos. El tamaño del bloque comprimido que verificamos suma es inferior a un kilobyte. Miré los tamaños de paquete en HEX. Esto no fue útil para mí: la representación binaria de los tamaños de paquetes y las sumas de verificación no se nota.

No solucioné el error. Estaba buscando patrones nuevamente. Tercer intento.

Por alguna razón, el error aparece solo en uno de los grupos : en las terceras réplicas en Vladimir DC (nos gusta llamar a los centros de datos por nombres de ciudades). En febrero de 2019, también apareció un error en Vladimirs DC, pero en una versión diferente de ClickHouse. Este es otro argumento en contra de la hipótesis de que escribimos el código incorrecto. Ya lo reescribimos tres veces de febrero a mayo; el error probablemente no está en el código .

Todos los errores al leer paquetes a través de la red:while receiving packet from. El paquete en el que ocurrió el error depende de la estructura de la solicitud. Para solicitudes que difieren en estructura, un error en diferentes sumas de verificación. Pero en las solicitudes donde el error está en la misma suma de verificación, las constantes difieren.

Todas las solicitudes con un error, excepto una, son GLOBAL JOIN. Pero para comparar, hay una solicitud inusualmente simple, y el tamaño del bloque comprimido es solo de 75 bytes.

SELECT max(ReceiveTimestamp) FROM tracking_events_all 
WHERE APIKey = 1111 AND (OperatingSystem IN ('android', 'ios'))

Rechazamos la hipótesis de la influencia GLOBAL JOIN.

Lo más interesante es que los servidores afectados se agrupan en rangos por sus nombres :
mtxxxlog01-{39..44 57..58 64 68..71 73..74 76}-3.

Estaba cansado y desesperado, comencé a buscar patrones completamente delirantes. Es bueno que no haya podido depurar el código usando la numerología. Pero todavía había pistas.

  • Los grupos de servidores problemáticos son los mismos que en febrero.
  • Los servidores problemáticos se encuentran en ciertas partes del centro de datos. En DC Vladimir hay las llamadas líneas, sus diferentes partes: VLA-02, VLA-03, VLA-04. Los errores están claramente agrupados: en algunas colas es bueno (VLA-02), en otros problemas (VLA-03, VLA-04).

Depuración de mecanografía


Solo quedaba depurar usando el método de "lanza". Esto significa formar la hipótesis "¿Qué sucede si intentas hacerlo?" y recopilar datos. Por ejemplo, encontré una query_logconsulta simple con un error en la tabla para la cual el tamaño del paquete es size of compressed blockmuy pequeño (= 107).



Tomé la solicitud, la copié y la ejecuté manualmente usando clickhouse-local.

strace -f -e trace=network -s 1000 -x \
clickhouse-local --query "
    SELECT uniqIf(DeviceIDHash, SessionType = 0)
    FROM remote('127.0.0.{2,3}', mobile.generic_events)
    WHERE StartDate = '2019-02-07' AND APIKey IN (616988,711663,507671,835591,262098,159700,635121,509222)
        AND EventType = 1 WITH TOTALS" --config config.xml

Con la ayuda de strace recibí una instantánea (volcado) de bloques a través de la red, exactamente los mismos paquetes que se reciben cuando se ejecuta esta solicitud, y puedo estudiarlos. Puede usar tcpdump para esto, pero es inconveniente: es difícil aislar una solicitud específica del tráfico de producción.

Usando strace, puede rastrear el servidor ClickHouse. Pero este servidor funciona en producción, si hago esto obtendré una variedad de información incomprensible. Por lo tanto, lancé un programa separado que ejecuta exactamente una solicitud. Ya para este programa ejecuto strace y obtengo lo que se transmitió a través de la red.

La solicitud se ejecuta sin errores; el error no se reproduce . Si se reproduce, el problema se resolvería. Por lo tanto, copié los paquetes en un archivo de texto y comencé a analizar el protocolo manualmente.



El monto del cheque fue el mismo que se esperaba. Este es exactamente el paquete en el que a veces, en otro momento, en otras solicitudes, se produjeron errores. Pero hasta ahora no ha habido errores.

Escribí un programa simple que toma un paquete y verifica la cantidad del cheque al reemplazar un bit en cada byte. El programa realizó un cambio de bit en cada posición posible y leyó la cantidad del cheque.



Comencé el programa y descubrí que si cambias el valor de un bit, obtienes exactamente esa suma de verificación rota, por lo que hay una queja

Problema de hardware


Si hay un error en el software (por ejemplo, conducir a través de la memoria), es improbable que se invierta un bit. Por lo tanto, apareció una nueva hipótesis: el problema está en la glándula.

Uno podría cerrar la tapa de la computadora portátil y decir: "El problema no está de nuestro lado, sino en el hardware, no hacemos esto". Pero no, intentemos comprender dónde está el problema: en la RAM, en el disco duro, en el procesador, en la tarjeta de red o en la RAM de la tarjeta de red en el equipo de red.

¿Cómo localizar un problema de hardware?

  • El problema surgió y desapareció en ciertas fechas.
  • Servidores afectados se agrupan por sus nombres: mtxxxlog01-{39..44 57..58 64 68..71 73..74 76}-3.
  • Los grupos de servidores problemáticos son los mismos que en febrero.
  • Los servidores con problemas solo se encuentran en ciertas colas del centro de datos.

Hubo preguntas para los ingenieros de redes: los datos están latiendo en los conmutadores de red. Resulta que los ingenieros de red intercambiaron conmutadores por otros exactamente en esas fechas. Después de una pregunta, los reemplazaron con los anteriores y el problema desapareció.

El problema está resuelto, pero aún quedan preguntas (ya no son para ingenieros).

¿Por qué no ayuda ECC (memoria de corrección de errores) en los conmutadores de red? Debido a que la inversión de múltiples bits puede compensarse entre sí, se obtiene un error no detectado.

¿Por qué no ayudan las sumas de comprobación de TCP? Ellos son debiles. Si solo un bit ha cambiado en los datos, las sumas de verificación TCP siempre verán el cambio. Si dos bits han cambiado, entonces los cambios pueden no detectarse: se cancelan entre sí.

Solo un bit ha cambiado en nuestro paquete, pero el error no es visible. Esto se debe a que 2 bits cambiaron en el segmento TCP: calcularon la suma de verificación a partir de él, coincidió. Pero en un segmento TCP se encuentra más de un paquete de nuestra aplicación. Y para uno de ellos, ya consideramos nuestra suma de verificación. Solo un bit ha cambiado en este paquete.

¿Por qué las sumas de verificación de Ethernet no ayudan? ¿Son más fuertes que TCP? Cantidad de comprobación de EthernetVerifique y resuma los datos para que no se rompan durante la transmisión a través de un segmento (puedo estar equivocado con la terminología, no soy un ingeniero de red). El equipo de red reenvía estos paquetes y puede reenviar algunos datos durante el reenvío. Por lo tanto, los montos de los cheques son simplemente contados. Verificamos: en el cable los paquetes no han cambiado. Pero si golpean el conmutador de red en sí, volverá a calcular la cantidad del cheque (será diferente) y reenviará el paquete aún más.
Nada te salvará, comprueba tú mismo. No esperes que alguien haga esto por ti.
Para los bloques de datos, se considera una suma de verificación de 128 bits (esta exageración por si acaso). Informamos correctamente al usuario sobre el error. Los datos se transmiten a través de la red, están dañados, pero no los registramos en ningún lado: todos nuestros datos están en orden, no puede preocuparse.

Los datos que se almacenan en ClickHouse permanecen consistentes. Use sumas de verificación en ClickHouse. Nos encantan las sumas de cheques tanto que inmediatamente consideramos tres opciones:

  • Para bloques de datos comprimidos al escribir en un archivo, en la red.
  • La verificación total es la suma de datos comprimidos para la verificación de conciliación.
  • La verificación total es la suma de datos sin comprimir para la verificación de conciliación.

Hay errores en los algoritmos de compresión de datos, este es un caso conocido. Por lo tanto, cuando los datos se replican, también consideramos la suma total de verificación de los datos comprimidos y la cantidad total de datos sin comprimir.
No tenga miedo de contar los montos de los cheques, no disminuyen la velocidad.
Por supuesto, depende de cuáles y cómo contar. Hay matices, pero asegúrese de considerar el monto del cheque. Por ejemplo, si cuenta a partir de los datos comprimidos, habrá menos datos, no se ralentizarán.

Mensaje de error mejorado


¿Cómo explicarle al usuario cuando recibe un mensaje de error tal que se trata de un problema de hardware?



Si la suma de verificación no coincide, antes de enviar una excepción, trato de cambiar cada bit, por si acaso. Si la suma de verificación converge al cambiar y se cambia un bit, entonces el problema es muy probablemente el hardware.

Si podemos detectar este error, y si cambia cuando se cambia un bit, ¿por qué no solucionarlo? Podemos hacer esto, pero si corregimos los errores todo el tiempo, el usuario no sabrá que el equipo tiene un problema.

Cuando descubrimos que había problemas en los interruptores, las personas de otros departamentos comenzaron a informar: “¡Y tenemos un poco incorrectamente escrito en Mongo! ¡Y algo nos atrapó en PostgreSQL! ” Esto es bueno, pero es mejor informar problemas antes.

Cuando lanzamos una nueva versión de diagnóstico, el primer usuario con el que trabajó escribió una semana después: "Aquí está el mensaje: ¿cuál es el problema?" Lamentablemente, no lo leyó. Pero leí y sugerí con una probabilidad del 99% que si el error aparece en un servidor, entonces el problema está en el hardware. Dejo el porcentaje restante en caso de que escribí el código incorrectamente, esto sucede. Como resultado, el usuario reemplazó el SSD y el problema desapareció.

"Delirio" en los datos


Este interesante e inesperado problema me hizo preocuparme. Tenemos datos Yandex.Metrica. Se escribe un JSON simple en la base de datos en una de las columnas: parámetros de usuario del código JavaScript del contador.

Realizo algún tipo de solicitud y el servidor ClickHouse se bloqueó con segfault. Desde el seguimiento de la pila, me di cuenta de cuál era el problema: un nuevo compromiso de nuestros colaboradores externos de otro país. El commit arreglado, segfault desapareció. Ejecuto

la misma solicitud: SELECTen ClickHouse, para obtener JSON, pero de nuevo, sin sentido, todo funciona lentamente. Me sale JSON, y es de 10 MB. Lo muestro y miro con más atención: {"jserrs": cannot find property of object undefind...y luego cayó un megabyte de código binario.



Hubo pensamientos de que esto es nuevamente un pasaje de la memoria o una condición de carrera. Muchos de estos datos binarios son malos, pueden contener cualquier cosa. Si es así, ahora encontraré contraseñas y claves privadas allí. Pero no encontré nada, por lo que inmediatamente rechacé la hipótesis. ¿Quizás es un error en mi programa en el servidor ClickHouse? Quizás en un programa que escribe (también está escrito en C ++), ¿de repente ella accidentalmente pone su memoria de volcado en ClickHouse? En este infierno, comencé a mirar de cerca las letras y me di cuenta de que no era tan simple.

Pista de pista


La misma basura se registró en dos grupos, independientemente uno del otro. Los datos son basura, pero son válidos UTF-8. Este UTF-8 tiene algunas URL extrañas, nombres de fuente y muchas letras "I" seguidas.

¿Qué tiene de especial el pequeño "yo" cirílico? No, esto no es Yandex. El hecho es que en la codificación de Windows 1251 es el 255o carácter. Y en nuestros servidores Linux, nadie usa la codificación de Windows 1251.

Resulta que este es un volcado del navegador: el código JavaScript del contador métrico recopila errores de JavaScript. Al final resultó que, la respuesta es simple: todo vino del usuario .

De aquí también se pueden sacar conclusiones.

Errores de todo el Internet


Yandex.Metrica recopila tráfico de mil millones de dispositivos en Internet: navegadores en PC, teléfonos celulares, tabletas. La basura vendrá inevitablemente : hay errores en los dispositivos de los usuarios, RAM en todas partes poco confiable y hardware terrible que se sobrecalienta.

La base de datos almacena más de 30 billones de líneas (páginas vistas). Si analiza los datos de esta tabla, puede encontrar cualquier cosa allí.

Por lo tanto, es correcto simplemente filtrar esta basura antes de escribir en la base de datos. No es necesario escribir basura en la base de datos, a ella no le gusta.

HighLoad++ ( 133 ), - , , ++ PHP Russia 2020 Online.

Badoo, PHP Russia 2020 Online . PHP Russia 2020 Online 13 , .

, .

All Articles