Executando verificação de segurança...
2

Debugando gargalos de performance em um sistema Java legado

Bom dia, pessoal! 👋
Gostaria de compartilhar uma situação que aconteceu comigo ontem.

Presto serviço terceirizado para um órgão do governo do meu estado, dando suporte a diversos sistemas — principalmente almoxarifado e patrimônio.

Ontem, fui acionado para apoiar um problema no fluxo de doação. O sistema em questão é antigo (cerca de 10 anos), desenvolvido em Java 6, com JSP no front. Tudo bem acoplado e… do jeitinho certo para dar dor de cabeça 😅. O código é bem legado, cheio de boilerplate, o que acaba trazendo muitas regras confusas e difíceis de manter.

O problema

O órgão não conseguia receber a doação porque o sistema estava dando timeout.
Investigando o código, encontrei o principal gargalo:

  • Dentro de um for que percorria os itens a serem recebidos, eram feitas várias consultas ao banco por item.
  • A doação tinha mais de 700 itens.
  • Para cada item, eram executadas pelo menos 5 consultas.

Resultado: o processo ultrapassava facilmente 1 minuto de execução.

A solução aplicada

Para resolver isso, fiz o seguinte:

  1. Movi as consultas para fora do for.
  2. Percorri a lista apenas para coletar os IDs dos itens.
  3. Executei uma única consulta ao banco usando essa lista de IDs.
  4. Montei um Map em memória, associando ID → valor.
  5. Dentro do for, passei a acessar apenas o valor pelo ID, sem novas consultas.

Essa mudança resolveu o principal gargalo e o desempenho melhorou bastante.

Novo desafio: persistência

Após isso, começaram os problemas na hora de salvar os dados:

  • O banco de homologação é bem mais lento que o de produção.
  • O uso de merge fazia várias consultas para validar se as entidades já existiam.
  • Tive problemas com entidades detached, mas consegui contornar.
  • Aparentemente, não há muita alternativa ao merge, já que ele garante a consistência.
  • Tentei usar Session.update diretamente, mas surgiram conflitos com objetos lazy já vinculados a outra sessão.

Conclusão

Consegui resolver o problema no mesmo dia e obter um desempenho bem melhor, eliminando o maior gargalo.
Ainda assim, fiquei com a dúvida:

Existe alguma outra forma mais eficiente e simples de lidar com esse tipo de timeout, especialmente em sistemas legados como esse?

Se alguém já passou por algo parecido ou tiver alguma sugestão, ficarei feliz em ouvir! 😊

Carregando publicação patrocinada...
1

que bom que o caso foi resolvido lendo código, as vezes não da pra encontrar e tem que usar ferramentas como jdk flight recorder.

Concordo com o que o Pilati disse: "observabilidade". Instrumentar observabilidade no sistema fica bem mais facil de enxergar esses gargalos. Em empresas grandes vi o Dynatrace ou Data Dog que ajudam muito para encontrar esses problemas.

1

O uso de merge fazia várias consultas para validar se as entidades já existiam.

Verifique se tem alguma forma de fazer merge em banco. No postgresql tem essa solução:

INSERT INTO order_items (
    id,
    quantity,
    price,
    created_at,
    updated_at
) values (
    @Id,
    @Quantity,
    @Price,
    NOW() AT TIME ZONE 'UTC',
    NOW() AT TIME ZONE 'UTC'
) ON CONFLICT (id) DO UPDATE SET
    quantity = EXCLUDED.quantity,
    price = EXCLUDED.price,
    updated_at = NOW() AT TIME ZONE 'UTC';";

Mysql:

INSERT INTO users (user_id, username, email)
VALUES (1, 'john_doe', '[email protected]')
ON DUPLICATE KEY UPDATE
    email = '[email protected]';

no postgres inclusive tem uma técnica de passar arrays para o banco (unnest), assim consigo incluir (ou atualizar) dezenas de registros com uma única query

Existe alguma outra forma mais eficiente e simples de lidar com esse tipo de timeout, especialmente em sistemas legados como esse?

Debug .. Identificar a causa raiz .. otimizar

Para não ser pego desprevenido: Observabilidade

Muitos sistemas ignoram completamente a observabilidade, mas é com ela que conseguimos identificar problemas de desempenho antes deles se tornarem críticos.

1

Nesse caso, o banco é um Oracle 10g, bastante antigo. Concordo com você que, para obter mais performance, o uso de SQL puro seria o caminho mais rápido.

No entanto, meu receio em seguir por essa abordagem está relacionado à auditoria que é aplicada nas entidades, pois ela também acaba impactando o tempo de execução.

Talvez a melhor solução seja utilizar SQL puro e realizar o INSERT manual na auditoria, mas fico receoso de acabar esquecendo algum campo ou regra, já que as classes são bem grandes e complexas.