Step Delay
Ductwork Pro lets you tell a step to wait before it runs. When the pipeline advances to a delayed step, its job is created with a start time in the future, and job workers leave it alone until that time passes.
Nothing is held open while waiting. There’s no sleeping thread and no special “waiting” status. The delay is just a timestamp on the job’s availability record, so a pipeline with a week-long delay in the middle costs you one row in the database and nothing else.
Declaring delays
Section titled “Declaring delays”Add a delay option to any transition in your pipeline definition. Values can be integers (seconds) or ActiveSupport::Duration objects:
define do |pipeline| pipeline.start(SendWelcomeEmail) .chain(to: CheckProfileComplete, delay: 1.day) .chain(to: SendReminderEmail, delay: 3.days)endHere CheckProfileComplete runs a day after SendWelcomeEmail finishes, and SendReminderEmail runs three days after that. Each delay is measured from when the previous step completed, not from when the pipeline was triggered.
Anything other than an integer or duration raises an ArgumentError when the pipeline class is loaded, as does a negative value.
Delaying the first step
Section titled “Delaying the first step”A delay on start delays the whole pipeline. The first job is enqueued at trigger time with a start time in the future:
define do |pipeline| pipeline.start(SendFollowUp, delay: 2.hours)endYou can also decide this at trigger time. trigger accepts either a delay (a duration or integer) or a delay_until (a timestamp). Passing one overrides whatever delay is in the definition. Passing both raises an ArgumentError, and so does a delay_until in the past.
SendFollowUpPipeline.trigger(user.id, delay: 30.minutes)SendFollowUpPipeline.trigger(user.id, delay_until: user.trial_ends_at)Timing precision
Section titled “Timing precision”A delay is a lower bound. Once the start time passes, the job is picked up on the next job worker poll, so the real wait is the delay plus up to one polling interval. Start times are computed with the database clock, so it doesn’t matter whether your app servers and workers agree on what time it is.
Combining delays with other features
Section titled “Combining delays with other features”A step can have both a delay and a timeout. The timeout clock doesn’t start until the step actually begins executing, so a long delay never eats into the timeout budget:
pipeline.chain(to: CallExternalApi, delay: 5.minutes, timeout: 30.seconds)Delays work on every transition, not just chain. A delay on a fan-out like divide or expand applies to each step it creates. A delay on a fan-in like combine or collapse applies to the step that gathers the results, counting from when the last branch finished.