Como backends idle do Postgres podem manter memória alocada, por que isso acontece e como mitigar o problema com PgBouncer.
Em muitos ambientes PostgreSQL, uma consulta pesada pode terminar, a sessão pode voltar ao estado idle, e ainda assim o processo backend continuar mantendo uma quantidade significativa de memória alocada. A princípio, isso pode parecer um vazamento de memória no Postgres, mas na verdade é consequência da interação entre o ciclo de vida dos backends do Postgres, sessões longas e o comportamento do alocador de memória da glibc.
Esta palestra demonstra, de forma prática, como esse comportamento pode aumentar o consumo residente de memória ao longo do tempo e, em cenários com muitas conexões ou consultas que fazem grandes alocações temporárias, contribuir para pressão de memória e até eventos de OOM.
A palestra será dividida em três partes.
Na primeira parte, farei uma introdução breve ao funcionamento do malloc() da glibc e por que chamar free() não significa necessariamente devolver memória ao sistema operacional imediatamente. Também apresentarei a função malloc_trim(), que pode forçar a devolução de parte da memória livre ao sistema, mas com custo de processamento e efeitos colaterais de desempenho.
Na segunda parte, mostrarei exemplos reproduzíveis do impacto desse comportamento em backends PostgreSQL. A ideia é demonstrar que um backend pode executar uma consulta que exige bastante memória, terminar a consulta, ficar idle, e ainda assim continuar com um RSS elevado. Também discutirei formas de observar esse fenômeno com ferramentas do sistema operacional e, em ambiente controlado, como conectar em um processo com gdb e executar malloc_trim() manualmente para demonstrar a diferença.
Em seguida, analisarei três caminhos possíveis para mitigar o problema dentro ou ao redor do PostgreSQL:
- chamar malloc_trim() automaticamente, por exemplo diretamente no Postgres ou via hook;
- criar uma GUC para controlar esse comportamento;
- expor uma função que permita ao usuário chamar malloc_trim() sob demanda.
A proposta é discutir os prós e contras de cada alternativa. Chamar malloc_trim() automaticamente pode reduzir memória residente, mas pode ter impacto relevante na performance. Uma GUC específica para esse comportamento pode ser problemática por depender da glibc e não ser portável para todos os sistemas. Uma função manual oferece controle, mas transfere ao usuário a responsabilidade de monitorar, decidir quando chamar e aceitar o custo operacional.
Na terceira parte, defenderei uma solução de melhor custo-benefício para muitos ambientes reais: usar PgBouncer como pooler de conexões. Em vez de tentar "limpar" a memória de backends que permanecem vivos por muito tempo, PgBouncer pode reciclar periodicamente as conexões servidor por meio do parâmetro server_lifetime, cujo padrão é uma hora. Ao encerrar e recriar a conexão servidor, o processo backend correspondente termina, e toda a memória mantida por ele é efetivamente liberada pelo sistema operacional.
O objetivo da palestra não é afirmar que PgBouncer resolve todos os casos, nem substituir investigação de vazamentos reais. O objetivo é mostrar um problema comum, frequentemente mal interpretado, explicar sua causa em nível de sistema, reproduzi-lo de forma simples, avaliar alternativas técnicas e apresentar uma mitigação prática que reduz o risco operacional sem inserir chamadas caras de malloc_trim() no caminho crítico das consultas.
Ao final, o público deverá sair com uma compreensão mais clara de por que conexões idle não são necessariamente "baratas", quais ferramentas podem ser usadas para investigar o problema e por que reciclar conexões servidor pode ser uma solução simples, pragmática e eficiente para manter o uso de memória em níveis aceitáveis.
Conexões ociosas não são gratuitas
William Ivanski
EnterpriseDB
Como backends idle do Postgres podem manter memória alocada, por que isso acontece e como mitigar o problema com PgBouncer.
Em muitos ambientes PostgreSQL, uma consulta pesada pode terminar, a sessão pode voltar ao estado idle, e ainda assim o processo backend continuar mantendo uma quantidade significativa de memória alocada. A princípio, isso pode parecer um vazamento de memória no Postgres, mas na verdade é consequência da interação entre o ciclo de vida dos backends do Postgres, sessões longas e o comportamento do alocador de memória da glibc.
Esta palestra demonstra, de forma prática, como esse comportamento pode aumentar o consumo residente de memória ao longo do tempo e, em cenários com muitas conexões ou consultas que fazem grandes alocações temporárias, contribuir para pressão de memória e até eventos de OOM.
A palestra será dividida em três partes.
Na primeira parte, farei uma introdução breve ao funcionamento do malloc() da glibc e por que chamar free() não significa necessariamente devolver memória ao sistema operacional imediatamente. Também apresentarei a função malloc_trim(), que pode forçar a devolução de parte da memória livre ao sistema, mas com custo de processamento e efeitos colaterais de desempenho.
Na segunda parte, mostrarei exemplos reproduzíveis do impacto desse comportamento em backends PostgreSQL. A ideia é demonstrar que um backend pode executar uma consulta que exige bastante memória, terminar a consulta, ficar idle, e ainda assim continuar com um RSS elevado. Também discutirei formas de observar esse fenômeno com ferramentas do sistema operacional e, em ambiente controlado, como conectar em um processo com gdb e executar malloc_trim() manualmente para demonstrar a diferença.
Em seguida, analisarei três caminhos possíveis para mitigar o problema dentro ou ao redor do PostgreSQL:
- chamar malloc_trim() automaticamente, por exemplo diretamente no Postgres ou via hook;
- criar uma GUC para controlar esse comportamento;
- expor uma função que permita ao usuário chamar malloc_trim() sob demanda.
A proposta é discutir os prós e contras de cada alternativa. Chamar malloc_trim() automaticamente pode reduzir memória residente, mas pode ter impacto relevante na performance. Uma GUC específica para esse comportamento pode ser problemática por depender da glibc e não ser portável para todos os sistemas. Uma função manual oferece controle, mas transfere ao usuário a responsabilidade de monitorar, decidir quando chamar e aceitar o custo operacional.
Na terceira parte, defenderei uma solução de melhor custo-benefício para muitos ambientes reais: usar PgBouncer como pooler de conexões. Em vez de tentar "limpar" a memória de backends que permanecem vivos por muito tempo, PgBouncer pode reciclar periodicamente as conexões servidor por meio do parâmetro server_lifetime, cujo padrão é uma hora. Ao encerrar e recriar a conexão servidor, o processo backend correspondente termina, e toda a memória mantida por ele é efetivamente liberada pelo sistema operacional.
O objetivo da palestra não é afirmar que PgBouncer resolve todos os casos, nem substituir investigação de vazamentos reais. O objetivo é mostrar um problema comum, frequentemente mal interpretado, explicar sua causa em nível de sistema, reproduzi-lo de forma simples, avaliar alternativas técnicas e apresentar uma mitigação prática que reduz o risco operacional sem inserir chamadas caras de malloc_trim() no caminho crítico das consultas.
Ao final, o público deverá sair com uma compreensão mais clara de por que conexões idle não são necessariamente "baratas", quais ferramentas podem ser usadas para investigar o problema e por que reciclar conexões servidor pode ser uma solução simples, pragmática e eficiente para manter o uso de memória em níveis aceitáveis.