Skip to main content

Status experimental

A API do cliente TCP nativo é experimental. Sua interface pública pode mudar em um lançamento futuro.
As interfaces principais do cliente, da fonte de dados, da sessão e das operações, bem como as extensões de injeção de dependência, geram o aviso do compilador CHTCP0001. Para indicar que você está ciente desse aviso durante a avaliação da API, adicione-o a NoWarn no arquivo do projeto:
Suprima o aviso somente depois de aceitar que o código que usa a API pode precisar de alterações em um lançamento futuro.

Versões do .NET compatíveis

O cliente TCP nativo é compatível com as seguintes versões do .NET:
  • .NET 8.0
  • .NET 9.0
  • .NET 10.0

Versões compatíveis do ClickHouse

O cliente TCP oferece suporte oficial às duas versões LTS mais recentes e aos três lançamentos mais recentes do servidor ClickHouse.

Instalação

O cliente TCP nativo está incluído no pacote ClickHouse.Driver. Instale-o pelo NuGet:
Ou pelo NuGet Package Manager:

Início rápido

Migração do cliente HTTP

A API TCP é independente de IClickHouseClient; não é uma opção de transporte para ClickHouseClient nem para ClickHouseConnection. A maior parte do código SQL pode ser reutilizada, mas é necessário criar um ClickHouseTcpClient e migrar cada operação para seu equivalente em TCP.

Métodos equivalentes

Por exemplo, um loop de leitura com o cliente HTTP:
passa a ser um fluxo assíncrono de linhas via TCP:

Opções e parâmetros

As classes HTTP QueryOptions e ClickHouseParameterCollection não são intercambiáveis com suas equivalentes TCP. Transfira os valores para ClickHouseTcpQueryOptions, onde as configurações são strings e os parâmetros fazem parte do objeto de opções:
O cliente TCP oferece suporte aos placeholders {name:Type} nativos do ClickHouse, mas não reescreve placeholders @name no estilo ADO.NET. Ele também não permite definir, por consulta, banco de dados, função, bearer token, cabeçalho personalizado, resolvedor ou formatador de parâmetros nem conversor de leitura. Consulte as limitações para saber quais recursos continuam disponíveis apenas via HTTP.

Ciclo de vida da conexão e dos resultados

Ambos os clientes de alto nível são thread-safe e devem ser reutilizados. O cliente TCP mantém conexões nativas persistentes, em vez de conexões HTTP, implementa IAsyncDisposable e normalmente deve ser descartado de forma assíncrona quando a aplicação é encerrada. As linhas de QueryAsync pertencem a quem faz a chamada. Os blocos de StreamAsync e suas colunas são emprestados e válidos apenas durante a iteração atual; copie os valores que precisarão continuar disponíveis depois dela. Sempre conclua ou descarte a enumeração para que a conexão retorne ao pool. Use OpenSessionAsync quando várias operações precisarem ser executadas na mesma conexão e preservar o estado da sessão. Consulte o exemplo completo de migração de HTTP para TCP para ver os dois clientes em uso com a mesma tabela.

Configuração

Há duas maneiras de configurar um cliente TCP nativo:
  • String de conexão: Pares de chave/valor separados por ponto e vírgula que especificam o endpoint do servidor, as credenciais de autenticação e outras opções de conexão.
  • Objeto ClickHouseTcpClientOptions: Um objeto de configuração imutável e fortemente tipado definido no código.
Passe uma das duas formas ao construtor de ClickHouseTcpClient. Use ClickHouseTcpConnectionStringBuilder para interpretar ou criar uma string de conexão e convertê-la em ClickHouseTcpClientOptions.
As seções a seguir apresentam as configurações disponíveis, seus valores padrão e os efeitos de cada uma.

Configurações de conexão

Os valores das propriedades TimeSpan na string de conexão são expressos em segundos.

Formato de dados e serialização

Pool de conexões

Cada conexão executa uma operação por vez. O pool permite executar operações simultaneamente e reutilizar as conexões entre elas.

Segurança

Sem TLS, a senha e todos os dados das consultas trafegam pela rede sem criptografia. Mantenha TlsAllowInvalidCertificates definido como false fora do ambiente de desenvolvimento local.
O protocolo nativo seguro usa um endpoint separado no servidor, não uma atualização da conexão existente. Um cliente TLS deve se conectar a uma porta segura do protocolo nativo; ele não passa a usar uma conexão sem criptografia. ConfigureTls é aplicado por último e pode substituir a validação do certificado e do nome do host. Por isso, revise com cuidado os callbacks personalizados.

Logging e depuração

O logger do cliente registra eventos do ciclo de vida e das operações no lado do cliente. Já os pacotes de log do servidor são enviados para ClickHouseTcpQueryCallbacks.OnLog. Como o texto da instrução pode conter dados confidenciais, ele é excluído das tags de atividade por padrão e bastante truncado nos logs de depuração.

