0

Các kỹ thuật nâng cao trong Ruby on Rails dành cho **Senior Developer**.

1. Hiệu suất và tối ưu hóa

1.1. Tránh N+1 Query với Eager Loading

Nếu không dùng eager loading, Rails sẽ chạy N+1 query khi truy vấn dữ liệu có liên kết.

Ví dụ:

# N+1 Query (Không tối ưu)
users = User.all
users.each do |user|
  puts user.posts.count  # Mỗi lần gọi .posts.count sẽ thực hiện một truy vấn SQL riêng
end

Giả sử có 100 users, điều này sẽ chạy 1 truy vấn lấy users + 100 truy vấn lấy posts.

🔹 Giải pháp: Dùng includes để eager load dữ liệu.

# Tối ưu hóa với Eager Loading
users = User.includes(:posts).all
users.each do |user|
  puts user.posts.count  # Không truy vấn SQL lặp lại
end

Rails sẽ chỉ chạy 2 query:

  1. Lấy tất cả users
  2. Lấy tất cả posts liên quan

Bạn có thể dùng Bullet gem để phát hiện lỗi N+1:

# Gemfile
gem 'bullet', group: :development

Sau đó, cấu hình Bullet để log cảnh báo:

# config/environments/development.rb
config.after_initialize do
  Bullet.enable = true
  Bullet.alert = true
  Bullet.bullet_logger = true
end

1.2. Caching nâng cao

Cache Fragment (Lưu trữ từng phần HTML)

<% cache @user do %>
  <%= @user.name %>
  <%= @user.bio %>
<% end %>

Rails sẽ lưu kết quả vào cache để không cần truy vấn lại DB nếu dữ liệu chưa thay đổi.

Low-level Caching (Lưu trữ dữ liệu trong bộ nhớ)

Rails.cache.fetch("top_articles", expires_in: 1.hour) do
  Article.order(views: :desc).limit(10)
end

🔹 Redis cache được dùng để lưu trữ dữ liệu nhanh hơn:

# Cấu hình Redis
config.cache_store = :redis_cache_store, { url: "redis://localhost:6379/0" }

2. Cấu trúc dự án và kiến trúc phần mềm

2.1. Service Objects (Tách logic ra khỏi Model & Controller)

Trước đây (Viết hết trong Controller - Code lộn xộn)

class UsersController < ApplicationController
  def create
    @user = User.new(user_params)
    if @user.save
      UserMailer.welcome_email(@user).deliver_later
      redirect_to @user
    else
      render :new
    end
  end
end

🔹 Tối ưu với Service Object:

# app/services/user_creator.rb
class UserCreator
  def initialize(user_params)
    @user_params = user_params
  end

  def call
    user = User.create(@user_params)
    UserMailer.welcome_email(user).deliver_later if user.persisted?
    user
  end
end

Controller gọi service:

class UsersController < ApplicationController
  def create
    @user = UserCreator.new(user_params).call
    if @user.persisted?
      redirect_to @user
    else
      render :new
    end
  end
end

2.2. Query Objects (Tách truy vấn phức tạp ra khỏi Model)

Thay vì viết query dài trong Model:

class User < ApplicationRecord
  scope :active, -> { where(active: true) }
end

Dùng Query Object để quản lý:

# app/queries/active_users_query.rb
class ActiveUsersQuery
  def initialize(relation = User.all)
    @relation = relation
  end

  def call
    @relation.where(active: true)
  end
end

Gọi như sau:

users = ActiveUsersQuery.new.call

3. API và GraphQL

3.1. GraphQL API

Dùng graphql-ruby để định nghĩa API linh hoạt hơn REST.

Tạo một query:

class Types::QueryType < Types::BaseObject
  field :user, Types::UserType, null: false do
    argument :id, ID, required: true
  end

  def user(id:)
    User.find(id)
  end
end

Gọi API:

query {
  user(id: 1) {
    id
    name
    email
  }
}

4. Bảo mật nâng cao

4.1. Rate Limiting (Giới hạn số request tránh DDoS)

Sử dụng Rack::Attack để giới hạn request:

class Rack::Attack
  throttle("req/ip", limit: 100, period: 1.minute) do |req|
    req.ip
  end
end

5. Tối ưu Active Record

5.1. Partitioning Tables

Chia bảng theo tháng giúp truy vấn nhanh hơn.

CREATE TABLE orders (
  id SERIAL PRIMARY KEY,
  created_at TIMESTAMP NOT NULL
) PARTITION BY RANGE (created_at);

Tạo bảng con cho từng tháng:

CREATE TABLE orders_2025_03 PARTITION OF orders
FOR VALUES FROM ('2025-03-01') TO ('2025-04-01');

6. Quản lý công việc lớn và batch processing

6.1. Batch Processing với Sidekiq

Dùng Sidekiq để xử lý job nặng:

class ImportCsvJob
  include Sidekiq::Worker

  def perform(csv_file_path)
    CSV.foreach(csv_file_path, headers: true) do |row|
      User.create!(row.to_h)
    end
  end
end

Gọi job từ controller:

ImportCsvJob.perform_async("/path/to/file.csv")

7. DevOps và CI/CD

7.1. Dockerizing Rails

Tạo Dockerfile để deploy Rails app:

FROM ruby:3.2
WORKDIR /app
COPY . .
RUN bundle install
CMD ["rails", "server"]

Chạy app với Docker Compose:

version: "3"
services:
  web:
    build: .
    ports:
      - "3000:3000"

8. WebSockets và Real-time Features

8.1. ActionCable (Realtime Chat)

Rails hỗ trợ WebSockets với ActionCable:

class ChatChannel < ApplicationCable::Channel
  def subscribed
    stream_from "chat_#{params[:room]}"
  end
end

Frontend kết nối WebSocket:

App.cable.subscriptions.create({ channel: "ChatChannel", room: "general" }, {
  received(data) { console.log(data); }
});

Tóm tắt

✅ Tránh N+1 query với Eager Loading
✅ Sử dụng Service ObjectQuery Object
✅ Tăng tốc API với GraphQLRedis cache
✅ Bảo mật với JWT, Rate Limiting
✅ Dùng Sidekiq, Docker, WebSockets cho hiệu suất cao


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí