De Cassandra para Scylla — parte 02

Ivan Stoiev
Cobli
Published in
7 min readJan 28, 2022

Dissecando um processo de migração de base de dados na vida real

Na parte 01, conseguimos o sinal verde para a migração. Agora é hora de chamar a geral e rachar a cuca para traçar uma estratégia.

Quando digo “chamar a geral” é “A Geral” mesmo: times de suporte ao cliente, vendas, diretoria, financeiro, equipes de tech… Não, não criamos um meeting com 100 pessoas. A solução foi sendo costurada por nosso time através de interações pontuais com as outras equipes, trocando idéias, negociando budgets e downtimes, colhendo feedbacks…

É importante que toda a empresa esteja alinhada e engajada em uma mudança que, ao fim, pode impactar a todos.

Mas, como essa série é prioritariamente técnica, vamos deixar a política (sem nenhum demérito - pelo contrário) de lado e entrar de cabeça nos dirty details.

Disclosure: o processo de pesquisa e desenvolvimento do plano de migração foi muito menos linear do que este post faz parecer. A disposição de eventos aqui prioriza a didática, mas é importante ter em mente que o processo de descobertas técnicas e alinhamentos internos seguiu um percurso bem mais orgânico.

A solução da Scylla

A proposta de migração sugerida pela Scylla está esmiuçadamente descrita no site deles. Vou simplificar os passos:

  1. Criar o schema de sua base de dados em Scylla (como já fizemos na parte 01)
  2. Configurar as aplicações para escrita dupla (ou seja, qualquer insert/delete/update vai tanto para o Cassandra quanto para o Scylla)
  3. Tirar um snapshot de todos os dados do Cassandra (gerando arquivos sstables)
  4. Carregar os arquivos sstables no Scylla usando o sstableloader (que basicamente vai transformar o monte de binário do arquivo em queries e jogar na nova base)

Nesse ponto basicamente temos todos os dados (atuais e históricos) na nova nova base de dados

‎‏‏‎ ‎5. ‏‏‎ ‎Esperar o período de validação: escrever e ler das duas bases, deixando o Scylla como fonte da verdade. Comparar as leituras e gravar (via métrica ou log) toda vez que houver inconsistências.

‎‏‏‎ ‎6. ‏‏‎ ‎ No momento que a métrica/log deixar de apontar inconsistências durante algum tempo (a ser definido caso a caso), retirar o Cassandra de qualquer processo de leitura/escrita

Estratégia de migração sugerida pela Scylla

“Parafuso universal” não existe

Apesar de ser uma abordagem segura, generalista e entregar uma migração sem downtime, o caminho sugerido pela Scylla é bastante trabalhoso. Certamente é um bom caminho pra muitos casos, porém no nosso havia algumas pedras, a saber:

  • Temos um número razoavelmente grande de sistemas clientes desta base de dados . Dentre eles, quase 20 jobs independentes de processamento de streaming de dados (feitos em Apache Flink). Tudo precisaria ser alterado e deployado 3 vezes para mudar os esquemas de escrita/leitura.
  • Também por características do Flink — durante o tempo de dual writes — teríamos que “nivelar por baixo” a disponibilidade e a latência de nosso serviço , ou seja, teríamos o pior tempo das duas bases e indisponibilidade se qualquer uma delas estiver indisponível.
  • Tínhamos restrições de tempo — cerca de 2 meses — para conseguirmos os impactos de redução de custo ainda dentro do ciclo e batermos nosso KR.
  • Precisaríamos manter as duas bases de dados em escala de produção por um tempo maior (do procedimento 2 ao 5), o que seguramente aumentaria os custos de migração e diminuiria o ROI do projeto (afetando também nosso KR de redução de custo).

Tínhamos motivos fortes para tentar uma abordagem, digamos, mais “corajosa”.

Tá... é meio auto-ajuda. Mas é auto-ajuda raiz, antes de ser modinha, viu? Rs

Enquanto corriam as descobertas técnicas, vínhamos conversando com toda a empresa sobre uma janela de migração. Conseguimos um certo consenso: 6 horas de indisponibilidade em uma madrugada de um domingo seria viável sem causar grandes impactos no negócio.

Sendo assim, começamos a “namorar” a possibilidade da famosa….

Migração “stop-the-world”

Aviso: o texto a seguir abusa da vulgarização de conceitos em prol da fluidez. Para puristas, os links contêm descrições mais exatas.

Em um sistema de monitoramento de frota, os instantes são cruciais: saber onde o carro esteve em cada instante, em que instante ele ultrapassou o limite de velocidade, em que instante ele efetuou uma parada.

