Artigo de convidado: este artigo foi escrito por Emmanuel Sibanda e é publicado aqui com a autorização do autor. Encontre Emmanuel no LinkedIn ou em emmanuelsibanda.com.
São 9h de segunda-feira. Seu servidor GizmoSQL passou o fim de semana funcionando. Ninguém executou uma consulta desde sexta-feira à tarde — mesmo assim, o uso de memória está em 80%, e o primeiro analista a abrir um painel enfrenta uma pausa.
Não há nada de errado com suas consultas. O problema é que as consultas foram embora, mas as conexões ficaram.
Por que conexões ociosas são um problema
O GizmoSQL coloca o DuckDB na rede para que muitos clientes compartilhem um único processo servidor. Essa é a ideia: você executa um processo, dimensiona-o uma vez e todos se conectam a ele.
Mas cada conexão de cliente se torna uma sessão, e cada sessão mantém sua própria conexão DuckDB — ou seja, cada sessão ocupa RAM nesse processo enquanto existir. Uma aba do DBeaver que alguém esqueceu de fechar na quinta-feira. Um pool de conexões que envia um ping a cada 30 segundos, mas nunca executa SQL. Um kernel de notebook que está "conectado" há uma semana. Cada um ocupa silenciosamente uma vaga, e o processo só tem um número limitado delas.
Sem controle, esse crescimento não tem limite. Com sessões ociosas suficientes, o processo trava — ou morre, derrubando junto todas as conexões ativas.
Dois recursos resolvem isso. Um limita o número de sessões que podem existir; o outro encerra as que pararam de trabalhar.
Etapa 1: limite o número de sessões
--max-sessions define um limite rígido para sessões não administrativas, permitindo dimensionar o servidor para sua arquitetura em vez de torcer para que ninguém abra abas demais.
CLI:
GIZMOSQL_PASSWORD="gizmosql_password" gizmosql_server \
--database-filename lakehouse.db \
--max-sessions 32Variável de ambiente:
export GIZMOSQL_MAX_SESSIONS=32
GIZMOSQL_PASSWORD="gizmosql_password" gizmosql_server \
--database-filename lakehouse.dbSessões administrativas não entram nesse limite. Assim, você sempre consegue acessar o servidor para investigar, mesmo quando ele está lotado.
Etapa 2: encerre as sessões que pararam de trabalhar
Só um limite não basta. Um cliente pode ocupar uma das suas 32 vagas indefinidamente sem executar uma única instrução. É o problema da aba do DBeaver aberta desde quinta-feira: a RAM está alocada, mas não está sendo usada para nada.
--session-idle-timeout estabelece um prazo para a inatividade. Se uma sessão passar esse número de segundos sem fazer trabalho real, o servidor a encerra, fecha a conexão DuckDB e devolve a RAM às sessões que precisam dela.
CLI:
GIZMOSQL_PASSWORD="gizmosql_password" gizmosql_server \
--database-filename lakehouse.db \
--session-idle-timeout 300Variável de ambiente:
export GIZMOSQL_SESSION_IDLE_TIMEOUT=300
GIZMOSQL_PASSWORD="gizmosql_password" gizmosql_server \
--database-filename lakehouse.dbO que conta como "ociosidade"? (Essa é a parte essencial.)
A forma óbvia de implementar um temporizador de ociosidade é reiniciá-lo sempre que o servidor termina uma consulta. Deliberadamente não fizemos isso, porque haveria um erro importante nessa abordagem.
Uma consulta não termina quando o servidor termina. Ela termina quando o cliente recebe as linhas.
Um SELECT grande pode terminar o processamento em segundos e depois transmitir resultados por minutos. Se só o processamento contasse como trabalho, um temporizador ingênuo encerraria a sessão no meio do download — e uma consulta que o usuário já executou com sucesso falharia de repente. Por isso, no GizmoSQL, cada lote de linhas de resultado obtido pelo cliente conta como parte do mesmo SQL do usuário. O relógio de ociosidade só começa quando o download para.
Na prática, estas ações reiniciam o relógio de ociosidade:
- Executar uma instrução
- Preparar uma instrução
- Ingerir dados
- Buscar linhas de resultado de uma instrução
E estas não reiniciam:
- Apenas manter uma sessão aberta
- Manter uma janela do DBeaver (ou de qualquer IDE) aberta sem executar nada
- Um pool de conexões que só envia pings de keepalive
- Consultas a metadados e ao catálogo interno
Essas sessões continuam ocupando uma vaga e mantendo sua RAM. Só não podem prorrogar sua permanência. Apenas SQL real do usuário mantém uma sessão viva além do tempo limite.
Como funciona por dentro
Enquanto --session-idle-timeout for maior que zero, o servidor faz uma verificação em segundo plano a cada segundo. Para cada sessão, verifica duas condições: o SQL do usuário ainda está executando ou transmitindo resultados? e o tempo limite já passou desde o último registro de atividade SQL do usuário? Uma sessão ociosa além do limite é encerrada exatamente como seria em uma desconexão normal do cliente.
Alguns pontos para entender antes de ativar o recurso:
- Administradores não estão isentos. Ao contrário de
--max-sessions, o tempo limite de ociosidade também se aplica ao papeladmin. Se você deixar uma sessão administrativa aberta e se afastar, ela poderá ser encerrada como qualquer outra. - O cliente recebe um erro claro, sem ficar travado. Após o encerramento, a próxima solicitação do cliente falha com Flight
UNAUTHENTICATEDe a mensagem "Session not found — it may have been evicted. Please re-connect." (sessão não encontrada; ela pode ter sido encerrada. Reconecte-se). A solução é se reconectar. - O trabalho não confirmado é desfeito. Encerrar a sessão fecha a conexão DuckDB, desfazendo qualquer transação iniciada que ainda não tenha sido confirmada.
- O recurso vem desativado por padrão. Nada muda até você configurá-lo.
Combinando os recursos
Na maioria das implantações, a combinação é simples: defina --max-sessions de acordo com o que seu processo comporta com folga e ajuste --session-idle-timeout para um valor entre alguns minutos e uma hora, dependendo da frequência de atividade dos clientes. Ferramentas interativas de BI costumam precisar de uma janela maior; tarefas em lote e serviços com pool podem funcionar com uma janela menor.
O resultado é um servidor que permanece no tamanho para o qual foi dimensionado — inclusive na segunda-feira de manhã.
Sobre o autor: Emmanuel Sibanda escreveu este artigo como convidado para o blog da GizmoData.
Da equipe GizmoData: as duas opções foram lançadas no GizmoSQL v1.36.0 e funcionam no Core (Apache-2.0) e no Enterprise. Seus valores atuais podem ser consultados em um servidor ativo com SELECT * FROM gizmosql_settings(); veja a referência de configurações e a tabela completa de opções do servidor. Para experimentar, instale o GizmoSQL Core ou fale conosco sobre o dimensionamento de uma implantação em produção.