Configurações personalizadas

ClickHouseTcpClientOptions.CustomSettings aplica as configurações do servidor ClickHouse a todas as consultas e inserções. Em cada operação, os valores de ClickHouseTcpQueryOptions.Settings substituem os valores de nível do cliente que tenham o mesmo nome.
Em uma string de conexão, adicione o prefixo set_ a cada configuração do servidor:
Não inclua o prefixo set_ ao adicionar uma configuração a CustomSettings ou às Settings de cada operação.

ClickHouseTcpQueryOptions

ClickHouseTcpQueryOptions permite definir configurações para cada operação. Todas as propriedades são opcionais.

ClickHouseTcpInsertOptions

ClickHouseTcpInsertOptions estende ClickHouseTcpQueryOptions com configurações para inserts nativos em blocos. Todas as propriedades das opções de consulta também estão disponíveis.

ClickHouseTcpClient

ClickHouseTcpClient é a API de alto nível para o protocolo TCP nativo. Executa instruções e consultas escalares, transmite resultados como blocos colunares, linhas ou POCOs, insere dados colunares ou organizados em linhas e abre sessões vinculadas a uma conexão. O cliente é thread-safe e mantém um pool de conexões. Crie um cliente por endpoint, compartilhe-o por toda a aplicação e libere seus recursos ao encerrá-la. As operações são executadas em paralelo até o limite de MaxPoolSize; as demais aguardam uma conexão por até PoolTimeout.

Criando um cliente

Crie um cliente usando uma string de conexão ou ClickHouseTcpClientOptions. Consulte Configuração para conhecer as configurações disponíveis. Usando uma string de conexão:
Usando opções fortemente tipadas:
Prefira o descarte assíncrono. Ao descartar o cliente, as conexões ociosas são fechadas e as operações ativas são aguardadas por até PoolTimeout antes de serem abortadas.

Injeção de dependência

AddClickHouseTcpDataSource registra uma única instância de fonte de dados e disponibiliza o cliente dessa instância como IClickHouseTcpClient e IClickHouseTcpOperations:
O provedor de serviços é responsável pela fonte de dados e pelo pool de conexões. Não descarte um cliente injetado; descarte o provedor ao encerrar a aplicação. Se as opções não especificarem um LoggerFactory, o registro usará o ILoggerFactory do contêiner. Uma serviceKey pode registrar várias fontes de dados independentes.

Executando consultas

Use ExecuteAsync para instruções que não retornam linhas, como instruções DDL e mutações:
O método é concluído quando o servidor confirma a instrução. Se a instrução retornar linhas, ExecuteAsync as lê e descarta. Para INSERT INTO ... VALUES, use InsertAsync ou InsertRowsAsync e encerre a instrução SQL em VALUES, sem incluir valores diretamente na instrução.

Leitura de dados

Escolha a API de leitura conforme a estrutura dos dados de que a aplicação precisa:

Leituras escalares

ExecuteScalarAsync retorna a primeira coluna da primeira linha como um object:
O método lê a resposta inteira e descarta todos os valores após o primeiro. Escreva uma consulta que retorne apenas uma linha, em vez de usar o método para interromper a leitura de um resultado grande. Tanto um primeiro valor NULL quanto uma consulta que não retorna nenhuma linha produzem null.

Leitura de linhas

QueryAsync fornece cada linha como um object[] independente, com os valores na ordem das colunas do resultado:
O array da linha continua válido após o avanço da enumeração. Esse nível é conveniente quando não há um modelo de resultado, mas aloca um array por linha e encapsula os tipos de valor em objetos.

Leitura de POCOs

QueryAsync<T> mapeia as colunas para propriedades públicas que podem ser alteradas. A correspondência entre os nomes ignora diferenças entre maiúsculas e minúsculas e caracteres de sublinhado; use ClickHouseTcpColumn para definir um nome explícito e ClickHouseTcpNotMapped para excluir uma propriedade.
O mapeamento de POCOs faz conversões e aloca um objeto por linha.

Leituras de blocos colunares

StreamAsync disponibiliza o resultado no formato colunar nativo como uma sequência de valores Block. Essa é a forma mais rápida de ler os dados.
Os blocos são emprestados. Um bloco, suas colunas e os segmentos de valores dessas colunas só são válidos durante a iteração atual. Não os descarte nem os mantenha após a iteração; copie explicitamente para uma área de memória sob seu controle os dados que precisarem continuar disponíveis depois dela.
Sempre conclua a enumeração ou descarte o enumerador para que a conexão retorne ao pool.
Block.Column<T> acessa o tipo CLR produzido pelo decodificador. Use Block.ReadAs<T> ou uma interface especializada, como IDateTimeColumn, quando precisar converter os valores.

Inserção de dados

