Acordar bancos de dados sem conexões mortas
Os apps publicados no Mythex, e os bancos de dados deles, entram em repouso quando ninguém os usa. Quando um app acordava, podia segurar a primeira requisição por 10 a 12 segundos, porque tentava reutilizar conexões com o banco de dados que tinham morrido enquanto tudo dormia. Agora damos aos apps Spring Boot configurações de banco seguras através do repouso, e pedimos ao agente que construa apps de qualquer stack do mesmo jeito. Na plataforma de hospedagem, a primeira requisição depois de acordar caiu de 12,7 segundos para 2,3 segundos.
Por que apps e bancos dormem
Um app publicado no Mythex roda em uma máquina que é suspensa após alguns minutos ociosa, e o banco de dados dele é um Postgres serverless que entra em repouso após cinco minutos ociosos e acorda na próxima requisição. É isso que mantém barato um app tranquilo: você paga pelo que o banco realmente usou, e um app em repouso não gasta nada com computação. Escrevemos sobre o lado da cobrança em cobrança de bancos de dados pelo que eles realmente usam.
O porém é que os dois dormem separadamente e não concordam sobre o que é uma conexão.
O sintoma: uma primeira visita lenta
A maioria dos apps de servidor mantém um pool de conexões: um punhado de conexões abertas com o banco, prontas, para que cada requisição não precise pagar o custo de abrir uma nova. É o desenho certo para um app sempre ligado.
Eis o que acontece com o repouso:
- O app está rodando, com algumas conexões ociosas no pool.
- Ninguém visita. A máquina do app é suspensa, congelada, com o pool intacto.
- O banco entra em repouso e derruba a sua ponta de cada conexão.
- Um visitante chega. O app acorda exatamente como estava, ainda segurando conexões que o banco não conhece mais.
- O pool entrega uma dessas conexões. Ele precisa descobrir que ela morreu, tentando usá-la e esperando, antes de desistir dela.
Em um app Spring Boot, validar essas conexões mortas bloqueava a primeira requisição por cerca de 10 segundos, até que ela falhasse com "Connection is not available". Para o visitante, o site simplesmente travava, e às vezes mostrava um erro.
Por que os apps Spring foram os mais afetados
Spring Boot é um framework Java popular, e usa por padrão um pool de conexões chamado HikariCP. O Spring pode configurar esse pool por meio de configurações, incluindo quantas conexões ociosas manter.
Mas o Mythex entrega o banco de cada app como uma única DATABASE_URL no formato postgresql://user:pass@host/db. O Spring não consegue ler esse formato diretamente. Então os apps Spring construídos no Mythex montavam o próprio pool no código, analisando essa URL, e um pool montado à mão ignora todas as configurações spring.datasource.* do Spring. Mesmo que alguém soubesse as configurações certas do pool, não havia lugar onde elas fariam efeito.
A correção, parte 1: configurações que o Spring pode usar
Quando um projeto tem um banco de dados, agora também recebe variáveis de ambiente que o Spring lê nativamente:
SPRING_DATASOURCE_URL,SPRING_DATASOURCE_USERNAMEeSPRING_DATASOURCE_PASSWORD, convertidas a partir deDATABASE_URL(com SSL obrigatório, a menos que a URL diga o contrário).- Configurações do pool que sobrevivem ao repouso:
| Configuração | Valor | Por quê |
|---|---|---|
| Mínimo de conexões ociosas | 0 | Não manter nada aberto entre requisições, para que não haja nada que fique desatualizado |
| Tempo limite de ociosidade | 30 segundos | Fechar conexões não usadas logo |
| Tempo de vida máximo | 2 minutos | Aposentar toda conexão antes que ela envelheça |
| Tempo limite de conexão | 5 segundos | Não prender uma requisição por muito tempo se o banco demorar a responder |
Sem conexões ociosas mantidas, um app acordado abre uma conexão nova em vez de vadear por conexões mortas.
Com essas variáveis presentes, o Spring configura o próprio pool, e as configurações valem. O que o projeto já tiver definido sempre prevalece; nós só preenchemos o que falta. Apps que não são Spring ignoram essas variáveis.
O detalhe que quase fez tudo não funcionar
O Spring mapeia variáveis de ambiente para as suas configurações por uma regra fácil de errar. A configuração minimum-idle precisa ser escrita como SPRING_DATASOURCE_HIKARI_MINIMUMIDLE: sem traços, com as palavras juntas. Escrevê-la como SPRING_DATASOURCE_HIKARI_MINIMUM_IDLE, que parece mais natural, aponta para outra configuração que não existe (minimum.idle) e é ignorada sem aviso.
Esse é o tipo de erro que passa em todas as revisões e não faz nada em produção. Por isso, um teste verifica tanto que os valores estão definidos quanto que nenhuma variável usa a forma separada por sublinhado.
A correção, parte 2: todas as outras stacks
Spring não é o único framework com pool, e não dá para pré-configurar todas as bibliotecas de todas as linguagens. Então o agente do Mythex, a IA que escreve o seu app, agora recebe uma instrução permanente para qualquer stack: apps publicados e os bancos deles entram em repouso quando ociosos, então uma conexão mantida aberta pode estar morta na requisição seguinte. Ele deve:
- não manter conexões de banco ociosas abertas, ou verificar cada uma antes de usar,
- tentar de novo uma vez em caso de erro de conexão, e
- usar as configurações de banco que a plataforma fornece no ambiente, em vez de montar o próprio pool analisando
DATABASE_URL.
Esse último ponto fecha o ciclo com a parte 1: um app construído sobre as configurações fornecidas recebe automaticamente a configuração segura para o repouso.
O resultado
Medido na plataforma de hospedagem onde rodamos os apps publicados, a primeira requisição depois de acordar caiu de 12,7 segundos para 2,3 segundos.
O que tiramos disso
- Toda camada que entra em repouso precisa concordar sobre o que sobrevive. Uma máquina congelada e um banco em repouso fizeram, cada um, uma escolha razoável. Juntos, o app acordou segurando um estado que já não era verdadeiro.
- Padrões pensados para servidores sempre ligados não servem para apps que escalam até zero. Manter conexões aquecidas é uma boa ideia até que tudo ao redor esfrie.
- Entregue aos frameworks os dados no formato deles. Uma URL única era simples para nós, mas obrigava todo app Spring a montar um pool à mão que ignorava a própria configuração.
- Teste se uma configuração está escrita do jeito que o framework a lê. Um valor correto com o nome errado não faz nada, e não avisa.
Se você é novo em como os bancos funcionam no Mythex, veja a documentação do banco de dados e nosso texto explicativo sobre o que é o Postgres.