Os erros notórios e como evitá-los no exemplo do ClickHouse

Se você estiver escrevendo código, prepare-se para problemas. Definitivamente serão, e devem ser esperados de todos os lados: do seu código e compilador, do sistema operacional e do hardware, e os usuários às vezes lançam "surpresas". Se você escalou o cluster para escalas cósmicas, espere erros de "espaço". Especialmente quando se trata de dados do tráfego da Internet.


Alexey Milovidov (o6CuFl2Q) falará sobre os problemas mais ridículos, desanimadores e sem esperança da sua experiência no desenvolvimento e suporte ao ClickHouse. Vamos ver como eles tiveram que ser depurados e quais medidas os desenvolvedores devem tomar desde o início, para que haja menos problemas.

Erros notórios


Se você escreveu algum código, prepare-se para problemas imediatamente.

Erros no código. Eles serão necessários. Mas digamos que você escreveu o código perfeito, compilado, mas os bugs aparecerão no compilador e o código não funcionará corretamente. Consertamos o compilador, tudo compilado - execute-o. Mas (inesperadamente) tudo funciona incorretamente, porque também há bugs no kernel do sistema operacional .

Se não houver erros no sistema operacional, inevitavelmente, eles estarão no hardware . Mesmo se você escreveu o código perfeito que funciona perfeitamente no hardware perfeito, ainda encontrará problemas, por exemplo, erros de configuração . Parece que você fez tudo certo, mas alguém cometeu um erro no arquivo de configuração e tudo não funciona novamente.

Quando todos os erros foram corrigidos, os usuários o terminam, porque constantemente usam seu código "incorretamente". Mas o problema definitivamente não está nos usuários, mas no código: você escreveu algo que é difícil de usar .

Vejamos esses erros com alguns exemplos.

Erros de configuração


Exclusão de dados . O primeiro caso da prática. Felizmente, nem o meu nem o Yandex, não se preocupe.

Introdutório primeiro. A arquitetura de um cluster de redução de mapa (como o Hadoop) consiste em vários servidores de dados (nós de dados) que armazenam dados e em um ou mais servidores principais que sabem a localização de todos os dados nos servidores.

Os nós de dados conhecem o endereço do mestre e se conectam a ele. O assistente monitora onde e quais dados devem ser localizados e fornece comandos diferentes para os nós de dados: “Faça o download dos dados X, você deve ter os dados Y e exclua os dados Z”. O que poderia dar errado?

Quando um novo arquivo de configuração foi carregado em todos os nós de dados, eles se conectaram por engano ao mestre de outro cluster e não ao próprio. O mestre examinou os dados sobre os quais os nós foram informados, decidiu que os dados estavam incorretos e deveriam ser excluídos. O problema foi percebido quando metade dos dados foi apagada.


Os bugs mais épicos são aqueles que levam à exclusão acidental de dados.
Evitar isso é muito simples.

Não apague dados . Por exemplo, reserve em um diretório separado ou exclua com um atraso. Primeiro, transferimos para que eles não fiquem visíveis para o usuário e, se ele descobrir que algo desapareceu dentro de alguns dias, nós o devolveremos.

Não exclua dados inesperados se a causa for desconhecida . Limite programaticamente o início da exclusão de dados desconhecidos: inesperado, com nomes estranhos ou se houver muitos deles. O administrador notará que o servidor não inicia e grava alguma mensagem e entenderá.

Se o programa executar ações destrutivas - isole os testes e a produção no nível da rede(iptables). Por exemplo, excluir arquivos ou enviar e-mail é uma ação destrutiva, pois "consome" a atenção de alguém. Coloque um limite neles: cem cartas podem ser enviadas e, para mil, coloque uma caixa de seleção de segurança, que é definida antes que algo terrível aconteça.

Configurações . O segundo exemplo já é da minha prática.

