<@ULVA73B9P> i want to create work queue via the p...
# ask-marvin
a
@Marvin i want to create work queue via the python sdk. using prefect==3.2.12
m
thought for 386.1 seconds
Got it! In Prefect 3.2.12 you can create a work queue via the Python SDK using the orchestration client. Here are minimal examples for both async and sync clients. - Async (recommended in Prefect 3.x)
Copy code
from prefect.client.orchestration import get_client

async def main():
    async with get_client() as client:
        wq = await client.create_work_queue(
            name="my-queue",
            work_pool_name="my-pool",        # the pool must already exist
            description="Queue created via SDK",
            concurrency_limit=10,            # optional
            priority=1,                      # optional
            is_paused=False,                 # optional
        )
        print(wq)

# If running as a script:
# import asyncio; asyncio.run(main())
- Sync
Copy code
from prefect.client.orchestration import SyncPrefectClient

with SyncPrefectClient() as client:
    wq = client.create_work_queue(
        name="my-queue",
        work_pool_name="my-pool",        # the pool must already exist
        description="Queue created via SDK",
        concurrency_limit=10,            # optional
        priority=1,                      # optional
        is_paused=False,                 # optional
    )
    print(wq)
- Optionally, read a queue by name (useful for idempotent scripts)
Copy code
from prefect.client.orchestration import SyncPrefectClient

with SyncPrefectClient() as client:
    wq = client.read_work_queue_by_name(name="my-queue", work_pool_name="my-pool")
    print(wq)
