We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Let `Task.async_stream/3` emit fast work first
almirsarajcic
Task.async_stream/3 quietly holds fast results behind the earliest slow input because ordered defaults to true. When independent IO work does not need input order, opt out explicitly and set a bounded concurrency limit.
defmodule BillingSync do
def sync_all(accounts) do
accounts
|> Task.async_stream(
fn account ->
Billing.fetch_usage!(account)
end,
max_concurrency: 20,
ordered: false,
on_timeout: :kill_task,
timeout: 15_000
)
|> Enum.each(fn
{:ok, usage} ->
Billing.store_usage!(usage)
{:exit, :timeout} ->
raise "usage sync task timed out"
end)
end
end
With ordered output, Elixir buffers later completions until it can emit every earlier input. In a local run with one task sleeping for 200 ms and two sleeping for 10 ms, ordered delivery emitted all three at 204 ms. With ordered: false, the two fast results arrived at 11 ms and the slow one at 201 ms. The trade-off is real: results now arrive in completion order, not input order.
max_concurrency defaults to System.schedulers_online/0, a CPU-oriented default that is often too small for IO-bound work. Choose a higher bounded number only after considering the remote service’s rate limit, the HTTP client’s pool, and your own capacity.
The per-task timeout default is 5,000 ms. Its on_timeout default, :exit, exits the caller; :kill_task instead kills that task and emits {:exit, :timeout}. zip_input_on_exit also defaults to false; enable it when exit results need the original input attached. This changes stream options only—use the Task.Supervisor stream APIs when you also need supervision semantics.
copied to clipboard