ActiveJob Adapter Setup
This guide covers the setup and configuration for using Axn with the ActiveJob adapter.
Basic Setup
To use ActiveJob with Axn, configure your actions with async :active_job:
class SendEmailAction
include Axn
async :active_job do
queue_as :mailers
retry_on StandardError, wait: 5.seconds, attempts: 3
end
expects :user, :template
def call
# Send email logic
end
end
# Execute in background
SendEmailAction.call_async(user: user, template: "welcome")How It Works
Unlike the Sidekiq adapter (where your action class becomes the worker), the ActiveJob adapter creates a proxy job class that inherits from ActiveJob::Base. This proxy:
- Receives the job arguments
- Calls your action with those arguments
- Handles the result (re-raising exceptions for retry, swallowing business failures)
Configuration
Per-Action Configuration
Use a block to configure ActiveJob options:
class MyAction
include Axn
async :active_job do
queue_as :critical
retry_on NetworkError, wait: :polynomially_longer, attempts: 5
discard_on ActiveRecord::RecordNotFound
end
endWARNING
ActiveJob adapter requires a configuration block. Keyword arguments are not supported because ActiveJob methods like retry_on and discard_on require exception classes as arguments:
# ❌ This will raise an error
async :active_job, queue: "critical"
# ✅ Use a block instead
async :active_job do
queue_as :critical
endGlobal Default
Set a default ActiveJob configuration for all actions:
Axn.configure do |c|
c.set_default_async(:active_job) do
queue_as :default
retry_on StandardError, wait: 5.seconds, attempts: 3
end
endError Handling Behavior
Business Failures (fail!)
When an action calls fail!, it's treated as a deliberate business decision:
- The job does NOT retry
- The job completes successfully (from ActiveJob's perspective)
on_exceptionis NOT triggered
class PaymentAction
include Axn
async :active_job
def call
# This will NOT trigger retries - job completes immediately
fail! "Card declined" if card_declined?
end
endUnexpected Exceptions
When an unexpected exception occurs:
- The job DOES retry (based on your
retry_onconfiguration) on_exceptionis triggered based onasync_exception_reportingsetting
class SyncAction
include Axn
async :active_job do
retry_on NetworkError, wait: 5.seconds, attempts: 3
end
def call
# This WILL trigger retries (if NetworkError)
raise NetworkError, "Connection timeout"
end
endRetry Tracking
ActiveJob provides retry information through its built-in executions counter:
executions == 1: First attemptexecutions == 2: First retryexecutions == 3: Second retry- etc.
Axn uses this to build retry context for exception reporting.
Max Retries Detection
Axn determines max retries in this order:
- Job's
retry_limit: If your job class defines a retry limit viaretry_on Axn.config.async_max_retries: If you've explicitly configured this- Backend detection: Axn attempts to detect if Sidekiq is the backend and uses its default (25)
- Fallback: 5 (matches ActiveJob's
retry_ondefault)
Retry Context in Exception Reports
When on_exception is triggered, the context includes retry information:
Axn.configure do |c|
c.on_exception = proc do |e, action:, context:|
if context[:async]
puts "Adapter: #{context[:async][:adapter]}" # => :active_job
puts "Attempt: #{context[:async][:attempt]}"
puts "Max retries: #{context[:async][:max_retries]}"
puts "Job ID: #{context[:async][:job_id]}"
end
Honeybadger.notify(e, context: context)
end
endUsing with Sidekiq Backend
If you're using ActiveJob with Sidekiq as the backend:
# config/application.rb
config.active_job.queue_adapter = :sidekiqIn this case:
- Retry tracking uses ActiveJob's
executionscounter (not Sidekiq'sretry_count) - You don't need to register Sidekiq middleware for ActiveJob actions
- The Sidekiq middleware only applies to actions using
async :sidekiqdirectly
Choosing Between Adapters
Use async :sidekiq when... | Use async :active_job when... |
|---|---|
| You want direct Sidekiq control | You want backend portability |
| You need Sidekiq-specific features (batches, etc.) | You're using non-Sidekiq backend |
| Performance is critical | You prefer Rails conventions |
Discarded Job Handling (Rails 7.1+)
On Rails 7.1+, Axn automatically registers an after_discard callback on the proxy job class. This triggers on_exception when:
discard_oncatches an exceptionretry_onexhausts all retries- An unhandled exception causes the job to be discarded
This means :first_and_exhausted and :only_exhausted modes work correctly—exceptions are reported when the job is actually discarded, not just on the final attempt.
class MyAction
include Axn
async :active_job do
discard_on ValidationError # Will trigger on_exception when discarded
retry_on NetworkError, attempts: 3 # Will trigger on_exception when exhausted
end
def call
# ...
end
endThe discard context includes discarded: true in the async info:
Axn.configure do |c|
c.on_exception = proc do |e, context:|
if context.dig(:async, :discarded)
puts "Job was discarded!"
end
end
endWARNING
Rails 7.1+ is required for :first_and_exhausted and :only_exhausted modes with the ActiveJob adapter. These modes rely on after_discard which was introduced in Rails 7.1. On older Rails versions, Axn will raise an error if you try to use these modes with ActiveJob.
Limitations
Retry Configuration
ActiveJob's retry behavior is defined per-exception type via retry_on, while Sidekiq uses a global retry count. Make sure to configure retry_on for the exceptions you expect:
async :active_job do
# Retry network errors
retry_on NetworkError, wait: :polynomially_longer, attempts: 5
# Retry rate limiting with longer wait
retry_on RateLimitError, wait: 1.minute, attempts: 10
# Don't retry validation errors
discard_on ValidationError
endTroubleshooting
Jobs not retrying
Ensure you've configured retry_on for the exception types you expect:
async :active_job do
retry_on StandardError, attempts: 3 # Catch-all retry
endWrong max_retries in context
If context[:async][:max_retries] doesn't match your expectations:
- Check if your job has
retry_limitdefined (fromretry_on) - Try setting
Axn.config.async_max_retriesexplicitly - Verify Sidekiq detection is working (if using Sidekiq backend)
Jobs not completing on fail!
This is expected behavior. fail! indicates a business decision, and the job completes successfully (from ActiveJob's perspective) without retrying.