Desempenho e Otimização em Ruby on Rails
Tags: ruby, rails, webdev, programming
Published: 2024-02-26 16:43:34

<p>
Desempenho e otimização em aplicações Ruby on Rails são temas cruciais para garantir uma experiência rápida e eficiente para os usuários. Isso envolve diversas técnicas, desde caching até ajustes de configuração e escalabilidade.
</p>

<h2>Caching</h2>

<p>
Armazena o HTML gerado das páginas em cache para servir rapidamente para os usuários subsequentes, evitando a necessidade de processar a requisição novamente.
Obviamente, isso é super rápido. Infelizmente, não pode ser aplicado a todas as situações (como páginas que precisam de autenticação) e como o servidor web está literalmente apenas servindo um arquivo do sistema de arquivos, a expiração do cache é um problema que precisa ser resolvido.
</p>

<p>
Para habilitar o cache de páginas, você precisa usar o método "caches_page".
</p>

```ruby
class ProductsController < ActionController
 
  caches_page :index
 
  def index
    @products = Products.all
  end
end
```

<h2>Fragment Caching</h2>

<p>
Cacheia partes específicas das páginas, como pedaços de HTML, para reduzir o tempo de renderização.
Por exemplo, se você quiser mostrar todos os pedidos feitos em seu site em tempo real e não quiser armazenar em cache aquela parte da página, mas quiser armazenar em cache a parte da página que lista todos os produtos disponíveis, você poderia use este trecho de código:
</p>

```ruby
<% Order.find_recent.each do |o| %>
  <%= o.buyer.name %> bought <%= o.product.name %>
<% end %>
 
<% cache do %>
  All available products:
  <% Product.all.each do |p| %>
    <%= link_to p.name, product_url(p) %>
  <% end %>
<% end %>
```

<p>
O bloco de cache em nosso exemplo será vinculado à ação que o chamou e será gravado no mesmo local que o Action Cache, o que significa que se você quiser armazenar em cache vários fragmentos por ação, deverá fornecer um "action_suffix" para a chamada de cache:
</p>

```ruby
% cache(:action => 'recent', :action_suffix => 'all_products') do %>
  All available products:
```
<p>
e você pode expirar usando o método "expire_fragment", assim:
</p>

```ruby
expire_fragment(:controller => 'products', :action => 'recent', :action_suffix => 'all_products')
```

<p>
Se você não deseja que o bloco de cache seja vinculado à ação que o chamou, você também pode usar fragmentos com chave global chamando o método de cache com uma chave, da seguinte forma:
</p>

```ruby
<% cache('all_available_products') do %>
  All available products:
<% end %>
```

<p>
Este fragmento fica então disponível para todas as ações no ProductsController usando a chave e pode expirar da mesma maneira:
</p>

```ruby
expire_fragment('all_available_products')
```

<h2>Escalabilidade</h2>

<p>
Otimização de consultas SQL, uso de índices e sharding para distribuir dados em múltiplos bancos é um recurso do Rails que armazena em cache o conjunto de resultados retornado por cada consulta para que se o Rails encontrar a mesma consulta novamente para aquela solicitação, ele usará o conjunto de resultados em cache ao invés de executar a consulta no banco de dados novamente. Por exemplo:
</p>

```ruby
class ProductsController < ActionController
 
  def index
    # Run a find query
    @products = Product.all
 
    ...
 
    # Run the same query again
    @products = Product.all
  end
 
end
```

<p>
Na segunda vez que a mesma consulta for executada no banco de dados, ela não atingirá o banco de dados. Na primeira vez que o resultado da consulta é retornado, ele é armazenado no cache da consulta (na memória) e na segunda vez é extraído da memória.
</p>

<h2>Servidores de Aplicação</h2>
<p>
Utilização de balanceadores de carga e escalonamento horizontal para lidar com aumento de tráfego.
</p>
<p>
Exemplo: Configuração de servidores de aplicação como Puma ou Unicorn para balanceamento de carga.
</p>

<h2>Cache Distribuído</h2>
<p>
Utilização de cache distribuído para compartilhar dados em servidores múltiplos.
</p>
<p>
Exemplo: Utilizando Redis como cache compartilhado entre instâncias de aplicação.
</p>

```ruby
# Configuração do Redis para cache distribuído em config/initializers/cache.rb
Rails.application.config.cache_store = :redis_store, {
  host: "localhost",
  port: 6379,
  db: 0,
  namespace: "app_cache"
}
```

<h2>Ajustes de Configuração</h2>
<p>
Configurações de Ambiente: Ajustes específicos para diferentes ambientes (desenvolvimento, produção, teste) para otimizar o desempenho em cada fase do ciclo de vida da aplicação.
</p>
<p>
Exemplo: Configuração de diferentes bancos de dados para ambiente de teste e produção.
</p>

<h2>Tunning do Servidor</h2>
<p>
Ajustes de configuração do servidor web e banco de dados para melhor desempenho.
</p>
<p>
Exemplo: Configuração do número máximo de conexões simultâneas no servidor web.
</p>

<h2>Conclusão</h2>
<p>
Em resumo, otimizar o desempenho de aplicações Ruby on Rails envolve uma combinação de caching inteligente, escalabilidade eficiente e ajustes precisos de configuração. Essas práticas garantem que a aplicação possa lidar com grandes volumes de tráfego de forma rápida e responsiva, proporcionando uma melhor experiência para os usuários.
</p>
<p>
Essas são apenas alguns exemplos de como as técnicas de desempenho e otimização podem ser aplicadas em cenários do mundo real em aplicações Ruby on Rails.
</p>