Bom, vamos inicializar as aplicações agora, a page produtos, a page orders e começar a gerar insumos ali para a gente capturar os eventos de tracing e analisar. Então estou com aqui o path da infra na linha de comando, vou rodar docker compose app, não vou rodar em background porque eu quero ver os logs da aplicação e eu vou esperar ela subir e assim que subir, a gente vai começar a visualizar algumas informações já no serviço do Jaeger. A aplicação terminou de subir, vamos acessar agora o serviço do Jaeger, não tem por enquanto nenhum serviço aqui, vamos atualizar a página e já começa a aparecer aqui alguns serviços do Store Orders API e Store Products API. Aqui aparece o Jager All-in-One, porque ele também está monitorando o tracing do próprio serviço. Uma vez aparecendo o serviço, você consegue filtrar os traces, filtrando aqui por horário, por exemplo, os últimos cinco minutos, etc. Limitar o resultado da busca e você pode até filtrar pela duração. Vamos supor que você queira filtrar aqueles eventos que duraram acima de um segundo, de dois, para facilitar o seu troubleshooting, aqui pode ser uma configuração então quero serviços que estão levando de um a dois segundos porque eles são críticos eu quero atualizar quero atuar em cima deles aqui vai aparecer a opção de filtro pra você é se eu filtrar agora o find traces vai aparecer alguns eventos de conexão por exemplo das aplicações porque elas começaram já a interagir por exemplo com a base de dados, eu estou capturando os eventos de base de dados assim que a aplicação subiu ela já testou a conexão com a base se eu pegar por exemplo story products API aqui também, inclusive ele faz a carga dos dados de uma pré-carga dos dados de produtos por exemplo, então ele está trazendo aqui essas informações e se eu quiser filtrar pela operação eu tenho as operações aqui no canto direito de conexão, posso filtrar, eventos de carry, enfim eu posso fazer uma especialização da busca. Vamos começar a estimular a aplicação agora. Vou abrir aqui o Insomnia, vou fazer uma requisição para a criação de uma nova order, de uma nova solicitação, vou enviar o evento, e vamos observar como que isso vai refletir no Jaeger. Observe que está levando um pouco mais de tempo agora. Está levando bastante tempo. Esse é um excelente caso, um excelente cenário para a gente fazer essa análise, esse troubleshooting utilizando o Jäger. Observe que agora a requisição está levando 9 segundos após a implementação, utilizando comunicação HTTP. Vamos entender o que está acontecendo, por que está levando 9 segundos após a implementação ali, utilizando comunicação HTTP. Vamos entender o que está acontecendo e por que está levando esse tempo. Bom, vamos olhar agora no Jaeger como que ficou. Se eu vir aqui em Store Orders API, Find Traces, observe que agora já aparece ali um Post Orders, com o trace do Post order para a gente olhar. Então, acessando aqui, vamos... Aqui tem o horário, inclusive, da requisição, quanto tempo foi feita essa requisição. Foi há dois minutos atrás. Vamos acessar. Bom, esse spam maior aqui, que tem a rota aqui, que é o carro-chefe, vamos dizer assim, ela representa esse Trace ID, porque é de onde partiu todo o processo de comunicação e integração. Esse spam, ele incorpora os spams filhos aqui embaixo. E aqui, por exemplo, ele incorpora esse spam, que é o Store Orders API, só que mais a etapa de get pro serviço de produtos. Então, ele divide aqui em duas etapas. A etapa que eu identifico a oportunidade de fazer a requisição do produto pra rota de produtos, ele é representado por esse spam em azul e dentro desse spam eu tenho de fato a do lado da API de produtos o spam que representa a recepção do evento de solicitação de uma consulta de produtos até o processamento e retorno desse resultado o spam do lado da API de orders ele tá levando aqui em torno de 4.46 segundos né para esse evento especificamente e dentro da API de fato de produtos ele levou aqui 2.74 segundos, então observe que eu tenho algum processamento interno da aplicação que está levando praticamente o dobro observe que eu tenho uma oportunidade aqui potencialmente eu tenho uma oportunidade aqui mesmo que a aplicação esteja levando esse tempo Observe que eu tenho uma oportunidade aqui, potencialmente eu tenho uma oportunidade aqui. Mesmo que a aplicação esteja levando esse tempo, eventualmente pode ser que eu tenha uma oportunidade dentro da aplicação de orders para analisar o que pode estar acontecendo. Então, feita a requisição, dentro da API de produtos eu tenho agora a interação com a base de dados. Essa interação tá levando é 1.7 segundo eu pode ser de novo um problema de performance da base se a conexão não está legal enfim mas aqui toda a comunicação com a da page produtos com a base tá levando 1.7 segundos, dos quais, e aí está faltando algum detalhe de eventos, talvez, mas aqui eu tenho as carries que rodaram no banco de dados, então eu tenho a carry, né, que busca o dado de fato, aqui é talvez a comunicação com a base está rápida, o processamento inteiro da rota de get, que está levando 1.7 segundos, 1.7 segundos, aqui na rota de, na comunicação com a base de dados, eu tenho um tempo relativamente curto, 25 milissegundos e 37 milissegundos para o resultado. Aqui ele divide em duas etapas, a carry e o result set. Vou mostrar já já, entrar no detalhe aqui desses eventos, a parte de contexto, mas vamos continuar aqui ainda no mais alto nível. Considerando que eu tenho dois itens no meu carrinho de compras, eu tenho dois produtos consequentemente, eu tenho esses dois grupos de consultas e interação com a base de serviço de orders. E aí, como eu faço essa consulta de forma cascateada e não paralelizada, aqui eu posso acabar trazendo, enfim, acaba o resultado agravando e trazendo mais tempo de consulta, ou seja, a minha aplicação que poderia retornar mais rápido, como ela não está implementando esse paralelismo, acaba tendo um tempo maior de consulta, e aqui é uma oportunidade que você pode capturar com Trace Distribuído. O Trace Distribuído pode revelar isso para você. E isso eu consigo notar aqui, ele fez a requisição para Page Orders aqui, para o primeiro produto da lista e aqui embaixo ele fez a segunda solicitação observe que ele até levou um pouco menos de tempo, mas ainda assim ele fez de forma cascateada, primeiro ele fez esse, concluiu depois ele fez esse de baixo e depois que eu consulto os dados do produto eu venho, a requisição volta para a aplicação de orders e aonde ela vai pegar aqueles dados e de fato persistir tem um processo de cálculo né da quantidade de do preço total do produto que é em função do preço do produto versus a quantidade de cada item que eu coloquei daquele produto no carrinho. Então ele faz algumas carries localmente aqui, tem o resultado da carry, enfim depois ele persiste em algum momento, vai consultar alguma informação e persistir esse dado de fato e aqui eu tenho de forma granular qual tempo é que cada uma das interações está levando. Eu posso consultar os detalhes também de cada uma dessas interações. Se você entrar aqui, por exemplo, no primeiro spam, você tem os dados do de contexto. Então, além do ID do você tem aqui o spam ID e esse spam ID ele vai ser passado pro próximo spam, né? parent ID, não necessariamente com essa nomenclatura, mas a biblioteca que vai determinar a melhor nomenclatura para esses campos, mas o ponto é que você tem essa identificação e aí você tem as tags que vai identificar aquele evento, que vai trazer contexto para aquele evento a rota que eu estou chamando, o path o protocolo que é o protocolo né que é o o tlp o protocolo do Open telemetry o método de requisição http no caso post se a requisição foi com sucesso a biblioteca que eu tô utilizando, a versão da biblioteca do OpenTelemetry, o status code que foi retornado, o spam é um spam de servidor de back-end, a URI aqui de novo e aqui eu tenho o process que identifica que são os metadados da aplicação, eu estou utilizando a linguagem Java, o SDK OpenTelemetry e a versão do SDK 1.31. Você pode copiar esse conteúdo, esses dados, em formato JSON, enfim, são facilidades aqui da interface. E aí esses dados vão ser repassados, principalmente o Span ID e e o trace ID, e para cada uma das etapas você tem dados adicionais de contexto. Eu sei que, por exemplo, a page orders interagiu com o get product ID, e às vezes mesmo sem conhecer o código, você já consegue tirar conclusões. Eu sei que ele está chamando a rota de produtos por esse endpoint, o path product, eu estou chamando o id do produto 1 então para esse id do produto aqui pode ser até uma oportunidade se eu identifico que determinado produto está levando mais tempo do que deveria ou tá retornando algum tipo de erro o trace distribuído vai conseguir me revelar isso. Então ele vai vai ser o dedo duro nesse caso, ele vai dizer olha esse produto aqui X tem algum tipo de problema, sempre que entra nele, sempre que entra nessa rota especificamente para esse produto você tem esse tipo de comportamento. Então dados de contexto vai ajudar nesse sentido. De novo o processo e cada etapa eu vou tendo os dados detalhados. Então se eu entrar aqui, por exemplo, em products, vou entrar lá aqui na carry, eu consigo até saber qual carry que foi executado. É claro que ele não vai expor os campos, porque o JPA vai encapsular alguma coisa para a gente. Mas se eu expandir aqui em tags, para a rota de get products, eu sei qual carry que ele rodou, e até consigo criticar e julgar se essa carry é a mais performática possível de repente eu posso identificar algum possível gargalo no banco de dados, e esse gargalo ser resultado por exemplo, de uma má indexação do banco de dados, levelando a oportunidade de criar um índice adicional para o banco de dados, levelando a oportunidade de criar um índice adicional para o banco de dados. Aqui eu sei que ele está rodando um select, passando os campos, from a tabela, products, e where, o id daquele product, igual ao valor que eu estou passando, que é da carry string. Então, não preciso nem saber muito, conhecer muito o código da aplicação, eu sei qual carry que ele está rodando no banco de dados. Aí aqui no result set ele vai trazer alguma informação adicional, ele vai identificar aquele como um span, mas ali é o processo de captura dos dados da base de dados, transformação nisso para que eu consiga retornar a resultar para quem está chamando, e eu consigo distinguir o tempo que está levando para cada uma das etapas. A duração de cada um desses eventos, aqui eu tenho segregado 25 milissegundos, esse aqui levou 37 milissegundos. Aqui eu posso identificar como oportunidade também, se eu estou retornando muitos dados e eu tenho uma restrição forte de memória, aqui pode ser que eu leve mais tempo para conseguir tratar todos os dados que estão retornando da base e é isso vai refletir negativamente no tempo de latência. Claro que essa mesma visão consigo visualizar no próximo etapa aqui onde ele consulta, faz a outra consulta por outro produto, aqui eu consigo também ter o detalhe de qual produto está sendo consultada, a Carry é a mesma, aqui mais acima a rota de fato da consulta de produto, observe que eu tenho aqui products2, o id do produto é o 2 e aqui embaixo é a story orders API, eu também tenho as carries que estão sendo rodadas, executadas no banco de dados e observe que aqui na primeira execução em tags aqui eu tenho até o aqui eu faço uma inserção de onde eu passo os campos, status, total e o preço e os valores. Aqui mais pra baixo eu tenho uma outra que é pra selecionar aqui ó ou seja essa não orders item seek ou seja, essa carry não é uma carry que a minha aplicação tá criando explicitamente é uma carry que o JPA tá realizando será que eu concordo? Será que ela é performática ou não? Enfim, é uma oportunidade de avaliar se o framework tá sendo bem implementado ou se eu deveria implementar algum tipo de customização ou uma implementação explícita pra eu evitar esse tipo de geração de código automático do Spring. Aqui o result set, ou seja, o resultado dos dados estão chegando e aqui mais carries também, aqui eu tenho uma outra carry, depois que eu faço a inserção da order, eu tenho a inserção dos itens da order, ou seja, o carrinho de compras. Aqui eu tenho o insert de carrinho de compras, onde eu passo o ID do produto, a quantidade, e o ID dessa tabela e os valores estão chegando aqui. Eu faço mais inserções, etc. Aqui eu tenho outra inserção de outro produto. Eu tenho aqui o resultado. Inclusive, aqui ele mostra que foi uma linha afetada seja ele conseguiu inserir com sucesso aqui também eu tenho é linha afetada seja deu certo essa inserção e indo mais abaixo aqui depois que eu faço a inserção do segundo produto segundo item no carrinho de compras aqui eu faço uma outra um update de fato onde eu defino é eu defino qual que é a order é o ID da solicitação da order para aquele carrinho de compras né para aquele item no carrinho de compras será que eu concordo também com essa etapa adicional do JPA será que ele já não poder inserir order items passando o ID de order já no momento que ele já não poderia inserir OrderItems passando o ID de Order já no momento que ele fez a inserção aqui em cima? Por que ele está atualizando depois? Então, de novo, pode ser oportunidade de fazer um fine-tuning do JPA, do Spring Data aqui. Aqui embaixo é a mesma coisa. Ou seja, em outras palavras, se eu tenho um carrinho de compras com 100 itens, ele vai realizar 100 inserts para cada um e fazer 100 updates minha leitura, pelo menos sobre o tracing aqui, está revelando que ele faria isso e isso afetaria negativamente o tempo, se bem que ele está levando aqui um milissegundo para fazer esse update mas de novo tem oportunidade, se eu estiver pensando numa escala de um milhão de eventos por hora, por dia ou seja, se eu tiver muitos dados muitos clientes, vamos dizer assim, milhares milhões de clientes interagindo com o sistema será que isso poderia se tornar um problema um gargalo e impactaria a eficiência da minha aplicação eu tenho uma oportunidade aqui de avaliar um possível refactoring da aplicação enfim é uma oportunidade interessante aqui que o tracing tá nos revelando vamos fazer uma requisição agora de get vou fazer mais algumas solicitações de post aqui, para gerar mais produtos na base e vou fazer uma requisição de get para consultar todos os produtos para a gente visualizar como que isso vai performar então o post o insomnium disse aqui que levou em torno de de 4 segundos para fazer essa consulta de todos os produtos. Lembrando que essa rota tem um mecanismo de paginação, mas ela vai trazer pelo menos 100 itens por página. Se eu voltar aqui em Search e buscar por Orders e fazer um Find Trace dos últimos 5 minutos, ele vai trazer aqui para mim o get orders eu fiz outros posts está aqui embaixo ele trazer aqui o get orders e observe que eu tenho 76 spans sendo 28 spans do é esse daqui que conta como 3 segundos e 96, 900 milissegundos. e aí dentro de orders API eu tenho uma conexão com o banco onde eu consulto primeiro os dados daquela order eu faço uma carry para selecionar os dados da base de orders e aí eu tenho o processamento do resultado daquela solicitação aqui eu tô levando 5 milissegundos para executar a carry e 814 milissegundos para processar o resultado observe que ele trouxe seis linhas de resultado daquela daquela carry e aqui embaixo tem uma outra carry que é para selecionar agora do carrinho de compras, eu faço a seleção aqui da tabela de order items, que é o nosso carrinho de compras, fazendo join com produtos, aqui é o item que eu comentei, eu busquei de forma não exaustiva implementar, splitar essas aplicações, eu não quebrei a base, então ainda existe esse laço, esse vínculo com a tabela de produtos, que explica essas aplicações eu não quebrei a base então ainda existe esse laço ainda se esse esse vínculo com a tabela de produtos mas a ideia que numa solução ideal você não tivesse tipo de implementação que você que o produto morasse no outro a base de dados e aqui você só traz trouxesse o resultado de order items e não mais com join com a tabela de products mas seguindo aqui eu percebo que está fazendo esse join ele vai fazer isso para todos os itens de order e observe que aqui talvez tenha uma oportunidade porque será que ele está fazendo várias vezes, será que ele vai fazer essas any carries assim de forma cascateada como consigo ver aqui praticamente individualmente para cada order é o melhor para cada hora lá então seja para cada que a gente compra será que tem oportunidade aqui então de novo a é o item que pode ser avaliado o que mais aqui acho que essa quer e a mesma é a mesma coisa e essa aqui também