Acelerando o Boot da sua aplicação Rails
Tags: rails, ruby, tutorial, performance
Published: 2022-05-03 21:32:41

Quando falamos de Ruby on Rails, estamos falando do principal framework MVC da linguagem Ruby, que contém todas as partes de uma aplicação completa, tanto na parte do front-end quanto na parte do back-end.

Conforme vamos adicionando mais extensões, ou dados na nossa aplicação Rails, as coisas começam a ficar um pouco mais “trabalhosas” de serem executadas, e com a gema que iremos ver hoje, podemos facilmente implementar e perceber a diferença no carregamento da aplicação Rails.

Então, vamos usar uma extensão/gema chamada [Bootsnap](https://github.com/Shopify/bootsnap), desenvolvida pelo Shopify. 
De acordo com alguns testes, a gema promete tornar a inicialização de qualquer app em Rails ou Ruby, 50% mais rápido, sem comprometer as funcionalidades ou os assets dentro da aplicação Rails.

O que a gema faz por debaixo dos panos, é utilizar a linguagem C para fazer um patch dos arquivos em Ruby, e com isso, conseguimos diminuir drasticamente o boot da aplicação.

Portanto, antes de aprofundarmos em como melhorar o desempenho da aplicação, vamos entender por partes, como funciona a inicialização.

Ao rodarmos `rails console` ou `rails server` para iniciar a aplicação em Rails, invocamos uma ordem na qual os pacotes, arquivos e as configurações são iniciadas na seguinte ordem:

> **bin/rails → Gemfile → boot.rb → config/application.rb
→ Mensagem básica do servidor é mostrada na tela 
→ config/environments/development.rb**  
**→  Arquivos que estão na pasta `initializer`** 
**→  config/routes.rb**  
**→  config/environment.rb**  
→  **config.ru (se houver)**
> 

Acima sumarizo o processo dos arquivos de boot de forma bastante resumida, caso queira entender de forma profunda o que contém e como funciona cada arquivo, acesse este link do [Guia Ruby on Rails](https://guides.rubyonrails.org/initialization.html).

A principio, quando a execução cai no Gemfile, o Rails carrega todas as extensões contidas dentro desse arquivo, além de suas proprias *engines*, que são:

- Actioncable
- Actionmailer
- Actionpack
- Actionview
- Activejob
- Activemodel
- Activerecord
- Activestorage
- Activesupport
- Actionmailbox
- Actiontext

Para carregar essas e outras extensões, é como se o Rails perguntasse ao computador se aquela extensão que ele está requerindo está disponivel em um diretório especifico, caso não esteja, ele continua percorrendo os diretórios e a execução continua somente quando o diretório em questão é encontrado.

A grande mudança que a gema Bootsnap encorpora na nossa aplicação Rails, é fazer um cache dentro do diretório `tmp/cache/` da aplicação, com um arquivo contendo todos os caminhos pré estabelecidos, assim, quando inicializamos nossa aplicação, fica muito menos trabalhosa essa etapa de requerir todos os arquivos de uma só vez.

Para utilizarmos essa gema, primeiro precisamos declará-la no `Gemfile`:

```ruby
# Gemfile
gem 'bootsnap', require: false
```

Após rodar o `bundle install`, precisamos requerir esta gema no arquivo `config/boot.rb` imediatamente depois do `require 'bundler/setup'`:

```ruby
# ./config/boot.rb

ENV['BUNDLE_GEMFILE'] ||= File.expand.path('../Gemfile', __dir__)

require 'bundle/setup'
require 'bootsnap/setup'
```

E pronto! Agora a aplicação está pronta para ser iniciada novamente.

Para efeitos de comparação, utilizarei neste exemplo uma aplicação Rails já criada em meu ambiente local, assim posso ilustrar a diferença real entre a aplicação com o boot “padrão” e depois, com a gema Bootsnap.

## Testando as diferenças

Vamos aos testes, pra isso, primeiramente, vou criar um arquivo do tipo Shell Script (`.sh`), assim posso invocar a chamada de um `rails runner` utilizando o comando `time` do terminal.

```bash
#!/usr/bin/env bash
rails runner "puts Tillage.count"
```

Basicamente, este script irá contar a quantidade de registros provenientes do modelo `Tillage` da minha aplicação, então rodaremos esse script primeiro **sem o Bootsnap:**

```bash
➜  solocamp-tech git:(master) ✗ time ./test/script.sh
Running via Spring preloader in process 15855
1028
./test/script.sh  1.05s user 0.69s system 31% cpu 5.482 total
➜  solocamp-tech git:(master) ✗ time ./test/script.sh
Running via Spring preloader in process 16135
1028
./test/script.sh  0.70s user 0.48s system 79% cpu 1.483 total
➜  solocamp-tech git:(master) ✗ time ./test/script.sh
Running via Spring preloader in process 16416
1028
./test/script.sh  0.70s user 0.53s system 75% cpu 1.615 total
➜  solocamp-tech git:(master) ✗ time ./test/script.sh
Running via Spring preloader in process 16695
1028
./test/script.sh  0.77s user 0.52s system 82% cpu 1.562 total
```

Podemos notar que a execução demorou em média **1,5s** para ser executado (ignorando a primeira execução). 

Agora vamos ao teste **com o Bootsnap**:

```bash
➜  solocamp-tech git:(master) ✗ time ./test/script.sh
Running via Spring preloader in process 8567
28
./test/script.sh  0.66s user 0.44s system 81% cpu 1.360 total
➜  solocamp-tech git:(master) ✗ time ./test/script.sh
Running via Spring preloader in process 8847
28
./test/script.sh  0.66s user 0.44s system 80% cpu 1.372 total
➜  solocamp-tech git:(master) ✗ time ./test/script.sh
Running via Spring preloader in process 9128
28
./test/script.sh  0.67s user 0.47s system 80% cpu 1.408 total
```

Podemos ver que o tempo de execução foi reduzido, neste caso da amostra, a diferença não é muito grande, porém conseguimos manter uma media de **1,3s** para cada execução.
Caso o script fosse maior, isso é, se incluisse mais  contagens de outros modelos, essa diferença seria bem maior! 

Então podemos notar que quanto maior a aplicação Rails, maior a diferença entre os tempos de boot utilizando **Bootsnap!**