Fala dataholics, esse post faz parte da série sobre Azure Functions para Analytics, no post de hoje veremos como utilizar Delta Lake sem Spark, isso mesmo que você leu, parece soar estranho, é quase como um "Amor sem beijinho, Buchecha sem Claudinho", mas é totalmente possível manipular tabelas no Delta Lake sem precisar de um cluster Spark ou Databricks.
Se ainda não viu, dá uma conferida nos posts anteriores:
O que veremos nesse post:
O que é delta-rs
Quais funcionalidades disponíveis
Lendo API de Bitcoins
Escrevendo no Delta Lake
Lendo Delta Table com PyArrow e DuckDB
Otimizando tabela com Optimize e Vacuum
Cuidado com as limitações
Quanto custa? Economizando grana
Resumo
Além das demonstrações por imagens, nesse post teremos 2 videos curtos de 1 minuto mostrando na prática.
O que é delta-rs
O delta-rs é uma biblioteca construída com Rust que pode ser usada com Python e Rust para manipular tabelas no Delta Lake sem a necessidade de Spark. É como um conector que você pode usar nas suas aplicações para integrar com Delta Lake, é realmente sensacional.
Referência: https://github.com/delta-io/delta-rs
Esse projeto é mantido pelo própria comunidade do Delta Lake.
O objetivo do projeto delta-rs não é acabar com a utilização do Spark, longe disso, o foco é dar flexibilidade para desenvolvedores conseguirem integrar o Delta Lake com as suas aplicações sem precisar criar um cluster de Spark, que para alguns cenários é literalmente matar uma formiga com uma bazuca, logo o Delta-rs dá essa possibilidade para seus usuários.
No post de hoje utilizaremos o delta-rs numa Azure Function, o que nos possibilita criar aplicativos que manipulam tabelas delta com baixo custo, pois, não precisaremos criar um cluster Spark ou um cluster no Databricks para manipular as tabelas.
Tome muito cuidado para não cair na armadilha de querer converter tudo para delta-rs, vamos falar de algumas limitações muito importantes e você precisa ficar ligado.
Vejamos as features disponíveis na biblioteca:
Integração com todos os principais Object Store do mercado, ou seja, Azure ADLS, OneLake (Fabric), GCS e S3, um detalhe para o S3, pois, ele tem algumas observações para escrita concorrente.
Operações suportadas para manipulação das tabelas Delta, veja que podemos aplicar as principais, sendo DELETE, INSERT, SELECT, MERGE, OPTIMIZE e VACUUM.
Ambiente e mão na massa
Para nossa demonstração criaremos uma ingestão de dados com Azure Function e gravar em uma tabela no Delta Lake, posteriormente leremos essa tabela e aplicar operações de manutenção, leremos essa tabela também com o Databricks.
Antes do delta-rs, quem já usava Azure Function para ingestão, gravava os dados primeiro numa camada Raw em PARQUET, posteriormente chamada um Job no Databricks para processar a camada Bronze, aqui podemos economizar nessa fase e aplicar tudo dentro do Azure Function.
A ingestão será sobre dados de Bitcoins e vamos gravar esses dados numa tabela delta a cada 10 segundos para fazermos analise em near real-time.
Vejamos o código da nossa Function detalhadamente, não se preocupe também estará disponível no Github.
Dividirei em 3 imagens com blocos comentados.
Nessa primeira imagem temos alguns detalhes importantes:
1 - No primeiro bloco marcado temos a importação da biblioteca delta-rs estamos importando as duas principais funções write_deltalake e DeltaTable.
2 - Veja a variavel Storage_options, estou informando que nosso Storage é um Azure ADLS e informando o storage e access key, essa informação podemos depois guardar em uma key Vault.
3 - Nesse último bloco de código temos a ingestão da API de Bitcoins, iremos salvar essa informação em uma tabela Delta.
Nessa segunda imagem temos dois blocos:
1 - No primeiro bloco temos a definição do schema da nossa tabela, depois estamos extraindo os dados do retorno da API para gravar somente o que é relevante, veremos depois as imagens com o retorno da API.
2 - No segundo bloco estamos convertendo nossos dados para um Dataframe Pandas e depois passando ele para a função WRITE_DELTALAKE com o modo APPEND, ou seja, estamos fazendo um INSERT na nossa tabela Delta.
A API que estamos usando é gratuita abaixo a descrição dos dados que estamos extraindo:
Nessa terceira e última imagem temos partes bem importantes:
1 - No primeiro bloco temos a leitura da tabela delta (SELECT), nessa leitura estamos informando o caminho do Storage onde ela se encontra e o Storage Options para autenticação.
Note que estamos convertendo o retorno para PyArrow Table para manipularmos os dados.
2 - No segundo Bloco estamos lendo a quantidade de arquivos da versão atual (Não conta arquivos marcados como excluídos usados no Time Travel).
Também estamos listando o History da tabela (Describe History) e mostrando a última operação que aconteceu na tabela.
3 - No terceiro bloco estávamos validando se a tabela possui mais de 10 arquivos (Esse valor é apenas de teste, não é uma recomendação), se ela tiver mais de 10 arquivos é aplicado uma operação de OPTIMIZE e na sequência um VACUUM com retenção de 0 horas para limpar todo o histórico, com isso mantemos nossa tabela otimizado sem não vamos deixar nosso storage crescer sem controle.
Vejamos os logs da nossa Function rodando.
Após publicado a function para o Function App no Azure, já podemos acompanhar os logs na nossa aba de monitoramento.
Repare nesse bloco de logs:
Quantidade de arquivos: 3
Quantidade de registros: 3
Ultima operação: WRITE
Payload retornado pela API: '[[397480,1,1706989320073,0,216196.0,1,215003,1,0.00102628,0],[397480,1,1706989320073,0,216196.0,1,216306,1,0.1,1]]'
Olhando no Storage:
E continua incrementando novos arquivos a cada 10 segundos, quando ele atingir 11 arquivos uma operação de Optimize e Vacuum será disparada e veremos no log as mensagens.
Após a operação de Optimize e Vacuum já podemos notar que agora temos somente 2 arquivos, mas 12 registros, nesse caso sempre teremos 1 arquivo por INSERT, o que é ruim, pois sofreremos de Small File Problem, por isso a operação de OPTIMIZE é tão importante, falaremos de auto compaction depois.
Importante ressaltar que rodando vacuum com retenção de 0 horas você perde toda a capacidade de Time travel.
Podemos consultar ela pela Azure Function, como estamos fazendo para aplicar o Count, mas por ser uma tabela Delta, podemos ler ela de qualquer ferramenta que tenha suporte, vejamos nossa tabela no Databricks:
Note que estamos fazendo SELECT direto no Storage, embora, podemos criar ela no catálogo do HIVE ou Unity Catalog, falaremos mais desse tema em outro post.
Podemos rodar um Describe Detail para ver suas propriedades, note essa informação, falaremos bastante dela.
Para facilitar a manipulação nas queries, criei uma VIEW para referenciar nosso Storage, como mencionei, poderíamos criar uma entrada dela no catálogo HIVE ou UC.
Vamos ver ao vivo e a cores, só por imagens não fica tão nítido, veja esse vídeo de 1 minuto.
Nesse próximo vídeo podemos ver nossa tabela Delta atualizando, consultando ela via Databricks Warehouse, ou seja, posso criar Dashboards em cima dessa tabela atualizando de minuto em minuto se for interessante para o meu negócio.
E onde entra o PyArrow e DuckDB que você falou?
Como não temos o PySpark para criar Dataframes e manipular os dados, temos outras opções como Pandas, PyArrow e DuckDB, são ótimas opções para manipular os dados vindos da nossa tabela Delta.
Observe que no nosso código carrega nossa Delta Table para um uma tabela do PyArrow.
O PyArrow é importado dentro do projeto do delta-rs.
Você pode ao invés de criar uma tabela PyArrow, converter em um PyArrow Dataset e usar DuckDB para fazer queries mais complexas.
Curiosidade:
Reginaldo, como posso descobrir os parâmetros aceito pelas funções do delta-rs?
Se você não encontrar na documentação, lembre-se, Delta Lake é código aberto, portanto o delta-rs também é, você simplesmente pode abrir o código no Github e entender melhor como ele funciona.
Cuidados com as limitações
Bom como vimos anteriormente a nossa tabela é criada com a versão mais Básica, logo, não temos acesso a muitas features, se quiser entender um pouco mais sobre as versões e protocolos dá uma olhada nesse post:
Essa é a tabela de versões suportada pelo Delta-rs:
Basicamente só temos suporte para MinReaderVersion=1 e MinWriterVersion=2.
Abaixo nossa tabela de Features por protocolo de leitura e escrita:
Ou seja, se eu quiser usar CDF, Check Constraints, Column Mapping entre outras features, ainda não temos suporte.
Isso pode ser um grande impeditivo e faça uma avaliação com cautela antes de colocar em produção, apesar do delta-rs estar evoluindo bastante.
O que acontece se eu habilitar uma Feature como CDF?
Para isso você precisa fazer Upgrade da sua tabela (Isso é irreversível):
Pronto, nossa tabela não aceita mais INSERTS via Delta-rs:
Erro: This table's min_writer_version is 4, but this method only supports version 2
Então fique atento as suas necessidades antes de sair implementando.
Quanto custa? Economizando grana
Vamos pensar que se isso fosse um Job Databricks, para esse nosso caso precisaríamos de um Job Cluster ligado 24/7, então teríamos algo nesse valor:
Databricks: R$ 2051,18 por mês
Agora com a nossa Function rodando no Azure Function:
Function App: R$ 0,0 por mês
Estou simulando 259 mil execuções por mês com tempo médio de10 segundos.
O mais caro nessa demonstração seria o Storage, pois estamos fazendo muita escrita de arquivos pequenos e como pagamos por transação, isso pode custar um pouco caro.
Essa é uma comparação bem simples, existem outras variáveis a considerar.
Resumo
"Reginaldo, isso é maravilhoso, começarei amanha mesmo migrar meus Jobs Databricks para Azure Function."
Calma, muita calma nessa hora, é sim um recurso sensacional e muito útil para vários cenários, embora, você precisa considerar algumas coisas além das limitações que já citei anteriormente.
Você já consegue despejar seus dados na Bronze\Raw em uma tabela Delta, porém, você ainda precisa processar as outras camadas do seu Lakehouse, geralmente você ainda continuará precisando de Spark\Databricks no seu ambiente dependendo de como a sua arquitetura esta desenhada.
O Azure Function não tem um poder computacional grande, logo ele tende a ser usado para ingestões com poucas transformações e geralmente baseadas em evento, tome cuidado com isso, ele não é um Spark, então você precisa sair da caixinha de processamento de grandes batches e pensar em pequenos lotes contínuos.
Você pode utilizar para fazer ingestão de APIs ou mesmo receber eventos utilizando os Bindings, ou seja, você não precisa criar um cluster Databricks para fazer a Ingestão, você pode acabar economizando nessa parte.
Minha opinião: Não viveremos num mundo sem Spark tão cedo, ele ainda será ótimo para diversos cenários, é uma ferramenta muito evoluída.
O delta-rs vem para dar mais poder aos seus usuários, flexibilizando integração com sistemas, de quebra você pode encaixar em pipelines do seu ambiente para reduzir custo.
É importante ficar claro que o delta-rs não é focado para redução de custo, embora, se bem pensado, pode gerar sim uma boa redução.
Fique bem e até a próxima.
Link Github:
Referências:
Comments