Your background job works until the process it drives has to survive a crash, wait three days, and resume exactly where it stopped. ChronoForge is the durable-workflow engine you write in plain Ruby.
ChronoForge is a gem over your existing database and ActiveJob backend: no DSL to learn, no separate server to run. Workflows are ordinary Ruby methods, so if/else, loops, and early returns just work. In production at achieve by Petra (3.6M+ workflows, 32M+ durable steps). Works with any ActiveJob backend on Rails 7.1+.
# Gemfile
gem "chrono_forge"
bundle install
rails g chrono_forge:install
rails db:migrate
Want ChronoForge's tables in their own database? rails g chrono_forge:install --database=chrono_forge.
A workflow is an ActiveJob class that prepends the executor. Each durable step is just a method:
class OnboardingWorkflow < ApplicationJob
prepend ChronoForge::Executor
def perform(user_id:)
@user_id = user_id
durably_execute :send_welcome_email
wait 2.days, :remind_delay
durably_execute :remind_of_tasks
wait 15.days, :onboarding_delay
durably_execute :complete_onboarding
end
# ... step methods ...
end
# Run it with a unique key that identifies this run for its whole life
OnboardingWorkflow.perform_later("onboard-user-42", user_id: 42)
The waits hold no worker: the workflow persists its position and resumes on schedule. Each step is checkpointed by name, so a crash and resume skip what already completed and pick up where it stopped.
wait_until), and event-driven waits (continue_if) that all survive restarts.durably_repeat runs a step on a schedule until a condition holds, with catch-up for missed runs.branch / spawn / spawn_each fan out into concurrent children, streaming large sets in constant memory.running by a hard-killed worker.