Async Execution

Servus provides asynchronous execution via ActiveJob. Services run identically whether called sync or async - they're unaware of execution context.

Usage

Call .call_async(**args) instead of .call(**args) to execute in the background. The service is enqueued immediately and executed by a worker.

# Synchronous
result = ProcessReport::Service.call(user_id: user.id, report_type: :monthly)
result.data[:report] # Available immediately

# Asynchronous
ProcessReport::Service.call_async(user_id: user.id, report_type: :monthly)
# Returns true if enqueued successfully
# Result not available (service hasn't run yet)

Services must accept JSON-serializable arguments for async execution (primitives, hashes, arrays, ActiveRecord objects via GlobalID). Complex objects like Procs won't work.

Queue and Scheduling Options

Pass ActiveJob options to control execution:

ProcessReport::Service.call_async(
  user_id: user.id,
  queue: :critical,      # Specify queue
  priority: 10,          # Higher priority
  wait: 5.minutes        # Delay execution
)

Result Handling

Async services can't return results to callers (the service hasn't executed yet). If you need results, implement persistence in the service:

class GenerateReport::Service < Servus::Base
  def call
    report_data = generate_report

    # Persist result
    Report.create!(
      user_id: @user_id,
      data: report_data,
      status: 'completed'
    )

    # Optionally notify user
    UserMailer.report_ready(@user_id).deliver_now

    success(data: report_data)
  end
end

# Controller creates placeholder, service updates it
report = Report.create!(user_id: user.id, status: 'pending')
GenerateReport::Service.call_async(user_id: user.id, report_id: report.id)

Error Handling

Failures (business logic) don't trigger retries - the job completes successfully but returns a failure Response.

Exceptions (system errors) trigger ActiveJob retry logic. Use rescue_from to convert transient errors into exceptions:

class Service < Servus::Base
  rescue_from Net::HTTPError, Timeout::Error use: ServiceUnavailableError
end

When to Use Async

Good candidates: Email sending, report generation, data imports, long-running API calls, cleanup tasks

Poor candidates: Operations requiring immediate feedback, fast operations (<100ms), critical path operations where user waits for result