Banco de dados postgres - resolvido

Sistema funciona normal, mas quando vou pelo DBeaver, não consigo conectar no banco de dados e mostra esta mensagem FATAL: sorry, too many clients already

No arquivo postgresql.conf, tem o parametro: shared_buffers, está com 900MB.

Sistema é spring-boot

no properties está assim:

spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://xxxxx
spring.datasource.username=xxxxx
spring.datasource.password=xxxxx
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=false
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=none

O que mais preciso fazer ?

Isso parece que vc ultrapassou o limite de conexões abertas simultaneamente com o banco

1 curtida

Você pode tentar mitigar configurando um pool de conexões nas aplicações cliente que acessam essa base de dados!

Por ser uma aplicação Spring você consegue usar as configurações que o Hikari oferece, se não me engano, o Hikari vem junto com a dependência do Spring Data.

Exemplo:

spring:
    datasource:
        url: ${url}
        username: ${username}
        password: ${password}
        hikari:
            auto-commit: false
            connection-timeout: 250
            max-lifetime: 600000
            maximum-pool-size: 20
            minimum-idle: 10
            pool-name: master
2 curtidas

Sim. Aumentei para 200. Chega a 109 conexões abertas.

Mas como saber aonde esta o gargalo no sistema ?

Fiz o que vc disse.

Mas não adiantou, pois tem conexões abertas que não estão sendo fechadas.

Ficou assim:

spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://${DATABASE_HOSTNAME}:${DATABASE_PORT}/${DATABASE_NAME}
spring.datasource.username=${DATABASE_USERNAME}
spring.datasource.password=${DATABASE_PASSWORD}
spring.datasource.hikari.auto-commit=false
spring.datasource.hikari.connection-timeout=250
spring.datasource.hikari.max-lifetime=600000
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.pool-name=master
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=false
sspring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=none

Exemplos de Repository que trabalho

@Repository
public interface ArquivoExemploRepository extends JpaRepository<ArquivoExemplo, String> {

    @Query(value = "SELECT e FROM ArquivoExemplo e WHERE (e.tipoArquivoExemplo = :tipoArquivoExemplo) AND (e.statusDoRegistro = 'ATIVO') ")
    Optional<ArquivoExemplo> buscaPorTipo(@Param("tipoArquivoExemplo") TipoArquivoExemploEnum tipoArquivoExemplo);
}

no service, faço o seguinte

package br.com.ghnetsoft.forca_venda.pessoa.service;

import static br.com.ghnetsoft.principal.enuns.StatusDoRegistroEnum.INATIVO;
import static br.com.ghnetsoft.principal.enuns.TipoArquivoImportacaoEnum.ARQUIVO_EXEMPLO;