As inserções nativas enviam dados em blocos colunares no formato wire. O SQL deve ser uma instrução INSERT INTO ... VALUES sem uma lista de valores na própria instrução. Use colunas tipadas para minimizar o processamento no lado do cliente ou as sobrecargas para linhas e objetos POCO quando a aplicação já tiver dados organizados em linhas.

Inserções colunares

InsertAsync aceita dados já organizados em colunas com tipos definidos. Essa é a maneira mais rápida de inserir dados.
As colunas são associadas à lista de colunas da instrução SQL pelo nome, não pela ordem dos argumentos, e devem ter o mesmo número de linhas. O servidor fornece os valores padrão para as colunas da tabela omitidas da instrução. Não modifique os arrays do chamador até que a inserção seja concluída. Esta API dispensa a conversão de linhas em colunas e o encapsulamento dos tipos de valor exigidos pelas inserções por linha. Uma coluna lida de um bloco de resultados emprestado também pode ser passada diretamente a InsertAsync, desde que a inserção seja concluída antes que esse bloco seja liberado.

Inserção de linhas

InsertRowsAsync aceita linhas do tipo object[]. Os valores são associados às colunas da lista SQL conforme a posição:
As linhas são convertidas em colunas, um bloco por vez. Cada linha deve conter um valor para cada coluna indicada na instrução, e os valores de uma mesma coluna devem ter o mesmo tipo CLR, exceto quando o destino for Variant ou Dynamic. Não modifique as linhas até que a operação seja concluída.

Inserção com POCO

A sobrecarga genérica InsertRowsAsync<T> mapeia as colunas de destino para propriedades públicas de leitura, seguindo as mesmas regras de nomenclatura e atributos usadas nas leituras com POCO:
Cada coluna citada na instrução deve corresponder a um getter público compatível. As linhas POCO são convertidas um bloco wire por vez. Por isso, um valor inválido em um bloco posterior pode ser detectado após o envio dos blocos anteriores. ClickHouseTcpNotMapped exclui do mapeamento propriedades usadas apenas pela aplicação, como ImportBatch.

Parâmetros SQL

O cliente nativo vincula valores aos marcadores de posição {name:Type} do ClickHouse. Ele não reescreve marcadores de posição @name no estilo ADO.NET.
Cada parâmetro precisa ter um tipo especificado no placeholder SQL ou na propriedade ClickHouseTcpParameter.ClickHouseType. Use o tipo Identifier para nomes de tabelas e colunas. Quando um DateTime ou DateTimeOffset representar um instante específico, informe um fuso horário, como {timestamp:DateTime('UTC')}; caso contrário, o fuso horário da sessão do servidor poderá alterar sua interpretação.

ID da consulta

Toda operação tem um ID de consulta usado pelas tabelas de sistema do ClickHouse, pelos logs do cliente e pelos spans de rastreamento. Quando QueryId é nulo ou vazio, o cliente gera um novo GUID. Defina esse ID quando for necessário correlacionar a operação com um identificador da aplicação:

Progresso e metadados da consulta

Configure ClickHouseTcpQueryOptions.Callbacks para receber metadados intercalados com a resposta:
Os valores de progresso representam incrementos, não totais acumulados. Os callbacks são executados de forma síncrona na thread que lê a resposta, na ordem dos pacotes; mantenha-os rápidos e não lance exceções. Os blocos fornecidos a OnLog, OnProfileEvents, OnTotals e OnExtremes são emprestados e não devem ser usados após o retorno do callback. O ClickHouse não envia pacotes de progresso da consulta para linhas enviadas em uma inserção. Use OnBlockWritten para acompanhar os blocos enviados pelo cliente; ele informa o progresso da transmissão, mas não indica que o servidor confirmou a inserção.

Cancelamento

Todas as operações aceitam um CancellationToken:
Cancelar ou abandonar um resultado em streaming impede que a conexão seja reutilizada com segurança. Por isso, o cliente fecha essa conexão em vez de devolvê-la ao pool. O cliente continua disponível e abre ou reutiliza outra conexão para operações posteriores. Em uma sessão vinculada a uma conexão, o cancelamento pode encerrar a sessão e causar a perda das tabelas temporárias e configurações da sessão.

Informações do servidor e verificações de saúde

GetServerInfoAsync retorna a identidade, a versão, o fuso horário e as informações do protocolo negociado do servidor obtidas durante o handshake do protocolo nativo:
Condicione os recursos no nível do wire a ProtocolRevision, a revisão negociada entre o cliente e o servidor. Condicione os recursos SQL, como tipos de dados e funções, a Version. O método reutiliza uma conexão quando há uma disponível e abre outra quando o pool está vazio. Use PingAsync para verificar se o serviço está pronto para receber tráfego pelo protocolo nativo:
Um ping bem-sucedido confirma a conectividade nativa e a autenticação, mas não verifica o acesso a uma tabela da aplicação.