Uma boa empresa de alguma forma tinha um cluster ClickHouse estranho. O estranho é que as réplicas não foram sincronizadas. Quando o servidor foi reiniciado, ele não foi iniciado e apareceu uma mensagem informando que todos os dados estavam incorretos: “Existem muitos dados inesperados, não inicio. Temos que armar a bandeira force_restore_datae descobrir.

Ninguém conseguiu descobrir isso na empresa - eles apenas definiram a bandeira. Ao mesmo tempo, metade dos dados desapareceu em algum lugar, resultando em gráficos com lacunas. Os desenvolvedores se voltaram para mim, pensei que algo interessante estava acontecendo e decidi investigar. Quando a manhã chegou algumas horas depois e os pássaros começaram a cantar do lado de fora da janela, percebi que não entendia nada.

O servidor ClickHouse usa o serviço ZooKeeper para coordenação. O ClickHouse armazena dados e o ZooKeeper determina em quais servidores quais dados devem estar: armazena metadados sobre quais dados em que réplica deve estar. O ZooKeeper também é um cluster - ele se replica de acordo com um algoritmo de consenso distribuído muito bom, com consistência estrita.

Como regra, o ZooKeeper é de 3 máquinas, às vezes 5. Todas as máquinas são listadas na configuração do ClickHouse de uma só vez, uma conexão é estabelecida com uma máquina aleatória, interage com ela e esse servidor replica todas as solicitações.

O que aconteceu? A empresa tinha três servidores ZooKeeper. Mas eles não funcionaram como um cluster de três nós , mas como três nós independentes - três clusters de um nó. Um ClickHouse se conecta a um servidor e grava dados. As réplicas desejam fazer o download desses dados, mas eles não são encontrados em nenhum lugar. Ao reiniciar, o servidor se conecta a outro ZooKeeper: vê que os dados com os quais trabalhou antes são supérfluos, devem ser adiados em algum lugar. Ele não os exclui, mas os transfere para um diretório separado - os dados do ClickHouse não são excluídos com tanta facilidade.

Decido corrigir a configuração do ZooKeeper. Renomeio todos os dados e solicito ATTACHpartes dos dados do diretório detached/unexpeted_*.

Como resultado, todos os dados foram restaurados, as réplicas foram sincronizadas, não houve perdas, os gráficos foram contínuos. A empresa está satisfeita, agradecida, como se já tivessem esquecido como tudo funcionara mal antes.

Esses eram erros de configuração simples. Mais bugs estarão no código.

Erros no código


Escrevemos código em C ++. Isso significa que já temos problemas.
O próximo exemplo é um bug real da produção no cluster Yandex.Metrica (2015) - uma consequência do código C ++. O erro era que, às vezes, o usuário, em vez de responder à solicitação, recebia uma mensagem de erro:

  • “A soma de verificação não corresponde, dados corrompidos” - a soma de verificação não corresponde, os dados estão quebrados - assustador!
  • "O LRUCache se tornou inconsistente. Deve haver um bug nele ”- o cache se tornou inconsistente, provavelmente um bug nele.

O código que escrevemos informa que existe um bug lá.

"A soma de verificação não corresponde, dados corrompidos ." As somas dos blocos de dados compactados são verificadas antes de serem descompactadas. Geralmente, esse erro aparece quando os dados são quebrados no sistema de arquivos. Por várias razões, alguns arquivos acabam sendo lixo quando o servidor é reiniciado.

Mas aqui está outro caso: eu li o arquivo manualmente, a soma da verificação corresponde, não há erro. Uma vez exibido, o erro é reproduzido de forma estável mediante solicitação repetida. Quando o servidor é reiniciado, o erro desaparece por um tempo e, em seguida, aparece de forma estável.

