← 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
Nói qua về luồng hoạt động của chức năng này:
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:
- Đầu tiên người dùng nhấn vào nút
Export - Client gọi ajax lên Server yêu cầu tạo và xuất file xlsx
- 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
- Server giao việc cho job và trả về định danh (ID) của nó cho Client
- 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
- 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
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?