Mais exemplos

Para ver exemplos completos e prontos para execução sobre leituras, inserções, tipos, sessões, TLS, observabilidade e tratamento de erros, consulte os exemplos de TCP nativo no repositório do GitHub.

Sessões

As operações comuns do cliente utilizam qualquer conexão disponível no pool. Por isso, o estado local da conexão não fica isolado nem tem garantia de persistir entre chamadas. As conexões do pool podem se conectar a servidores diferentes. Abra uma sessão quando tabelas temporárias, instruções SET ou outra sequência de operações precisarem usar a mesma conexão.
IClickHouseTcpSession oferece as mesmas APIs de consulta, execução, inserção, ping e informações do servidor que o cliente. A sessão não é segura para acesso por múltiplas threads. Suas operações são sequenciais: não é permitido iniciar outra operação antes que a atual termine. Um resultado em streaming mantém a sessão ocupada até ser totalmente percorrido ou até que seu enumerador seja descartado. Cada sessão ocupa um slot de MaxPoolSize durante todo o seu ciclo de vida. O cliente pode continuar usando outras conexões do pool, mas, quando há apenas uma conexão no pool, as demais operações do cliente aguardam até que a sessão seja descartada ou PoolTimeout expire. Descarte a sessão antes de descartar o cliente que a criou. Ao descartar a sessão, a conexão dedicada é fechada em vez de devolvida ao pool, impedindo que chamadas posteriores herdem suas tabelas temporárias ou configurações. Uma falha na conexão ou no protocolo, um cancelamento ou um fluxo de resultados incompleto pode tornar a sessão inutilizável; quando IsOpen passar a ser false, abra uma nova sessão.

Boas práticas

Ciclo de vida do cliente e pooling

Crie um ClickHouseTcpClient ou ClickHouseTcpDataSource para cada combinação de endpoint e configuração e reutilize-o durante todo o ciclo de vida da aplicação. O cliente é thread-safe e mantém seu próprio pool de conexões; criar um cliente por operação gera um novo pool a cada vez e impede a reutilização das conexões. Em uma aplicação com injeção de dependências, registre AddClickHouseTcpDataSource uma única vez e injete IClickHouseTcpClient. O provedor de serviços é responsável pelo ciclo de vida do cliente, portanto os componentes que o utilizam não devem descartar a instância injetada. Fora da injeção de dependências, descarte o cliente ao encerrar a aplicação com await using. Cada conexão nativa executa uma operação por vez. Portanto, MaxPoolSize limita tanto o número de conexões abertas quanto o de operações simultâneas; as demais chamadas aguardam uma vaga por até PoolTimeout. Escolha um valor que atenda à demanda real por operações simultâneas da aplicação sem exceder os limites de conexões e consultas do lado do servidor. Aumentar esse valor não acelera uma consulta individual. Considere estas situações em que o pool é utilizado:
  • Um resultado em streaming mantém sua conexão até que seu enumerador seja descartado.
  • Uma sessão mantém uma conexão durante todo o seu ciclo de vida e aceita uma operação por vez.
  • Se um resultado for abandonado, uma operação em andamento for cancelada ou ocorrer uma falha de protocolo ou conexão, a conexão será fechada em vez de reutilizada.
Descarte as sessões assim que não forem mais necessárias e sempre percorra os resultados assíncronos até o fim ou descarte o enumerador. Use a categoria de logging ClickHouse.Driver.Tcp.Pool para diagnosticar o esgotamento do pool, o fechamento inesperado de conexões ou reconexões frequentes.

Tempos limite e cancelamento

O cliente TCP tem limites distintos para cada tipo de espera: Uma obtenção de conexão que primeiro espera por uma vaga no pool e depois abre uma nova conexão pode levar até PoolTimeout + DialTimeout. ReadTimeout é reiniciado a cada leitura do transporte. Portanto, uma consulta longa que continua enviando informações de progresso ou dados pode durar mais que esse valor. Configure-o com um valor alto o suficiente para períodos normais sem resposta do servidor; use TimeSpan.Zero somente quando outro tempo limite restringir de forma confiável a duração da operação. Use um token de cancelamento quando precisar de um tempo limite para toda a operação:
O cliente tenta enviar um pacote Cancel nativo quando é seguro fazê-lo e, em seguida, fecha a conexão cuja resposta não foi totalmente consumida. As operações seguintes no cliente com pool de conexões continuam funcionando, mas o cancelamento pode tornar uma sessão fixa inutilizável. Uma inserção cancelada ou que excedeu o tempo limite pode ter um resultado incerto; siga as orientações para tentar novamente a inserção em vez de reexecutá-la sem verificar o resultado.

Tratamento de data e hora

