- A plataforma de pagamentos da Uber migrou uma arquitetura híbrida de DynamoDB, TerraBlob e LedgerStore para uma abordagem centrada no LedgerStore, à medida que os dados de ledger acumulados desde 2017 passaram de 1 trilhão de registros e chegaram à escala de vários PB
- Por causa do custo do DynamoDB, a arquitetura mantinha apenas as 12 semanas mais recentes de dados; o LedgerStore, um armazenamento de ledger append-only, foi escolhido como solução de longo prazo
- A validação combinou validação em sombra, que replica o tráfego de produção, com validação offline comparando dumps completos, separando a verificação de risco entre dados atuais e dados frios
- Como o backfill pode gerar carga maior que o tráfego normal, ele foi conduzido gradualmente com pequenos lotes, idempotência, controle de taxa, parada de emergência e isolamento de registros problemáticos
- A migração foi distribuída de forma conservadora ao longo de algumas semanas e concluída sem interrupções, desde o fallback inicial e a manutenção dos dados no DynamoDB por um mês até o backup final e a exclusão das tabelas
Alvo da migração e arquitetura de armazenamento anterior
- A plataforma de pagamentos da Uber, Gulfstream, usava o DynamoDB como armazenamento quando foi lançada em 2017
- Com o aumento dos custos do DynamoDB na escala da Uber, a arquitetura de armazenamento foi dividida em três partes
- Dados quentes das 12 semanas mais recentes eram armazenados no DynamoDB
- Dados frios mais antigos eram armazenados no TerraBlob, o armazenamento de blobs interno da Uber
- Os dados já eram escritos no LedgerStore, que se tornou o destino final da migração
- O escopo da migração era o conjunto de dados de ledger de todos os negócios da Uber desde 2017
- Registros imutáveis: 1,2 PB comprimidos
- Índices secundários: 0,5 PB não comprimidos
- Depois de escrito, um registro de ledger é, na prática, imutável; quando é necessário corrigir problemas, os dados de índices secundários podem ser modificados
Por que o LedgerStore foi escolhido
- LedgerStore é um banco de dados em estilo ledger append-only
- Seu projeto voltado a dados de pagamento atendia aos requisitos do Gulfstream
- Imutabilidade verificável, que usa assinaturas criptográficas para confirmar se registros foram alterados
- Armazenamento em camadas, separando dados quentes e frios conforme o processamento de requisições e o custo de armazenamento
- Melhores características de latência para índices secundários com consistência eventual
- Reduzir três armazenamentos a um simplifica o código de acesso a armazenamento e o design de criação de índices do Gulfstream
- O LedgerStore roda on-premises nos datacenters da Uber, podendo oferecer menor latência de rede
- A migração para o LedgerStore também gera grande redução de custos recorrentes
Estabilidade do tráfego atual verificada por validação em sombra
- Para avaliar se o backfill estava correto, foram definidos cinco critérios
- Completude: todos os registros foram preenchidos pelo backfill
- Exatidão: todos os registros estão corretos
- Carga: o LedgerStore consegue lidar com a carga atual
- Latência: a latência P99 do LedgerStore está dentro do intervalo aceitável
- Atraso de índice: o atraso na criação de índices secundários em background está dentro do intervalo aceitável
- A validação em sombra compara as respostas baseadas no armazenamento existente com as respostas quando o LedgerStore é usado como fonte de dados
- O objetivo era levar a completude e a exatidão do backfill, segundo a validação em sombra, a pelo menos 99,99%, mas com 99,9999% como limite superior
- Um limite superior é necessário porque, em validações de dados em grande escala, investigar todos os casos suspeitos até o fim pode paralisar o projeto
- Migrações de dados antigos podem incluir escritas incorretas do início do desenvolvimento ou corrupção de dados causada pela escala
- Mesmo que o S3 garanta durabilidade de 11 noves, em uma escala de 1 trilhão de registros pode-se esperar 10 registros corrompidos
- Em índices com consistência eventual, registros que apareceriam alguns segundos depois podem parecer ausentes na validação em sombra, gerando falsos positivos
- Para verificar 6 noves com confiabilidade, é preciso comparar 100 milhões de registros; a 1.000 comparações por segundo, isso exige mais de um dia de coleta de dados
- Para 7 noves, nas mesmas condições, seria necessário esperar 12 dias
- Ao replicar o tráfego de produção para o LedgerStore, também foi possível verificar carga, latência, atraso de índices e a confiabilidade do código de acesso
- Problemas de latência e de atraso de índices encontrados durante a migração levaram a várias correções
- Otimização da chave de partição para melhorar a distribuição dos dados de índice
- Correção de um problema de índice que provocava varredura de registros em vez de consultas pontuais
- A validação em sombra ao vivo é útil para dados acessados atualmente, mas é difícil obter garantias fortes sobre todo o conjunto de dados históricos quase nunca acessados
Validação offline e backfill incremental
- A validação offline compara todo o conjunto de dados do LedgerStore com dumps de dados do DynamoDB
- Como o tráfego ao vivo acessa principalmente dados recentes, problemas escondidos em dados frios são difíceis de detectar apenas com validação em sombra
- Registros com problemas de dados precisam ser ignorados para que o backfill prossiga, e também é preciso considerar a possibilidade de bugs no próprio job de backfill
- A maior tarefa de validação envolveu 70 TB comprimidos, estimados em 300 TB não comprimidos, e comparou 760 bilhões de registros em um único job
- Um job Apache Spark nessa escala exigiu shuffle de dados e usou Distributed Shuffle as a Service for Spark, Dynamic Resource Allocation e Speculative Execution em conjunto
- Os registros ausentes encontrados pela validação offline foram usados como entrada para backfill incremental
- Ao repetir validação e backfill, confirmou-se que todos os registros foram escritos
Problemas operacionais enfrentados no backfill
- O backfill deve começar em pequena escala e crescer gradualmente até os limites do sistema
- Se forçar além do limite sem critério, a situação vira um DDoS contra o próprio sistema
- É preciso encontrar e resolver gargalos antes de escalar novamente
- Após cada expansão, é necessário monitoramento próximo
- Fazer backfill de vários anos de dados em poucos meses gera uma carga muito maior que o tráfego normal
- Se a produção processa 1.000 registros por segundo, fazer backfill de 100 bilhões de registros a 10.000 por segundo leva 120 dias
- Se houver possibilidade de o job de backfill causar uma falha durante a execução, ele deve ser interrompido imediatamente
- O backfill não deve ser um job que roda do início ao fim de uma vez, mas sim dividido em lotes incrementais
- Cada lote deve ser pequeno o suficiente para terminar em poucos minutos
- Como o job pode ser encerrado no meio de um lote, ele precisa ser idempotente
- Ao concluir um lote, estatísticas como número de registros lidos e registros preenchidos pelo backfill são gravadas em arquivos e agregadas para acompanhar o progresso
- Um backfill seguro requer controle de taxa ajustável
- Em Java/Scala, é possível usar o RateLimiter do Guava
- Se for possível rodar mais rápido quando o tráfego de produção estiver baixo, o RPS é ajustado monitorando o estado do sistema
- A Uber ajustou o RPS usando a estratégia additive increase/multiplicative decrease, mas manteve um limite superior por segurança
- Se houver suspeita de falha ou sobrecarga, deve ser possível interromper rapidamente o backfill
- Durante uma falha, o backfill deve ser interrompido como medida preventiva e para reduzir ruído
- Mesmo após a falha, a recuperação do sistema pode gerar carga adicional
- Um recurso de parada de emergência também ajuda a depurar problemas relacionados a escala
Arquivos grandes, tolerância a falhas e logging
- É adequado manter os arquivos de dump de dados em torno de 1 GB, com flexibilidade de cerca de 10 vezes para cima ou para baixo
- Se os arquivos forem grandes demais, podem esbarrar nos limites de MultiPart de várias ferramentas
- Se forem pequenos demais, haverá tantos arquivos que apenas listar todos eles levará muito tempo
- Ao executar comandos de shell, é possível atingir o limite ARGMAX
- No processo de transformação de dados do backfill, problemas de qualidade de dados ou registros corrompidos inevitavelmente aparecem
- Como registros problemáticos estão distribuídos aleatoriamente, não é possível parar o job toda vez
- Ao mesmo tempo, pode ser um bug no código, então eles também não devem ser ignorados
- Registros problemáticos são despejados separadamente, e as estatísticas são monitoradas
- Se a taxa de falhas for alta, o backfill é interrompido manualmente, o problema é corrigido e depois retomado
- Escritas de registros podem falhar por timeout de RPC
- Retentativas são possíveis, mas em algum momento é preciso desistir e seguir adiante independentemente do motivo, para que o trabalho completo avance
- Mesmo que se queira gerar muitos logs para depuração e acompanhamento de progresso, isso pode pressionar bastante a infraestrutura de logging
- Mesmo quando é possível registrar logs, o volume a armazenar pode se tornar excessivo
- Limitação de taxa é aplicada nas partes que geram muitos logs
- Se os erros forem raros, também é possível registrar todos os logs de erro
Rollout gradual e remoção do fallback
- A migração para o LedgerStore reduziu riscos por meio de um rollout conservador, além da análise das estatísticas de validação e backfill
- O rollout ocorreu ao longo de algumas semanas, com aprovação dos engenheiros de plantão dos principais serviços chamadores
- No início, foi usado um fallback que buscava os dados no DynamoDB caso eles não fossem encontrados no LedgerStore
- Cada registro marcado como ausente nos logs de fallback foi verificado novamente para confirmar se ele realmente faltava no LedgerStore
- Mesmo após remover o fallback, os dados no DynamoDB foram mantidos por um mês
- Depois disso, as escritas no DynamoDB foram interrompidas, um backup final foi criado e as tabelas foram excluídas
- Toda a migração ocorreu ao longo de 2 anos e foi concluída sem downtime nem incidentes durante ou depois do processo
1 comentários
Opiniões no Hacker News
Fico curioso se daria para colocar 1,7 petabyte de dados (1 trilhão de registros indexados) em um único servidor bare-metal muito potente, por alguns milhares de dólares por mês ou menos, e servi-los com SQLite
Por exemplo, assim: https://use.expensify.com/blog/scaling-sqlite-to-4m-qps-on-a...
O SQLite suporta bancos de dados de até 281 terabytes, supondo que você consiga encontrar um disco e um sistema de arquivos que suportem um arquivo de 281 terabytes. Ainda assim, a documentação diz que, se o tamanho do conteúdo der sinais de crescer para a casa dos terabytes, é melhor considerar um banco de dados cliente/servidor centralizado em vez do SQLite
Parece difícil colocar tanto armazenamento em um único servidor, e ainda mais difícil por alguns milhares de dólares por mês. O SQLite também não serve para esse caso de uso
Uma empresa com capital e capacidade organizacional suficientes pode construir sua própria nuvem por muito menos, mas boa parte do cálculo está em terceirizar fatores de risco
[0] https://www.sqlite.org/releaselog/3_33_0.html
[1] https://www.sqlite.org/limits.html (#12)
O LedgerStore não parece ser open source [1], e para encontrar informações relacionadas é preciso seguir posts do blog da Uber que apontam uns para os outros
Este parece ser o texto de 2021 com mais informações sobre o LedgerStore:
https://www.uber.com/en-US/blog/dynamodb-to-docstore-migrati...
[1]:https://github.com/uber
No geral, a Uber parece ter uma forte tendência a fazer internamente. Eles parecem concluir que as soluções open source existentes não são suficientes e preferem criar as próprias. É diferente, por exemplo, da abordagem do Facebook de melhorar o MySQL com MyRocks/RocksDB e mantê-lo como open source
Ao ler o texto, fica claro que a Uber percebeu bem cedo que estava usando o DynamoDB do jeito errado
Parece que algumas jornadas críticas de usuário precisavam de consistência forte, e transações antigas exigiam data warehousing em grande escala
É estranho que a arquitetura com 2 tabelas no DynamoDB não tenha sido primeiro transformada em algo como DynamoDB + Redshift. É um padrão bastante comum
Por volta de 2015, houve uma fase em que empresas de tecnologia legais como Netflix, Spotify, SoundCloud e Uber criavam muitas ferramentas de infraestrutura e banco de dados
Hoje em dia, os engenheiros costumam falar em termos de AWS/nuvem
É revigorante ver que ainda há organizações criando esse tipo de ferramenta por conta própria
Não sei a economia específica deste projeto, mas o DynamoDB é realmente caro
Em certo momento eu achava que todo mundo estava usando DynamoDB errado, fazendo scans e queries em vez de consultas pontuais em tabelas pré-computadas
Mas, mesmo usando como uma tabela hash distribuída, você ainda paga um prêmio alto
Como 1 RCU lê até 4 KB, para ler 100 MB você precisa de 100.000 RCU, o que dá US$ 30.000 por ano ou US$ 2.500 por mês. Se meu cálculo não estiver errado, não vejo nada comparável em preço
Fico imaginando se eles avaliaram https://tigerbeetle.com
E a Uber provavelmente é uma das poucas grandes empresas que têm um contrato de suporte com a Zig Foundation
Parabéns às pessoas que participaram desse trabalho. Dito isso, só o custo de operar essa equipe já deve ser bem alto e talvez não muito diferente da economia de US$ 6 milhões, além do peso de manutenção
Também parece pouco provável que o sistema de pagamentos seja uma aposta de longo prazo, então é interessante ver por que equipes assumem projetos assim. Seria uma espécie de custo afundado causado por já ter equipes de engenharia disponíveis?
Há bastante espaço no meio para uma estimativa de custo mais realista
Se dados e índices somam 1,7 PB, pelo preço de tabela do armazenamento do DynamoDB isso dá cerca de US$ 5,1 milhões por ano
Parece bastante realista para ter um sistema dedicado bem ajustado a uma parte central do negócio
Não é uma tarefa tão grande quanto as pessoas estão dizendo. Além disso, como os registros são imutáveis, muita coisa fica bem mais fácil
Fico imaginando se este é mais um caso excepcional em que, ao chegar a certa escala, compensa mais construir por conta própria. A escala que a Uber precisa lidar é bem impressionante
Só pelo texto original, não fica claro qual é o custo total de propriedade do serviço recém-refatorado. Agora eles não precisam gerenciar o próprio banco de dados e o armazenamento por trás dele? Estou deixando passar algo?
A empresa não vendia um produto para usuários finais; o produto era um sistema de arquivos distribuído
Na minha opinião, a maioria das empresas não tem a especialização para criar sistemas como bancos de dados e, mesmo quando o custo parece favorecer o desenvolvimento, na prática elas têm receio de fazer isso
Acho que é um ótimo exemplo de como um armazenamento de dados proprietário baseado em nuvem pode ficar caro, e de que migrar dele para outra coisa é realisticamente possível
Supondo que as pessoas ajam de forma racional, acho que a hora do acerto de contas para provedores de serviços em nuvem vai chegar
Já fiz trabalho contratado para uma empresa pequena que usava GCP Bigtable para gerar relatórios a partir de dados vindos de um banco MySQL de 375 MB, e gastava mais de US$ 11.000 por mês
Eles contrataram um cientista de dados recém-saído da faculdade para criar os relatórios, e ele estava fazendo algo absurdamente ineficiente com um conjunto de dados minúsculo. Pediram para eu consertar no dia seguinte por uma mixaria, e eu recusei
É simplesmente um sistema mal projetado. O mesmo problema aconteceria se tivessem superprovisionado de forma absurda um banco de dados on-premises para fazer a mesma coisa