Notes: - Ensure your environment can talk to your Prefect API (e.g.,
PREFECT_API_URL
and, if using Cloud,
PREFECT_API_KEY
). - The
work_pool_name
you pass must already exist. If you need help creating a work pool via SDK as well, let me know which type (process, Docker, Kubernetes) you want. Docs for reference: - Python client (PrefectClient) - Work pools concept - REST: Create work queue (for field reference)
a
@Marvin when working with work queues locally using docker worker, flows behave as expected and the concurrency limit is respected by prefect. however, when deploying the same code into ecs (using ecs worker rather than docker worker ) the limit is not respected. any other configuration i should take into account when deploying and using work queues inecs ?
m
thought for 503.5 seconds
Got it — this should work the same regardless of worker type, so if concurrency is respected with a Docker worker but not with an ECS worker, there’s likely a configuration mismatch. A few key points about concurrency in Prefect 3.x: - Server-side limits (enforced regardless of worker type) - Work pool concurrency limit: caps the number of concurrent flow runs across the entire pool. - Tag-based concurrency: caps task runs for specific tags. - Deployment-level concurrency (if you’re using it): caps concurrent flow runs per deployment. - Worker-local limit - The worker’s
--limit
flag only controls how many flow runs a single worker process executes concurrently. It is not a server-side limit. What to check for ECS 1) Are your Docker and ECS runs using the same work pool/queue? - If your ECS worker is pointing at a different pool (or queue), any concurrency limits you set on the Docker pool won’t apply. - Verify and compare: - List pools:
Copy code
prefect work-pool ls
- Inspect the ECS pool and confirm concurrency limit:
Copy code
prefect work-pool inspect <ecs-pool-name>
- If needed, set a pool-wide concurrency limit:
Copy code
prefect work-pool set-concurrency-limit <ecs-pool-name> 2
2) Are you relying on a worker-local limit on Docker but not setting it for ECS? - If your local Docker worker ran with
--limit 1
but your ECS worker runs without a limit, the ECS worker can launch multiple flow runs concurrently. - Start the ECS worker with a limit as well:
Copy code
prefect worker start -p <ecs-pool-name> --work-queue <queue-name> --limit 2
3) Are there multiple ECS workers polling the same pool/queue? - If you’ve got several ECS worker tasks/services running, each with a
--limit
, your aggregate concurrency across workers could be higher than expected. Server-side pool or deployment concurrency will still cap, but if you relied only on
--limit
when testing locally, you’ll see more runs in ECS. - If you want a hard cap across workers, prefer setting the pool’s concurrency limit (server-side) in addition to any worker-local limits. 4) If you’re using tag-based concurrency for tasks - Confirm your task tags are present in the deployed code and that the concurrency limits exist:
Copy code
prefect concurrency-limit ls
- When enforced, you’ll see tasks enter an AwaitingConcurrencySlot state in the UI until a slot frees up. 5) If you’re using an ECS “push” work pool instead of a polling worker - There’s no
prefect worker start
in push mode; the server launches ECS tasks directly. Worker-local limits won’t apply here. Use a work pool concurrency limit (and/or deployment concurrency) for caps. If you can share a bit more detail, I can be more precise: - Which concurrency limit are you relying on: work pool, deployment, or tag-based? - Are you using an ECS pull worker (
prefect worker start -p <ecs-pool>
) or an ECS push work pool? - How many ECS worker replicas are running? - Pool/queue names for Docker vs ECS, and any
--limit
you used locally. Helpful docs: - Work pools and workers: Work pools and Workers - Work pool concurrency: Work pool CLI (set-concurrency-limit) - Tag-based concurrency: Tag-based concurrency limits - ECS guidance: ECS work pools/workers
a
- Which concurrency limit are you relying on: work pool, deployment, or tag-based? -> using the work queue model. this is the code: async def create_work_queue(namestr,pool namestr,concurrency_limit:int=1)->UUID: """Creates a work queue and returns the queue ID. Args: name (str): The name of the work queue. pool_name (str): The name of the work pool to associate with the queue. concurrency_limit (int, optional): The concurrency limit for the work queue. Defaults to 1. """ async with get_client() as client: try: # Try to read existing queue existing = await client.read_work_queue_by_name(name, work_pool_name=pool_name) print(f"Work queue '{name}' already exists (ID: {existing.id})") return existing.id except ObjectNotFound: # Not found → create it wq = await client.create_work_queue( name=name, work_pool_name=pool_name, concurrency_limit=concurrency_limit, ) print(f"Created new work queue: {wq.name} ({wq.id})") return wq.id - Are you using an ECS pull worker (prefect worker start -p <ecs-pool>) or an ECS push work pool? : -> pull worker - How many ECS worker replicas are running? -> one. which handles all the flows (3) two of them have their own dedicated work queueus ( new flow runs occur via automation ) btw, trying to force deployment of the worker service on ecs, just in case
@Marvin - Which concurrency limit are you relying on: work pool, deployment, or tag-based? -> using the work queue model. this is the code: async def create_work_queue(namestr,pool namestr,concurrency_limit:int=1)->UUID: """Creates a work queue and returns the queue ID. Args: name (str): The name of the work queue. pool_name (str): The name of the work pool to associate with the queue. concurrency_limit (int, optional): The concurrency limit for the work queue. Defaults to 1. """ async with get_client() as client: try: # Try to read existing queue existing = await client.read_work_queue_by_name(name, work_pool_name=pool_name) print(f"Work queue '{name}' already exists (ID: {existing.id})") return existing.id except ObjectNotFound: # Not found → create it wq = await client.create_work_queue( name=name, work_pool_name=pool_name, concurrency_limit=concurrency_limit, ) print(f"Created new work queue: {wq.name} ({wq.id})") return wq.id - Are you using an ECS pull worker (prefect worker start -p <ecs-pool>) or an ECS push work pool? : -> pull worker - How many ECS worker replicas are running? -> one. which handles all the flows (3) two of them have their own dedicated work queueus ( new flow runs occur via automation ) btw, trying to force deployment of the worker service on ecs, just in case
m
thought for 277.5 seconds
Thanks for the details — that helps a lot. Given: - You’re using a pull ECS worker (one replica) - You’re setting per-queue concurrency limits via the client - Two flows each have their own dedicated queues and new runs are created via automations The most common reasons “queue concurrency” appears to be ignored in this setup are: 1) You’re seeing concurrency across different queues (expected) - Work queue concurrency is per-queue. If each dedicated queue has
concurrency_limit=1
, you can still see multiple flows running at the same time if they come from different queues (e.g., one run from Queue A and one from Queue B). - If you want a global cap across all flows/queues, set a work pool concurrency limit (server-side cap across the pool) or run the worker with
--limit 1
(local cap on a single worker). Pool concurrency is the more robust global cap. 2) The queue already existed without a limit (your code doesn’t update it) - Your function returns immediately if the queue exists, so any previously-created queue without a concurrency limit will stay that way. - Add an update when the queue exists:
Copy code
existing = await client.read_work_queue_by_name(name, work_pool_name=pool_name)
  if existing.concurrency_limit != concurrency_limit:
      await client.update_work_queue(id=existing.id, concurrency_limit=concurrency_limit)
  return existing.id