Prefira DateTime('UTC') ou DateTime64(S, 'UTC') para timestamps e use DateTimeOffset ou um DateTime cujo Kind seja Utc no código da aplicação. Isso deixa explícito o instante representado e evita a dependência do fuso horário do servidor ou da sessão. O cliente TCP aplica estas regras ao gravar valores DateTime: Uma hora inexistente devido à transição para o horário de verão é rejeitada porque não corresponde a nenhum instante. Se a hora for ambígua no retorno do horário de verão, é escolhida a primeira ocorrência. Use DateTimeOffset quando a escolha precisar ser explícita. Para parâmetros SQL que representam um instante, inclua o fuso horário no marcador de posição:
Nas leituras colunares, DateTime e DateTime64 expõem, por padrão, suas contagens brutas como uint ou long. Use Block.ReadAs<DateTimeOffset>, IDateTimeColumn.GetDateTimeOffset ou uma propriedade POCO correspondente quando for mais conveniente trabalhar com uma data e hora. Mantenha a contagem bruta quando cada dígito importar: DateTimeOffset tem resolução de 100 nanossegundos. Por isso, ler DateTime64 com escala 8 ou 9 por meio dele faz perder a precisão das frações de tick. Use DateOnly para Date e Date32. Use TimeSpan para valores de Time e Time64 que possam ser negativos ou ultrapassar 24 horas; TimeOnly só é adequado quando a coluna está limitada a um horário do dia.

Streaming e ciclo de vida dos resultados

Use StreamAsync e colunas tipadas para processar dados com alta vazão. Essa abordagem acompanha a organização colunar do ClickHouse e evita arrays por linha, boxing e alocação de POCOs. Use QueryAsync<T> quando for mais importante ter um modelo próprio da aplicação do que minimizar as alocações. Use a versão não tipada de QueryAsync apenas quando o schema não for conhecido em tempo de compilação. Os blocos retornados por StreamAsync são emprestados. O bloco, suas colunas, as colunas filhas de tipos compostos e todos os spans são válidos apenas durante a iteração atual do laço. Processe os dados ali mesmo ou copie os valores que precisam permanecer disponíveis após a iteração:
Não descarte por conta própria um bloco produzido pela iteração. Deixe que await foreach descarte o enumerador. Se você precisar de apenas parte do resultado, use LIMIT no SQL e selecione somente as colunas necessárias. Interromper a iteração antes do fim cancela o restante da resposta e impede a reutilização da conexão.

Formato da inserção e agrupamento em lote

Prefira InsertAsync com colunas tipadas quando os dados já estiverem em formato colunar. Essa opção exige menos conversões no cliente. InsertRowsAsync<T> é a escolha natural para dados POCO; linhas object[] sem tipagem oferecem mais flexibilidade, mas alocam arrays e fazem o boxing de tipos de valor. Envie uma quantidade significativa de linhas por chamada de inserção, em vez de fazer muitas inserções pequenas. Em cada chamada, MaxRowsPerBlock divide os dados em blocos no formato wire nativo. O valor padrão de 50.000 linhas também limita o tamanho dos buffers temporários de coluna usados na conversão de entradas em formato de linha e POCO. Aumentá-lo pode melhorar a vazão para linhas com poucas colunas, mas aumenta o pico de uso de memória; defini-lo como null grava toda a inserção em um único bloco e converte de uma só vez toda a entrada orientada a linhas desse bloco. MaxSendBufferBytes limita, de forma independente, a quantidade de dados codificados mantidos no buffer antes de serem gravados no socket. É um limite flexível verificado entre colunas, portanto uma única coluna larga pode ultrapassá-lo. Entradas em formato de linha e POCO são convertidas um bloco por vez. Assim, um valor inválido em um bloco posterior pode ser detectado depois que os blocos anteriores já tiverem sido enviados. Valide a possibilidade de valores nulos e os tipos CLR antes da inserção quando a qualidade dos dados for incerta e defina um DeduplicationToken derivado dos dados sempre que o lote lógico puder ser inserido novamente.

Tipos de dados compatíveis

As tabelas abaixo diferenciam o tipo retornado pelo IColumn<T> decodificado dos tipos adicionais disponíveis por meio da conversão com Block.ReadAs<T>, do mapeamento de POCO com QueryAsync<T> ou de inserções. Você pode consultar o mesmo resolvedor usado por essas operações para verificar se um determinado mapeamento é compatível:
Os mapeamentos de Nullable, Array, Tuple, Map e LowCardinality também se aplicam recursivamente aos seus tipos internos.

Mapeamento de tipos: leitura a partir do ClickHouse

Default .NET type corresponde ao T exposto por Block.Column<T> e IColumn<T>. Os tipos listados na coluna Also readable as podem ser lidos por meio de Block.ReadAs<T> e das propriedades de POCO.

Tipos inteiros

Tipos de ponto flutuante

Tipos decimais

ClickHouseTcpDecimal armazena uma mantissa representada por um inteiro de tamanho arbitrário e uma escala, preservando valores cuja precisão excede a capacidade de representação de decimal.