import java.io.IOException;
import java.security.Principal;
import java.util.Collection;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import br.com.ghnetsoft.forca_venda.model.Arquivo;
import br.com.ghnetsoft.forca_venda.model.ArquivoExemplo;
import br.com.ghnetsoft.forca_venda.pessoa.preencher.ArquivoPreencher;
import br.com.ghnetsoft.forca_venda.repository.ArquivoExemploRepository;
import br.com.ghnetsoft.principal.dto.ArquivoDTO;
import br.com.ghnetsoft.principal.enuns.TipoArquivoExemploEnum;
import br.com.ghnetsoft.principal.exception.GeralException;
import br.com.ghnetsoft.principal.service.PrincipalService;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class ArquivoExemploImportarServico extends PrincipalService {

    private static final long serialVersionUID = -7065923051038009844L;
    @Autowired
    private transient ArquivoExemploRepository repository;
    @Autowired
    private ArquivoService arquivoService;
    @Autowired
    private ArquivoPreencher arquivoPreencher;

    @Transactional
    public ArquivoExemplo salvar(ArquivoExemplo entidade) {
        log.info("salvar entidade - antes: " + entidade.toString());
        entidade = repository.save(entidade);
        log.info("salvar entidade - depois: " + entidade.toString());
        return entidade;
    }

    @Transactional
    public void importacao(final Collection<MultipartFile> files, final String tipoArquivoExemplo, final Principal principal) {
        try {
            log.info("importacao - tipoArquivoExemplo: " + tipoArquivoExemplo);
            final TipoArquivoExemploEnum tipoArquivoExemploEnum = TipoArquivoExemploEnum.buscaTipoArquivoExemploEnum(tipoArquivoExemplo);
            if (tipoArquivoExemploEnum == null) {
                throw new GeralException("Tipo de arquivo de exemplo inválido !");
            }
            final Optional<ArquivoExemplo> arquivoExemploExiste = repository.buscaPorTipo(tipoArquivoExemploEnum);
            ArquivoExemplo entidade;
            if (arquivoExemploExiste.isPresent()) {
                entidade = arquivoExemploExiste.get();
                entidade.setStatusDoRegistro(INATIVO);
                salvar(entidade);
                final Arquivo arquivo = entidade.getArquivo();
                arquivo.setStatusDoRegistro(INATIVO);
                arquivoService.salvar(arquivo);
            }
            entidade = ArquivoExemplo.builder().build();
            for (final MultipartFile file : files) {
                final Arquivo arquivo = arquivoService.salvar(file, ARQUIVO_EXEMPLO);
                entidade.setArquivo(arquivo);
                entidade.setTipoArquivoExemplo(tipoArquivoExemploEnum);
                salvar(entidade);
            }
        } catch (final IOException e) {
            throw new GeralException("Erro ao salvar arquivo !");
        }
    }

    public ArquivoDTO buscaPorTipo(final String tipoArquivoExemplo) {
        log.info("buscaPorTipo - tipoArquivoExemplo: " + tipoArquivoExemplo);
        final TipoArquivoExemploEnum tipoArquivoExemploEnum = TipoArquivoExemploEnum.buscaTipoArquivoExemploEnum(tipoArquivoExemplo);
        if (tipoArquivoExemploEnum == null) {
            throw new GeralException("Tipo de arquivo de exemplo inválido !");
        }
        final Optional<ArquivoExemplo> arquivoExemploExiste = repository.buscaPorTipo(tipoArquivoExemploEnum);
        if (!arquivoExemploExiste.isPresent()) {
            throw new GeralException("Não existe arquivos para este tipo !");
        }
        return arquivoPreencher.preencherDto(arquivoExemploExiste.get().getArquivo());
    }
}

Consulta realizada no banco de dados

  SELECT
  pid AS identificador,
  usename AS nome_usuario,
  datname AS nome_database,
  client_addr AS ip_origem,
  now() - query_start AS tempo_em_execucao,
  query AS operacao,
  state AS estado
FROM
   pg_stat_activity
ORDER BY
  query_start;

Como descobrir e como fazer para ajustar as conexões abertas sem a necessidade ?

A ideia do pool de conexões é gerenciar o limite de conexões entre outras coisas em relação ao seu banco de dados por instância de aplicação, o trabalho de criar e fechar conexões é de responsabilidade do entityManager que roda por baixo dos panos no spring data!

O exemplo de configuração que te mandei ele mantém um pool de 10 conexões em idle, o que significa que estas conexões estão abertas e adormecidas esperando alguma operação com o banco para que possam ser utilizadas, o conceito de conexão em idle traz o ganho de você não precisar toda vez que for exercer alguma operação com o banco ter o custo de tempo de abrir uma conexão para que depois possa utilizar.

E o tamanho do pool em 20 diz que por instância a aplicação pode ter no máximo 20 conexões ativas ao mesmo tempo (10 idle + 10 sob demanda).

Lembrando, isso não é a solução, é uma forma de iniciar a mitigação do problema e inclusive é uma boa prática!

Agora se você quer à todo custo deixar sua aplicação sem nenhuma conexão “adormecida”, teoricamente falando, bastaria zerar o valor de conexões em idle, porém, vale ressaltar que o custo de ficar criando conexão a todo instante para cada operação que for realizar pode ser mais agravante do que ter conexões prontas e adormecidas para uso.

1 curtida

Então para cada aplicação que eu tenho para este banco, ele tem estas 20 conexões ativas. o que é mostrado nesta consulta SELECT * FROM pg_stat_activity; no banco de dados.

Se tenho 10 sistemas conectadas a este banco, sempre vai ter 100 conexões ativas e adormecidas ?

Exatamente!

Esse número 10 (idle) é um número mágico, da mesma forma que o 20 (sob demanda) também é.

Você pode adaptá-los para a sua realidade e necessidade!

O importante é sempre ter em mente que a quantidade de conexões idle é: valor definido para idle x número de instâncias.

Da mesma forma o máximo de conexões ativas que você pode atingir é: valor definido para máximo x número de instâncias.

1 curtida

Obrigado pela ajuda

@Jonathan_Medeiros