Talvez o problema esteja na RAM? Uma situação típica é quando os bits estão batendo nela. Eu olho em dmesg(kern.log), mas não há exceções na verificação da máquina - elas geralmente escrevem quando algo está errado com a RAM. Se o servidor tivesse ultrapassado a RAM, não apenas o meu programa funcionaria incorretamente, mas todos os outros gerariam erros aleatoriamente. No entanto, não há outras manifestações do erro.

"O LRUCache se tornou inconsistente. Deve haver um bug nele. " Este é um erro claro no código, e estamos escrevendo em C ++ - talvez acesso à memória? Mas os testes em AddressSanitizer, ThreadSanitizer, MemorySanitizer, UndefinedBehaviorSanitizer no IC não mostram nada.

Talvez alguns casos de teste não sejam cobertos? Eu coleciono o servidor com o AddressSanitizer, o executo na produção - ele não captura nada. Por algum tempo, o erro é resolvido ao redefinir algum cache de marca (cache de sachê).

Uma das regras de programação diz: se não estiver claro qual é o erro, observe atentamente o código, esperando encontrar algo lá. Fiz isso, encontrei um bug, consertei - não ajudou. Eu olho para outro lugar no código - também há um bug. Corrigido, novamente não ajudou. Corrigi mais um pouco, o código melhorou, mas o erro ainda não desapareceu!

Causa. Tentando encontrar um padrão por servidor, por tempo, pela natureza da carga - nada ajuda. Então ele percebeu que o problema se manifesta apenas em um dos agrupamentos e nunca nos outros. O erro não é reproduzido com tanta frequência, mas sempre aparece em um cluster após uma reinicialização e tudo está limpo no outro.

Verificou-se que o motivo é que no cluster “problema” eles usaram um novo recurso - dicionários de cache. Eles usam o alocador de memória manuscrita ArenaWithFreeLists . Não apenas escrevemos em C ++, mas também vimos algum tipo de alocadores personalizados - nos condenamos a problemas duas vezes.

ArenaWithFreeLists é uma parte da memória na qual a memória é alocada consecutivamente em tamanhos divisíveis por dois: 16, 32, 64 bytes. Se a memória for liberada, eles formarão uma lista isolada de blocos gratuitos de FreeLists.

Vamos dar uma olhada no 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] ->...
    }
}

Ele usa uma função _bit_scan_reversecom um sublinhado no início.
Existe uma regra não escrita: "Se uma função tiver um sublinhado no início, leia a documentação uma vez e, se houver duas, leia duas vezes".
Ouvimos e lemos a documentação: “int _bit_scan_reverse (int a). Defina dst como o índice do bit mais alto definido no número inteiro de 32 bits a. Se nenhum bit estiver definido em um, então dst será indefinido . " Parece que encontramos um problema.

No C ++, essa situação é considerada impossível para o compilador. O compilador pode usar um comportamento indefinido, essa "impossibilidade", como uma suposição para otimizar o código.

O compilador não faz nada errado - honestamente gera instruções de montagem bsr %edi, %eax. Mas, se o operando for zero, a instrução bsrterá um comportamento indefinido, não no nível C ++, mas no nível da CPU. Se o registro de origem for zero, o registro de destino não será alterado: havia algum lixo na entrada, esse lixo também permanecerá na saída.

O resultado depende de onde o compilador coloca essa instrução. Às vezes, uma função com esta instrução está embutida, às vezes não. No segundo caso, haverá algo como este código:

bsrl %edi, %eax
retq

Então eu olhei para um exemplo de código semelhante no meu binário usando objdump.



De acordo com os resultados, vejo que algumas vezes o registro de origem e o destino são iguais. Se houvesse zero, o resultado também será zero - está tudo bem. Mas às vezes os registros são diferentes, e o resultado será lixo.

