ReqFly.Orchestrator (req_fly v1.0.0)
View SourceMulti-step workflow orchestration for complex Fly.io operations.
This module provides high-level orchestration functions that combine multiple API calls with polling and waiting logic to simplify complex workflows like creating and waiting for apps or machines to reach desired states.
Polling and Backoff Strategy
All orchestration functions use exponential backoff with jitter:
- Initial delay: 500ms
- Backoff multiplier: 1.5x
- Maximum delay: 5000ms
- Jitter: 0-20% random variance
Telemetry
All orchestration functions emit telemetry events:
[:req_fly, :orchestrator, :wait, :start]- When waiting begins[:req_fly, :orchestrator, :wait, :stop]- When waiting completes successfully[:req_fly, :orchestrator, :wait, :timeout]- When waiting times out
Metadata includes:
operation- The operation being performed (e.g., "create_app_and_wait")duration- Time elapsed in milliseconds (for stop/timeout events)attempts- Number of polling attempts madereason- Error reason (for timeout events)
Examples
req = Req.new() |> ReqFly.attach(token: "fly_token")
# Create app and wait for it to become active
{:ok, app} = ReqFly.Orchestrator.create_app_and_wait(req,
app_name: "my-app",
org_slug: "my-org",
timeout: 120
)
# Create machine and wait for it to start
config = %{
image: "flyio/hellofly:latest",
guest: %{cpus: 1, memory_mb: 256}
}
{:ok, machine} = ReqFly.Orchestrator.create_machine_and_wait(req,
app_name: "my-app",
config: config,
state: "started",
timeout: 90
)
Summary
Functions
Creates a Fly.io app and waits for it to become active.
Creates a machine and waits for it to reach the desired state.
Generic polling function with exponential backoff.
Functions
@spec create_app_and_wait( Req.Request.t(), keyword() ) :: {:ok, map()} | {:error, ReqFly.Error.t()}
Creates a Fly.io app and waits for it to become active.
This orchestration function combines app creation with polling to ensure the app is fully provisioned before returning.
Parameters
req- A Req.Request with ReqFly attachedopts- Options keyword list:app_name- Name of the app to create (required):org_slug- Organization slug (required):timeout- Maximum time to wait in seconds (default: 60)
Returns
{:ok, app}- App details when active{:error, %ReqFly.Error{}}- Error details (creation failure or timeout)
Examples
req = Req.new() |> ReqFly.attach(token: "fly_token")
{:ok, app} = ReqFly.Orchestrator.create_app_and_wait(req,
app_name: "my-app",
org_slug: "my-org"
)
# With custom timeout
{:ok, app} = ReqFly.Orchestrator.create_app_and_wait(req,
app_name: "my-app",
org_slug: "my-org",
timeout: 120
)
@spec create_machine_and_wait( Req.Request.t(), keyword() ) :: {:ok, map()} | {:error, ReqFly.Error.t()}
Creates a machine and waits for it to reach the desired state.
This orchestration function creates a machine and then waits for it to reach the specified state. It first attempts to use the wait endpoint (if an instance_id is available), then falls back to polling if needed.
Parameters
req- A Req.Request with ReqFly attachedopts- Options keyword list:app_name- Name of the app (required):config- Machine configuration map (required):region- Region to create the machine in (optional):state- Desired state to wait for (default: "started"):timeout- Maximum time to wait in seconds (default: 60)
Returns
{:ok, machine}- Machine details when desired state is reached{:error, %ReqFly.Error{}}- Error details (creation failure or timeout)
Examples
req = Req.new() |> ReqFly.attach(token: "fly_token")
config = %{
image: "flyio/hellofly:latest",
guest: %{cpus: 1, memory_mb: 256}
}
# Create and wait for machine to start
{:ok, machine} = ReqFly.Orchestrator.create_machine_and_wait(req,
app_name: "my-app",
config: config
)
# Create and wait for specific state
{:ok, machine} = ReqFly.Orchestrator.create_machine_and_wait(req,
app_name: "my-app",
config: config,
state: "stopped",
timeout: 90
)
@spec poll_until( Req.Request.t(), (-> {:ok, term()} | {:continue, term()} | {:error, term()}), keyword() ) :: {:ok, term()} | {:error, ReqFly.Error.t()}
Generic polling function with exponential backoff.
Repeatedly calls a check function until it returns success, error, or timeout. Uses exponential backoff with jitter between attempts.
Parameters
req- A Req.Request (used for telemetry context)check_fn- Function that returns:{:ok, result}- Success, stop polling{:continue, reason}- Keep polling{:error, error}- Error, stop polling
opts- Options keyword list:timeout- Maximum time to wait in seconds (required):operation- Operation name for telemetry (optional):error_message- Custom timeout error message (optional)
Returns
{:ok, result}- When check_fn returns success{:error, %ReqFly.Error{}}- On timeout or when check_fn returns error
Examples
check_fn = fn ->
case MyAPI.get_status() do
{:ok, %{ready: true}} -> {:ok, :ready}
{:ok, _} -> {:continue, "not ready"}
error -> {:error, error}
end
end
{:ok, :ready} = ReqFly.Orchestrator.poll_until(req, check_fn,
timeout: 30,
operation: "wait_for_ready"
)