← Quay lại
14/02/2026 3 phút

Export file lúc được lúc không

Đã bao giờ bạn gặp vấn đề kiểu một chức năng nào đó chạy lúc được lúc không không? Dạo gần đây mình gặp phải nó với chức năng xuất file. Bình thường người dùng có thể tải xuống file excel của bảng đang hiện trên trang web, thế nhưng đôi lúc lại tải không được. Họ ấn nút Export mà đợi mãi không xong. Đến lượt mình kiểm tra thì cũng như vậy. Tự nhiên thấy hoang mang ngang.

Nói qua về luồng hoạt động của chức năng này:

  1. Đầu tiên người dùng nhấn vào nút Export
  2. Client gọi ajax lên Server yêu cầu tạo và xuất file xlsx
  3. Vì quá trình này tốn nhiều thời gian nên Server giao cho job ngầm thực hiện để người dùng không phải đợi mà có thể thao tác trên trang
  4. Server giao việc cho job và trả về định danh (ID) của nó cho Client
  5. Client nhận job ID để theo dõi đúng kênh ActionCable mà job ngầm sẽ truyền link về sau khi tạo file xong
  6. Client tải tệp theo link đó
 
Đến khi mình kiểm tra network mới phát hiện ra job ngầm đã truyền link về trước khi Client theo dõi kênh truyền tin. Đây là một kiểu Race Condition điển hình khi hai bên chạy đua với nhau làm cho hệ thống không còn khớp với ý tưởng ban đầu.

Vấn đề ở đây là job thực thi quá nhanh trước cả khi client kịp bắt đường truyền dẫn tới việc job truyền tin nhưng không ai nhận và khi có người nhận lại không tin nào truyền đi nữa.

Vì mình nhanh trí lười biếng nên bắt job làm việc chậm lại. "Ê cái thằng job này ai cho phép mày nhanh như thế, tao bắt mày ngủ 🛌 nửa giây." 

sleep 0.5

Rất đơn giản và cực kỳ hiệu quả.

Sau hơn tháng vào một đêm nọ mình giật mình tỉnh giấc sau cơn mơ với mồ hôi ướt đẫm lưng áo vì cảnh khách hàng chửi do đã làm họ đợi lâu quá. Mặc dù đợi thêm chưa được 1 giây! Thế là mình suy nghĩ lại một hướng giải quyết triệt để hơn.

ActionCable cung cấp hook subscribed được kích hoạt khi Client theo dõi kênh thành công. Ở đây mình sẽ thêm logic gửi link về Client nếu job đã hoàn tất. Cụ thể hơn nếu Client theo dõi kênh trước khi job xong thì như bình thường khi nào xong sẽ nhận link còn nếu theo dõi sau khi job đã hoàn thành thì logic mới sẽ gửi thẳng link về cho Client luôn. Đây là đoạn code thực tế đọc cho có không khí ha:

class BackgroundJobsNotificationsChannel < ApplicationCable::Channel
  def subscribed
    stream_from "BackgroundJobsNotifications:#{params[:type]}:#{params[:id]}"

    if params[:type] == 'ExportCancelledSchedules'
      job_record = MasterExport.find_by(id: params[:id])

      if job_record&.finish?
        url = job_record.download_url
        transmit({ url: url })
      end
    end
  end

  def unsubscribed
    stop_all_streams
  end
end


Một chuyện tuy đơn giản nhưng cũng đáng để suy ngẫm. Khi sửa một vấn đề liệu bạn sẽ sửa cho xong hay sẽ tìm cách tốt nhất trong khả năng của mình dù nó hơi tốn công?
Cảm ơn bạn đã đọc bài.
0

Open to Work

Looking for a Fullstack Ruby on Rails developer?

I am open to new remote/hybrid opportunities and contract work.

Thảo luận trên Bluesky

Đi

Đang tải bình luận...

Powered by Bluesky AT Protocol