Como evoluímos a plataforma de dados da EmCasa

Jason Carneiro
EmCasa
Published in
6 min readNov 3, 2022

--

A EmCasa surgiu com o propósito de transformar o processo de compra e venda de imóveis no Brasil, tornando-o prático, transparente e seguro. Para que este objetivo seja atingido, além de termos o comprador de imóveis no centro de todas as áreas, oferecemos o máximo de dados e métricas aos nossos times, auxiliando-os na gestão do dia a dia e gerando mais assertividade nas tomadas de decisão.

Nesse post, vamos compartilhar um pouco de como é a engenharia de dados da EmCasa, citando alguns pontos iniciais e como o time de Plataforma (Jason Carneiro e Daniel Sousa) evolue o trabalho de dados.

Introduzindo a arquitetura inicial

Assim como descrito por André Sionek, em What I learned from being a startup’s first Data Engineer, a arquitetura de dados da EmCasa nasceu com “algumas tabelas em nosso banco de dados e uma ferramenta simples de Business Intelligence” e foi se transformando em algo mais robusto com o crescimento do negócio.

À medida que a atuação da EmCasa foi se expandindo, fontes já integradas passaram a fornecer mais dados, novas fontes foram introduzidas, novos produtos de dados foram implementados e o esforço de manter tudo monitorado tornou-se mais complexo. Ferramentas introduzidas na nossa stack começaram a demandar um esforço maior de gerenciamento, entre elas:

  • Redshift: tornou-se gargalo na arquitetura devido a alguns problemas de concorrência de recursos e espaço de disco. À medida que a empresa foi crescendo, novos pipelines ETL foram implementados, aumentando o número de escrita e leitura no cluster;
  • Glue Jobs: o gerenciamento de pacotes e bibliotecas internas tornou-se uma etapa relativamente complexa. Paralelo a isso, o Glue é uma ferramenta acessível somente dentro do console da AWS, o que dificulta que pessoas não desenvolvedoras acessem o histórico de execuções ou sejam notificadas quando algo não foi executado como o esperado.

Naquele momento, olhando de uma forma ampla para a nossa arquitetura, observamos que haviam alguns pontos de ambiguidade:

  • Tínhamos duas ferramentas para armazenar/consultar dados: AWS Redshift e Athena;
  • Tínhamos duas ferramentas para hospedar e gerenciar nossos pipelines ETL: AWS Glue e Apache Airflow.

Frente a essas observações e os problemas já apresentados, somados ao estágio em que a EmCasa estava, passamos a questionar o andamento da evolução da arquitetura.

Nova arquitetura de dados

Com o objetivo de tornar a arquitetura mais escalável, democrática, fácil de observar e gerenciar, propomos migrar alguns componentes da arquitetura. Os pontos que foram colocados em pauta foram:

  • Utilizar uma ferramenta para armazenamento de dados mais escalável e menos burocrática de gerenciar;
  • Unificar nossa ferramenta de consulta aos dados;
  • Unificar a ferramenta de hospedagem e gerenciamento de pipelines ETL;
  • Implementar um fluxo simples e de fácil acesso para monitorar os pipelines e alertar quando algo demonstrasse um comportamento fora do esperado.

Decidimos então:

  • Partir para uma arquitetura de Data Lake utilizando o S3 da AWS;
  • Utilizar o Athena como única ferramenta de consulta aos dados;
  • Utilizar somente o Airflow para gerenciamento e hospedagem dos nossos pipelines ETL.

Monitoria e observabilidade

Nesse ponto, dado o know-how do time em gerenciamento de infraestrutura, decidimos subir grande parte dos nossos componentes em um cluster Kubernetes, utilizando o serviço AWS EKS. Isso nos possibilitou manter a hegemonia no gerenciamento de logs e métricas de todas as nossas aplicações no cluster que eram direcionadas e rotacionadas via Prometheus, para o nosso serviço de análise de logs e métricas, o Grafana.

Uma decisão importante foi utilizarmos o serviço AWS EMR para executar nossos scripts de transformação e load dos dados, mas na versão containers. Isso nos possibilitou hospedar a execução desses scripts na infraestrutura Kubernetes, reforçando o ponto de manter a hegemonia no controle dos nossos logs e métricas, associado ao fato de termos uma maneira mais flexível de escalabilidade de recursos das aplicações Spark e também da personalização de pacotes e dependências dessas aplicações. As características de ambientes em containers acabaram nos trazendo grandes benefícios.

Em resumo, a nossa stack eram tarefas do Airflow, aplicações Spark e produtos de dados rodando numa infraestrutura que tínhamos bastante controle, executando no Kubernetes, com o Athena e S3 rodando em uma arquitetura serverless que nos proporciona uma alta escalabilidade.

Novos componentes

Embora tenhamos decidido continuar utilizando o Airflow, optamos por introduzir um novo aspecto nele, com o objetivo de facilitar ainda mais a adoção por outros times, que tinham a necessidade de implementar pipelines, agilizando assim o dia a dia da Engenharia de Dados:

