Tipos de teste
Há os seguintes testes no ClickHouse:
- Testes funcionais - um conjunto de consultas e scripts que inclui os seguintes subconjuntos sobrepostos
- Teste rápido - o subconjunto mínimo
- Testes sem estado que não exigem o preenchimento de bancos de dados com dados
- Testes sequenciais que não podem ser executados em paralelo
- Testes de integração, executados pelo
pytestem um cluster - Testes unitários
- Testes de desempenho
- Testes de build
- Sanitizers
- Fuzzers e alguns outros; veja as seções abaixo.
Testes funcionais
Os testes funcionais são os mais simples e convenientes de usar. A maior parte dos recursos do ClickHouse pode ser testada com testes funcionais, e eles são obrigatórios para toda alteração no código do ClickHouse que possa ser testada dessa forma.
Cada teste funcional envia uma ou mais consultas ao servidor ClickHouse em execução e compara o resultado com a referência.
Os testes ficam no diretório ./tests/queries.
Cada teste pode ser de um destes dois tipos: .sql e .sh.
- Um teste
.sqlé um script SQL simples enviado por pipe para oclickhouse-client. - Um teste
.shé um script executado por conta própria.
Em geral, testes SQL são preferíveis a testes .sh.
Você deve usar testes .sh apenas quando precisar testar algum recurso que não possa ser exercitado com SQL puro, como enviar dados de entrada por pipe para o clickhouse-client ou testar o clickhouse-local.
Executando um teste localmente
Inicie o servidor ClickHouse localmente, escutando na porta padrão (9000).
Para executar, por exemplo, o teste 01428_hash_set_nan_key, vá para a pasta do repositório e execute o seguinte comando:
PATH=<path to clickhouse-client>:$PATH tests/clickhouse-test 01428_hash_set_nan_keyOs resultados dos testes (stderr e stdout) são gravados nos arquivos 01428_hash_set_nan_key.[stderr|stdout], que ficam ao lado do próprio teste (para queries/0_stateless/foo.sql, a saída ficará em queries/0_stateless/foo.stdout).
Consulte tests/clickhouse-test --help para ver todas as opções de clickhouse-test.
Você pode executar todos os testes ou apenas um subconjunto, fornecendo um filter para os nomes dos testes: ./clickhouse-test substring.
Também há opções para executar os testes em paralelo ou em ordem aleatória.
Executando testes no macOS (Darwin)
Muitos testes funcionais recorrem a utilitários GNU de linha de comando (timeout, head, sed, grep, date etc.). O macOS vem com as variantes BSD dessas ferramentas, cujo comportamento e opções diferem (por exemplo, o head do BSD rejeita head -c 1G, o ps do BSD não oferece as opções longas -- e não existe timeout). Executar os testes com as ferramentas BSD gera falhas espúrias.
Os runners de CI do macOS instalam as ferramentas GNU via Homebrew e as colocam antes das ferramentas BSD no PATH. Reproduza isso localmente:
brew install coreutils gnu-sed grep
export PATH="$(brew --prefix)/opt/coreutils/libexec/gnubin:$(brew --prefix)/opt/gnu-sed/libexec/gnubin:$(brew --prefix)/opt/grep/libexec/gnubin:$PATH"coreutils fornece o GNU timeout, head, date e afins; gnu-sed e grep fornecem o GNU sed e grep. Depois disso, which timeout head sed grep deve apontar para os caminhos gnubin.
Executando testes rápidos
Talvez você precise de uma máquina razoavelmente potente para executar um subconjunto de testes (chamado "teste rápido"). O procedimento a seguir funciona em uma instância AWS Ubuntu amd64 t3.2xlarge com 100 GB de armazenamento.
- Instale os pré-requisitos e faça login novamente.
sudo apt-get update
sudo apt-get install docker.io
sudo usermod -aG docker "$USER"- Baixe o código-fonte.
git clone --single-branch https://github.com/ClickHouse/ClickHouse
cd ClickHouse- Compile o código e execute os "testes rápidos".
python -m ci.praktika run fastO resultado deve ser
Failed: 0, Passed: 7394, Skipped: 1795Se você deixar a execução sem supervisão, poderá usar nohup ou disown para mantê-la em execução mesmo após a perda da conexão ssh.
Executando testes sem estado
Você pode precisar de uma máquina razoavelmente potente para executar testes sem estado. A configuração a seguir funciona em uma instância AWS Ubuntu amd64 m7i.8xlarge, com 200 GB de armazenamento.
- Instale os pré-requisitos e faça login novamente.
sudo apt-get update
sudo apt-get install docker.io
sudo usermod -aG docker "$USER"
sudo tee /etc/docker/daemon.json <<'EOF'
{
"ipv6": true,
"ip6tables": true
}
EOF
sudo systemctl restart docker- Baixe o código-fonte.
git clone --single-branch https://github.com/ClickHouse/ClickHouse
cd ClickHouse- Compile o código.
python -m ci.praktika run build_debug
cp ci/tmp/build/programs/clickhouse ci/tmp- Execute testes sem estado, que podem ser executados em paralelo.
python -m ci.praktika run functionalO resultado deve ser
Failed: 0, Passed: 8497, Skipped: 103Observação: os comandos python -m ci.praktika run executam um job específico de integração contínua; você pode ler mais sobre a integração contínua do ClickHouse aqui.
Adicionando um novo teste
Para adicionar um novo teste, primeiro crie um arquivo .sql ou .sh no diretório queries/0_stateless.
Em seguida, gere o arquivo .reference correspondente usando clickhouse-client < 12345_test.sql > 12345_test.reference ou ./12345_test.sh > ./12345_test.reference.
Os testes devem apenas criar, remover, fazer SELECT em etc. tabelas no banco de dados test, que é criado automaticamente com antecedência.
É permitido usar tabelas temporárias.
Para configurar localmente o mesmo ambiente da CI, instale as configurações de teste (elas usarão uma implementação simulada do ZooKeeper e ajustarão algumas configurações)
cd <repository>/tests/config
sudo ./install.shTestes com Template usando Jinja
Um teste .sql pode ser escrito como um Template Jinja2 adicionando o sufixo .j2 ao nome do arquivo, de modo que foo.sql passe a ser foo.sql.j2. Antes de executar o teste, o clickhouse-test renderiza o Template como um script .sql comum e executa o resultado.
Isso é útil quando um teste repete a mesma consulta com pequenas variações: um loop gera as consultas a partir de um Template compacto, em vez de escrever cada uma manualmente. As construções mais usadas são:
{% for ... %} ... {% endfor %}para repetir um bloco,{{ expression }}para inserir um valor na saída,-%}e{%-para remover espaços em branco adjacentes, para que o script gerado permaneça limpo.
Por exemplo, este Template:
{% for type in ['UInt8', 'UInt16', 'UInt32'] -%}
SELECT toTypeName(0::{{ type }});
{% endfor -%}resulta em:
SELECT toTypeName(0::UInt8);
SELECT toTypeName(0::UInt16);
SELECT toTypeName(0::UInt32);A saída esperada pode ser fornecida tanto como um arquivo <name>.reference simples, contendo os resultados totalmente expandidos, quanto como um Template <name>.reference.j2, que o clickhouse-test renderiza da mesma maneira antes de comparar. Use a forma com Template quando a saída esperada também seguir um padrão repetitivo. Para mais exemplos, consulte os arquivos *.sql.j2 existentes em tests/queries/0_stateless/.
Restringindo execuções de teste
Um teste pode ter zero ou mais tags que especificam restrições sobre em quais contextos ele é executado no CI.
Em testes .sql, as tags são colocadas na primeira linha como um comentário SQL:
-- Tags: no-fasttest, no-replicated-database
-- no-fasttest: <provide_a_reason_for_the_tag_here>
-- no-replicated-database: <provide_a_reason_here>
SELECT 1Nos testes .sh, as tags são escritas como comentário na segunda linha:
#!/usr/bin/env bash
# Tags: no-fasttest, no-replicated-database
# - no-fasttest: <provide_a_reason_for_the_tag_here>
# - no-replicated-database: <provide_a_reason_here>Lista de tags disponíveis:
| Tag name | What it does | Usage example |
|---|---|---|
disabled |
O teste não é executado | |
long |
O tempo de execução do teste é estendido de 1 para 10 minutos | |
deadlock |
O teste é executado em loop por um longo período | |
race |
Igual a deadlock. Prefira deadlock |
|
shard |
O servidor precisa escutar em 127.0.0.* |
|
distributed |
Igual a shard. Prefira shard |
|
global |
Igual a shard. Prefira shard |
|
zookeeper |
O teste requer Zookeeper ou ClickHouse Keeper para ser executado | O teste usa ReplicatedMergeTree |
replica |
Igual a zookeeper. Prefira zookeeper |
|
no-fasttest |
O teste não é executado no teste rápido | O teste usa o table engine MySQL, que é desabilitado no teste rápido |
fasttest-only |
O teste é executado apenas no teste rápido | |
no-[asan, tsan, msan, ubsan] |
Desabilita testes em builds com sanitizers | O teste é executado no QEMU, que não funciona com sanitizers |
no-replicated-database |
Desabilita o teste quando o banco de dados padrão usa ReplicatedDatabaseEngine |
|
no-ordinary-database |
Desabilita o teste quando o database engine padrão é Ordinary |
|
no-parallel |
Desabilita a execução de outros testes em paralelo com este | O teste lê tabelas system, e os invariantes podem ser violados |
no-parallel-replicas |
Desabilita o teste quando parallel replicas estão habilitadas | |
no-debug |
Desabilita testes em builds Debug | |
no-release |
Desabilita testes em builds Release | |
no-darwin |
Desabilita o teste no macOS (Darwin) | O teste depende de recursos específicos do Linux, como consultas distribuídas, procfs ou servidor HTTP |
As opções a seguir também são compatíveis: no-polymorphic-parts, no-random-settings, no-random-merge-tree-settings, no-backward-compatibility-check, no-cpu-x86_64, no-cpu-aarch64, no-cpu-ppc64le, no-s3-storage.
Além das configurações acima, você pode usar flags USE_* de system.build_options para definir o uso de recursos específicos do ClickHouse.
Por exemplo, se seu teste usa uma tabela MySQL, você deve adicionar a tag use-mysql.
Especificando limites para configurações aleatórias
Um teste pode especificar os valores mínimo e máximo permitidos para configurações que podem ser randomizadas durante a execução do teste.
Para testes .sh, os limites são escritos como um comentário na linha ao lado das tags ou na segunda linha, caso nenhuma tag seja especificada:
#!/usr/bin/env bash
# Tags: no-fasttest
# Limites de configurações aleatórias: max_block_size=(1000, 10000); index_granularity=(100, None)Para testes .sql, as tags são colocadas como comentário SQL na linha ao lado delas ou na primeira linha:
-- Tags: no-fasttest
-- Random settings limits: max_block_size=(1000, 10000); index_granularity=(100, None)
SELECT 1Se você precisar especificar apenas um limite, pode usar None para o outro.
Escolhendo o nome do teste
O nome do teste começa com um prefixo de cinco dígitos, seguido de um nome descritivo, como 00422_hash_function_constexpr.sql.
Para escolher o prefixo, encontre o maior prefixo já presente no diretório e acrescente um a ele.
ls tests/queries/0_stateless/[0-9]*.reference | tail -n 1Enquanto isso, alguns outros testes podem ser adicionados com o mesmo prefixo numérico, mas isso não tem problema e não causa nenhum inconveniente; não é preciso alterá-lo depois.
Verificando se ocorre um erro esperado
Às vezes, você quer testar se ocorre um erro do servidor ao executar uma consulta incorreta. Oferecemos suporte a anotações especiais para isso em testes SQL, no seguinte formato:
SELECT x; -- { serverError 49 }Este teste garante que o servidor retorne um erro com o código 49 informando que a coluna x é desconhecida.
Se não houver erro, ou se o erro for diferente, o teste falhará.
Se você quiser garantir que um erro ocorra no lado do cliente, use a anotação clientError.
Não verifique uma redação específica da mensagem de erro, pois ela pode mudar no futuro, e o teste pode falhar desnecessariamente. Verifique apenas o código do erro. Se o código de erro existente não for preciso o suficiente para o que você precisa, considere adicionar um novo.
Testando uma consulta distribuída
Se você quiser usar consultas distribuídas em testes funcionais, pode usar a table function remote com endereços 127.0.0.{1..2} para que o servidor consulte a si próprio; ou usar clusters de teste predefinidos no arquivo de configuração do servidor, como test_shard_localhost.
Lembre-se de adicionar as palavras shard ou distributed ao nome do teste, para que ele seja executado no CI com as configurações corretas, nas quais o servidor esteja configurado para oferecer suporte a consultas distribuídas.
Trabalhando com arquivos temporários
Às vezes, em um teste de shell, você pode precisar criar um arquivo na hora para usar.
Tenha em mente que algumas verificações de CI executam testes em paralelo; portanto, se você criar ou remover um arquivo temporário no script sem usar um nome exclusivo, isso pode fazer com que algumas dessas verificações, como o Flaky, falhem.
Para contornar isso, use a variável de ambiente $CLICKHOUSE_TEST_UNIQUE_NAME para dar aos arquivos temporários um nome exclusivo para o teste em execução.
Assim, você garante que o arquivo criado durante a configuração ou removido durante a limpeza está sendo usado apenas por aquele teste, e não por outro teste em execução em paralelo.
Bugs conhecidos
Se conhecemos bugs que podem ser facilmente reproduzidos por testes funcionais, colocamos testes funcionais preparados no diretório tests/queries/bugs.
Esses testes serão movidos para tests/queries/0_stateless quando os bugs forem corrigidos.
Testes de integração
Os testes de integração permitem testar o ClickHouse em uma configuração de cluster e a interação do ClickHouse com outros servidores, como MySQL, Postgres e MongoDB. Eles são úteis para simular partições de rede, perda de pacotes etc. Esses testes são executados com Docker e criam vários contêineres com diferentes softwares.
Consulte tests/integration/README.md para saber como executar esses testes.
Observe que a integração do ClickHouse com drivers de terceiros não é testada. Além disso, no momento, não temos testes de integração com nossos drivers JDBC e ODBC.
Testes unitários
Os testes unitários são úteis quando você quer testar não o ClickHouse como um todo, mas uma única biblioteca ou classe isolada.
Você pode habilitar ou desabilitar a compilação dos testes com a opção ENABLE_TESTS do CMake.
Os testes unitários (e outros programas de teste) ficam nos subdiretórios tests espalhados pelo código.
Para executar os testes unitários, digite ninja test.
Alguns testes usam gtest, mas outros são apenas programas que retornam um código de saída diferente de zero em caso de falha.
Não é necessário ter testes unitários se o código já estiver coberto por testes funcionais (e os testes funcionais geralmente são muito mais simples de usar).
Você pode executar verificações individuais do gtest chamando o executável diretamente, por exemplo:
$ ./src/unit_tests_dbms --gtest_filter=LocalAddress*Testes de desempenho
Os testes de desempenho permitem medir e comparar o desempenho de partes isoladas do ClickHouse com consultas sintéticas.
Os testes de desempenho ficam em tests/performance/.
Cada teste é representado por um arquivo .xml com a descrição do caso de teste.
Os testes são executados com a ferramenta docker/test/performance-comparison. Consulte o arquivo readme para ver como executá-la.
Cada teste executa uma ou mais consultas (possivelmente com combinações de parâmetros) em loop.
Se você quiser melhorar o desempenho do ClickHouse em algum cenário, e se as melhorias puderem ser observadas em consultas simples, é altamente recomendável escrever um teste de desempenho.
Além disso, também é recomendável escrever testes de desempenho ao adicionar ou modificar funções SQL relativamente isoladas e não muito obscuras.
Sempre vale a pena usar perf top ou outras ferramentas perf durante os testes.
Ferramentas e scripts de teste
Alguns programas no diretório tests não são testes prontos, e sim ferramentas de teste.
Por exemplo, para Lexer existe a ferramenta src/Parsers/tests/lexer, que apenas faz a tokenização da entrada padrão e grava o resultado colorido na saída padrão.
Você pode usar ferramentas desse tipo como exemplos de código e para exploração e testes manuais.
Testes diversos
Há testes para modelos de aprendizado de máquina em tests/external_models.
Esses testes não são mantidos atualizados e devem ser transferidos para os testes de integração.
Há um teste separado para inserts com quórum.
Esse teste executa um cluster do ClickHouse em servidores separados e emula vários casos de falha: partição de rede, perda de pacotes (entre nós do ClickHouse, entre o ClickHouse e o ZooKeeper, entre o servidor ClickHouse e o cliente etc.), kill -9, kill -STOP e kill -CONT, como o Jepsen. Em seguida, o teste verifica se todos os inserts confirmados foram gravados e se todos os inserts rejeitados não foram.
Teste manual
Ao desenvolver um novo recurso, também faz sentido testá-lo manualmente. Você pode fazer isso seguindo estas etapas:
Compile o ClickHouse. Execute o ClickHouse no terminal: mude para o diretório programs/clickhouse-server e execute-o com ./clickhouse-server. Por padrão, ele usará a configuração (config.xml, users.xml e os arquivos nos diretórios config.d e users.d) do diretório atual. Para se conectar ao servidor ClickHouse, execute programs/clickhouse-client/clickhouse-client.
Observe que todas as ferramentas do ClickHouse (servidor, cliente etc.) são apenas links simbólicos para um único binário chamado clickhouse.
Você pode encontrar esse binário em programs/clickhouse.
Todas as ferramentas também podem ser invocadas como clickhouse tool em vez de clickhouse-tool.
Como alternativa, você pode instalar o pacote do ClickHouse: seja a versão estável do repositório do ClickHouse, seja compilando o pacote por conta própria com ./release na raiz do código-fonte do ClickHouse.
Em seguida, inicie o servidor com sudo clickhouse start (ou stop para parar o servidor).
Verifique os logs em /etc/clickhouse-server/clickhouse-server.log.
Quando o ClickHouse já estiver instalado no seu sistema, você pode compilar um novo binário clickhouse e substituir o binário existente:
$ sudo clickhouse stop
$ sudo cp ./clickhouse /usr/bin/
$ sudo clickhouse startVocê também pode parar o clickhouse-server do sistema e executar sua própria instância com a mesma configuração, mas com logging no terminal:
$ sudo clickhouse stop
$ sudo -u clickhouse /usr/bin/clickhouse server --config-file /etc/clickhouse-server/config.xmlExemplo com gdb:
$ sudo -u clickhouse gdb --args /usr/bin/clickhouse server --config-file /etc/clickhouse-server/config.xmlSe o clickhouse-server do sistema já estiver em execução e você não quiser pará-lo, pode alterar os números de porta no seu config.xml (ou sobrescrevê-los em um arquivo no diretório config.d), definir o caminho de dados adequado e executá-lo.
O binário clickhouse quase não tem dependências e funciona em uma ampla variedade de distribuições Linux.
Para testar suas alterações de forma rápida e prática em um servidor, você pode simplesmente usar scp para copiar seu binário clickhouse recém-compilado para o servidor e então executá-lo como nos exemplos acima.
Testes de build
Os testes de build permitem verificar se a build não está com problemas em várias configurações alternativas e em alguns outros sistemas. Esses testes também são automatizados.
Exemplos:
- compilação cruzada para Darwin x86_64 (macOS)
- compilação cruzada para FreeBSD x86_64
- compilação cruzada para Linux AArch64
- build no Ubuntu com bibliotecas de pacotes do sistema (desaconselhado)
- build com vinculação dinâmica de bibliotecas (desaconselhado)
Por exemplo, fazer build com pacotes do sistema é uma prática ruim, porque não podemos garantir qual versão exata dos pacotes um sistema terá. Mas isso é realmente necessário para os mantenedores do Debian. Por esse motivo, pelo menos temos que oferecer suporte a essa variante de build. Outro exemplo: a vinculação dinâmica é uma fonte comum de problemas, mas é necessária para alguns entusiastas.
Embora não possamos executar todos os testes em todas as variantes de build, queremos verificar pelo menos que as várias variantes de build não estejam com problemas. Para isso, usamos testes de build.
Também testamos se não há unidades de tradução longas demais para compilar ou que exijam RAM demais.
Também testamos se não há frames de pilha grandes demais.
Testando a compatibilidade do protocolo
Quando estendemos o protocolo de rede do ClickHouse, testamos manualmente se o clickhouse-client antigo funciona com o clickhouse-server novo e se o clickhouse-client novo funciona com o clickhouse-server antigo (simplesmente executando os binários dos pacotes correspondentes).
Também testamos alguns casos automaticamente com testes de integração:
- se os dados gravados por uma versão antiga do ClickHouse podem ser lidos com sucesso pela nova versão;
- se as consultas distribuídas funcionam em um cluster com diferentes versões do ClickHouse.
Ajuda do compilador
O código principal do ClickHouse (ou seja, o que fica no diretório src) é compilado com -Wall -Wextra -Werror e com alguns avisos adicionais habilitados.
No entanto, essas opções não são habilitadas para bibliotecas de terceiros.
O Clang tem ainda mais avisos úteis — você pode procurá-los com -Weverything e escolher alguns para incluir na compilação padrão.
Sempre usamos clang para compilar o ClickHouse, tanto em desenvolvimento quanto em produção.
Você pode compilar na sua própria máquina no modo de depuração (para economizar a bateria do seu laptop), mas observe que o compilador é capaz de gerar mais avisos com -O3 devido ao melhor controle de fluxo e à análise interprocedural.
Ao compilar com clang no modo de depuração, é usada a versão de depuração do libc++, o que permite detectar mais erros em tempo de execução.
Sanitizers
Sanitizador de endereços
Executamos testes funcionais, de integração, de estresse e unitários sob ASan a cada commit.
Sanitizador de threads
Executamos testes funcionais, de integração, de estresse e unitários com o TSan a cada commit.
Sanitizador de memória
Executamos testes funcionais, de integração, de estresse e unitários com o MSan a cada commit.
Sanitizador de comportamento indefinido
Executamos testes funcionais, de integração, de estresse e unitários com o UBSan a cada commit. O código de algumas bibliotecas de terceiros não é instrumentado para detectar UB.
Valgrind (memcheck)
Costumávamos executar os testes funcionais no Valgrind durante a noite, mas isso não é mais feito.
Isso leva várias horas.
Atualmente, há um falso positivo conhecido na biblioteca re2; veja este artigo.
Fuzzing
O fuzzing do ClickHouse é implementado tanto com libFuzzer quanto com consultas SQL aleatórias. Todos os testes de fuzzing devem ser executados com sanitizers (Address e Undefined).
O LibFuzzer é usado para testes de fuzzing isolados de código de biblioteca.
Os fuzzers são implementados como parte do código de teste e têm o sufixo "_fuzzer" no nome.
Um exemplo de fuzzer pode ser encontrado em src/Parsers/fuzzers/lexer_fuzzer.cpp.
As configurações, os dicionários e o corpus específicos do LibFuzzer são armazenados em tests/fuzz.
Incentivamos você a escrever testes de fuzzing para toda funcionalidade que lide com entrada do usuário.
Os fuzzers não são compilados por padrão.
Para compilar os fuzzers, as opções -DENABLE_FUZZING=1 e -DENABLE_TESTS=1 devem ser definidas.
Recomendamos desabilitar o Jemalloc ao compilar os fuzzers.
A configuração usada para integrar o fuzzing do ClickHouse ao
Google OSS-Fuzz pode ser encontrada em docker/fuzz.
Também usamos um teste de fuzzing simples para gerar consultas SQL aleatórias e verificar se o servidor não falha ao executá-las.
Você pode encontrá-lo em 00746_sql_fuzzy.pl.
Esse teste deve ser executado continuamente (durante a noite e por mais tempo).
Também usamos um fuzzer de consultas sofisticado baseado em AST, capaz de encontrar uma enorme quantidade de casos extremos. Ele faz permutações e substituições aleatórias na AST das consultas. Ele reutiliza nós da AST de testes anteriores no fuzzing de testes subsequentes, processando-os em ordem aleatória. Você pode saber mais sobre esse fuzzer neste artigo do blog.
Teste de estresse
Testes de estresse são outro caso de fuzzing. Executa todos os testes funcionais em paralelo, em ordem aleatória, com um único servidor. Os resultados dos testes não são verificados.
Verifica-se que:
- o servidor não trava, e nenhum trap de depuração ou do sanitizer é acionado;
- não há deadlocks;
- a estrutura do banco de dados é consistente;
- o servidor pode ser encerrado com sucesso após o teste e iniciado novamente sem exceções.
Há cinco variantes (Debug, ASan, TSan, MSan, UBSan).
Thread fuzzer
Thread Fuzzer (por favor, não confunda com o sanitizador de thread) é outro tipo de fuzzing que permite randomizar a ordem de execução das threads. Ele ajuda a encontrar ainda mais casos de borda.
Auditoria de segurança
Nossa Equipe de Segurança fez uma análise geral básica dos recursos do ClickHouse sob a ótica da segurança.
Analisadores estáticos
Executamos o clang-tidy a cada commit.
As verificações do clang-static-analyzer também estão ativadas.
O clang-tidy também é usado para algumas verificações de estilo.
Avaliamos clang-tidy, Coverity, cppcheck, PVS-Studio, tscancode, CodeQL.
Você encontrará instruções de uso no diretório tests/instructions/.
Se você usa o CLion como IDE, pode aproveitar algumas verificações do clang-tidy prontas para uso.
Também usamos shellcheck para a análise estática de scripts shell.
Endurecimento
Na compilação de depuração, usamos um allocator personalizado que aplica ASLR às alocações em nível de usuário.
Também protegemos manualmente regiões de memória que devem permanecer somente leitura após a alocação.
Na compilação de depuração, também incluímos uma customização da libc que garante que nenhuma função "prejudicial" (obsoleta, insegura, não segura para threads) seja chamada.
Asserções de depuração são amplamente usadas.
Na compilação de depuração, se for lançada uma exceção com o código "logical error" (o que implica um bug), o programa é encerrado imediatamente. Isso permite usar exceções na compilação de release, mas tratá-las como uma asserção na compilação de depuração.
A versão de depuração do jemalloc é usada nas compilações de depuração. A versão de depuração do libc++ é usada nas compilações de depuração.
Verificações de integridade em tempo de execução
Os dados armazenados em disco têm checksum. Os dados em tabelas MergeTree têm checksum de três formas simultaneamente* (blocos de dados comprimidos, blocos de dados descomprimidos, checksum total dos blocos). Os dados transferidos pela rede entre cliente e servidor ou entre servidores também têm checksum. A replicação garante que os dados nas réplicas sejam idênticos bit a bit.
Isso é necessário para proteger contra falhas de hardware (bit rot em mídias de armazenamento, inversões de bits na RAM do servidor, inversões de bits na RAM do controlador de rede, inversões de bits na RAM do switch de rede, inversões de bits na RAM do cliente, inversões de bits no wire). Observe que inversões de bits são comuns e podem ocorrer até mesmo com RAM ECC e na presença de checksums TCP (se você consegue executar milhares de servidores processando petabytes de dados todos os dias). Veja o vídeo (russo).
O ClickHouse fornece diagnósticos que ajudam engenheiros de operações a identificar hardware defeituoso.
- e isso não é lento.
Estilo de código
As regras de estilo de código estão descritas aqui.
Para verificar algumas violações comuns de estilo, você pode usar o script utils/check-style.
Para garantir o estilo adequado do seu código, você pode usar clang-format.
O arquivo .clang-format fica na raiz do código-fonte.
Ele corresponde, em grande parte, ao nosso estilo de código atual.
Mas não é recomendado aplicar clang-format a arquivos existentes, porque isso piora a formatação.
Você pode usar a ferramenta clang-format-diff, que pode ser encontrada no repositório do código-fonte do clang.
Como alternativa, você pode tentar usar a ferramenta uncrustify para reformatar seu código.
A configuração está em uncrustify.cfg, na raiz do código-fonte.
Ela é menos testada do que clang-format.
O CLion tem seu próprio formatador de código, que precisa ser ajustado ao nosso estilo de código.
Cobertura de testes
Também monitoramos a cobertura de testes, mas apenas dos testes funcionais e apenas do clickhouse-server. Ela é realizada diariamente.
Testes dos testes
Há uma verificação automatizada para detectar testes instáveis. Ela executa todos os novos testes 100 vezes (nos testes funcionais) ou 10 vezes (nos testes de integração). Se o teste falhar ao menos uma vez, ele é considerado instável.
Automação de testes
Executamos os testes com GitHub Actions.
Os jobs de build e os testes são executados no Sandbox a cada commit. Os pacotes gerados e os resultados dos testes são publicados no GitHub e podem ser baixados por links diretos. Os artefatos ficam armazenados por vários meses. Ao enviar um pull request no GitHub, nós o marcamos como "can be tested", e nosso sistema de CI compila pacotes do ClickHouse (release, debug, com address sanitizer etc.) para você.