Tipo booleano

Tipos de string

Use IStringColumn.GetBytes(row) ou leia os dados como byte[] quando eles puderem não ser UTF-8 válido. FixedString(N) preserva todos os N bytes, inclusive os bytes zero no final.

Tipos de data e hora

A representação como inteiro bruto é o padrão para que as leituras colunares preservem o valor wire exato. As conversões de data e hora usam o fuso horário declarado no tipo ou, se não houver um, o fuso horário da sessão. A leitura de Time ou Time64 como TimeOnly lança uma exceção se o valor for negativo ou igual ou superior a 24 horas; use TimeSpan para abranger todo o intervalo do ClickHouse.

Tipos enum

IEnumColumn também expõe o mapeamento declarado entre rótulos e valores ordinais.

Outros tipos escalares

Tipos compostos

Tuplas podem conter até sete elementos. Uma coluna Nested pode ter qualquer número de campos e deve ser lida preferencialmente por meio de INestedColumn, que expõe as colunas planas dos campos nomeados e os offsets de linha compartilhados sem alocar registros encapsulados em objetos. Com a configuração padrão flatten_nested = 1 do ClickHouse, os campos aninhados chegam como colunas Array(T) separadas, cujos nomes contêm pontos.

Tipos Variant, Dynamic e JSON

Variant e Dynamic retornam o valor CLR do tipo selecionado em cada linha ou null. As visões colunares desses tipos expõem o fluxo de discriminadores e uma coluna filha tipada para cada tipo em tempo de execução, evitando o boxing no processamento de dados por coluna. O cliente TCP lê JSON usando a serialização de String do ClickHouse. Por padrão, o cliente habilita output_format_native_write_json_as_string = 1; se essa configuração for desabilitada, as leituras de JSON ficam sem suporte. Como o ClickHouse interpreta e normaliza o JSON, o texto retornado pode diferir do texto inserido quanto à ordem das chaves, aos espaços em branco e à formatação dos números.

Tipos geométricos

Geometry é representado por uma IVariantColumn que pode conter qualquer um dos seis tipos geométricos.

Tipo QBit

O cliente TCP oferece suporte à forma QBit(T, N), com dois argumentos e sem stride. O layout com três argumentos e stride não é compatível.

Tipos de AggregateFunction

Uma coluna AggregateFunction contém um estado intermediário específico da função. Finalize-o no servidor, por exemplo, com sumMerge(column), e leia o valor resultante.

Mapeamento de tipos: gravação no ClickHouse

Cada entrada representa o tipo CLR de uma linha da IColumn passada a InsertAsync. Esses mapeamentos são exatos: o cliente TCP não realiza coerções de tipo como as de Convert.

Tipos inteiros

Tipos de ponto flutuante

Ao gravar um BFloat16, cada valor float é truncado para a representação de ponto flutuante brain de 16 bits.

Tipos decimais

A gravação lança OverflowException quando a mantissa ajustada à escala excede a precisão declarada.

Tipo booleano

Tipos de string

FixedString(N) não aceita string de propósito; codifique o valor e complete-o com bytes explicitamente para que sua representação binária exata não deixe dúvidas.

Tipos de data e hora

Os valores inteiros brutos usam as unidades indicadas na tabela de leitura. DateTimeOffset preserva o instante. DateTime segue o valor de Kind; um valor Unspecified é interpretado como a hora local no fuso horário da coluna ou, se a coluna não especificar um, no fuso horário da sessão.

Tipos enum

Um rótulo desconhecido é rejeitado antes do envio dos dados.

Outros tipos escalares

Tipos compostos

O formato aceito é recursivo: Array(Nullable(DateTime)) aceita um DateTime?[] para cada linha, e Map(String, UInt32) aceita um KeyValuePair<string, uint>[]. As gravações diretas de Nested usam o formato de colunas densas retornado por uma leitura anterior. Para inserções criadas pela aplicação, mantenha a representação padrão flatten_nested = 1 e grave cada campo Array(T) cujo nome contém um ponto em sua própria coluna.

Tipos Variant, Dynamic e JSON

Para Variant, o tipo em tempo de execução deve corresponder ao tipo CLR padrão de uma única alternativa. Mapeamentos de conveniência, como gravar um DateTime em uma alternativa DateTime, não são usados dentro de uma variante; use o tipo bruto padrão da alternativa. Dynamic aceita valores escalares, além de arrays, maps e tuplas com suporte recursivo.

Tipo geométrico

Em uma coluna Geometry criada pela aplicação, apenas Point e MultiPolygon têm estruturas CLR exclusivas. Ring e LineString têm a mesma estrutura, assim como Polygon e MultiLineString. Por isso, o cliente não consegue inferir a qual dessas alternativas um valor object corresponde. Uma coluna Geometry decodificada preserva seus discriminadores e pode ser inserida novamente sem essa ambiguidade.

