Migrate

# From GoodJob

Both are PostgreSQL-native with LISTEN/NOTIFY — swap advisory locks and good_jobs for PGMQ.

## What changes

GoodJob and pgbus are both PostgreSQL-native with LISTEN/NOTIFY. The architectural difference: GoodJob uses advisory locks and a `good_jobs` table; pgbus uses PGMQ — a dedicated message-queue extension — with visibility timeouts. Both are pure ActiveJob adapters, so jobs move over unchanged.

**Effort:** low for standard ActiveJob; medium if you rely on GoodJob's concurrency controls, batches, or cron.

## Swap the gem and adapter

```ruby
# Remove
gem "good_job"

# Add
gem "pgbus"
```

```shell
bundle install && rails generate pgbus:install && rails db:migrate
```

```ruby
config.active_job.queue_adapter = :pgbus # was :good_job
```

## Concurrency and cron

GoodJob's `good_job_control_concurrency_with` maps to pgbus's `limits_concurrency`; GoodJob's `config.good_job.cron` maps to pgbus's recurring tasks. Both DSLs are auto-included — no explicit require:

```ruby
# GoodJob: good_job_control_concurrency_with(total_limit: 1, key: -> { ... })
# pgbus:
class ProcessOrderJob < ApplicationJob
  limits_concurrency to: 1, key: ->(order_id) { "ProcessOrder-#{order_id}" }
end
```

> **Tip:** See [Concurrency & uniqueness](https://pgbus.zoolutions.llc/docs/concurrency-uniqueness) and [Recurring tasks](https://pgbus.zoolutions.llc/docs/recurring-tasks) for the full APIs; [Batches](https://pgbus.zoolutions.llc/docs/batches) replaces GoodJob::Batch.

## What you gain

- **Dead-letter queues** — GoodJob retries in place; pgbus routes exhausted jobs to a `_dlq` queue for inspection.
- **Worker recycling** — memory, job-count, and lifetime limits.
- **An event bus** and **Postgres-SSE streams** — on the same database.

## Gotchas

- **Locking model** — GoodJob's advisory locks are held for the duration of a job; PGMQ uses a visibility timeout that expires and re-delivers. A job that outlives its `visibility_timeout` can be re-read, so size the timeout above your longest job (or extend it in-job).
- **Table cleanup** — after jobs drain, drop the `good_jobs` tables; pgbus doesn't read them.