Sidekiq Error Handling
Sidekiq là framework để thực hiện các background job được sử dụng rất nhiều trong các rails app hiện nay. Trong bài viết này tôi sẽ giới thiệu một vài vấn đề về xử lý lỗi trong sidekiq. 1. Best Practices Sử dụng một dịch vụ báo lỗi - Honeybadger, Airbrake, Rollbar, BugSnag, Sentry, ...
Sidekiq là framework để thực hiện các background job được sử dụng rất nhiều trong các rails app hiện nay. Trong bài viết này tôi sẽ giới thiệu một vài vấn đề về xử lý lỗi trong sidekiq.
1. Best Practices
- Sử dụng một dịch vụ báo lỗi - Honeybadger, Airbrake, Rollbar, BugSnag, Sentry, Exceptiontrap, Raygun ... Chúng khá giống nhau về tính năng và giá cả, chỉ cần chọn một loại và sử dụng. Nó sẽ gửi mail mỗi khi một job nào đấy bị lỗi (hoặc thông minh hơn giống như Honeybadger, sẽ gửi mail vào các lần lỗi thứ 1, 3 và 10 để không làm quá tải hòm thư của bạn nếu có đến 1000 job lỗi)
- Hãy để Sidekiq bắt được lỗi do job của bạn gây ra. Cơ chế retry tích hợp trong Sidekiq sẽ bắt lỗi và retry chúng thường xuyên. Dịch vụ báo lỗi sẽ thông báo cho bạn lỗi đó. Bạn cần sửa và deploy lại, sau đó Sidekiq sẽ retry thành công job của bạn.
- Nếu bạn không fix kịp bug trước 25 lần retry của job (trong khoảng 21 ngày), Sidekiq sẽ dừng retry và đẩy job đó vào Dead Job Queue. Bạn có thể retry lại job đó bằng tay qua giao diện web của Sidekiq trong vòng 6 tháng tiếp theo.
- Sau 6 tháng, những job lỗi đó sẽ bị xóa.
2. Error Handlers
Sử dụng gem của dịch vụ báo lỗi đính kém với trình xử lý lỗi global của Sidekiq để chúng được thông báo bất cứ lúc nào có lỗi trong Sidekiq. Dịch vụ bào lỗi nên cung cấp tích hợp tự động bằng cách cài gem của họ trong Gemfile trong ứng dụng của bạn.
Bạn có thể tạo trình xử lý lỗi của riêng mình bằng cách cung cấp phản hồi kiểu: call(exception, context_hash) :
Sidekiq.configure_server do |config| config.error_handlers << Proc.new {|ex,ctx_hash| MyErrorService.notify(ex, ctx_hash) } end
Lưu ý là các trình xử lý lỗi chỉ liên quan đến máy Sidekiq Server. Chúng không hoạt động trong Rails Console.
3. Backtrace Logging
Khi bật backtrace logging cho job thì quá trình ghi log sẽ duy trì suốt dòng đời của job. Đặc biệt cẩn thận vì backtraces có thể ngốn 1-4k bộ nhớ Redis. Với mỗi lượng lớn các job lỗi, con số này lại tăng lên đáng kể.
sidekiq_options backtrace: true
Bạn nên sử dụng thận trọng khi bật backtrace bằng cách giới hạn số dòng log, hoặc sử dụng dịch vụ báo lỗi để theo dõi lỗi và backtrace liên quan.
sidekiq_options backtrace: 20 # top 20 lines
4. Automatic job retry
Sidekiq sẽ tự retry với thời gian theo công thức: (retry_count ** 4) + 15 + (rand(30) * (retry_count + 1)) Nó sẽ retry 25 lần trong khoảng 21 ngày. Giả sử bạn fix bug và deploy lại trong thời gian đó thì job sẽ được retry thành công. Sau 25 lần, Sidekiq sẽ tự chuyển job lỗi về Dead Job queue. Khi đó cần thao tác thủ công để tác động vào job đó.
Số lần thử lại tối đá được cấu hình toàn cục bằng cách thêm vào file sidekiq.yml:
max_retries: 1
Dưới đây là bàng thời gian chờ gần đúng (hàm rand(30) luôn trả về 15)
# | Next retry backoff | Total waiting time ------------------------------------------- 1 | 0d 0h 0m 30s | 0d 0h 0m 30s 2 | 0d 0h 0m 46s | 0d 0h 1m 16s 3 | 0d 0h 1m 16s | 0d 0h 2m 32s 4 | 0d 0h 2m 36s | 0d 0h 5m 8s 5 | 0d 0h 5m 46s | 0d 0h 10m 54s 6 | 0d 0h 12m 10s | 0d 0h 23m 4s 7 | 0d 0h 23m 36s | 0d 0h 46m 40s 8 | 0d 0h 42m 16s | 0d 1h 28m 56s 9 | 0d 1h 10m 46s | 0d 2h 39m 42s 10 | 0d 1h 52m 6s | 0d 4h 31m 48s 11 | 0d 2h 49m 40s | 0d 7h 21m 28s 12 | 0d 4h 7m 16s | 0d 11h 28m 44s 13 | 0d 5h 49m 6s | 0d 17h 17m 50s 14 | 0d 7h 59m 46s | 1d 1h 17m 36s 15 | 0d 10h 44m 16s | 1d 12h 1m 52s 16 | 0d 14h 8m 0s | 2d 2h 9m 52s 17 | 0d 18h 16m 46s | 2d 20h 26m 38s 18 | 0d 23h 16m 46s | 3d 19h 43m 24s 19 | 1d 5h 14m 36s | 5d 0h 58m 0s 20 | 1d 12h 17m 16s | 6d 13h 15m 16s 21 | 1d 20h 32m 10s | 8d 9h 47m 26s 22 | 2d 6h 7m 6s | 10d 15h 54m 32s 23 | 2d 17h 10m 16s | 13d 9h 4m 48s 24 | 3d 5h 50m 16s | 16d 14h 55m 4s 25 | 3d 20h 16m 6s | 20d 11h 11m 10s
5. Web UI
Giao diện người dùng của Sidekiq có 2 tab "Retries" và "Dead" liệt kê các job fail và cho phép bạn chạy chúng, kiểm tra hoặc xóa chúng.
6. Dead Job Queue
Dead Job Queue giữ các job fail sau khi chạy tất cả các lần retry. Sidekiq sẽ không retry các job này nữa, nếu muốn bạn có thể tự retry bằng tay trên Web UI. Để tránh trường hợp lưu vô hạn trong Dead Job Queue thì chỉ có tối đa 10000 job được lưu lại và các job lỗi quá 6 tháng sẽ bị xóa. Chỉ những job được config với số lần retry là 0 hoặc lớn hơn thì khi lỗi và hết số lần retry mới dc chuyển vào Dead Job Queue. Nếu config :retry => false, job này chỉ thực hiện đúng 1 lần dù có lỗi gì xảy ra.
7. Configuration
Nếu thấy 25 lần retry là quá nhiều, bạn có thể chỉ định một số khác:
class LessRetryableWorker include Sidekiq::Worker sidekiq_options :retry => 5 # Only five retries and then to the Dead Job Queue def perform(...) end end
Hoặc bạn có thể disable luôn việc retry (đồng nghĩa với việc job đó có lỗi thì cũng không được chuyển vào Dead Job Queue):
class NonRetryableWorker include Sidekiq::Worker sidekiq_options :retry => false # job will be discarded immediately if failed def perform(...) end end
Hoặc có thể vừa config số lẫn retry nhưng không muốn chuyển vào Dead Job Queue khi retry hết số lượt:
class NoDeathWorker include Sidekiq::Worker sidekiq_options :retry => 5, :dead => false def perform(...) end end
Độ trể của các lần retry cũng có thể tùy chỉnh được:
class WorkerWithCustomRetry include Sidekiq::Worker sidekiq_options :retry => 5 # The current retry count is yielded. The return value of the block must be # an integer. It is used as the delay, in seconds. A return value of nil will # use the default. sidekiq_retry_in do |count| 10 * (count + 1) # (i.e. 10, 20, 30, 40, 50) end def perform(...) end end
Khi chạy hết số lần retry, Sidekiq sẽ họi hàm sidekiq_retries_exhausted nếu bạn có định nghĩa sẵn.
class FailingWorker include Sidekiq::Worker sidekiq_retries_exhausted do |msg, ex| Sidekiq.logger.warn "Failed #{msg['class']} with #{msg['args']}: #{msg['error_message']}" end def perform(*args) raise "or I don't work" end end
8. Death Notification
Hàm sidekiq_retries_exhausted dành riêng cho class Worker. Bắt đầu từ bản 5.1, Sidekiq sẽ kích hoạt callback toàn cục khi có job die:
# this goes in your initializer Sidekiq.configure_server do |config| config.death_handlers << ->(job, ex) do puts "Uh oh, #{job['class']} #{job["jid"]} just died with error #{ex.message}." end end
Với callback này bạn có thể gửi email hay tin nhắn thông báo khi có lỗi.
Nguồn
https://github.com/mperham/sidekiq/wiki/Error-Handling