Dá pra imaginar que perguntas do tipo “quais foram as paradas que esse veículo fez entre as 8:00 e 12:00 de ontem, ordenadas pela data/hora”, são bem comuns por aqui.

Não à toa, a maior parte das nossas grandes tabelas contêm dados “time series like” (dados indexados pelo tempo). No Cassandra, a implementação de tabelas time series é feita com o clustering key — um índice especial responsável pelo ordenamento dos dados salvos em base— preenchido com o instante relativo àquele dado. Isso permite filtrar dados por intervalos temporais e ordená-los pelo tempo de forma muito eficiente.

Exemplo de dados time-series: “viagens completadas” pelos veículos monitorados ao longo do tempo

Ah, outra aspecto interessante das nossas grandes tabelas: são imutáveis. Linhas inseridas não são atualizadas (tá, tem um caso ou outro, mas raramente alteramos linhas com mais de um mês de vida).

Bem, o que tudo isso tem a ver com a migração? Simples: isso possibilita que migremos dados baseados em blocos de tempo. “Ah, quero migrar todos os dados do ano passado” — fácil! A busca de dados na base antiga por períodos de tempo é “barata” e os dados vem “automaticamente consistentes” (pois são imutáveis).

Todo esse imbróglio pra chegar em uma estratégia de migração extremamente simples…

  1. migrar em background (sem impactar sistemas) parte dos dados das tabelas time series — basicamente da “primeira linha” até o momento da migração (vou chamar de dia “D” daqui em diante)
  2. no dia D, parar todos os serviços, migrar os dados que faltam o mais rápido possível, e subir tudo novamente
Parece que sim, mas não sem antes da famosa e necessária…

Hora do “critique

Até agora falei dos pros e cons da migração proposta pela Scylla. Nada mais justo que delinear pros e cons (vantagens e desvantagens) da nova estratégia aplicada ao nosso caso.

Pros:

  • garantia de consistência: os dados históricos vem na primeira leva, e como são imutáveis, são automaticamente consistentes na nova base. No dia D, garantimos que nenhum dado novo é gravado durante o processo de backup/restore. Ao subir os serviços, os dados estarão consistentes.
  • procedimento operacional simples e pontual — em nenhum momento duas bases afetam a produção ao mesmo tempo; no dia “D”, precisamos apenas baixar e subir todos os serviço uma só vez
  • migração determinística em relação ao tempo — testes com migrações parciais conseguem dar uma boa estimativa de quanto levará a migração do histórico e dos dados restantes durante o dia “D”

Cons:

  • alto custo do dia “D”ao meu ver, o pior cons desse cenário. Esse custo envolveu: deixar os clientes sem acesso ao sistema durante horas; desenvolver um plano minucioso e ferramentas para reduzir o período de downtime ao máximo; alocação de equipe fora do horário normal de trabalho; trabalho de comunicação interna e externa para janela de downtime; implementação de uma página de manutenção operada por feature flags (que não tínhamos até então)

Deve-se agregar aqui a possibilidade de falha do dia “D”, o que replicaria parte desses custos, além do custo de manter as duas bases de dados “de pé” por mais tempo. Não foi nosso caso, felizmente.

  • Baixa visibilidade do pós-migraçãosaberemos como a nova base lidará com a carga de produção somente quando virarmos a chave; esse é um risco bem considerável que decidimos encarar.

É possível mitigar esse risco de diversas formas, desde uma comparação simples de performance com algumas queries até um replay de queries do ambiente de produção. Como já citado na parte 01, fizemos o básico e acabamos encontrando problemas pós-migração, o que poderia ser tema para uma outra série de posts.

  • Scylla-migrator não permitia filtro de dadoso job Spark copiava tabelas completas sem nenhuma opção de filtro, o que impedia cópias por intervalos de tempo; esse cons foi rapidamente sanado com uma pequena contribuição ao projeto.

Não foi uma decisão simples, mas pesou bastante a previsibilidade em relação ao tempo e custo da migração, além da maior segurança operacional que tínhamos com a nova estratégia.

A migração foi planejada para ser concluída em cerca de 1 mês. Se fossemos pela solução proposta pela Scylla, só as alterações e os inúmeros deploys dos nossos jobs em Flink comeriam facilmente esse tempo.

Hora do Show!

Não, Elvis não compunha as músicas que cantava. E 100 em 100 memes com essa frase são injustos com os reais autores

Agora que temos um plano, tá mais que na hora de colocá-lo em prática. No próximo post vamos escarafunchar os bits e bytes e o monte de aprendizado que tivemos no processo.

Até breve!

--

--