@clickhouse/client- somente Node.js@clickhouse/client-web- navegadores (Chrome/Firefox), Cloudflare Workers
skills para agentes de IAO cliente JS inclui skills para agentes de IA que podem ajudar agentes de codificação a usar o cliente. Instale-as com:
Requisitos de ambiente (node.js)
Requisitos de ambiente (web)
Instalação
Compatibilidade com ClickHouse
É provável que o cliente também funcione com versões mais antigas; no entanto, esse suporte é oferecido em regime de best effort e não é garantido. Se você estiver usando uma versão do ClickHouse anterior à 23.3, consulte a política de segurança do ClickHouse e considere atualizar.
Exemplos
API do cliente
Criando uma instância de cliente
createClient:
Configuração
Parâmetros de configuração específicos do Node.js
Configuração de URL
http[s]://[username:password@]hostname:port[/database][?param1=value1¶m2=value2]. Em quase todos os casos, o nome de um determinado parâmetro reflete seu caminho na interface de opções de configuração, com algumas exceções. Há suporte para os seguintes parâmetros:
- (1) Para booleanos, os valores válidos são
true/1efalse/0. - (2) Qualquer parâmetro com o prefixo
clickhouse_setting_ouch_terá esse prefixo removido, e o restante será adicionado aclickhouse_settingsdo cliente. Por exemplo,?ch_async_insert=1&ch_wait_for_async_insert=1será o mesmo que:
clickhouse_settings devem ser informados como 1/0 na URL.
- (3) Semelhante a (2), mas para a configuração
http_header. Por exemplo,?http_header_x-clickhouse-auth=foobarequivale a:
Conexão
Reúna os detalhes da conexão
Os detalhes do seu serviço do ClickHouse Cloud estão disponíveis no console do ClickHouse Cloud.
Selecione um serviço e clique em Connect:
Escolha HTTPS. Os detalhes de conexão são exibidos em um comando
curl de exemplo.
Se você estiver usando ClickHouse autogerenciado, os detalhes de conexão são definidos pelo administrador do seu ClickHouse.
Visão geral da conexão
url (incluindo
protocolo e porta) e password sejam especificados por meio de variáveis de ambiente, e que o usuário default seja usado.
Exemplo: Criando uma instância do cliente Node.js usando variáveis de ambiente na configuração.
Pool de conexões (somente Node.js)
10, mas você pode alterá-lo com a opção de configuração max_open_connections.
Não há garantia de que a mesma conexão do pool será usada em consultas subsequentes, a menos que o usuário defina max_open_connections: 1. Isso raramente é necessário, mas pode ser exigido em casos em que os usuários estejam usando tabelas temporárias.
Veja também: configuração de Keep-Alive.
ID da consulta
command, exec, insert, select) fornecerá query_id no resultado. Esse identificador exclusivo é atribuído pelo cliente a cada consulta e pode ser útil para obter os dados de system.query_log,
se ele estiver habilitado na configuração do servidor, ou para cancelar consultas de longa duração (veja o exemplo). Se necessário, o query_id pode ser substituído pelo usuário nos parâmetros dos métodos command/query/exec/insert.
Parâmetros base para todos os métodos do cliente
Método query
SELECT, ou para enviar DDLs, como CREATE TABLE, e deve ser aguardado. Espera-se que o conjunto de resultados retornado seja consumido pela aplicação.
Abstrações de conjunto de resultados e linhas
ResultSet fornece vários métodos práticos para o processamento de dados na sua aplicação.
A implementação de ResultSet no Node.js usa Stream.Readable internamente, enquanto a versão web usa a Web API ReadableStream.
Você pode consumir o ResultSet chamando os métodos text ou json em ResultSet e carregar na memória todo o conjunto de linhas retornado pela consulta.
Você deve começar a consumir o ResultSet o quanto antes, pois ele mantém o fluxo de resposta aberto e, consequentemente, a conexão subjacente ocupada. O cliente não armazena os dados recebidos em buffer para evitar um possível uso excessivo de memória pela aplicação.
Como alternativa, se ele for grande demais para caber na memória de uma só vez, você pode chamar o método stream e processar os dados em modo de streaming. Nesse caso, cada fragmento da resposta será transformado em arrays relativamente pequenos de linhas (o tamanho desse array depende do tamanho de um fragmento específico que o cliente recebe do servidor, já que ele pode variar, e do tamanho de uma linha individual), um fragmento por vez.
Consulte a lista de formatos de dados compatíveis para determinar qual é o melhor formato para streaming no seu caso. Por exemplo, se você quiser processar objetos JSON em streaming, pode escolher JSONEachRow, e cada linha será analisada como um objeto JS ou, talvez, o formato mais compacto JSONCompactColumns, no qual cada linha será um array compacto de valores. Veja também: arquivos em streaming.
JSONEachRow, consumindo todo o fluxo e interpretando o conteúdo como objetos JS.
Código-fonte.
JSONEachRow, usando a abordagem clássica on('data'). Isso pode ser usado de forma intercambiável com a sintaxe for await const. Código-fonte.
CSV usando a abordagem clássica com on('data'). Isso pode ser usado de forma intercambiável com a sintaxe for await const.
Código-fonte
JSONEachRow, consumidos com a sintaxe for await const. Isso pode ser usado de forma intercambiável com a abordagem clássica on('data').
Código-fonte.
A sintaxe
for await const tem um pouco menos de código do que a abordagem on('data'), mas pode ter um impacto negativo no desempenho.
Veja esta issue no repositório do Node.js para mais detalhes.ReadableStream de objetos.
Método insert
insert, a instrução insert não será enviada ao servidor; em vez disso, o método será resolvido imediatamente com { query_id: '...', executed: false }. Se o query_id não tiver sido fornecido nos parâmetros do método nesse caso, ele será uma string vazia no resultado, já que retornar um UUID aleatório gerado pelo cliente poderia causar confusão, pois a consulta com esse query_id não existirá na tabela system.query_log.
Se a instrução insert tiver sido enviada ao servidor, o sinalizador executed será true.
Método insert e streaming no Node.js
Stream.Readable quanto com um Array<T> simples, dependendo do formato de dados especificado no método insert. Veja também esta seção sobre streaming de arquivos.
O método insert deve ser aguardado com await; no entanto, é possível especificar um stream de entrada e aguardar a operação insert mais tarde, apenas quando o stream for concluído (o que também resolverá a promise de insert). Isso pode ser útil para listeners de eventos e cenários semelhantes, mas o tratamento de erros pode não ser trivial, com vários casos de borda no lado do cliente. Em vez disso, considere usar async inserts, como mostrado neste exemplo.
INSERT.
Considere uma definição de tabela como a seguinte:
Limitações da versão web
@clickhouse/client-web funcionam apenas com os formatos Array<T> e JSON*.
A inserção via streams ainda não é compatível com a versão web devido à baixa compatibilidade dos navegadores.
Consequentemente, a interface InsertParams da versão web é ligeiramente diferente da versão do Node.js,
pois values se limitam ao tipo ReadonlyArray<T>:
Método Command
CREATE TABLE ou ALTER TABLE.
Deve-se aguardar sua conclusão.
O fluxo de resposta é destruído imediatamente, o que significa que o socket subjacente é liberado.
Método exec
query/insert
e quiser o resultado, pode usar exec como alternativa a command.
exec retorna um stream de leitura que DEVE ser consumido ou destruído pela aplicação.
Ping
ping, usado para verificar o status da conectividade, retorna true se o servidor puder ser acessado.
Se o servidor estiver inacessível, o erro subjacente também será incluído no resultado.
/ping, enquanto a versão Web usa uma consulta SELECT 1 simples para obter um resultado semelhante, já que o endpoint /ping não oferece suporte a CORS.
Exemplo: (Node.js/Web) Um ping simples para a instância do servidor ClickHouse. Observação: para a versão Web, os erros capturados serão diferentes.
Código-fonte.
ping ou especificar parâmetros adicionais, como query_id, poderá usá-lo da seguinte forma:
query — consulte a definição de tipo PingParamsWithSelectQuery.
Close (somente Node.js)
Streaming de arquivos (apenas Node.js)
- Streaming a partir de um arquivo NDJSON
- Streaming a partir de um arquivo CSV
- Streaming a partir de um arquivo Parquet
- Streaming para um arquivo Parquet
query (JSONEachRow, CSV etc.) e o nome do arquivo de saída.
Formatos de dados suportados
format como um dos formatos da família JSON (JSONEachRow, JSONCompactEachRow etc.), o cliente serializará e desserializará os dados durante a comunicação via wire.
Os dados fornecidos nos formatos de texto “brutos” (famílias CSV, TabSeparated e CustomSeparated) são enviados via wire sem transformações adicionais.
Para Parquet, o principal caso de uso de instruções
SELECT provavelmente será gravar o fluxo resultante em um arquivo. Veja o exemplo no repositório da biblioteca cliente.
JSONEachRowWithProgress é um formato somente de saída compatível com o envio de informações de progresso no fluxo. Veja este exemplo para mais detalhes.
A lista completa de formatos de entrada e saída do ClickHouse está disponível
aqui.
Tipos de dados do ClickHouse suportados
O tipo JS correspondente se aplica a quaisquer formatos
JSON*, exceto aos que representam tudo como string (por exemplo, JSONStringEachRow)
A lista completa dos formatos suportados pelo ClickHouse está disponível
aqui.
Veja também:
Ressalvas sobre os tipos Date/Date32
Date/Date32 só podem ser inseridas como
strings.
Exemplo: Insira um valor do tipo Date.
Código-fonte
DateTime ou DateTime64, poderá usar tanto strings quanto objetos Date do JS. Objetos Date do JS podem ser passados para insert sem alterações, com date_time_input_format definido como best_effort. Veja este exemplo para mais detalhes.
Ressalvas sobre os tipos Decimal*
JSON*. Supondo que tenhamos uma tabela definida como:
JSON*, o ClickHouse retorna valores Decimal como números por padrão, o que pode causar perda de precisão. Para evitar isso, você pode converter os valores Decimal em string na consulta:
Tipos integrais: Int64, Int128, Int256, UInt64, UInt128, UInt256
JSON* para evitar
estouro de inteiros, já que os valores máximos desses tipos são maiores que Number.MAX_SAFE_INTEGER.
Esse comportamento, no entanto, pode ser modificado
com a configuração output_format_json_quote_64bit_integers
.
Exemplo: Ajuste o formato de saída JSON para números de 64 bits.
Configurações do ClickHouse
Tópicos avançados
Consultas com parâmetros
name— Identificador do placeholder.data_type- Tipo de dado do valor do parâmetro da aplicação.
Compressão
GZIP é compatível por meio de zlib.
response: trueinstrui o servidor ClickHouse a responder com o corpo da resposta comprimido. Valor padrão:response: falserequest: trueativa a compressão no corpo da requisição do cliente. Valor padrão:request: false
Logging (apenas para Node.js)
logger emite registros de log em stdout por meio dos métodos console.debug/info e em stderr por meio dos métodos console.warn/error.
Você pode personalizar a lógica de logging fornecendo uma LoggerClass e escolher o nível de log desejado por meio do parâmetro level (o padrão é WARN):
TRACE- informações de baixo nível sobre o ciclo de vida dos sockets Keep-AliveDEBUG- informações da resposta (sem os headers de autorização nem informações do host)INFO- praticamente não é usado; exibirá o nível de log atual quando o cliente for inicializadoWARN- erros não fatais; uma solicitaçãopingcom falha é registrada como aviso, pois o erro subjacente está incluído no resultado retornadoERROR- erros fatais dos métodosquery/insert/exec/command, como uma solicitação com falha
Certificados TLS (somente para Node.js)
certs
e que o nome do arquivo da CA seja CA.pem:
Configuração de Keep-Alive (somente Node.js)
Connection: keep-alive será enviado. Os sockets ociosos permanecerão no connection pool por 2500 milissegundos por padrão (veja as notas sobre como ajustar esta opção).
Espera-se que keep_alive.idle_socket_ttl tenha um valor consideravelmente menor do que a configuração do servidor/LB. O principal motivo é que, como o HTTP/1.1 permite que o servidor feche os sockets sem notificar o cliente, se o servidor ou o balanceador de carga fechar a connection antes de o cliente fazer isso, o cliente poderá tentar reutilizar o socket fechado, resultando em um erro socket hang up.
Se você estiver modificando keep_alive.idle_socket_ttl, tenha em mente que ele deve estar sempre em sincronia com a configuração de Keep-Alive do seu servidor/LB e deve ser sempre menor do que ela, garantindo que o servidor nunca feche primeiro a connection aberta.
Ajustando idle_socket_ttl
keep_alive.idle_socket_ttl como 2500 milissegundos, por ser considerado o padrão mais seguro; no lado do servidor, keep_alive_timeout pode estar definido em apenas 3 segundos em versões do ClickHouse anteriores à versão 23.11 sem modificações no config.xml.
Você pode encontrar o valor correto do timeout de Keep-Alive nos cabeçalhos de resposta do servidor executando o seguinte comando:
Connection e Keep-Alive na resposta. Por exemplo:
keep_alive_timeout é de 10 segundos, e você pode tentar aumentar keep_alive.idle_socket_ttl para 9000 ou até 9500 milissegundos para manter os sockets ociosos abertos por um pouco mais de tempo do que o padrão. Fique atento a possíveis erros de “Socket hang-up”, que indicam que o servidor fecha as conexões antes do cliente, e reduza o valor até que os erros desapareçam.
Solução de problemas
socket hang up mesmo usando a versão mais recente do cliente, há as seguintes opções para resolver esse problema:
-
Habilite logs com pelo menos o nível de log
WARN(padrão). Isso permitirá verificar se há algum stream não consumido ou pendente no código da aplicação: a camada de transporte registrará isso no nível WARN, pois isso pode fazer com que o socket seja fechado pelo servidor. Você pode habilitar o logging na configuração do cliente da seguinte forma: -
Certifique-se de que a configuração desejada está sendo aplicada à instância correta do cliente. Se você tiver várias instâncias de cliente na sua aplicação, verifique novamente se aquela que está usando para consultas tem o valor correto de
keep_alive.idle_socket_ttl. -
Reduza a configuração
keep_alive.idle_socket_ttlem 500 milissegundos na configuração do cliente. Em certas situações, por exemplo, alta latência de rede entre cliente e servidor, isso pode ser benéfico, pois elimina o cenário em que uma requisição de saída obtém um socket que o servidor está prestes a fechar. -
Se esse erro estiver acontecendo durante consultas de longa duração sem entrada ou saída de dados (por exemplo, um
INSERT FROM SELECTde longa duração), isso pode ser causado por um balanceador de carga ou outros componentes de rede fechando conexões de longa duração ou requisições demoradas. Você pode tentar forçar a entrada de alguns dados durante consultas de longa duração usando uma combinação destas configurações do ClickHouse:Tenha em mente, no entanto, que o tamanho total dos headers recebidos tem limite de 16 KB nas versões recentes do Node.js; após uma certa quantidade de headers de progresso recebidos, que foi em torno de 70-80 em nossos testes, uma exceção será gerada. Também é possível usar uma abordagem totalmente diferente, evitando completamente o tempo de espera no wire; isso pode ser feito aproveitando a “funcionalidade” da interface HTTP em que mutações não são canceladas quando a conexão é perdida. Consulte este exemplo (parte 2) para mais detalhes. -
O recurso Keep-Alive pode ser desabilitado totalmente. Nesse caso, o cliente também adicionará o header
Connection: closea cada requisição, e o agente HTTP subjacente não reutilizará as conexões. A configuraçãokeep_alive.idle_socket_ttlserá ignorada, pois não haverá sockets em estado idle. Isso resultará em sobrecarga adicional, pois uma nova conexão será estabelecida para cada requisição. -
Descarte possíveis problemas com o restante da pilha de rede, incluindo o próprio Node.js, executando um teste simples em linha de comando com a mesma instância do ClickHouse e o mesmo caminho de rede (ou seja, a partir da mesma máquina ou segmento de rede, por exemplo, um pod do Kubernetes), por exemplo, usando
curl:Talvez você queira executá-lo em loop por vários minutos. Se vir erros semelhantes nocurl, é provável que o problema não esteja relacionado à configuração do cliente, mas sim à pilha de rede ou à configuração do servidor. -
Para testar a conexão com a funcionalidade nativa do Node.js, você pode tentar criar uma requisição HTTP simples para o servidor ClickHouse usando a API
fetchincorporada:
-
Em alguns casos, o código da aplicação ou os adaptadores do framework podem adicionar um
ping()preventivo antes da execução real da consulta, o que pode levar a uma situação em que a requisiçãoping()é bem-sucedida, mas a requisição de consulta subsequente falha com um erro “socket hang up” devido ao mesmo problema subjacente com conexões ociosas. Se você observar esse padrão nos logs, verifique se há uma opção para desabilitar pings preventivos no seu framework ou no código da aplicação. Isso também deve ajudar a reduzir a probabilidade de sofrer limitação de taxa por algum componente intermediário da rede. - Certifique-se de que a própria aplicação esteja recebendo tempo de CPU suficiente e de que a rede não esteja sendo limitada pelo provedor de hospedagem. Várias formas de monitoramento, como métricas de pausa do GC, métricas de defasagem do loop de eventos e outras semelhantes, também podem ser úteis para descartar possíveis problemas de falta de recursos.
- Tente verificar o código da sua aplicação com a regra no-floating-promises do ESLint habilitada, o que ajudará a identificar promises não tratadas que podem levar a streams e sockets pendentes.
Usuários com acesso somente leitura
enable_http_compression. A configuração a seguir resultará em um erro:
Proxy com pathname
clickhouse_server como a opção de configuração pathname (com ou sem a barra inicial); caso contrário, se ele for fornecido diretamente em url, será interpretado como a opção database. Vários segmentos são compatíveis, por exemplo, /my_proxy/db.
Proxy reverso com autenticação
http_headers para fornecer os cabeçalhos necessários:
Agente HTTP/HTTPS personalizado (experimental, somente para Node.js)
max_open_connections, keep_alive.enabled, tls), que gerenciará as conexões com o servidor ClickHouse. Além disso, se certificados TLS forem usados, o agente subjacente será configurado com os certificados necessários, e os cabeçalhos corretos de autenticação TLS serão aplicados.
A partir da versão 1.2.0, é possível fornecer ao cliente um agente HTTP ou HTTPS personalizado, substituindo o agente padrão. Isso pode ser útil em caso de configurações de rede complexas. As seguintes condições se aplicam caso um agente personalizado seja fornecido:
- As opções
max_open_connectionsetlsnão terão nenhum efeito e serão ignoradas pelo cliente, pois fazem parte da configuração do agente subjacente. keep_alive.enabledregulará apenas o valor padrão do cabeçalhoConnection(true->Connection: keep-alive,false->Connection: close).- Embora o gerenciamento de sockets keep-alive ociosos continue funcionando (já que não está vinculado ao agente, mas a um socket específico), agora é possível desativá-lo completamente definindo o valor de
keep_alive.idle_socket_ttlcomo0.
Exemplos de uso de agente personalizado
set_basic_auth_header (introduzida na versão 1.2.0), pois ele entra em conflito com os cabeçalhos TLS. Todos os cabeçalhos TLS devem ser fornecidos manualmente.
Limitações conhecidas (Node.js/web)
- Não há mapeadores de dados para os conjuntos de resultados, portanto apenas primitivas da linguagem são usadas. Há planos para adicionar mapeadores para certos tipos de dados com suporte ao formato RowBinary.
- Há algumas ressalvas sobre os tipos de dados Decimal* e Date* / DateTime*.
- Ao usar formatos da família JSON*, números maiores que Int32 são representados como strings, pois os valores máximos dos tipos Int64+ são maiores que
Number.MAX_SAFE_INTEGER. Consulte a seção Tipos integrais para mais detalhes.
Limitações conhecidas (web)
- O streaming para consultas SELECT funciona, mas está desabilitado para inserções (inclusive no nível de tipo).
- A compressão da requisição está desabilitada, e a configuração é ignorada. A compressão da resposta funciona.
- Ainda não há suporte a logging.
Dicas para otimizar o desempenho
- Para reduzir o consumo de memória da aplicação, considere usar streams para inserts grandes (por exemplo, de arquivos) e selects, quando aplicável. Para listeners de eventos e casos de uso semelhantes, async inserts podem ser outra boa opção, permitindo minimizar ou até mesmo evitar completamente o agrupamento em lotes no lado do cliente. Exemplos de async insert estão disponíveis no repositório do cliente, com
async_insert_como prefixo do nome do arquivo. - O cliente não habilita a compressão de requisição ou resposta por padrão. No entanto, ao fazer selects ou inserts de grandes volumes de dados, você pode considerar habilitá-la via
ClickHouseClientConfigOptions.compression(seja apenas pararequestouresponse, ou para ambos). - A compressão tem um impacto significativo no desempenho. Habilitá-la para
requestouresponseafetará negativamente a velocidade de selects ou inserts, respectivamente, mas reduzirá a quantidade de tráfego de rede transferido pela aplicação.
Fale conosco
#clickhouse-js) ou pelo GitHub Issues.