Bounded Concurrency First

Bounded concurrency first

When fanning out async work, default to bounded concurrency. Unbounded spawn is the lazy answer that turns "many tasks" into "many simultaneous failures." The fix is small: a Semaphore plus a JoinSet, with the permit released as the task completes.

The pattern

use std::sync::Arc;
use tokio::sync::Semaphore;
use tokio::task::JoinSet;

let limit = Arc::newnew(16);
let mut set = JoinSet::new();

for item in items {
    let permit = limit.clone().acquire_owned().await?;
    set.spawn(async move {
        let _permit = permit;
        process_item(item).await
    });
}

while let Some(result) = set.join_next().await {
    handle(result)?;
}

At most 16 items are in flight at any moment. The acquire_owned call blocks new spawns once the limit is reached. The permit drops when the task completes, freeing the slot.

What unbounded fan-out actually breaks

Spawning one task per item from a 5,000-item list looks innocent until you run it on a real workload:

A semaphore caps every one of these failure modes by capping the input.

Picking the bound

The right limit depends on the bottleneck. Some heuristics:

The bound is a knob, not a magic number. The point is having a knob.

The complement: back-pressure all the way down

A semaphore on spawn alone isn't enough if the spawned work feeds into an unbounded channel downstream. Back-pressure works only when every step in the pipeline has a limit. Otherwise the bottleneck just moves.

If you have a mpsc::channel between stages, give it a finite capacity. If you have a result accumulator, cap its size. If you have a retry queue, bound it. The semaphore is the entry gate; the rest of the pipeline needs gates too.

When unbounded is acceptable

There are real cases where unbounded is fine:

For those, JoinSet without a semaphore is the right call. The rule is "bounded first," not "bounded always" — but the default should bias toward the bound, because the cases that need it are common and the cases that need the bound removed are rare.

See also