<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nguyễn Chí Cường</title>
    <description>The latest articles on DEV Community by Nguyễn Chí Cường (@cuongnc0211).</description>
    <link>https://dev.to/cuongnc0211</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F845758%2Fb818f5d0-b8c6-4a98-b9e1-9ee5b8257c3a.jpeg</url>
      <title>DEV Community: Nguyễn Chí Cường</title>
      <link>https://dev.to/cuongnc0211</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cuongnc0211"/>
    <language>en</language>
    <item>
      <title>Rage.rb — Khi Ruby Học Cách Không Chờ Đợi</title>
      <dc:creator>Nguyễn Chí Cường</dc:creator>
      <pubDate>Fri, 20 Mar 2026 06:14:04 +0000</pubDate>
      <link>https://dev.to/cuongnc0211/ragerb-khi-ruby-hoc-cach-khong-cho-doi-4k43</link>
      <guid>https://dev.to/cuongnc0211/ragerb-khi-ruby-hoc-cach-khong-cho-doi-4k43</guid>
      <description>&lt;h2&gt;
  
  
  Vấn đề muôn thuở của Rails dưới tải cao
&lt;/h2&gt;

&lt;p&gt;Bạn có một Rails API. Mọi thứ chạy tốt ở môi trường dev, production cũng ổn ở lượng traffic vừa phải. Rồi một ngày đẹp trời, traffic tăng đột biến — và bạn bắt đầu thấy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Response time leo thang&lt;/li&gt;
&lt;li&gt;Memory usage phình to&lt;/li&gt;
&lt;li&gt;Sidekiq queue tắc nghẽn&lt;/li&gt;
&lt;li&gt;Infra team hỏi "có cần thêm worker không?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vấn đề không phải ở code logic. Vấn đề nằm sâu hơn — ở cách Rails xử lý concurrency.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rails xử lý concurrency như thế nào?
&lt;/h2&gt;

&lt;p&gt;Rails với Puma dùng mô hình &lt;strong&gt;multi-threaded&lt;/strong&gt;. Mỗi HTTP request được xử lý bởi một thread riêng.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request 1 ──→ Thread 1: [connect DB] [======chờ 50ms======] [process] [respond]
Request 2 ──→ Thread 2: [connect DB] [======chờ 50ms======] [process] [respond]
Request 3 ──→ Thread 3: [connect DB] [======chờ 50ms======] [process] [respond]
              ...
Request N ──→ Thread N: phải chờ thread trống
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trong 50ms chờ database, thread đó &lt;strong&gt;không làm gì cả&lt;/strong&gt; — nó block hoàn toàn. Đây gọi là &lt;strong&gt;blocking I/O&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Giải pháp của Puma: tăng số thread. Nhưng mỗi thread tốn 1–8MB RAM và OS phải liên tục context switch giữa chúng. Với 1000 concurrent requests, bạn cần 1000 threads — tức là hàng GB RAM chỉ để... chờ.&lt;/p&gt;




&lt;h2&gt;
  
  
  Coroutine — Cơ chế nền tảng
&lt;/h2&gt;

&lt;p&gt;Trước khi nói về Rage, cần hiểu &lt;strong&gt;coroutine&lt;/strong&gt; — khái niệm mà Rage xây dựng lên trên đó.&lt;/p&gt;

&lt;p&gt;Function thông thường chạy theo mô hình một chiều: gọi → chạy hết → trả về. Không thể dừng giữa chừng.&lt;/p&gt;