Como esse bug se manifesta?

  • Usamos lixo como um índice na matriz FreeLists. Em vez de uma matriz, vamos para um endereço distante e obtemos acesso à memória.
  • Temos sorte, quase todos os endereços próximos são preenchidos com dados do cache - nós estragamos o cache. O cache contém deslocamentos de arquivo.
  • Lemos os arquivos no deslocamento errado. Do deslocamento errado, obtemos a soma do cheque. Mas não há uma soma de verificação, mas outra coisa - essa soma de verificação não coincidirá com os seguintes dados.
  • Recebemos o erro "A soma de verificação não corresponde, dados corrompidos".

Felizmente, não há dados corrompidos, mas apenas o cache da RAM. Fomos informados imediatamente sobre o erro, porque verificamos a soma dos dados. O erro foi corrigido em 27 de dezembro de 2015 e foi comemorado.

Como você pode ver, o código errado pode pelo menos ser corrigido. Mas como consertar bugs no hardware?

Erros no ferro


Esses nem são erros, mas leis físicas - efeitos inevitáveis. De acordo com as leis da física, o ferro é inevitavelmente danificado.

Gravação não atômica para RAID . Por exemplo, criamos o RAID1. Consiste em dois discos rígidos. Isso significa que um servidor é um sistema distribuído: os dados são gravados em um disco rígido e em outro. Mas e se os dados forem gravados em um disco e a energia for perdida durante a gravação no segundo? Os dados em uma matriz RAID1 não serão consistentes. Não conseguiremos entender quais dados estão corretos, porque leremos um byte ou outro.

Você pode lidar com isso colocando o log. Por exemplo, no ZFS, esse problema foi resolvido, mas mais tarde.

podridão de bits no HDD e SSD. Os bits nos discos rígidos e nos SSDs podem ficar ruins assim. Os SSDs modernos, especialmente aqueles com células de vários níveis, são projetados para garantir que as células se deteriorem constantemente. Os códigos de correção de erros ajudam, mas às vezes as células se deterioram tanto e até isso não salva. Erros não detectados são obtidos.

pouco vira na RAM (mas e o ECC?). Na RAM dos servidores, os bits também estão corrompidos. Também possui códigos de correção de erros. Quando ocorrem erros, eles geralmente são visíveis nas mensagens no log do kernel do Linux no dmesg. Quando houver muitos erros, veremos algo como: "N milhões de erros com memória foram corrigidos". Mas bits individuais não serão notados e, com certeza, algo ficará com erros.

bit vira no nível da CPU e da rede . Existem erros no nível da CPU, nos caches da CPU e, é claro, na transmissão de dados pela rede.

Como os erros de ferro geralmente se manifestam? O ticket " Um znode malformado impede que o ClickHouse seja iniciado " chega ao GitHub - os dados no nó ZooKeeper estão corrompidos.

No ZooKeeper, geralmente escrevemos alguns metadados em texto simples. Há algo errado com ele - " réplica " está escrito muito estranho.



Raramente acontece que, devido a um erro no código, um bit seja alterado. Claro, podemos escrever um código assim: pegamos o filtro Bloom, alteramos o bit em determinados endereços, calculamos os endereços incorretamente, alteramos o bit errado, ele cai em alguns dados. É isso aí, agora no ClickHouse não é " réplica" , mas " repli b a " e todos os dados estão errados. Mas geralmente, uma mudança em um bit é um sintoma de problemas de ferro.

Talvez você conheça o exemplo de bitsquatting. Artyom Dinaburg fez um experimento : existem domínios na Internet com muito tráfego, embora os usuários não acessem esses domínios por conta própria. Por exemplo, esse domínio FB-CDN.com é um CDN do Facebook.

Artyom registrou um domínio semelhante (e muitos outros), mas mudou um pouco. Por exemplo, FA-CDN.com em vez de FB-CDN.com. O domínio não foi publicado em nenhum lugar, mas o tráfego chegou a ele. Às vezes, o host FB-CDN foi gravado nos cabeçalhos HTTP e a solicitação foi para outro host devido a erros na RAM nos dispositivos dos usuários. RAM com correção de erros nem sempre ajuda. Às vezes, até interfere e leva a vulnerabilidades (leia sobre Rowhammer, ECCploit, RAMBleed).
Conclusão: sempre verifique os dados você mesmo.
Ao gravar no sistema de arquivos, verifique a soma sem falha. Ao transmitir pela rede, verifique também resumir - não espere que haja somas de verificação lá.