Tipo QBit

Tipos de AggregateFunction

Compressão

Por padrão, o cliente habilita a compressão e usa LZ4 nos blocos que grava; o ClickHouse também usa LZ4 por padrão nos blocos de resposta. A compressão se aplica aos blocos de resultado e aos blocos enviados em uma inserção, enquanto o texto da consulta, as informações de progresso, as exceções e os demais pacotes do protocolo mantêm sua codificação wire normal. Use a chave Compression da string de conexão para selecionar o codec usado pelo cliente:
A configuração equivalente e fortemente tipada usa IClickHouseCompressor:
Defina Compressor = null para desativar a compressão. Um compressor personalizado deve ser compatível com o enquadramento nativo de blocos; compressores que só oferecem suporte a corpos de requisições HTTP são rejeitados na criação do cliente.

Como selecionar o codec da resposta

Compressor determina como os blocos gravados pelo cliente são comprimidos. O pacote de consulta nativo solicita compressão ao servidor, mas não especifica um codec. Por isso, o ClickHouse seleciona o codec da resposta de acordo com a configuração network_compression_method. Por padrão, o servidor usa LZ4. Quando necessário, defina o codec da resposta para cada operação:
O LZ4 geralmente consome menos CPU e é a melhor opção padrão em redes rápidas. O ZSTD geralmente produz quadros menores, mas consome um pouco mais de CPU. Desativar a compressão pode ser vantajoso se o servidor estiver no mesmo host ou conectado por outro enlace muito rápido. Faça medições com dados representativos antes de alterar a configuração padrão.

Tratamento de erros

As falhas relatadas pelo ClickHouse ou pela conexão nativa derivam de ClickHouseTcpException, que, por sua vez, deriva de DbException: ClickHouseTcpServerException expõe RawCode, o código numérico exato enviado pelo servidor, e Code, o ClickHouseErrorCode correspondente, quando identificado pelo driver. Ela também contém o Name da exceção do servidor, ServerStackTrace e eventuais exceções aninhadas do servidor em InnerException.
Os códigos não representados por ClickHouseErrorCode têm Code == ClickHouseErrorCode.Unknown; consulte RawCode sem presumir que o enum inclua todos os erros do ClickHouse. Os erros de validação de argumentos e de ciclo de vida de objetos mantêm seus tipos habituais de exceção do .NET. O cancelamento gera OperationCanceledException. DialTimeout e ReadTimeout geram ClickHouseTcpConnectionException com uma TimeoutException como exceção interna, e a conexão é descartada. PoolTimeout gera uma TimeoutException simples, que não é uma ClickHouseTcpException: nenhuma conexão falhou. Para resolver o problema, reduza o número de operações simultâneas, aumente o pool ou conclua a enumeração dos resultados.

Como tentar operações novamente

Se ocorrer uma falha de conexão ou o tempo limite for excedido durante uma inserção, não será possível saber se ela foi concluída: o servidor pode ter confirmado as linhas antes que o cliente recebesse a resposta. Defina um DeduplicationToken estável para o lote lógico antes de tentar novamente:
Reutilize o mesmo token ao tentar novamente o envio desse lote e use outro token para novos dados. Para outras operações, tente novamente apenas se for seguro repetir a instrução e a falha específica for transitória; a maioria dos erros do servidor exige, em vez disso, alterar a consulta ou a configuração.

Logging e diagnóstico

O cliente TCP se integra ao Microsoft.Extensions.Logging. O logging é opcional; na ausência de uma LoggerFactory, o cliente não cria loggers nem formata mensagens de log.
Quando o cliente é registrado pelas extensões de injeção de dependência, ele usa o ILoggerFactory do contêiner, a menos que as opções fornecidas já especifiquem um.

Categorias de logging

Configure as categorias usando a configuração padrão de logging do .NET:
A mensagem de início da operação é registrada no nível Debug e inclui, no máximo, StatementMaxLength caracteres de SQL. O valor padrão é cinco caracteres. Defina o valor como zero ou menos para omitir o texto da instrução dos logs, ou aumente-o somente após considerar parâmetros e literais que possam conter dados sensíveis.

Mensagens de log do servidor

A integração de logging registra o comportamento do lado do cliente. Os pacotes do log de consultas do próprio ClickHouse são separados e enviados para ClickHouseTcpQueryCallbacks.OnLog. Habilite-os com a configuração do servidor send_logs_level:
O bloco de log é disponibilizado temporariamente e liberado quando o callback retorna. Processe-o de forma síncrona ou copie os valores que precisam ser mantidos. Os callbacks devem ser rápidos e não devem lançar exceções; uma exceção em um callback encerra a operação e a conexão associada.

OpenTelemetry

