← Blog Blog · Runtime

One config, N tables: matrix pipelines with bounded concurrency

A source runs one query — one stream. To move a hundred tables you don't write a hundred configs: you write one and fan it out into a matrix, running in parallel under a deliberate ceiling.

faucet-stream·August 25, 2026·~6 min read

One source is one stream

A faucet pipeline is one source and one sink. A Postgres source takes a single query and streams it as bounded pages — sequential, predictable, bounded memory. That's the unit. So "move these forty tables" isn't a single source with forty queries; it's forty units.

The matrix: fan one template into many runs

You express those forty units as a matrix — one row per table, each deep-merging its own patch (its query/table) over the shared connection config. Source discovery can even generate the matrix for you: one row per discovered table. One template, N executions, no copy-paste.

The same mechanism powers parent/child fan-out: a parent stream's records parameterize a child query per record (with safe bind parameters — see this post).

They run in parallel — under a ceiling

Matrix rows don't run one after another. The executor runs them concurrently under a semaphore whose width is execution.max_concurrent. The default is min(available CPUs, 8) — and the cap of 8 is deliberate:

Each row is a full pipeline with its own connection pools and clients, and matrix rows usually target the same database. An unbounded fan-out across a 64-core box wouldn't go faster — it would blow through that database's connection and rate limits. The ceiling protects the system you're reading from.

Workloads that genuinely benefit from more parallelism set execution.max_concurrent explicitly to opt out of the cap. And the number of hardware threads is read with available_parallelism(), which honors container CPU limits — so inside a pod pinned to 2 CPUs you get 2, not the host's 64.

Takeaway

One config becomes many parallel runs, each isolated and checkpointed on its own, with a concurrency default chosen to be fast and a good citizen of the database behind it. Scale the knob when you know the backend can take it.

Was this useful?

React with 👍, ❤️ or 🚀 to vote, or leave a comment. You sign in with GitHub inside the box below; this site never sees your account.

More on the blog, or read the documentation.

Get started

Your first pipeline runs in five minutes.

Install the CLI, scaffold a config, and move real data — nothing external to stand up.

curl -LsSf https://github.com/faucet-hq/faucet-stream/releases/latest/download/faucet-cli-installer.sh | sh
brew install faucet-hq/faucet-stream/faucet-cli