- You can verify the current value with:
Copy code
prefect work-queue inspect <queue-name> --pool <pool-name>
3) The ECS worker is pulling from more queues than you expect - If you do not pass
--work-queue
, the worker will pull from all queues in the pool, which can increase concurrency across queues. - Ensure you pin the worker to the intended queues and optionally cap its local concurrency:
Copy code
prefect worker start -p <ecs-pool> \
    --work-queue <queue-A> \
    --work-queue <queue-B> \
    --limit 1
Note:
--limit
caps concurrent flow runs for this worker instance only. Use a work pool concurrency limit to cap globally across multiple workers. 4) Version mismatch between local and ECS images - Make sure your local and ECS environments are running the same Prefect 3.x version. Check with:
Copy code
prefect version
If ECS is on an older build, update the image and redeploy the worker service. What I recommend you do next - Confirm the current queue concurrency values:
Copy code
prefect work-queue inspect <queue-A> --pool <pool-name>
  prefect work-queue inspect <queue-B> --pool <pool-name>
If needed, set them explicitly:
Copy code
prefect work-queue set-concurrency-limit <queue-A> 1 --pool <pool-name>
  prefect work-queue set-concurrency-limit <queue-B> 1 --pool <pool-name>
- Decide on your desired behavior: - Per-queue cap only (each queue limited independently): keep per-queue limits and ensure your worker pulls only the desired queues. - Global cap across all flows in the pool: set a work pool–level concurrency limit:
Copy code
prefect work-pool set-concurrency-limit <ecs-pool-name> 1
This will cap concurrent flow runs across all queues in the pool. - If you still observe more than 1 concurrent run from the same single queue, that would be unexpected — let me know and we can dig deeper (please share Prefect versions, pool/queue names, and a couple flow run IDs). Docs for reference: - Work queues CLI: prefect work-queue - Work pools and pool concurrency: prefect work-pool - Worker concepts: Workers - ECS guidance: ECS work pools/workers A couple of clarifying questions to ensure we pin it down: - Are you seeing more than 1 concurrent run from the same queue, or just more than 1 total across different queues? - What
prefect version
is running locally vs in your ECS container?
- What exact
prefect worker start
command/flags are you using on ECS?
a
@Marvin spot on. this is the fix: if existing.concurrency_limit != concurrency_limit: await client.update_work_queue(id=existing.id, concurrency_limit=concurrency_limit) ... when creating my deployment, this line creats a work queue with a default value of None: deployment_args["work_queue_name"] = f"{pool_type}-queue" .
m
thought for 18.6 seconds
Awesome — glad you found it! You’re exactly right: when a deployment references a
work_queue_name
that doesn’t exist, Prefect will auto-create the queue with
concurrency_limit=None
(i.e., unlimited). So if you want a per-queue cap, you must set it explicitly — either right after creation or by updating an existing queue. A small, idempotent helper you can run before/after deployment to ensure the queue has the limit you expect:
Copy code
from prefect.client.orchestration import get_client
from prefect.client.schemas.objects import ObjectNotFound

async def ensure_work_queue(name: str, pool_name: str, concurrency_limit: int) -> None:
    async with get_client() as client:
        try:
            wq = await client.read_work_queue_by_name(name, work_pool_name=pool_name)
            if wq.concurrency_limit != concurrency_limit:
                await client.update_work_queue(id=wq.id, concurrency_limit=concurrency_limit)
        except ObjectNotFound:
            await client.create_work_queue(
                name=name,
                work_pool_name=pool_name,
                concurrency_limit=concurrency_limit,
            )
Suggested flow for CI/CD or infra provisioning: - Create/update the work queue(s) with the desired concurrency first (using the function above or CLI). - Then create/update your deployment(s) that reference those queue names. CLI alternatives if you prefer:
Copy code
# Set per-queue concurrency
prefect work-queue set-concurrency-limit <queue-name> 1 --pool <pool-name>

# Inspect the current setting
prefect work-queue inspect <queue-name> --pool <pool-name>
Docs: - Work queue CLI: prefect work-queue - Work pools (for pool-wide caps if you need a global limit): prefect work-pool Tip: If you need a global cap across all queues in that pool, add a pool-level concurrency limit as well:
Copy code
prefect work-pool set-concurrency-limit <pool-name> 1
Shout if you want help wiring this into your deployment script so the order is guaranteed and there’s no race with automations kicking off runs.
a
@Marvin amazing. thx
m
thought for 5.2 seconds
You’re welcome! Glad it helped. If you want, I can help wire the queue update into your CI/CD or double-check your ECS worker start command.