&lt;p&gt;Coroutine thì khác: nó có thể &lt;strong&gt;tự pause, nhường quyền điều khiển, rồi resume lại từ đúng chỗ đã dừng&lt;/strong&gt; — với toàn bộ local state còn nguyên vẹn.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;barista&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Xay cà phê..."&lt;/span&gt;
  &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yield&lt;/span&gt; &lt;span class="s2"&gt;"Đang chờ nước sôi"&lt;/span&gt;   &lt;span class="c1"&gt;# pause, trả quyền về caller&lt;/span&gt;

  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Pha cà phê..."&lt;/span&gt;
  &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yield&lt;/span&gt; &lt;span class="s2"&gt;"Đang rót"&lt;/span&gt;            &lt;span class="c1"&gt;# pause lần 2&lt;/span&gt;

  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hoàn thành!"&lt;/span&gt;
  &lt;span class="s2"&gt;"Cà phê của bạn đây"&lt;/span&gt;              &lt;span class="c1"&gt;# kết thúc&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;barista&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;   &lt;span class="c1"&gt;# "Xay cà phê..." → "Đang chờ nước sôi"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Làm việc khác trong lúc chờ..."&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;barista&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;   &lt;span class="c1"&gt;# "Pha cà phê..." → "Đang rót"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;barista&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;   &lt;span class="c1"&gt;# "Hoàn thành!" → "Cà phê của bạn đây"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Điểm mấu chốt: sau mỗi &lt;code&gt;Fiber.yield&lt;/code&gt;, local variables và vị trí đang thực thi được &lt;strong&gt;giữ nguyên trên stack của fiber đó&lt;/strong&gt;. Caller tiếp tục làm việc khác, rồi resume lại bất cứ lúc nào.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coroutine khác Thread ở điểm gì?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread:    OS quyết định khi nào switch  →  preemptive (bị ngắt bất ngờ)
Coroutine: Code quyết định khi nào yield →  cooperative (tự nguyện nhường)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hệ quả trực tiếp: Thread cần mutex/lock để tránh race condition. Coroutine thì không — vì chỉ có 1 coroutine chạy tại một thời điểm, và switch chỉ xảy ra tại những điểm xác định.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tại sao Fiber nhẹ hơn Thread đến vậy?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread stack:  1MB – 8MB  (OS cấp phát, không thể thay đổi)
Fiber stack:   ~4KB        (Ruby quản lý trong heap, co giãn được)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tạo 10,000 threads → ~80GB RAM → không khả thi.&lt;br&gt;&lt;br&gt;
Tạo 10,000 fibers → ~40MB RAM → hoàn toàn bình thường.&lt;/p&gt;


&lt;h2&gt;
  
  
  Rage.rb áp dụng fiber như thế nào?
&lt;/h2&gt;

&lt;p&gt;Rage dùng &lt;strong&gt;Iodine&lt;/strong&gt; làm web server — một C extension tích hợp event loop và Fiber Scheduler của Ruby. Mỗi incoming request được wrap trong một fiber riêng.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Event Loop (1 thread duy nhất)
    │
    ├── Fiber A: request GET /posts
    │     └── SELECT * FROM posts... → YIELD (chờ PG)
    │
    ├── Fiber B: request POST /posts  ← tiếp nhận trong lúc A chờ
    │     └── INSERT INTO posts... → YIELD (chờ PG)
    │
    ├── Fiber C: request GET /users   ← tiếp nhận trong lúc A, B chờ
    │     └── SELECT * FROM users... → YIELD (chờ PG)
    │
    │   [PG trả kết quả cho A]
    ├── Fiber A: RESUME → render json → response ✓
    │
    │   [PG trả kết quả cho B]
    ├── Fiber B: RESUME → render json → response ✓
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Toàn bộ chạy trên &lt;strong&gt;1 thread duy nhất&lt;/strong&gt;, không có blocking, không có race condition.&lt;/p&gt;