Mais bugs! ..


Métricas de Cluster de Produção . Às vezes, os usuários em resposta a uma solicitação recebem uma exceção: “A soma de verificação não corresponde: dados corrompidos” - a soma da verificação não está correta, os dados estão corrompidos.



A mensagem de erro exibe dados detalhados: qual valor de cheque era esperado, qual valor de cheque realmente está nesses dados, o tamanho do bloco para o qual verificamos o valor de cheque e o contexto de exceção.

Quando recebemos o pacote pela rede de algum servidor, uma exceção apareceu - parece familiar. Talvez novamente passando pela memória, condição de raça ou outra coisa.

Esta exceção apareceu em 2015. O bug foi corrigido, não apareceu mais. Em fevereiro de 2019, ele apareceu de repente novamente. Nessa época, eu estava em uma das conferências, meus colegas lidaram com o problema. O erro foi reproduzido várias vezes ao dia entre 1000 servidores com ClickHouse: não é possível coletar estatísticas em um servidor e depois em outro. Ao mesmo tempo, não havia novos lançamentos no momento. Não deu certo e resolveu o problema, mas depois de alguns dias o erro desapareceu.

Eles esqueceram o erro e, em 15 de maio de 2019, repetiu. Continuamos a lidar com ela. A primeira coisa que fiz foi examinar todos os logs e gráficos disponíveis. Ele os estudou o dia inteiro, não entendeu nada, não encontrou nenhum padrão. Se o problema não puder ser reproduzido, a única opção é coletar todos os casos, procurar padrõese vícios. Talvez o kernel do Linux não funcione corretamente com o processador, salve ou carregue incorretamente quaisquer registros.

Hipóteses e padrões


7 de 9 servidores com E5-2683 v4 falharam. Mas, com relação ao erro, apenas cerca da metade do E5-2683 v4 é uma hipótese vazia.

Erros geralmente não são repetidos . Além do cluster mtauxyz, onde há realmente dados corrompidos (dados incorretos no disco). Este é outro caso, rejeitamos a hipótese.

O erro não depende do kernel do Linux - verificado em servidores diferentes, não encontrou nada. Nada interessante no kern.log, machine check exceptionsem mensagens . Em gráficos de rede, incluindo retransmissores, CPU, IO, Rede, nada de interessante. Todos os adaptadores de rede nos servidores em que os erros ocorrem e não aparecem são os mesmos.

Não há padrões . O que fazer? Continue procurando padrões. Segunda tentativa.

Eu olho para servidores de tempo de atividade:o tempo de atividade é alto, os servidores funcionam de forma estável , o segfault e algo assim não é. Sempre me alegro quando vejo que o programa travou com segfault - pelo menos ele travou. Pior, quando há um erro, estraga algo, mas ninguém percebe.

Os erros são agrupados por dia e ocorrem dentro de alguns dias. Em cerca de 2 dias, mais aparecem, em alguns menos e mais uma vez - não é possível determinar com precisão o tempo de ocorrência de erros.

Alguns erros correspondem aos pacotes e ao valor do cheque que esperávamos. A maioria dos erros possui apenas duas opções de pacote. Tive sorte porque, na mensagem de erro, adicionamos o próprio valor da soma de verificação, o que ajudou a compilar estatísticas.

Sem padrões de servidorde onde lemos os dados. O tamanho do bloco compactado que verificamos como soma é menor que um kilobyte. Observou os tamanhos das embalagens em HEX. Isso não foi útil para mim - a representação binária de tamanhos de pacotes e somas de verificação não é perceptível.