O cliente TCP emite spans de rastreamento distribuído por meio da API System.Diagnostics.Activity do .NET. Inscreva um provedor de rastreamento do OpenTelemetry na fonte de atividades TCP:
Para um aplicativo de console ou uma configuração manual:
O nome da fonte de dados de atividades é ClickHouse.Driver.Tcp, diferente da fonte de dados do cliente HTTP. Assim, é possível coletar dados de cada transporte de forma independente. Quando não há nenhum listener inscrito, as operações não geram atividades.

Spans e atributos

O cliente emite spans para operações SQL, verificações de conectividade e novas conexões. Um span SQL recebe o nome da palavra-chave que inicia a instrução, como SELECT ou INSERT; se não houver uma palavra-chave inicial reconhecível, o span recebe o nome query. Um span connect abrange a conexão via socket, a negociação TLS e o handshake nativo. Os spans de instruções também incluem o tempo de espera por uma conexão do pool. Os contadores só aparecem quando o servidor ou o cliente tem um valor relevante a informar. Por exemplo, uma inserção que envia blocos informa o número de linhas enviadas pelo cliente, mas não recebe contadores de leitura do servidor. Spans de operações que falharam também contêm um evento exception com o tipo da exceção, a mensagem e o rastreamento de pilha.

Texto SQL e dados sensíveis

Por padrão, o texto SQL não é incluído nos spans. Ative sua inclusão explicitamente e defina um limite adequado:
StatementMaxLength limita o tamanho do atributo db.query.text e do SQL nos logs de depuração. Um valor igual ou inferior a zero faz com que o texto SQL seja omitido de ambos. Revise também as mensagens de exceção e os rastreamentos de pilha exportados, pois os erros do servidor podem conter detalhes da consulta.

Propagação do contexto de rastreamento

Quando Activity.Current usa o formato de ID W3C, o cliente inclui o ID do rastreamento, o ID do span, os sinalizadores e o estado do rastreamento no pacote nativo de consulta. Assim, o ClickHouse pode criar spans do servidor no mesmo rastreamento distribuído. A coleta e a amostragem de spans do lado do servidor continuam a depender da configuração do ClickHouse; por exemplo, é possível definir a probabilidade de amostragem por consulta:
O ID da consulta propagado pode ser usado junto com o ID do trace para correlacionar o span do cliente com system.query_log e, se configurado, com system.opentelemetry_span_log do ClickHouse.

Limitações

O cliente TCP é experimental e não oferece todos os recursos do cliente HTTP.

Funcionalidades disponíveis apenas via HTTP

A API TCP não é um provedor ADO.NET e não pode ser usada com ORMs. Ela também não oferece suporte a entrada e saída em CSV, JSONEachRow, Parquet ou fluxo bruto; autenticação bearer ou cabeçalhos HTTP personalizados; resolução personalizada de tipos de parâmetros, formatação ou conversão na leitura; nem à definição de um banco de dados ou uma função para cada consulta. Use o cliente HTTP quando uma aplicação precisar de alguma dessas funcionalidades.

Parâmetros SQL

Apenas os placeholders {name:Type} nativos do ClickHouse são aceitos; o cliente TCP não reescreve placeholders @name no estilo do ADO.NET. Nas versões 25.8 a 26.6 do ClickHouse, nomes de parâmetros que coincidem com configurações do servidor, como limit ou offset, podem ser interpretados como configurações e rejeitados. Ao oferecer suporte a essas versões, renomeie os parâmetros, por exemplo, para row_limit.

Cobertura de tipos de dados

A seção tipos de dados compatíveis apresenta todos os mapeamentos. O codec nativo atual tem as seguintes limitações:
  • As tuplas podem conter, no máximo, sete elementos.
  • Estados intermediários de AggregateFunction(function, ...) não são compatíveis. Finalize-os no servidor, por exemplo, com sumMerge(column), e leia o resultado.
  • Apenas o layout QBit(T, N) com dois argumentos e sem stride é compatível.
  • JSON usa a serialização String do ClickHouse e exige output_format_native_write_json_as_string = 1, que o cliente solicita por padrão.
  • Inserções criadas pela aplicação não têm representação orientada a linhas para Nested(...) quando flatten_nested = 0. Em vez disso, use as colunas Array(T) achatadas, com nomes separados por pontos, que são o padrão.
  • Uma coluna Geometry criada pela aplicação não permite distinguir alternativas que compartilham a mesma estrutura CLR, como Ring e LineString. Insira o tipo geométrico específico ou reinsira uma coluna Geometry decodificada que preserve seus discriminadores.

Novas tentativas automáticas

O cliente não repete automaticamente as operações que falharam. Tente novamente apenas as operações que podem ser repetidas com segurança e defina um DeduplicationToken fixo antes de tentar novamente uma inserção cujo resultado é desconhecido. Consulte como tentar operações novamente para saber mais.
Última modificação em 14 de outubro de 2026