Support

Need help?

Most answers are already written down. For everything else, Oneop does not publish a response time — there is no SLA instrument behind one — so the fastest thing you can do is send a complete first message.

Search the help centreEmail support@oneop.ioOpen your workspace

Worth including

  • The email you log in with
  • The URL of the screen it happened on
  • What you expected, and what happened instead

Goes somewhere else

Automation

Durable automation: why delayed work must survive a restart

By The Oneop team8 min read

TL;DR

Durable workflow execution means a paused automation is a row in a database, not a timer in a running process. The pending step, its variables and its due time are written to storage; a worker claims the row, runs it, and marks it done only after the work returns. A restart, a deploy or a crash then delays the work instead of deleting it.

A follow-up scheduled for Tuesday is a promise your system made on Monday. If that promise exists only in the memory of a running process, then a deploy, a crash, an out-of-memory kill or a routine autoscaler decision quietly erases it. Nobody gets an error. The work simply does not happen, and the only evidence is a customer who was never contacted.

Delayed work is the part of automation most likely to be built wrong, and the least likely to announce that it was.

Why an in-memory timer is not a schedule

A timer created with setTimeout is not a schedule. It is a note held in the memory of one process, and it has exactly the lifetime of that process.

This is easy to miss because it works perfectly in development, where the process runs for hours and nothing kills it. It also works in production, right up until the first deploy that lands during a waiting period. There is no error, no retry, no alert. The state that said “do this at 14:00 on Tuesday” was never written anywhere that outlives the process, so after the restart there is nothing to recover and nothing to notice.

The property you actually need has a name: durable workflow execution. A paused automation must be represented by a record in storage, independent of any running process, that a future worker can find and act on.

What durable scheduling stores

Durable scheduling writes the pending action to storage and lets a separate worker claim it later. The record has to carry enough to continue the run without the original process, which is more than just a time.

In-memory timerDurable scheduled row
Where the pending work livesHeap of one processDatabase row
Survives a restartNoYes
Survives a crash mid-waitNoYes
Visible to operationsNo, invisible by constructionYes, queryable: what is pending, what is overdue
Maximum practical waitProcess lifetimeUnbounded
Failure modeSilent disappearanceLate execution, or a recorded failure
Can two workers run it at onceN/AOnly if you do not claim it first

The minimum useful record is: which run this is, which step it stopped at, the variables gathered so far, a snapshot of the flow definition as it was when the run started, and the time it becomes due. The flow snapshot matters more than it looks. If you re-read the live definition on resume, then editing an automation on Wednesday silently changes what every run already waiting is going to do on Thursday, including runs that already took a branch that no longer exists.

The Friday deploy that cancelled 400 follow-ups

Here is the concrete failure, and it is boringly common.

A support team builds a rule: when a ticket is resolved, wait 48 hours, then send a satisfaction follow-up. It is implemented as a timer in the application process. It passes QA, because QA never restarts the server mid-wait.

The team ships on Friday afternoon. The deploy rolls the process. Every follow-up scheduled in the previous 48 hours is destroyed at the moment of the rollout, because each one was a callback in the memory of a process that no longer exists. About 400 customers are never contacted. No row was written, so no row is overdue, so no dashboard shows a backlog. The queue is empty, which looks identical to the queue being done.

The team discovers it three weeks later, when someone asks why the response rate dropped, and the only way to reconstruct the damage is to query resolved tickets with no follow-up activity and diff them against the deploy timestamps. The lost work cannot be re-sent honestly either, because a satisfaction survey three weeks after the fact is a different message.

The lesson is not “timers are bad”. It is that the absence of persisted state makes the failure undetectable, and an undetectable failure is worse than a loud one.

At-least-once delivery is what durability buys you

Once pending work is a row, a worker has to claim it before running it, or two workers running at the same moment will both pick it up. Claiming usually means one conditional update: set the row’s due time forward, or set an owner and a lease expiry, and proceed only if the update actually changed a row. Losers of that race get zero rows changed and move on.

The lease matters because a worker can die mid-execution. If the row were marked done before the work ran, a crash would lose the work. So the row is marked done after the work returns, and if the worker dies in between, the lease expires and the row becomes claimable again.

That ordering gives you at-least-once delivery: nothing is lost, but a step interrupted after its side effects and before its completion record will run again. This is the correct trade for business automation, because a duplicate is visible and a loss is not. It is also an obligation, and it is the subject of the companion to this article: idempotency in business automation covers how to make the action itself safe to repeat. Durability solves “the work must not vanish”. Idempotency solves “the work must not count twice”. Neither substitutes for the other.

