A saga do upgrade: MongoDB 2.6 para 3.6 | Ruby on Rails 3.2 para 5.2

cliente: kmonline
equipe:
  • Rodrigo Guimaraes

Upgrade Rails on Rails to 5.2

O cenário é complexo e desesperador, mas infelizmente muito comum na área de software: A equipe vai trabalhando com um projeto legado, com vários débitos técnicos, até chegar o momento inevitável em que algo dá errado em produção, e é necessário parar tudo e necessariamente evoluir/alterar partes cruciais do projeto. Quando a mudança não afeta a estrutura e/ou arquitetura do software, menos mal. Porém e se o problema estiver no coração do projeto, vulgo banco de dados? Vamos tratar dessa situação crítica especificamente, no cenário de um projeto Ruby on Rails 3.2 utilizando o MongoDB 2.6, através do Mongoid 3.1.

Não é pretensão substituir nenhum guia oficial, mas sim complementar, uma vez que não se acham muitas referências pela web além do upgrade “padrão” do Rails - Active Record como ORM do PostgreSQL ou MySQL. Além do Mongoid não utilizar Active Record, a documentação oficial da biblioteca - antiga ou nova - não traz nenhum detalhe sobre um grande upgrade de versões. A pretensão com esse post, então, é servir de guia para que alguém em situação semelhante não se veja perdido no limbo das referências, como foi nosso caso.

Entendendo a dimensão do upgrade

A versão 2.6 do MongoDB foi lançada em março de 2014 e teve o final de suporte anunciado em outubro de 2016, ou seja, desde essa época o banco de dados que estava em uso em produção não recebia correções de erros, nem novas melhorias de funcionalidades e performance, nem updates de segurança. Indo além, era difícil até mesmo encontrar essa versão do mongodb para montar o ambiente de desenvolvimento (mesmo imagens docker), e ainda mais difícil encontrar um servidor em cloud que oferecia a versão abandonada.

“Certo, se o banco é tão antigo assim, é simples! Instale/Contrate uma versão mais atualizada e seja feliz!”, é o primeiro pensamento.

Mas é aí que estava o problema: todo o software girava em torno dessa versão antiga do mongodb. A versão do Ruby (2.2.6), a versão do Ruby on Rails (3.2), a versão do Mongoid (3.1.7), e todas as outras gems, direta ou indiretamente, ficaram “paradas no tempo”, sem atualizar e receber igualmente melhorias de performance, correções de erros e de segurança. Não dava para atualizar uma gem sem atualizar outras inúmeras.

Em resumo: necessário atualizar todo o software.

Uma das opções era criar um projeto novo do zero, já com as últimas versões das gems e com um ruby mais atualizado, e manualmente copiar o código antigo e ir “colando” no código novo. Porém além de ser muito propenso a erros, perderíamos todo o histórico de commits do git, algo bastante indesejado pois perderia memórias do projeto.

Então, finalmente, começamos a saga.

O processo de upgrade

Antes de mais nada é fundamental ter uma boa cobertura de testes, pois será seu melhor amigo nesse processo. A cada atualização de versão do Ruby e das gems, execute toda a suite de testes e vá corrigindo os deprecation warnings e fatal errors oriundos de atualizações de gems/linguagem. Para atualizar o Ruby on Rails, não adianta cortar caminhos. Não tente atualizar por exemplo do RoR 3.1 para o R.R 5.1 diretamente, porque vai dar errado. Fique bastante atento e siga todos os conselhos explicitados no guia oficial do Ruby on Rails. Tentar pular etapas vai acabar tomando mais tempo e levando a frustrações.

A estratégia utilizada, então, foi fazer um “caminho inverso” de dependências que deveriam ser atendidas a partir da versão desejada do MongoDB. Ou seja, para usarmos o banco versão X, qual versão do mongoid é necessária? Para essa versão do mongoid, qual versão do rails é necessária? Para essa versão do rails … e assim recursivamente, até atenderem todas as gems utilizadas.

