2 pontos por GN⁺ 2024-05-21 | 1 comentários | Compartilhar no WhatsApp
  • 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

 
GN⁺ 2024-05-21
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...

    • Colocar 1,7 petabyte no SQLite? A própria recomendação do SQLite é esta: se os dados provavelmente vão crescer tanto que será inconveniente ou impossível colocá-los em um único arquivo de disco, escolha outra solução em vez do SQLite
      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
    • Um SSD de 30,7 TB custa cerca de US$ 5.500 cada, e seriam necessários 56 deles para chegar a 1,7 PB mesmo sem redundância. Além disso, o tamanho máximo de banco de dados do SQLite é 140 TB
      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
    • A proposta de valor da nuvem comercial não é economia de custos, a menos que você quantifique todos os fatores secundários e externos, como riscos de segurança, climatização, equipe de data center e ciclo de vida de hardware
      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
    • Por melhor que seja o martelo, algumas coisas simplesmente não são pregos
    • Não dá. O SQLite funciona “só” até 281 TB [0] [1]
      [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

    • Sim. Parece uma solução interna
      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

    • Você pode postar alguma referência sobre esse padrão?
    • Não entendo por que era preciso colocar 2 semanas de transações imutáveis no Dynamo. Alguém tem alguma pista?
  • 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

    • Não entendo por que dizem que é caro. US$ 120 por ano para 100 WCU e US$ 30 por ano para 100 RCU não parecem caros
      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

    • Teria sido interessante. O TigerBeetle é escrito em Zig
      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?

    • Em uma ponta do espectro há pessoas que dizem que fariam esse software em um fim de semana. Na outra, há quem diga que precisa de um salário de US$ 600 mil e de mais 9 colegas para conseguir fazer algo assim
      Há bastante espaço no meio para uma estimativa de custo mais realista
    • Essa estimativa é suspeitosamente parecida com calcular apenas o custo de armazenamento de dados do DynamoDB
      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
    • Se desenvolver e manter um sistema de DB totalmente customizado nessa escala custar US$ 5 milhões por ano, daria para contratar cerca de 25 engenheiros seniores e ainda sobraria US$ 1 milhão para equipamentos
      Parece bastante realista para ter um sistema dedicado bem ajustado a uma parte central do negócio
    • O mais provável é que eles saibam muito mais sobre os próprios custos, e você quase nada. Não há muito valor em diminuir os membros da equipe desse jeito
    • Lendo o texto, parece que esse sistema era uma camada sobre o DynamoDB, que foi atualizada para usar o produto interno Docstore, e nesse processo foi preciso adicionar uma funcionalidade ao Docstore
      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?

    • Trabalhei em uma empresa que, na fase de protótipo, usava Redis e depois escreveu o próprio banco de dados para aumentar desempenho e resiliência
      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

    • Concordo com o ponto geral, mas não acho que esse exemplo seja um bom caso
      É 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