Em busca da latência perfeita

Fabricio Gonçalves
4 min readSep 30, 2022
Em busca da latência perfeita

Esse blogpost é sobre meu caminho para chegar no SLO de latência em um ambiente de microsserviços com arquitetura assíncrona e de como a ferramenta latency-agregattor me ajudou a chegar no meu objetivo. Também é meio que uma continuação sobre o tema observabilidade e SLO de posts passados.

Primeiro, o que é SLO? SLO é o acrônimo para Service-Level Objective, ou objetivo de nível de serviço. Na prática um acordo formal, normalmente dentro de uma SLA, entre produtos & negócios com o time de desenvolvimento para definir o valor alvo a ser atingindo sobre uma métrica específica, como tempo de atividade ou tempo de resposta. Você pode se aprofundar mais na fonte, o livro de SRE do google.

SLO — Tempo de resposta

Como foi dito, SLO é um acordo, e este acordo pode ser simples assim:

99% das requisições da página inicial devem retornar em menos de 100ms nos últimos 30 dias.

Pronto! Temos um SLO de latência, porém…SRE é User-Centred, SLO deve ser sobre as jornadas críticas dos seus usuários e não sobre serviços. Neste momento que as coisas ficam complicadas em sistemas com arquitetura assíncrona, porque, diferente de uma aplicação monolítica, o serviço não é o início e o fim, mas sim apenas um pedaço do todo.

SLO — Sistema monolítico

Partindo da premissa que estamos usando o serviço instrumentado para gerar a métrica, vamos precisar apenas marcar o início de um processo e no fim gerar um histograma com o valor da duração.

SLO — Arquitetura síncrona

Mesmo tendo mais serviços durante o fluxo, ainda temos um único ponto de entrada e saída, o que facilita gerarmos nossa métrica de latência.

SLO — Arquitetura Assíncrona

Aqui as coisas podem ser difíceis, por exemplo, você pode ter um serviço de pedidos que escreve em um tópico (ou fila) e um serviço de pagamento que vai efetuar o pagamento. O fluxo inicia em um serviço e termina no outro.

As coisas evoluem e surge um novo serviço depois do pagamento para fazer a entrega do pedido.

Neste cenário que o latency-agregattor poderá ser útil, atuando como um centralizador da métrica de latência, mas vamos ver primeiro como cheguei nele.

Latency agregattor

Precisava de uma forma simples de contabilizar a latência levando em conta todos os percalços no meio do caminho, como por exemplo atraso nos tópicos e erro no meio do fluxo.

Primeiro eu tentei gerar um histograma de cada serviço e juntar isso pra tentar chegar no valor de latência da jornada, porém, essa abordagem não leva em consideração o atraso do consumidor da fila, por exemplo, o que já gera um furo na nossa métrica.

Depois eu tentei passar esse o valor inicial da jornada(created_at) no serviço inicial, no serviço final usei o diff de tempo (now() - created_at) para gerar a métrica, porém… se aparecer um serviço depois do último furaria a métrica, se desse qualquer erro no meio ficaria complexo garantir que o serviço problemático “fechasse” a métrica.

Inspirado no Pushgateway e no Prometheus Aggregation Gateway que surgiu o latency-agregattor, que tem como objetivo deixar fácil a adição de novos serviços na jornada e tratar os casos onde temos problemas no meio.

Latency Agregattor na pratica

Você pode subir uma instância usando a imagem disponível docker hub que ira expor as métricas para o scrape do Prometheus no path /metrics, desta forma:

docker run -p 7070:7070 fsrg/latency-aggregator:0.0.1

Se estiver usando GoLang, basta usar a lib que o projeto oferece para submeter sua métrica

https://github.com/Espigah/latency-aggregator/blob/main/docs/exemple/main.go

Ou, se preferir, pode usar a API do serviço, assim:

https://github.com/Espigah/latency-aggregator#api

Sob o capô

O.K., mas como ele funciona?
A mecânica por de trás do Latency Agregattor é bem simples. Primeiro ele agenda o export da métrica a cada intervalo de life_span para um UUID, e a cada novo request ele reagenda esse export. Desta forma, se um request não chegar no tempo esperado, a métrica será gerada, ou seja, se algo de imprevisto acontecer no meio do fluxo, a métrica será gerada. Seguindo a mesma lógica ele encerra o fluxo, já que, depois do último request, a task agendada vai ser executado no final do life_span expondo a métrica com o tempo que levou do primeiro request até o último.

O serviço ainda esta sendo estressado por aqui, a lista de todo é grande, mas qualquer pitaco será sempre bem-vindo! vlw!

[]s

--

--

Fabricio Gonçalves

Não tenha medo de inovar e nem de desafios, tenha prazer em ajudar, seja autodidata e ....