Construindo um pipeline near real time resiliente de forma simples e rápida!

Rafael Anizio
4 min readDec 8, 2020

--

Aqui no Delivery Center temos desafios diários em todas as áreas da empresa. Como Engenheiro de Dados, participo desses desafios e um deles foi criar um pipeline para alimentar uma aplicação de visualização near real time. Como se não bastasse, o desafio proposto foi preparar este pipeline para utilização desta aplicação durante a black friday 😱.

Esta aplicação se chama “LiveOps”. Nela conseguimos acompanhar a nossa operação em três pilares: pedidos, entregadores e os hubs. Os pedidos basicamente são as compras feitas pelos consumidores no aplicativo, os entregadores são nossos parceiros que utilizam motos e bikes para entregar os pedidos aos consumidores e os hubs são centros de logística localizados em shoppings (uma espécie de transbordo no modelo logístico).

O objetivo do Delivery Center com o LiveOps é apoiar na tomada de decisão de forma rápida, para que lojistas (aqueles que disponibilizam os produtos para venda), entregadores e consumidores tenham uma experiência positiva durante a operação.

Desafio 🧐 !

O grande desafio foi desenvolver este pipeline na véspera da black friday, com os seguintes requisitos:

  • Ser near real time, ou seja, todo o fluxo com milhares de pedidos deveria ser atualizado em até 10 segundos
  • Ser de baixo custo, de rápida implantação e baixa complexidade
  • Ter baixo nível de granularidade de informações para tomar decisões com mais precisão

Como resolvemos 🤔 ?

Como precisávamos exibir o número de entregadores próximos do HUB, resolvemos utilizar as coordenadas dos pontos (Latitudade x Longitude) calculadas com o ST_MakePoint. Nesta abordagem, se o entregador estiver até 500 metros do HUB, ele fica disponível para aquele shopping, ou seja, se o entregador ir para 1 ou mais shoppings, vamos conseguir distinguir e exibir corretamente na aplicação em qual shopping ele está alocado.

Tecnicamente, a maioria de nossas aplicações rodam em cloud, algumas na AWS, mas as principais estão no Google Cloud Platform (GCP). No contexto do GCP, temos o Cloud SQL, que é um serviço gerenciado para hospedar bancos de dados relacionais. Este serviço é excelente para dar celeridade no desenvolvimento e garantir escalabilidade, porém, infelizmente, o Cloud SQL não permite replicar um banco Postgres de forma master <> master. E essa funcionalidade era fundamental para o trabalho😔.

Na prática fizemos o seguinte:

  1. Criamos uma instância com o Compute Engine (Máquina Virtual).
  2. Configuramos a máquina: instalação do python e postgres. E nosso escopo de tecnologia parou por aí. #simplicidade

Pensando em arquitetura conceitual e lógica, temos o seguinte:

Modelo Conceitual
Modelo Lógico

Observando a arquitetura conceitual, notamos que podemos ter uma ou mais aplicações, e elas podem ser consultadas simultaneamente. Perceba que conseguimos extrair dados de várias aplicações, desde que os bancos de dados estejam sincronizados de forma master <> master.

Já no modelo lógico, perceba que no fluxo 2 criamos queries para pegar dados do dia atual até D -14. Nelas extraímos somente o necessário, evitando alterar dados (transformação de dados) com o objetivo de não perder performance no tempo de execução da query.

Lembrando: Essa etapa é executada somente uma vez! 👈

Então, em nosso banco MASTER (Liveops Master) criamos views materializadas (mvw) e nelas fazemos todas as transformações nos dados(ETL).

Cada mvw precisa ter um index único para conseguirmos atualizar todas as views. Para isso, utilizamos o seguinte comando:
refresh materialized view concurrently <nome_view_materializada>

Esse comando atualiza a view materializada sem recarregá-la totalmente, ou seja, simplesmente captura os novos dados e insere na view materializada. Desta forma, a disponibilidade da aplicação é alta. #resiliente 💪

Como estes scripts funcionam?

Bom. Temos os seguintes scripts:

  • manager.sh : É responsável em gerenciar os demais scripts, inicializando no momento certo
  • architech.sh: Inicializa o start.py
  • bricklayer.sh: Gerencia todos os scripts incrementais e encerra após as 23:59:59
  • clean.sh: Inicializa o clean_old_database.py
  • starter.py: É executado uma vez, ele cria todas as tabelas, views materializadas, index e faz os inserts no banco do dia atual até D-14
  • clean_old_database.py: Deleta todos os dados maiores que D-14, limpa o cache da máquina e atualiza as views materializadas
  • incremental_XX.py : São scripts de execução contínua, selecionando todos os dados nas réplicas a partir do último registro inserido na Liveops Master
Rotina de execução dos scripts

Basicamente é isso galera!

Qualquer dúvida podem me adicionar no LinkedIn.

Agora, vocês podem ver como o Fernando Trevisan consumiu todos os dados com vários painéis e alertas utilizando o Grafana!🤩Clicando aqui !

Muito obrigado pessoal.

--

--