This guide helps you migrate from the Ruby good_job gem to the Elixir good_job library.
For compatibility details when running both Ruby and Elixir together, see COMPATIBILITY.md.
Overview
Both libraries provide a concurrent, Postgres-based job queue backend with the same database schema, but there are differences in API and integration patterns due to the different ecosystems (Rails vs Phoenix/Elixir).
API Mapping
Job Definition
Ruby (Rails/ActiveJob):
class MyJob < ApplicationJob
queue_as :high_priority
def perform(data)
# Your job logic here
puts "Processing: #{data}"
end
end
# Enqueue the job
MyJob.perform_later(data: "hello")Elixir:
defmodule MyApp.MyJob do
use GoodJob.Job, queue: "high_priority"
@impl GoodJob.Behaviour
def perform(%{data: data}) do
# Your job logic here
IO.puts("Processing: #{data}")
end
end
# Enqueue the job
MyApp.MyJob.enqueue(%{data: "hello"})Configuration
Ruby (Rails):
# config/initializers/good_job.rb
Rails.application.configure do
config.good_job = {
execution_mode: :external,
max_processes: 5,
queues: "*",
poll_interval: 10
}
endElixir:
# config/config.exs
config :good_job,
repo: MyApp.Repo,
execution_mode: :external,
max_processes: 5,
queues: "*",
poll_interval: 10Execution Modes
Both libraries support the same execution modes. See README.md for details.
Cron Jobs
Ruby:
# config/initializers/good_job.rb
Rails.application.configure do
config.good_job.cron = {
example: {
cron: "0 * * * *", # Every hour
class: "ExampleJob"
}
}
endElixir:
# config/config.exs
config :good_job,
cron: [
example: [
cron: "0 * * * *", # Every hour
job: ExampleJob
]
]Batches
Ruby:
batch = GoodJob::Batch.new(
description: "Process users",
on_finish: "BatchFinishedJob"
)
User.find_each do |user|
ProcessUserJob.perform_later(user.id, batch_id: batch.id)
endElixir:
batch = GoodJob.Batch.create(%{
description: "Process users",
on_finish: "BatchFinishedJob"
})
User
|> Repo.all()
|> Enum.each(fn user ->
ProcessUserJob.enqueue(%{user_id: user.id}, batch_id: batch.id)
end)Concurrency Controls
Ruby:
class ProcessUserJob < ApplicationJob
def perform(user_id)
# Job logic
end
def good_job_concurrency_key
"user_#{user_id}"
end
def good_job_concurrency_limit
5
end
endElixir:
defmodule ProcessUserJob do
use GoodJob.Job
@impl GoodJob.Behaviour
def perform(%{user_id: user_id}) do
# Job logic
end
def good_job_concurrency_config do
[key: fn %{user_id: user_id} -> "user_#{user_id}" end, limit: 5]
end
endRetries
Both libraries support automatic retries. Default max attempts is 5 (matches Ruby GoodJob). See COMPATIBILITY.md for retry and backoff compatibility details.
Testing
See README.md for testing examples.
Key Differences
1. Job Definition
- Ruby: Uses ActiveJob base class with
performmethod - Elixir: Uses
use GoodJob.Jobmacro with@impl GoodJob.Behaviourandperform/1callback
2. Error Handling
- Ruby: Exception-based (Rails convention) -
raiseexceptions for errors - Elixir: Explicit return values (Elixir convention) -
{:error, reason}for errors
3. Process Model
- Ruby: Uses OS threads (via Concurrent Ruby)
- Elixir: Uses OTP processes (GenServer, Task.Supervisor)
4. Configuration Parameter Names
- Ruby:
max_threads - Elixir:
max_processes(same functionality, different terminology)
5. Database Schema
The database schema is fully compatible. You can use the same migrations and share the same database.
Migration Steps
Install the Elixir package:
# mix.exs {:good_job, "~> 0.1.0"}Run migrations (if not already done):
mix good_job.install mix ecto.migrateConvert job definitions:
- Convert ActiveJob classes to Elixir modules using
use GoodJob.Job - Update method signatures to use pattern matching
- Convert Ruby hash arguments to Elixir maps
- Convert ActiveJob classes to Elixir modules using
Update configuration:
- Move from Rails initializers to Elixir config files
- Update configuration syntax
Update tests:
- Replace ActiveJob test helpers with
GoodJob.Testing - Update assertions to use Elixir syntax
- Replace ActiveJob test helpers with
Update deployment:
- Replace
bundle exec good_job startwithmix good_job.start(when available) - Or use
:asyncmode for in-process execution
- Replace
Compatibility Notes
- The database schema is fully compatible - you can use the same PostgreSQL database
- Job records created by Ruby version can be processed by Elixir version (and vice versa)
- Cron job definitions need to be converted to Elixir syntax
- Batch callbacks need to be converted to Elixir job modules
For detailed compatibility information when running both Ruby and Elixir together, see COMPATIBILITY.md.
Getting Help
If you encounter issues during migration:
- Check the documentation
- Open an issue on GitHub
- Review the CHANGELOG.md for recent changes