Repository F# setup
open System
open System.IO
open System.Threading
open System.Threading.Tasks
open Axial
open Axial.Layers
open Axial.Console
open Axial.FileSystem
open Axial.Hosting
open Axial.Hosting.Browser
open Axial.Hosting.Node
open Axial.HttpClient
open Axial.PlatformService
open Axial.Process
open Axial.State
open Axial.Telemetry
open Axial.Telemetry.JavaScriptScheduling and Retries
A Schedule decides whether a flow should run again and how long to wait. Creating a schedule doesn't run anything. Apply it with Schedule.retry or Schedule.repeat.
Use schedules for tasks such as retrying a request, adding exponential backoff, polling a service, or running a heartbeat.
Schedules don't store attempt state. Schedule.retry and Schedule.repeat track attempts for each run, so you can reuse one schedule value across unrelated flows.
Schedule works on .NET and Fable's JavaScript target.
How schedules work
A schedule makes two decisions after each flow execution:
- Whether to run the flow again.
- How long to wait before the next run.
The source flow always runs once before Axial consults the schedule. For example, Schedule.recurs 3 allows three more runs, for up to four runs in total.
Build a schedule
Choose a schedule based on how many times the flow can run and how long Axial should wait between runs.
Limit the number of recurrences
Use Schedule.recurs to set the number of additional runs.
// Run up to 6 times: 1 initial run and 5 additional runs.
let fiveMoreTimes = Schedule.recurs 5Use Schedule.spaced to keep running with the same delay between runs.
// Wait 1 second between runs.
let everySecond = Schedule.spaced (TimeSpan.FromSeconds 1.0)Use exponential backoff
Use Schedule.exponential when repeated attempts should wait progressively longer.
// Wait 100 ms, 200 ms, 400 ms, 800 ms, and so on.
let backoff = Schedule.exponential (TimeSpan.FromMilliseconds 100.0)If many clients retry at the same time, they can place another burst of load on the service. Jitter spreads those retries across a wider period.
Schedule.jitteredWith multiplies each delay by a sampled factor from 0.5 to 1.5. You provide the sample function, which keeps randomness explicit and replaceable in tests.
let policy =
Schedule.exponential (TimeSpan.FromMilliseconds 100.0)
|> Schedule.jitteredWith random.NextDoubleRetry failed flows
Use Schedule.retry to rerun a flow after an expected domain failure (Cause.Fail).
Schedule.retry doesn't retry defects (Cause.Die) or interruptions (Cause.Interrupt). Axial passes them through without consulting the schedule.
let unstableCall =
flow {
return! Flow.fail "temporary-error"
}
// Try up to 4 times: 1 initial attempt and 3 retries.
let resilientCall =
unstableCall
|> Schedule.retry (Schedule.recurs 3)Repeat successful flows
Use Schedule.repeat to run a successful flow again. This is useful for polling, heartbeats, and recurring background work.
let pollStatus =
flow {
return "Still working"
}
// Poll every 5 seconds until the flow fails or is interrupted.
let recurringPoll =
pollStatus
|> Schedule.repeat (Schedule.spaced (TimeSpan.FromSeconds 5.0))Schedule API reference
| Function | Signature | Behavior |
|---|---|---|
recurs |
int -> Schedule<'env, 'input, int> |
Allows exactly n additional runs and emits the zero-based recurrence index. |
spaced |
TimeSpan -> Schedule<'env, 'input, int> |
Continues with a fixed delay and emits the zero-based recurrence index. |
exponential |
TimeSpan -> Schedule<'env, 'input, TimeSpan> |
Continues with a delay that doubles after each run. |
jitteredWith |
(unit -> float) -> Schedule<'env, 'input, 'output> -> Schedule<'env, 'input, 'output> |
Adjusts each delay by a sampled factor, normally from 0.5 to 1.5. |
retry |
Schedule<'env, 'error, 'output> -> Flow<'env, 'error, 'value> -> Flow<'env, 'error, 'value> |
Retries the flow after Cause.Fail. |
repeat |
Schedule<'env, 'value, 'output> -> Flow<'env, 'error, 'value> -> Flow<'env, 'error, 'value> |
Repeats the flow after success. |

