Melhorando o tempo de consulta ao ERP em uma aplicação .NET
Recentemente meu time recebeu a tarefa de melhorar o tempo de consulta de uma das etapas mais importantes do nosso WMS.
O sistema não possui base própria, ressalva algumas de configurações de sistema, por isso dependemos de uma plataforma de ERP para a criação e consulta de dados, no caso o SAP. A etapa em questão, compõem-se de algumas requisições e validações que são necessárias para efetuar a conferência de um pedido.
Resumindo: a regra de negócio e a persistência dos dados.
Para realizar esta tarefa, a média de tempo que tínhamos durante a semana era de 15s. Faça você mesmo o teste. Pegue um cronômetro e espere 15s, sem fazer nada. É incômodo, não é? Mesmo assim, pode não parecer muito, falando de um pedido, mas quando analisamos os vários operadores, conferindo centenas de pedidos, esse número é aumentado exponencialmente. Fora que esses 15s é um tempo perdido na operação, porque o operador precisa ficar esperando até que possa continuar com o pedido em mãos.
Aplicando o que irei explicar a seguir, o tempo diminui para para uma média de 5 à 4s, tendo picos de tempo até menores, chegando próximo ao 2s e 1s. Faça o teste do cronômetro novamente e sinta o resultado!
E então, como resolver esse problema?
Arquitetura orientada a eventos, mensageria com RabbitMQ e uma base em cache com Redis.
Antes da etapa de conferência, onde o operador confere os itens do pedidos e sua etiqueta de rastreio, o pedido passa por uma etapa chamada “separação”. Como você pode pressupor, na “separação” o pedido é separado. E isso significa que todos seus itens são selecionados e separados em uma caixa com identificador. Após esta etapa, o pedido fica em stand-by por um tempo. Até que o mesmo operador, ou outro operador, prossiga com ele para a etapa de conferência. Visando aproveitar essa janela de tempo, alteramos o funcionamento do código para que continuasse rodando por debaixo dos panos após o usuário finalizar a separação.
Quando um pedido é conferido, é validada a caixa que está o pedido, a entidade do pedido em si, seus respectivos rastreamentos, canais de venda (se houver), itens e quantidades. Acabam que vários requests são necessários para acessar as camadas de dados do SAP, no caso feitas via arquivos XSOData e Service Layer.
Por se tratar de uma grande quantidade de requests feitos, essa etapa de conferência torna-se onerosa. Mudar todos esses requests para que sejam realizados após o fim da separação, faz com que esse tempo de 15s para conferir não afete o fluxo dos pedidos, e também que seja ignorado pelo usuário. Para realizar esta alteração, optamos por mudar um pouco a estrutura do código referente às etapas em questão.
Foi implementada uma arquitetura orientada a eventos, sendo a separação o publisher e a conferência seu consumer, quem produz em quem consome os eventos, respectivamente. Aqui podemos ver um dos comportamentos principais de quando trabalhamos com eventos e também mensageria.
Temos a entidade que produz o evento, seja esse evento a criação de um objeto ou o disparo de uma mensagem para comunicar outra entidade. E outra que “se inscreve” à entidade publicadora. Podemos entender isso como uma assinatura mesmo, por exemplo o de revistas mensais, onde as partes interessadas fazem a inscrição para receber a revista a cada nova publicação.
Para garantir a persistência dos dados, adicionamos um intermediador nestes eventos, usando o sistema de mensageria do RabbitMQ. Após o final da separação do pedido, a própria classe de Separação (no caso, nosso publisher) produz o evento que é lançado a um broker do Rabbit. Dentro do broker, o Rabbit se encarrega de enviar às respectivas filas relacionadas à Conferência. Desta forma, do outro lado a classe de Conferência (consumer) recebe o evento por meio desta “mensagem” do Rabbit.
Trazer o intermédio da mensageria faz com que haja uma maior segurança na disponibilidade dos dados, porque se caso alguma das API’s caírem e o evento não se completar, o RabbitMQ irá garantir que, quando os serviços estiverem restabelecidos, o evento possa ser reenviado e seu ciclo de vida completo.
Quando a Conferência recebe o evento, ela trata de aplicar toda a regra de negócio e o envio ao SAP. Desta forma não só foi desacoplado os domínios, como também garante uma maior disponibilidade dos serviços, como mencionado no parágrafo anterior. Depois que todo o processo de conferência é finalizado, os dados além de salvos no SAP, também são salvos em formato JSON, com uma chave identificadora, em um banco em memória distribuída, implementado com Redis. A partir do momento que a conferência for iniciada de fato — lembrando que até aqui somente sua entidade foi criada e validações “pré-carregadas” — , o sistema irá consumir do Redis os dados, sem ter que passar por todo o processo anterior de validações e criações no SAP.
Por fim
Desta forma, não somente ganhamos performance com a etapa da conferência, mas também foram feitas mudanças que poderão transformar todas as “etapas-chaves” do WMS em micros serviços independentes, descartando o monólito que é hoje. Com os eventos garantimos uma maior coesão e flexibilidade do código, permitindo serem feitas mudanças de forma mais fácil. Assim, podemos implementar novas funcionalidades que, por exemplo, dependem da etapa de Separação, apenas “inscrevendo-as” como consumers da Separação. Usar o RabbitMQ garante maior disponibilidade das informações, além de orquestrar todos os fluxos de mensagens/eventos que possam haver no sistema. E pra finalizar, o Redis nos disponibiliza todos os dados necessários por um cache disponibilizado anteriormente pelo publisher da Conferência. Todas as partes trabalhando juntas, de formas individuais em seus clusters, ajudam na persistência de todos os dados que transitam pela aplicação. No caso de haver uma queda nos serviços do web-portal, por exemplo, os dados não serão afetados e sua persistência poderá ser feita quando o outro serviço voltar a estabilizar.
Dados
Só para finalizar com dados em números dos ganhos que tivemos. Descemos de um tempo médio de 15 segundos para 4 a 5 segundos. Algumas vezes batendo até menos que isso, próximo dos 2 a 1 segundos (dependendo do pico de usabilidade do sistema). Abaixo temos um print do Grafana, sistema que analisa os dados referentes à nossa aplicação:
Desta forma, agregamos valor com uma maior agilidade no processo do sistema, ganhamos produtividade na logística e o tempo de vida do pedido na mesma, e também demos o pontapé inicial para desacoplar as funções dos WMS do monólito atual.
Espero que este artigo tenha agregado em seus conhecimentos de alguma forma, seja com alguma ideia ou até mesmo apenas um insight!
Bons códigos! ;)