Em nosso caso mais concreto, o objetivo era o MongoDB 3.6. Para isso, é necessário pelo menos o mongoid 6.x. Para isso, é necessário pelo menos o Ruby on Rails 5.x. A versão do Ruby utilizada já seria suficiente para este upgrade, mas resolvemos atualizar também pelo menos para a versão 2.5.x, pois é a mínima necessária para o futuro próximo do Rails 6.0.

Seguindo esta ideia, o primeiro passo foi atualizar a versão do Ruby 2.2.6 em baby steps até a versão mais atual, 2.5.x, lembrando que, toda vez que a versão do ruby é alterada, deve-se instalar novamente o bundler gem install bundler para que pegue a versão adequada.

Outro ponto que merece atenção nesta etapa é que deve-se desfazer de versões antigas do bundler não mais utilizadas, para evitar conflitos e erros durante o uso do bundle install.

Com a versão do ruby atualizada, e os testes passando novamente, a parte central e mais complicada é atualizar o ruby on rails e suas dependências - aqui entra-se facilmente no dependency hell.

Durante essa parte é difícil até mesmo conseguir fazer o bundler instalar tudo que precisa sem dar nenhum erro. Por isso, evite especificar versões na Gemfile - a menos que estritamente necessário - e, após a instalação sem problemas, pegue as versões que deram certo instalar e aí sim especifique-as.

Não tem como escapar. Partindo do Ruby on Rails 3.2, é necessário fazer o upgrade passo-a-passo, versão por versão, até chegar na desejada 5.2 - um trabalho de formiguinha. Houve tentativas anteriores do upgrade direto - falhou-se miseravelmente.

Importante frisar que os guias oficiais também não ajudam muito nesse loop de upgrades no caso do mongodb/mongoid, pois mesmo a rake task do próprio rails, rails app:update, muito útil certamente em projetos padrão Rails, não ajuda no caso do Mongoid. Na verdade, essa rake task faz é atrapalhar, porque tenta “corrigir” partes cruciais de config/application/environment do código que não funcionam com o mongodb/mongoid. Portanto nem é aconselhável utilizar essa ferramenta - a menos que saiba muito bem tudo que está acontecendo a cada sugestão automatizada de alteração.

Especialmente durante o upgrade de cada nova etapa do framework, além dos testes automatizados é importante realizar testes manualmente, especialmente das partes mais críticas/importantes/utilizadas, e verificar por conta própria se algo no frontend está quebrando, se os dados estão fluindo da maneira como estão - pois mesmo uma boa cobertura de testes geralmente não consegue cobrir o software como um todo.

Um dos lados positivos desse upgrade completo é a oportunidade de revisar partes do código que podem/devem ser refatorados, e avaliar a necessidade e uso das gems do projeto, pois com a evolução das dependências, é natural que alguma gem passe a cobrir features atendidas anteriormente por outras gems, que não são mais necessárias e podem ser removidas nesse momento, facilitando ainda mais a manutenção de agora em diante.

Após todo esse processo - que pode levar dias ou até semanas, a última parte é atualizar a gem do mongoid em si, e ficar atento à documentação mais recente sobre o arquivo de configuração (mongoid.yml), pois ele estruturalmente é diferente de versões anteriores. E também verificar se precisa mudar algo no seu código em relação a mudanças de versões.

O upgrade de um projeto Ruby on Rails 3.2 para 5.2, e também a atualização da versão do mongoDB para uma versão mais recente, estável e com suporte, é bastante trabalhoso e exige paciência, atenção e muitos testes - automáticos e manuais. Porém ao final desse processo vários débitos técnicos são mitigados, e a manutenção do projeto fica bem mais fácil até mesmo para uma próxima atualização - trazendo mais algum tempo de vida útil para aquela base de código e melhorias naturais de performance, funcionalidades e correções de erros e falhas de segurança.

Referências

compartilhar:


© structy - open source culture