&lt;p&gt;Magic xảy ra nhờ &lt;strong&gt;Ruby Fiber Scheduler interface&lt;/strong&gt; (có từ Ruby 3.0). Rage patch các thư viện I/O chuẩn để tự động yield:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bạn viết code này — trông hoàn toàn synchronous&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;RageController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;API&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;          &lt;span class="c1"&gt;# ← fiber tự động YIELD khi chờ PG&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;             &lt;span class="c1"&gt;# ← fiber RESUME khi có kết quả&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Không cần async/await, không cần callback, không cần thay đổi gì&lt;/span&gt;
&lt;span class="c1"&gt;# Rage tự xử lý phần còn lại&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rage tự động patch: &lt;code&gt;Net::HTTP&lt;/code&gt;, driver &lt;code&gt;pg&lt;/code&gt;, driver &lt;code&gt;mysql2&lt;/code&gt;, &lt;code&gt;Thread.join&lt;/code&gt;, &lt;code&gt;Ractor.join&lt;/code&gt;, và &lt;code&gt;sleep&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fiber.await — Parallel I/O trong 1 request
&lt;/h2&gt;

&lt;p&gt;Khi một request cần gọi nhiều nguồn dữ liệu, Rage cho phép chạy song song:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DashboardController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;RageController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;API&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="c1"&gt;# ❌ Sequential — tổng ~300ms&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;published&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;
    &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;

    &lt;span class="c1"&gt;# ✅ Parallel với Fiber.await — tổng ~100ms (bằng cái chậm nhất)&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;await&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;published&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;comments: &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Với Rails thuần, để đạt kết quả tương tự bạn phải dùng &lt;code&gt;concurrent-ruby&lt;/code&gt;, &lt;code&gt;async&lt;/code&gt; gem, hoặc tự quản lý threads — tất cả đều phức tạp hơn và dễ mắc lỗi.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rage hiệu quả nhất trong trường hợp nào?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ I/O-bound workloads — đây là sân nhà của Rage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Gọi external API&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_weather&lt;/span&gt;
  &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"https://api.weather.com/data"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Heavy database queries&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reports&lt;/span&gt;
  &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;joins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;created_at: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;month&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ago&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;
  &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Fan-out requests — gọi nhiều services cùng lúc&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;aggregated_data&lt;/span&gt;
  &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;await&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch_history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="no"&gt;Fiber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;NotificationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch_unread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ✅ High-concurrency API — nhiều requests đồng thời
&lt;/h3&gt;

&lt;p&gt;Rage outperforms Rails &lt;strong&gt;81–219%&lt;/strong&gt; trên database benchmarks (TechEmpower Round 23). Với I/O-heavy workloads, khoảng cách càng lớn hơn khi số concurrent requests tăng.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Khi muốn đơn giản hóa infrastructure
&lt;/h3&gt;

&lt;p&gt;Thay vì stack quen thuộc:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rails app + Puma
+ Sidekiq workers
+ Redis (cho Sidekiq + Action Cable)
+ Separate cable server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rage gộp lại thành:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rage app (1 process)
  ├── HTTP requests    (fiber per request)
  ├── Background jobs  (Rage::Deferred, in-process)
  ├── WebSockets       (Rage::Cable, không cần Redis)
  └── Domain events    (Rage::Events)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrdersController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;RageController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;API&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Background job — chạy in-process, không cần Redis/Sidekiq&lt;/span&gt;
    &lt;span class="no"&gt;SendConfirmationEmail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Broadcast WebSocket — không cần Action Cable/Redis&lt;/span&gt;
    &lt;span class="no"&gt;Rage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="s2"&gt;"created"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: :created&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ❌ Rage không phải silver bullet — CPU-bound tasks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# I/O-bound → Rage xử lý xuất sắc, fiber yield khi chờ&lt;/span&gt;
&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="s2"&gt;"published"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;

&lt;span class="c1"&gt;# CPU-bound → Rage không cải thiện được, fiber không yield&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_report&lt;/span&gt;
  &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;complex_calculation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# block cả event loop&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Với heavy CPU workloads, thread-based model của Puma thực ra tốt hơn vì OS có thể phân phối các threads lên nhiều CPU cores.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Gems dùng blocking I/O cũ
&lt;/h3&gt;

