Some work should not run inside the request that triggers it. Sending a hundred emails after a blog post is published or retrying a webhook delivery that failed six hours earlier are tasks that do not belong in the same request a user is waiting on.
Oolvay moves this kind of work off the request cycle through two separate mechanisms, and which one applies depends entirely on what triggers the work, not on how heavy the work is.
| Mechanism | Triggered by | Runs through |
|---|---|---|
| Inngest | An event that just happened | Event-driven worker |
| Cron | A fixed point in time, regardless of events | Vercel Cron |
Both run outside the normal request/response cycle. Neither is a substitute for the other.
A useful way to think about the difference:
Inngest is reactive. A blog post is published, an event is sent, and a worker function picks it up and runs, independently of the request that published the post, with its own retry behavior if the worker itself fails.
Cron is not reactive at all. It does not know or care whether anything happened. Oolvay’s one cron job runs on a fixed daily schedule and checks whether anything needs retrying, regardless of how many webhook failures occurred since the last run, or none.
A bulk email send is an Inngest problem: something specific just happened, and work needs to follow from it. A nightly sweep for failed webhook events is a Cron problem: nothing in particular needs to have happened for the job to be worth running.
Oolvay treats these two mechanisms differently in terms of how optional they are.
backgroundJobs.inngest.enabled is set to false, any work that would normally run through Inngest runs synchronously on the request that triggered it. Nothing is lost, but concurrency is.0 / 2,000 characters
This differs from most integrations in Oolvay. Ably, Sentry, and Inngest are optional layers you can disable without losing functionality. Cron is part of the application's required infrastructure, not an integration you opt into.