This is a quick roundup of what’s new with GoodJob v1.12.0 since the last update published for GoodJob v1.9.

GoodJob ( github ) is a multithreaded, Postgres-based, ActiveJob backend for Ruby on Rails. If you’re new to GoodJob, read the introductory blog post.

For further details on the following updates, check out GoodJob’s Changelog or Readme.

Cron-like replacement for repeating/recurring jobs

GoodJob now ships with a cron-like replacement for repeating/recurring jobs. The cron-like process runs either via the CLI, or async within the web server process. Repeating jobs can be scheduled to the second, powered by the Fugit gem. Here’s what the configuration looks like:

# config/environments/application.rb or a specific environment e.g. production.rb

# Enable cron in this process; e.g. only run on the first Heroku worker process
config.good_job.enable_cron = ENV['DYNO'] == 'worker.1' # or `true` or via $GOOD_JOB_ENABLE_CRON

# Configure cron with a hash that has a unique key for each recurring job
config.good_job.cron = {
  # Every 15 minutes, enqueue `ExampleJob.set(priority: -10).perform_later(52, name: "Alice")`
  frequent_task: { # each recurring job must have a unique key
    cron: "*/15 * * * *", # cron-style scheduling format by fugit gem
    class: "ExampleJob", # reference the Job class with a string
    args: [42, { name: "Alice" }], # arguments to pass; can also be a proc e.g. `-> { { when: } }`
    set: { priority: -10 }, # additional ActiveJob properties; can also be a lambda/proc e.g. `-> { { priority: [1,2].sample } }`
    description: "Something helpful", # optional description that appears in Dashboard (coming soon!)
  another_task: {
    cron: "0 0,12 * * *",
    class: "AnotherJob",
  # etc.

Concurrency controls

GoodJob now offers an ActiveJob extension to provide customizable limits on the number of jobs enqueued or executed concurrently. Rails might upstream it too. Here’s how to configure it:

# app/jobs/my_job.rb
class MyJob < ApplicationJob
  include GoodJob::ActiveJobExtensions::Concurrency

    # Maximum number of jobs with the concurrency key to be concurrently enqueued
    enqueue_limit: 2,

    # Maximum number of jobs with the concurrency key to be concurrently performed
    perform_limit: 1,

    # A unique key to be globally locked against.
    # Can be String or Lambda/Proc that is invoked in the context of the job.
    # Note: Arguments passed to #perform_later must be accessed through `arguments` method.
    key: -> { "Unique-#{arguments.first}" } #  MyJob.perform_later("Alice") => "Unique-Alice"

  def perform(first_name)
    # do work

Dashboard Demo

Don’t take my word for what a good job it is. Check out the new GoodJob Dashboard demo running on Heroku entirely within a single free dyno:

More news:

  • Wojciech Wnętrzak aka @morgoth became a GoodJob maintainer.
  • Wrote up details of the evolving development philosophy behind GoodJob.
  • The Dashboard now allows removing jobs, with more actions coming soon.


Code, documentation, and curiosity-based contributions are welcome! Check out the GoodJob Backlog , comment on or open a Github Issue, or make a Pull Request.

I also have a GitHub Sponsors Profile if you’re able to support GoodJob and me monetarily. It helps me stay in touch and send you project updates too.