Não corrigi o erro: estava novamente procurando padrões. Terceira tentativa.

Por alguma razão, o erro aparece apenas em um dos clusters - nas terceiras réplicas no Vladimir DC (gostamos de chamar data centers por nomes de cidades). Em fevereiro de 2019, um erro também apareceu no Vladimirs DC, mas em uma versão diferente do ClickHouse. Esse é outro argumento contra a hipótese de que escrevemos o código errado. Já o reescrevemos três vezes de fevereiro a maio - o erro provavelmente não está no código .

Todos os erros ao ler pacotes pela rede -while receiving packet from. O pacote no qual o erro ocorreu depende da estrutura da solicitação. Para solicitações que diferem na estrutura, um erro em diferentes somas de verificação. Mas nas solicitações em que o erro está na mesma soma de verificação, as constantes diferem.

Todas as solicitações com erro, exceto uma, são GLOBAL JOIN. Mas, para comparação, há uma solicitação incomumente simples e o tamanho do bloco compactado é de apenas 75 bytes.

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

Rejeitamos a hipótese de influência GLOBAL JOIN.

O mais interessante é que os servidores afetados são agrupados em faixas pelos seus nomes :
mtxxxlog01-{39..44 57..58 64 68..71 73..74 76}-3.

Eu estava cansado e desesperado, comecei a procurar padrões completamente ilusórios. É bom que eu não tenha conseguido depurar o código usando a numerologia. Mas ainda havia pistas.

  • Os grupos de servidores com problemas são os mesmos de fevereiro.
  • Os servidores com problemas estão localizados em certas partes do data center. Em DC Vladimir, existem as chamadas linhas - suas diferentes partes: VLA-02, VLA-03, VLA-04. Os erros estão claramente agrupados: em algumas filas é bom (VLA-02), em outros problemas (VLA-03, VLA-04).

Digitando depuração


Permaneceu apenas para depuração usando o método "spear". Isso significa formar a hipótese "O que acontece se você tentar fazer isso?" e coletar dados. Por exemplo, encontrei uma query_logconsulta simples com um erro na tabela para o qual o tamanho do pacote é size of compressed blockmuito pequeno (= 107).



Peguei a solicitação, copiei e executei 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

Com a ajuda do strace, recebi uma captura instantânea (despejo) de blocos na rede - exatamente os mesmos pacotes que são recebidos quando essa solicitação é executada e posso estudá-los. Você pode usar o tcpdump para isso, mas é inconveniente: é difícil isolar uma solicitação específica do tráfego de produção.

Usando strace, você pode rastrear o próprio servidor ClickHouse. Mas esse servidor funciona em produção, se eu fizer isso, receberei uma variedade de informações incompreensíveis. Portanto, lancei um programa separado que executa exatamente uma solicitação. Já para este programa eu corro strace e recebo o que foi transmitido pela rede.

A solicitação é executada sem erros - o erro não é reproduzido . Se reproduzido, o problema seria resolvido. Portanto, copiei os pacotes em um arquivo de texto e comecei a analisar manualmente o protocolo.



O valor do cheque era o mesmo que o esperado. Este é exatamente o pacote no qual, às vezes, em outros momentos, em outras solicitações, ocorrem erros. Mas até agora não houve erros.

Eu escrevi um programa simples que pega um pacote e verifica o valor do cheque ao substituir um bit em cada byte. O programa executou o bit flip em todas as posições possíveis e leu o valor do cheque.



Iniciei o programa e descobri que, se você alterar o valor de um bit, obtém exatamente esse valor de verificação quebrado, para o qual há uma reclamação

Problema de hardware


Se houver um erro no software (por exemplo, dirigir através da memória), é improvável que o flip de bit único. Portanto, surgiu uma nova hipótese - o problema está na glândula.

