Skip to content

← all backend comparisons

concurrency

Concurrent fan-out: errgroup vs JoinSet

Each warehouse query sleeps a per-warehouse latency_ms to simulate a remote call. Sum of latencies = 400 ms; max = 150 ms. Both backends complete in ~150 ms — the test asserts < 400 ms to catch any regression to serial execution.

Go (chi · sqlc · pgx)
go/internal/httpserver/stock.go
// shop-two-backends not found at build time
Rust (axum · sqlx · tokio)
rust/src/routes/stock.rs
// shop-two-backends not found at build time

What to take away

Go uses errgroup.WithContext. Each goroutine writes to its own pre-allocated results[i] slot — no shared mutation, no mutex. The buggy version (mutating a shared map without a lock) compiles cleanly in Go and fails only under go test -race or in production.

Rust uses tokio::task::JoinSet with each spawned task taking move ownership of its Warehouse. The borrow checker would refuse the shared-mutation bug at compile time. Tasks complete in arbitrary order, so the response is sorted by warehouse.id post-hoc for stable output.

The headline: in Go, the safety of your concurrent code is something you (and your reviewer) verify. In Rust, it's something the compiler refuses to let you ship without.