<@ULVA73B9P> is there another possibilty for fan o...
# ask-marvin
o
@Marvin is there another possibilty for fan out work than submit and map?
m
thought for 244.2 seconds
Short answer: yes. Assuming Prefect 3.x, here are common fan‑out patterns beyond
submit
and `map`: - Fan out via deployments (launch many child flow runs) - From a parent flow, trigger N child runs with
run_deployment
. This scales across workers/work pools and doesn’t tie all work to a single flow run’s resources. - Docs: How to: run deployments, API: API ref - Example:
Copy code
from prefect import flow, get_run_logger
    from prefect.deployments import run_deployment
    from prefect.flow_runs import wait_for_flow_run

    @flow
    def parent(items):
        logger = get_run_logger()
        runs = [
            run_deployment(
                name="child-flow/my-deployment",
                parameters={"item": item},
                as_subflow=True  # set False to detach from the parent
            )
            for item in items
        ]
        # optionally wait for completion
        for fr in runs:
            <http://logger.info|logger.info>(f"Waiting for {fr.name} ({fr.id})")
            wait_for_flow_run(fr.id)
- Fan out across a distributed task runner (Dask/Ray) - Use Prefect’s Dask or Ray integrations to spread work across a cluster. Your call pattern can stay simple (loop +
submit
or mapping), but execution fans out to the cluster. - Dask: Prefect Dask integration - Ray: Prefect Ray integration - Fan out using subflows (without deployments) - If each unit of work is itself a flow, call the child flow multiple times from the parent. For large scale-out, prefer the deployment approach above so runs can be distributed to workers. - Fan out inside a single task/flow using native concurrency - For I/O-heavy work that doesn’t need per-item task visibility, you can parallelize inside a single task or flow using
asyncio.gather
or thread/process pools. This is simple but trades away Prefect-level visibility per item. - Orchestration-level fan out via work pools/work queues - Combine the deployment approach with work pools to scale horizontally and control concurrency centrally. - Concepts: Work pools overview Nice-to-have utilities when fanning in results: -
prefect.futures.as_completed
and
prefect.futures.wait
let you consume results as they finish or wait on groups. Quick question to steer you better: - Do you want to fan out within a single flow run (same process/machine), or spawn many separate flow runs that workers can pick up across your infrastructure?
o
@Marvin what are standard fan outs in prefect that are tracked? differentiate
m
thought for 90.1 seconds
Great question. Here’s a quick breakdown of the standard fan-out patterns in Prefect (3.x) and how they’re tracked/observed in the UI, plus when to use each. 1) Task-level fan out (submit/map) — tracked as task runs - What it is: Call
my_task.submit(...)
in a loop or use
my_task.map(iterable=...)
to spawn many task runs within a single flow run. - How it’s tracked: Each item becomes its own Task Run with its own state, logs, retries, caching, duration, etc., under the same parent Flow Run. - When to use: You want per-item task visibility and retries, but all work runs on the same execution environment (unless you’re using a distributed task runner). - Docs: Task runners concept (covers
.submit
and mapping) 2) Subflow fan out (call flows from a flow) — tracked as subflow runs - What it is: Call another
@flow
multiple times from a parent flow. - How it’s tracked: Each call appears as a Subflow Run under the parent Flow Run with its own state, logs, and timings. By default, subflows share the parent’s process/executor unless you explicitly run deployments. - When to use: You want hierarchical structure and clear ownership between components, but do not need to scale each unit independently across your infrastructure. - Docs: Flows and subflows 3) Deployment-based fan out (launch many flow runs) — tracked as separate flow runs - What it is: From a parent flow, call
run_deployment(...)
many times to create child Flow Runs that workers pick up from work pools/queues. - How it’s tracked: Each item is a full Flow Run. If
as_subflow=True
, they appear as children of the parent; if
False
, they are detached runs. Each run has independent infra, scaling, retries, and concurrency control. - When to use: Large scale-out across multiple machines or environments, strong isolation per item, and central orchestration controls (concurrency limits, pools, queues). - How-to and API: Run deployments, API reference 4) Distributed task runners (Dask/Ray) — tracked as task runs, executed on a cluster - What it is: Use Prefect’s Dask or Ray integration so your `submit`/`map` fan-out runs on a distributed cluster. - How it’s tracked: Still individual Task Runs in the parent Flow Run, but scheduled/executed on the cluster for scale/performance. - When to use: You want the convenience of task-level fan out (per-item retries/visibility) with distributed execution. - Docs: Prefect Dask, Prefect Ray 5) In-task concurrency (asyncio, threads, processes) — not tracked per unit of work - What it is: Inside a single task or flow, use
asyncio.gather
,
concurrent.futures
, or other concurrency to parallelize. - How it’s tracked: Only the single task/flow run is tracked; individual units are not visible as separate runs. No per-item retries/caching/lineage. - When to use: Lightweight I/O concurrency where you don’t need per-item observability or retries. Helpful utilities when fanning in results -
prefect.futures.as_completed
and
prefect.futures.wait
help manage groups of Prefect futures. API ref: Prefect futures Rule of thumb - Need per-item visibility and retries within one flow run? Use task fan out (`submit`/`map`), optionally with Dask/Ray for scale. - Need hierarchical separation but staying in-process? Use subflows. - Need to scale each item independently across infra, with work pools/queues and isolation? Use deployment-based fan out (
run_deployment
). If you share your target scale and infrastructure (single machine vs. cluster vs. workers), I can recommend the cleanest pattern and a concrete example.
o
@Marvin create a most minimal example for map and submit
m
thought for 62.6 seconds
Here are two tiny, self-contained examples in Prefect 3.x showing map and submit. Minimal map example
Copy code
from prefect import flow, task