Fizemos algo parecido com template de DAGs: quando alguém precisasse implementar uma nova DAG, não seria mais necessário se preocupar com a estrutura do Airflow, nem com cada configuração que ele oferece. Bastaria somente especificar algumas regrinhas e a sua função Python para que o template ficasse responsável por renderizar a DAG. Nesse template também implementamos um fluxo para notificar o time responsável, via Slack, caso a DAG falhasse.

Dados incrementais

Em paralelo, com a adoção completa do Athena + S3 e o desuso de Redshift, encaramos um novo desafio: lidar com a ingestão de dados incrementais.

Das fontes que fazemos ingestão, algumas pertencem à categoria de dados incrementais, a qual era resolvida facilmente pelo Redshift, com a operação de UPDATE e DELETE. Porém, como persistimos os nossos dados no Data Lake, no formato Parquet, a natureza imutável desse formato não nos possibilita fazer essas operações de escrita especiais nativamente.

Começamos a procurar soluções do mercado que resolvessem esse tipo de operação de escrita (UPDATE, DELETE e UPSERT) e que integrassem facilmente com o ecossistema Spark. Estudamos algumas, como Delta e Apache Hudi. Dadas as integrações já existentes com o ambiente AWS, optamos por utilizar o Hudi.

Por um período de tempo, utilizamos o Hudi como intermediário para solucionar as interações com nossos dados armazenados no S3. Entretanto, começamos a experimentar alguns problemas:

  • No momento que introduzimos o Hudi na nossa plataforma, a versão disponibilizada pelo EMR estava desatualizada — precisávamos da versão 0.10.x e estava disponível apenas a versão 0.8.x. Isso nos forçou a incluir o .jar fornecido pela própria comunidade, nas nossas imagens do Spark;
  • Como fazíamos a ingestão de várias tabelas de uma instância RDS via DMS, utilizamos o componente Hoodie Delta Streamer para cuidar disso. Porém, ele demanda vários arquivos de configuração separados, o que exigiu um certo grau de esforço sempre que novas tabelas eram introduzidas, ou quando era necessário executar um full load;
  • Passamos a receber erros de Rate Limit de leitura no Athena, quando íamos consultar os dados. Isso acabou demandando um esforço considerável de tuning, comparado ao nosso volume de dados.

Por conta desses pontos, decidimos então substituir o Hudi pelo Apache Iceberg, que nos proporciona a mesma habilidade de realizar operações de UPDATE, DELETE e UPSERT e algumas outras funcionalidades que foram muito bem-vindas, como, principalmente, suporte à hidden partitioning e maior integração com Spark SQL.

No momento dessa mini-migração, fomos muito beneficiados pela utilização de uma biblioteca interna que decidimos implementar desde o início da migração da arquitetura como um todo — a biblioteca Emdata.

Emdata é uma biblioteca que surgiu a partir de uma ideia simples: centralizar toda a interação entre um consumidor do Data Lake, esteja ele lendo ou escrevendo, com o Spark. Com isso, todas as operações de escrita teriam que passar pela biblioteca, assim, poderíamos garantir que os dados persistiriam na camada, no formato e com o nome corretos. Além disso, seria possível implementar auditabilidade nas interações com o Data Lake.

Nos módulos de escrita da biblioteca, inicialmente implementamos a integração com o Hudi. Mais para frente, o substituímos pelo Iceberg. Dessa forma, quase nenhuma mudança foi necessária de ser aplicada nos nossos pipelines ETL, somente no Emdata.

Conclusão

Após todas as mudanças da arquitetura de dados da Emcasa, seja pela introdução de novos componentes ou aproveitando aqueles que já usávamos, conseguimos atingir nossos principais objetivos:

  • Todos os nossos dados são persistidos no S3 e também são separados fisicamente entre camadas. Podemos escalar nossa camada de armazenamento ao infinito e além!
  • Centralizamos nossa ferramenta SQL de acesso aos dados via Athena, possibilitando aos nossos analistas e cientistas não ficarem alternando entre os dialetos SQLs. Também nos beneficiamos de uma ferramenta serverless — embora Athena também tenha algumas limitações de escalabilidade;
  • Centralizamos todos os nossos pipelines ETL em uma única ferramenta — Airflow. Uma ferramenta bem robusta, com fácil interação de pessoas não desenvolvedoras e mantida pela comunidade;
  • Graças ao Grafana, todos os nossos logs e métricas são disponibilizados em uma única ferramenta e não precisamos ficar navegando em um mar de menus. Ela ainda nos alerta via Slack, e,dessa forma, torna o nosso trabalho de operação bem mais simples e centralizado;
  • Demos visibilidade e autonomia para os times consumirem e manterem seus processos.

Além dos objetivos iniciais, a nova arquitetura provou-se mais simples e menos custosa. Embora tenhamos aumentado a quantidade de dados processados pela plataforma, a nova arquitetura nos trouxe uma redução de quase 60% nos custos e nos possibilita, no futuro, aumentarmos o volume de processamento, sem aumentar proporcionalmente os custos.

--

--