&lt;p&gt;Không phải tất cả gems đều fiber-aware. Gem nào dùng blocking I/O thuần sẽ block toàn bộ event loop thay vì chỉ yield fiber. Cần kiểm tra compatibility trước khi migrate.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rage vs Rails — Điểm khác biệt thực tế khi code
&lt;/h2&gt;

&lt;p&gt;Rage được thiết kế để syntax gần giống Rails nhất có thể. Nhưng có vài điểm cần lưu ý:&lt;/p&gt;

&lt;h3&gt;
  
  
  Strong Parameters không tồn tại
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ Rails — không hoạt động trong Rage&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;post_params&lt;/span&gt;
  &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# ✅ Rage — params là plain Hash&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;post_params&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;transform_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:to_s&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Logger khác
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Rails&lt;/span&gt;
&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;"message"&lt;/span&gt;

&lt;span class="c1"&gt;# Rage&lt;/span&gt;
&lt;span class="no"&gt;Rage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;"message"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Controller base class khác
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Rails&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;

&lt;span class="c1"&gt;# Rage&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;RageController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;API&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Routes namespace khác
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Rails&lt;/span&gt;
&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:posts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Rage&lt;/span&gt;
&lt;span class="no"&gt;Rage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:posts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Đó là một số điểm khác nhau giữa Rage và Rails mà tôi khám phá ra trong khi thử implement một blog app nhỏ, có thể sẽ còn một số điểm khác biệt khác nữa. Nhưng nhìn chung là rất dễ học cho các Ruby on Rails developers&lt;/p&gt;




&lt;h2&gt;
  
  
  Khi nào nên cân nhắc migrate sang Rage?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Nên dùng Rage nếu:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;App là API-only, không có server-side rendering&lt;/li&gt;
&lt;li&gt;Workload chủ yếu là I/O-bound (DB queries, external API calls)&lt;/li&gt;
&lt;li&gt;Muốn reduce infrastructure complexity (bỏ Redis, Sidekiq)&lt;/li&gt;
&lt;li&gt;Cần handle lượng lớn concurrent connections (WebSocket, long-polling)&lt;/li&gt;
&lt;li&gt;Team muốn giữ Rails ergonomics nhưng cần performance cao hơn&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Chưa nên dùng Rage nếu:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;App có nhiều CPU-bound processing&lt;/li&gt;
&lt;li&gt;Đang dùng nhiều gems chưa được test với fiber scheduler&lt;/li&gt;
&lt;li&gt;App có views, helpers, assets (Rage là API-only)&lt;/li&gt;
&lt;li&gt;Team cần ecosystem rộng và mature của Rails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Chiến lược hybrid — Rails Integration mode:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rage không yêu cầu rewrite toàn bộ. Có thể tích hợp vào Rails app hiện có để Rage xử lý HTTP requests, Rails giữ phần còn lại:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Gemfile&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"rage-rb"&lt;/span&gt;

&lt;span class="c1"&gt;# config/application.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"rage/rails"&lt;/span&gt;

&lt;span class="c1"&gt;# Rage xử lý requests, Rails lo code loading, ActiveRecord, v.v.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Kết
&lt;/h2&gt;

&lt;p&gt;Rage.rb không phải là "Rails killer" hay framework bạn phải dùng cho mọi dự án. Nó là câu trả lời cho một vấn đề cụ thể: &lt;strong&gt;Ruby applications cần handle high-concurrency I/O-bound workloads mà không muốn trả giá bằng infrastructure complexity hay rewrite codebase.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Điểm thú vị nhất của Rage không phải benchmark numbers — mà là triết lý thiết kế: &lt;strong&gt;bạn viết code synchronous như bình thường, framework lo phần async&lt;/strong&gt;. Không có callback hell, không có async/await lan tràn khắp codebase, không có mental overhead của concurrent programming.&lt;/p&gt;

&lt;p&gt;Đó là một trade-off đáng cân nhắc.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>performance</category>
      <category>rails</category>
      <category>ruby</category>
    </item>
  </channel>
</rss>