Retries, backoff and the message that never succeeds

Durable execution makes retries possible, which makes retry policy necessary.

FailureWhat it meansCorrect response
Transient (timeout, 503, deadlock)The action may succeed laterRetry with exponential backoff and jitter
Rate limitedThe provider is asking you to slow downRetry after the stated interval, do not add attempts
Permanent (404 on a deleted record, validation error)It will never succeedFail the run immediately and record why
Ambiguous (connection dropped after sending)It may have already succeededRetry only if the action is idempotent
Poison (fails every attempt for unknown reasons)An attempt loop that never terminatesCap attempts, then dead-letter for a human

Exponential backoff exists so a struggling dependency is not hammered by every stuck run simultaneously. Jitter exists because without it, all the retries synchronise and arrive in one spike, which is how a brief outage becomes a long one.

The retry ceiling exists because of poison messages. A queued item with malformed data fails, is requeued, fails, is requeued. With no attempt cap it consumes a worker forever and produces an infinite log. Cap attempts, move the item to a dead-letter store, and treat that store as an operational surface a person actually looks at. A dead-letter queue nobody reads is a slower version of losing the work.

How Oneop handles waiting

Oneop’s ticket automations implement waits as durable state rather than timers. When a run reaches a wait step, the remaining steps, the step index it stopped at, the variables collected, the trace so far and a copy of the flow as it was when the run started are written to a database row with a time to resume. A separate process looks for rows that are due, claims a row before executing it, and marks the row done only after execution returns. A twenty-four hour wait set on Monday still runs on Tuesday even if the server was restarted in between. Full mechanics are on the automation page.

The limit is stated on that page rather than hidden: a resumed run restarts the step it was interrupted in, so a step whose side effect already happened can happen again. Oneop claims durability, not exactly-once execution, and those are different guarantees. Waits, approvals and the dry-run simulator are not behind a plan gate, which you can verify on pricing.

The same durability question applies to anything that waits, including an automation that pauses for a person. Deciding which steps deserve a human at all is a separate design problem, and one worth answering before you automate: when not to automate argues the case for leaving some things manual, and the autonomy levels post covers the suggest-approve-autonomous ladder for actions a model proposes.

The question to ask any automation vendor

Ask one question: “If you restart the server right now, what happens to everything currently waiting?”

A vendor with durable execution answers with a mechanism: a row, a due time, a worker, a claim. A vendor without one answers with reassurance. Then ask to see the pending work as a list. If pending work cannot be listed, it is not stored, and if it is not stored, it is not going to survive Friday’s deploy. Ticket-side automations that wait are described in more detail on the help desk page.

Frequently asked questions

What is durable workflow execution?

Durable workflow execution is a design where a paused or scheduled automation is persisted outside the process that created it. The step it stopped at, the variables it gathered and the time it should resume are written to storage. Any worker can later read that record and continue the run, so the work survives a restart, a deploy or a crashed process.

What happens to a scheduled automation when the server restarts?

It depends entirely on where the schedule lives. An in-process timer is destroyed with the process and the work simply never happens, silently. A persisted schedule is untouched by the restart: the row is still due, the next worker pass finds it, and the automation continues late rather than not at all.

Is at-least-once delivery good enough for business automation?

At-least-once delivery guarantees work is not lost but allows it to run twice. That is the correct trade for most business automation, because a lost follow-up is invisible and a repeated one is visible. It is only safe when the actions themselves tolerate repetition, which is a property you design into the action rather than into the queue.

What is a poison message in a workflow queue?

A poison message is a queued item that fails every time it is attempted, usually because of malformed data or a permanently missing record. Without a retry ceiling it is picked up, fails, and is requeued forever, burning capacity and filling logs. The fix is a maximum attempt count, after which the item is moved to a dead-letter store for a human to inspect.

How long can an automation wait before it resumes?

With durable scheduling there is no architectural ceiling, because waiting costs nothing but a row. A wait of one hour and a wait of thirty days use the same mechanism, and neither holds a process open. With in-memory timers the practical ceiling is however long your process happens to live, which on modern deploy cadences is often hours.

Written by

The Oneop team

Writing from the team that builds Oneop — an AI customer platform with web chat and email on top of a real CRM, help desk, ledger and warehouse.

Related reading

Start on the free plan and see the real thing

No credit card. Free plan, 2 seats.

Free forever · 2 seats · No credit card