@task
def square(x):
    return x * x

@flow
def mapped():
    nums = [1, 2, 3, 4]
    futures = square.map(nums)            # fan out to task runs
    results = [f.result() for f in futures]  # fan in results
    print(results)

if __name__ == "__main__":
    mapped()
Minimal submit example
Copy code
from prefect import flow, task

@task
def square(x):
    return x * x

@flow
def submitted():
    nums = [1, 2, 3, 4]
    futures = [square.submit(n) for n in nums]  # fan out to task runs
    results = [f.result() for f in futures]     # fan in results
    print(results)

if __name__ == "__main__":
    submitted()
Notes: - Both patterns create separate Task Runs you can observe in the UI. - Docs (task runners, submit/map): Task runners concept
o
@Marvin like this? from prefect import flow, task nums = [1, 2, 3, 4] @task def square(x): return x * x @flow def mapped(): futures = square.map(nums) # fan out to task runs results = [f.result() for f in futures] # fan in results print(results) @flow def submitted(): futures = [square.submit(n) for n in nums] # fan out to task runs results = [f.result() for f in futures] # fan in results print(results) if name == "__main__": mapped() submitted()
m
thought for 20.3 seconds
Yes — that works as-is in Prefect 3.x. Both flows will create separate flow runs; each `map`/`submit` call fans out into individual task runs, and your list comprehension fans them back in. Your example (valid/minimal):
Copy code
from prefect import flow, task

nums = [1, 2, 3, 4]

@task
def square(x):
    return x * x

@flow
def mapped():
    futures = square.map(nums)               # fan out
    results = [f.result() for f in futures]  # fan in
    print(results)

@flow
def submitted():
    futures = [square.submit(n) for n in nums]  # fan out
    results = [f.result() for f in futures]     # fan in
    print(results)

if __name__ == "__main__":
    mapped()
    submitted()
Tip: if you want results as they complete, you can iterate with
prefect.futures.as_completed(...)
. For background on how these run concurrently (and how to scale with different runners), see Task runners concept.