Pode-se fechar a tampa do laptop e dizer: "O problema não está do nosso lado, mas no hardware, não fazemos isso". Mas não, vamos tentar entender onde está o problema: na RAM, no disco rígido, no processador, na placa de rede ou na RAM da placa de rede no equipamento de rede.

Como localizar um problema de hardware?

  • O problema surgiu e desapareceu em determinadas datas.
  • Servidores afetados são agrupados por seus nomes: mtxxxlog01-{39..44 57..58 64 68..71 73..74 76}-3.
  • Os grupos de servidores com problemas são os mesmos de fevereiro.
  • Os servidores com problemas estão apenas em determinadas filas do datacenter.

Havia perguntas para os engenheiros de rede - os dados estão batendo nos comutadores de rede. Acontece que os engenheiros de rede trocaram switches por outros exatamente nessas datas. Após uma pergunta, eles foram substituídos pelos anteriores e o problema desapareceu.

O problema foi resolvido, mas ainda restam perguntas (não mais para engenheiros).

Por que o ECC (memória de correção de erros) não ajuda nos comutadores de rede? Como o flip de vários bits pode compensar um ao outro - você recebe um erro não detectado.

Por que as somas de verificação TCP não ajudam? Eles são fracos. Se apenas um bit tiver sido alterado nos dados, as somas de verificação TCP sempre verão a alteração. Se dois bits foram alterados, as alterações podem não ser detectadas - elas se cancelam.

Apenas um bit foi alterado em nosso pacote, mas o erro não é visível. Isso ocorre porque 2 bits foram alterados no segmento TCP: eles calcularam a soma de verificação, coincidiu. Mas em um segmento TCP, mais de um pacote de nosso aplicativo está localizado. E para um deles, já consideramos nossa soma de verificação. Apenas um bit foi alterado neste pacote.

Por que as somas de verificação de Ethernet não ajudam - elas são mais fortes que o TCP? Quantidade de verificação de Ethernetfaça um resumo dos dados para que eles não quebrem durante a transmissão por um segmento (posso estar errado com a terminologia, não sou engenheiro de rede). O equipamento de rede encaminha esses pacotes e pode encaminhar alguns dados durante o encaminhamento. Portanto, os valores dos cheques são simplesmente recontados. Verificamos - nos fios os pacotes não foram alterados. Mas se eles baterem no próprio switch de rede, ele recalculará o valor do cheque (será diferente) e encaminhará o pacote ainda mais.
Nada irá salvá-lo - soma você mesmo. Não espere que alguém faça isso por você.
Para blocos de dados, uma soma de verificação de 128 bits é considerada (esse exagero apenas por precaução). Informamos corretamente o usuário sobre o erro. Os dados são transmitidos pela rede, estão danificados, mas não os gravamos em nenhum lugar - todos os nossos dados estão em ordem, não se preocupe.

Os dados armazenados no ClickHouse permanecem consistentes. Use somas de verificação no ClickHouse. Adoramos tanto os cheques que consideramos imediatamente três opções:

  • Para blocos de dados compactados ao gravar em um arquivo, na rede.
  • A verificação total é a soma dos dados compactados para verificação de reconciliação.
  • Verificação total é a soma dos dados não compactados para verificação de reconciliação.

Existem erros nos algoritmos de compactação de dados, este é um caso conhecido. Portanto, quando os dados são replicados, também consideramos a soma total de verificação dos dados compactados e a quantidade total de dados não compactados.
Não tenha medo de contar os valores dos cheques, pois eles não diminuem a velocidade.
Obviamente, depende de quais e como contar. Existem nuances, mas não se esqueça de considerar o valor do cheque. Por exemplo, se você contar a partir dos dados compactados, haverá menos dados, eles não diminuirão a velocidade.

Mensagem de erro aprimorada


Como explicar ao usuário quando ele recebe uma mensagem de erro que este é um problema de hardware?



