Mehdi Lallouache
04/16/2026, 11:35 AMMarvin
04/16/2026, 11:35 AMMarvin
04/16/2026, 11:38 AMwait_for so later tasks cannot start until earlier ones finish.
Examples
1) Prioritize some tasks over others
from prefect import flow, task
from prefect_dask import DaskTaskRunner
import dask
@task
def do_work(i):
return i
@flow(task_runner=DaskTaskRunner(cluster_kwargs={"n_workers": 3, "processes": True}))
def my_flow():
hi = []
with dask.annotate(priority=10): # higher priority → runs first when workers free
hi = [do_work.submit(i) for i in range(0, 5)]
lo = []
with dask.annotate(priority=0): # default priority
lo = [do_work.submit(i) for i in range(5, 15)]
return [f.result() for f in hi + lo]
2) Enforce strict ordering between tasks
from prefect import flow, task
from prefect_dask import DaskTaskRunner
@task
def step(i):
return i
@flow(task_runner=DaskTaskRunner(cluster_kwargs={"n_workers": 3, "processes": True}))
def my_flow():
a = step.submit(1)
# b will not start until a finishes
b = step.submit(2, wait_for=[a])
return a.result(), b.result()
Key takeaways
- Submission order alone is not a guarantee of execution order.
- Use dask.annotate(priority=...) to influence which queued tasks get picked first.
- Use wait_for=[...] for deterministic ordering.
Links
- Prefect Dask integration: Prefect + Dask docs
- Dask task annotations (priority): Dask annotations: priorityMehdi Lallouache
04/16/2026, 1:05 PMMarvin
04/16/2026, 1:05 PMMarvin
04/16/2026, 1:08 PMfrom dask.distributed import annotate
from prefect import flow, task
from prefect_dask import DaskTaskRunner
@task
def work(i):
...
@flow(task_runner=DaskTaskRunner(cluster_kwargs={"n_workers": 2, "processes": True}))
def f():
with annotate(priority=10):
hi = [work.submit(i) for i in range(3)]
with annotate(priority=0):
lo = [work.submit(i) for i in range(3, 10)]
return [f.result() for f in hi + lo]
- Important: even with priorities, Dask will only choose among “ready” (dependency-free) tasks and it will not preempt already-running tasks. So you’ll see the effect mainly as workers become free.
Current Prefect limitation to be aware of
- Prefect 3 does not expose a first-class way to pass priority via task.submit or DaskTaskRunner.submit. If the runner/client passes an explicit priority=0 to Client.submit (common), it will override the distributed.annotate context. In that case, all tasks effectively have the same priority.
- For strict ordering, prefer wait_for dependencies:
b = work.submit(2, wait_for=[a])
- For “soft” prioritization without true Dask priority, submit in phases: enqueue high-priority tasks first, then the rest.
If you can share a minimal snippet (how you imported annotate, your flow/task definitions, and your DaskTaskRunner config), I can pinpoint whether the issue is the annotate import, Prefect overriding priority, or something else (e.g., tasks already running, resource constraints, or fairness windows).
Links
- Prefect + Dask integration: Prefect + Dask docs
- Dask task annotations (note the difference between dask.annotate and distributed.annotate): Dask annotationsMarvin
04/16/2026, 1:14 PMMarvin
04/16/2026, 1:16 PMdask.annotate(...) (from the top-level dask module). That only affects Dask collections/delayed graphs and not distributed.Client.submit.
- Even if you switch to from dask.distributed import annotate, Prefect’s Dask integration currently forwards an explicit priority=0 to Dask’s Client.submit for each task. In Dask, an explicit priority argument overrides any annotate context. Net effect: your annotations don’t change scheduling order.
Why that matches what you saw
- All tasks end up with priority 0 at the Dask scheduler, so the scheduler just runs ready tasks as workers free up. Submission order may appear to matter a bit, but it’s not guaranteed and won’t reflect your annotated priorities.
Practical ways to get the desired behavior today
- Stage submissions (simple and effective):
- Submit the “high-priority” tasks first, then submit the rest after a short delay or after gathering the high-priority futures.
- Because m > n, the first n submitted tend to start filling worker slots.
futures = []
# submit critical tasks first
for x in critical_items:
futures.append(bar.submit(x=x))
# optionally wait for critical tasks to complete before submitting the rest
for f in futures:
f.result()
# now submit the rest
rest = [bar.submit(x=x) for x in other_items]
for f in rest:
f.result()
- Enforce strict ordering via dependencies:
- Gate the “low-priority” tasks so they can’t start until the high-priority set completes.
hi = [bar.submit(x=x) for x in critical_items]
lo = [bar.submit(x=x, wait_for=hi) for x in other_items]
# collect results if needed
for f in hi + lo:
f.result()
- If you must use true Dask priorities:
- This requires bypassing the default behavior. Two options:
1) Subclass DaskTaskRunner to accept and pass priority through to the underlying client.
2) Call the runner’s client directly and pass priority (advanced; you’ll need to handle results carefully since Prefect normally returns PrefectDaskFuture objects).
If you want, I can share a minimal custom runner that adds a priority kwarg and preserves Prefect’s usual future/result behavior.
Notes
- Dask does not preempt running tasks. If lower-priority tasks are already running, your higher-priority ones will wait until workers free up.
- For “soft” control, staging submissions is usually sufficient and simplest.
Refs
- Prefect + Dask integration: Prefect + Dask docs
- Dask annotations (use distributed.annotate for scheduler hints, though note the override caveat above): Dask annotationsMehdi Lallouache
04/16/2026, 1:18 PMMarvin
04/16/2026, 1:19 PMMarvin
04/16/2026, 1:19 PMdask.annotate(...), but in Prefect 3.x with DaskTaskRunner the priority you annotate does not take effect because Prefect’s Dask client sends an explicit priority=0 to distributed.Client.submit(...) for every task. In Dask, an explicit priority kwarg overrides any annotation context. So even distributed.annotate(priority=...) won’t help with the current integration.
Why the docs say that:
- That example predates the current Prefect 3.x Dask integration (or applies to using Dask directly without Prefect’s submission wrapper). It’s effectively out-of-date for Prefect 3.x’s DaskTaskRunner behavior.
Workarounds you can use now:
- Simple and effective: submit in the order you want to run (batch higher-“priority” first). In your case, sort by len(x) descending:
@flow(task_runner=DaskTaskRunner(cluster_kwargs={"n_workers": 20, "threads_per_worker": 1}))
def foo(a):
futures = []
for x in sorted(a, key=len, reverse=True):
futures.append(bar.submit(x=x))
for f in futures:
f.result()
- Strict sequencing: gate low-priority tasks with wait_for so they cannot start until the high-priority batch completes.
hi = [bar.submit(x=x) for x in high_items]
lo = [bar.submit(x=x, wait_for=hi) for x in low_items]
for f in hi + lo:
f.result()
- Advanced: subclass DaskTaskRunner to pass a priority kwarg through to the underlying client, or call the runner’s client directly with priority=... (happy to share a minimal example if you want to go this route).
If you can share your Prefect/prefect-dask/Distributed versions, I can double-check there’s not a version-specific nuance:
python -c "import prefect, prefect_dask, distributed; print(prefect.__version__, prefect_dask.__version__, distributed.__version__)"
If you’d like, I can also open a docs issue to correct that section so it reflects current behavior. References:
- Prefect + Dask docs: Prefect + Dask integration
- The page you cited: Prefect Dask “with Prefect”
- Dask annotations (why explicit kwargs override): Dask annotationsMehdi Lallouache
04/16/2026, 1:21 PMMarvin
04/16/2026, 1:21 PMMarvin
04/16/2026, 1:22 PMMehdi Lallouache
04/20/2026, 9:12 AMMarvin
04/20/2026, 9:12 AMMarvin
04/20/2026, 9:13 AM### Affected page
<https://docs.prefect.io/integrations/prefect-dask#with-prefect>
### Summary
The docs suggest using `dask.annotate(priority=...)` to influence task execution order when running with Prefect’s DaskTaskRunner. In Prefect 3.x, this has no effect because Prefect’s Dask client submits tasks to Dask with an explicit `priority=0`, which overrides any annotation context. As a result, annotated priorities are ignored and users see no change in scheduling order.
### Reproduction (minimal)
from prefect import flow, task
from prefect_dask import DaskTaskRunner
import dask
@task
def bar(x):
return len(x)
@flow(
task_runner=DaskTaskRunner(
cluster_kwargs={"n_workers": 20, "threads_per_worker": 1, "memory_limit": "20GiB", "silence_logs": False}
),
log_prints=True
)
def foo(a):
futures = []
for x in a:
with dask.annotate(priority=len(x)):
futures.append(bar.submit(x=x))
return [f.result() for f in futures]
# Expected (per docs): higher-priority tasks start first
# Actual: no observable prioritization; tasks are scheduled as workers free up
### Expected behavior
Annotated tasks with higher `priority` should be scheduled ahead of lower-priority tasks.
### Actual behavior
No prioritization effect observed with DaskTaskRunner; all tasks appear to run with the same priority.
### Why this happens (current behavior)
- `DaskTaskRunner.submit(...)` does not expose a `priority` argument.
- `PrefectDaskClient.submit(...)` passes an explicit `priority` keyword (default 0) to `distributed.Client.submit(...)`.
- In Dask, an explicit `priority=` kwarg overrides any `annotate(...)` context, so annotations do not apply here.
### Proposed doc changes
- Update <https://docs.prefect.io/integrations/prefect-dask#with-prefect> to state that `dask.annotate(priority=...)` (and even `distributed.annotate`) will not affect task priority when using Prefect’s DaskTaskRunner in 3.x.
- Provide workarounds:
- Use dependency gating with `wait_for` for strict ordering.
- Stage submissions (enqueue high-priority batch first, then others) for soft prioritization.
- Optionally note a future enhancement: expose a `priority` parameter in `Task.submit(...)` and/or `DaskTaskRunner.submit(...)` that is forwarded to Dask.
### Workarounds today
- Submit higher-priority tasks first; submit the rest after (or gate them with `wait_for`).
- For strict order: `low = [bar.submit(x, wait_for=high)]`.
### Environment
- Prefect: 3.x
- prefect-dask: (version)
- dask/distributed: (versions)
- Python: (version)
- OS: (OS)
### Related links
- Docs page: <https://docs.prefect.io/integrations/prefect-dask#with-prefect>
- Dask annotations: <https://distributed.dask.org/en/stable/annotations.html>
- Prior discussion capturing the discrepancy: <https://github.com/PrefectHQ/prefect/discussions/21568>
If you want, share your exact versions and I’ll tailor the issue text further. Also, if you prefer, I can leave a note on the discussion asking maintainers to convert it to an issue.Mehdi Lallouache
04/20/2026, 9:19 AMMarvin
04/20/2026, 9:19 AMMarvin
04/20/2026, 9:20 AM### Environment
- Prefect: 3.4.25
- prefect-dask: 0.3.6
- dask: <fill-in>
- distributed: <fill-in>
- Python: <fill-in>
- OS: <fill-in>Mehdi Lallouache
04/20/2026, 9:21 AMMarvin
04/20/2026, 9:21 AMMarvin
04/20/2026, 9:21 AM### Affected page
<https://docs.prefect.io/integrations/prefect-dask#with-prefect>
### Summary
The docs suggest using `dask.annotate(priority=...)` to influence task execution order when running with Prefect’s DaskTaskRunner. In Prefect 3.x, this has no effect because Prefect’s Dask client submits tasks to Dask with an explicit `priority=0`, which overrides any annotation context. As a result, annotated priorities are ignored and users see no change in scheduling order.
### Reproduction (minimal)
from prefect import flow, task
from prefect_dask import DaskTaskRunner
import dask
@task
def bar(x):
print(f"running {x}")
return x
@flow(
task_runner=DaskTaskRunner(
cluster_kwargs={
"n_workers": 20,
"threads_per_worker": 1,
"memory_limit": "20GiB",
"silence_logs": False,
}
),
log_prints=True,
)
def foo(a):
futures = []
for x in a:
# Per docs, higher priority should run sooner; here we use len(x) as a proxy
with dask.annotate(priority=len(x)):
futures.append(bar.submit(x=x))
return [f.result() for f in futures]
if __name__ == "__main__":
# Expectation from docs: longer strings (higher priority) should run first
print(foo(["a", "bbbbbbbb", "cc", "dddd", "eee", "fffffffff", "g"]))
# Expected (per docs): tasks with higher `priority` start first.
# Actual: no prioritization effect; tasks are scheduled as workers free up.
### Expected behavior
Annotated tasks with higher `priority` should be scheduled ahead of lower-priority tasks.
### Actual behavior
No prioritization effect observed with DaskTaskRunner; all tasks appear to run with the same priority.
### Why this happens (current behavior)
- `DaskTaskRunner.submit(...)` does not expose a `priority` argument.
- `PrefectDaskClient.submit(...)` passes an explicit `priority` kwarg (default 0) to `distributed.Client.submit(...)`.
- In Dask, an explicit `priority=` kwarg overrides any `annotate(...)` context, so annotations do not apply here.
### Proposed doc changes
- Update <https://docs.prefect.io/integrations/prefect-dask#with-prefect> to state that `dask.annotate(priority=...)` (and even `distributed.annotate`) will not affect task priority when using Prefect’s DaskTaskRunner in 3.x.
- Provide workarounds:
- Use dependency gating with `wait_for` for strict ordering.
- Stage submissions (enqueue high-priority batch first, then others) for soft prioritization.
- Optionally note a future enhancement: expose a `priority` parameter in `Task.submit(...)` and/or `DaskTaskRunner.submit(...)` that is forwarded to Dask.
### Workarounds today
- Submit higher-priority tasks first; submit the rest after (or gate them with `wait_for`).
- For strict order: `low = [bar.submit(x, wait_for=high)]`.
### Environment
- Prefect: 3.4.25
- prefect-dask: 0.3.6
- dask: <fill-in>
- distributed: <fill-in>
- Python: <fill-in>
- OS: <fill-in>
### Related links
- Docs page: <https://docs.prefect.io/integrations/prefect-dask#with-prefect>
- Dask annotations: <https://distributed.dask.org/en/stable/annotations.html>
- Prior discussion capturing the discrepancy: <https://github.com/PrefectHQ/prefect/discussions/21568>