Se a soma de verificação não corresponder, antes de enviar uma exceção, tento mudar todos os bits - apenas por precaução. Se a soma da verificação convergir durante a alteração e um bit for alterado, o problema provavelmente será o hardware.

Se podemos detectar esse erro e se ele muda quando um bit é alterado, por que não corrigi-lo? Podemos fazer isso, mas se corrigirmos erros o tempo todo, o usuário não saberá que o equipamento está com problemas.

Quando descobrimos que havia problemas nos comutadores, pessoas de outros departamentos começaram a relatar: “E nós escrevemos um pouco incorretamente para o Mongo! E algo nos atingiu no PostgreSQL! ” Isso é bom, mas é melhor relatar problemas mais cedo.

Quando lançamos uma nova versão de diagnóstico, o primeiro usuário a quem ele trabalhou escreveu uma semana depois: "Aqui está a mensagem - qual é o problema?" Infelizmente, ele não leu. Mas eu li e sugeri com uma probabilidade de 99% que, se o erro aparecer em um servidor, o problema esteja no hardware. Deixo a porcentagem restante no caso de escrever o código incorretamente - isso acontece. Como resultado, o usuário substituiu o SSD e o problema desapareceu.

"Delirium" nos dados


Esse problema interessante e inesperado me deixou preocupada. Temos dados Yandex.Metrica. Um JSON simples é gravado no banco de dados em uma das colunas - parâmetros do usuário do código JavaScript do contador.

Eu faço algum tipo de solicitação e o servidor ClickHouse travou com o segfault. A partir do rastreamento da pilha, percebi qual era o problema - um novo commit de nossos colaboradores externos de outro país. A consolidação corrigida, segfault desapareceu.

Eu executo o mesmo pedido: SELECTno ClickHouse, para obter JSON, mas, novamente, bobagem, tudo funciona lentamente. Eu recebo JSON, e é 10 MB. Eu o mostro e olho com mais atenção: {"jserrs": cannot find property of object undefind...e então um megabyte de código binário caiu.



Havia pensamentos de que isso é novamente uma passagem da memória ou uma condição de raça. Muitos desses dados binários são ruins, podem conter qualquer coisa. Nesse caso, agora vou encontrar senhas e chaves privadas lá. Mas como não encontrei nada, rejeitei imediatamente a hipótese. Talvez este seja um erro no meu programa no servidor ClickHouse? Talvez em um programa que escreve (também é escrito em C ++) - de repente ela acidentalmente coloca sua memória de despejo no ClickHouse? Neste inferno, comecei a olhar atentamente para as cartas e percebi que não era tão simples.

Caminho da pista


O mesmo lixo foi registrado em dois grupos, independentemente um do outro. Os dados são lixo, mas são UTF-8 válidos. Este UTF-8 possui alguns URLs estranhos, nomes de fontes e muitas letras "I" seguidas.

O que há de especial no pequeno "eu" cirílico? Não, este não é o Yandex. O fato é que, na codificação do Windows 1251, é o 255º caractere. E em nossos servidores Linux, ninguém usa a codificação do Windows 1251.

Acontece que este é um despejo do navegador: o código JavaScript do contador de métricas coleta erros de JavaScript. Como se viu, a resposta é simples - tudo veio do usuário .

A partir daqui também é possível tirar conclusões.

Erros de toda a Internet


O Yandex.Metrica coleta tráfego de 1 bilhão de dispositivos na Internet: navegadores em PCs, telefones celulares, tablets. O lixo virá inevitavelmente : existem bugs nos dispositivos dos usuários, em todos os lugares RAM não confiável e hardware terrível que superaquece.

O banco de dados armazena mais de 30 trilhões de linhas (visualizações de página). Se você analisar os dados desta tabela, poderá encontrar qualquer coisa lá.

Portanto, é correto simplesmente filtrar esse lixo antes de gravar no banco de dados. Não há necessidade de escrever lixo no banco de dados - ela não gosta.

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

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

, .

All Articles