Learn how to control concurrency and apply rate limits using Prefect’s provided utilities.
Use global concurrency limits to control how many operations run simultaneously, and rate limits to control how frequently operations can start. This guide shows you how to create, manage, and use these limits in your workflows.For a deeper understanding of how global concurrency limits work and when to use them, see the global concurrency limits concept page.
You can create, read, edit, and delete concurrency limits through the Prefect UI, CLI, Python SDK, Terraform, or API.When creating a concurrency limit, you can specify:
Name: How you’ll reference the limit in your code (no special characters like /, %, &, >, <)
Concurrency Limit: Maximum number of slots available
Slot Decay Per Second: Rate at which slots are released (required for rate limiting)
Active: Whether the limit is enforced (true) or disabled (false)
prefect gcl delete my-concurrency-limitAre you sure you want to delete global concurrency limit 'my-concurrency-limit'? [y/N]: yDeleted global concurrency limit with name 'my-concurrency-limit'.
See all available commands and options with prefect gcl --help.
Control concurrent operations using the concurrency context manager. Choose the synchronous or asynchronous version based on your code.
By default, if a concurrency limit doesn’t exist or lease renewal fails, a warning is logged but execution continues.Use strict=True to raise an error instead. This ensures concurrency enforcement is guaranteed, useful for preventing resource exhaustion like database connection pool limits.Use raise_on_lease_renewal_failure=False to allow long-running tasks to tolerate transient lease renewal errors even when strict=True is set.
import asynciofrom prefect import flow, taskfrom prefect.concurrency.asyncio import concurrencyfrom prefect.futures import wait@taskasync def process_data(x, y): async with concurrency("database", occupy=1): return x + y@flowdef my_flow(): futures = [] for x, y in [(1, 2), (2, 3), (3, 4), (4, 5)]: futures.append(process_data.submit(x, y)) wait(futures)if __name__ == "__main__": my_flow()
In both examples, the concurrency context manager occupies one slot on the database concurrency limit. If no slots are available, execution blocks until a slot becomes available.
Enable strict mode to ensure errors are raised if the limit doesn’t exist or if lease renewal fails:
from prefect import flow, taskfrom prefect.concurrency.sync import concurrency@taskdef process_critical_data(x, y): # strict=True ensures this task fails fast if concurrency can't be enforced with concurrency("database", occupy=1, strict=True): return x + y@flowdef critical_flow(): process_critical_data(1, 2)
Use raise_on_lease_renewal_failure to control lease renewal behavior independently of strict. For example, use strict=True to require the limit exists at acquisition time, while allowing long-running tasks to continue through transient renewal errors:
from prefect.concurrency.sync import concurrencywith concurrency("database", occupy=1, strict=True, raise_on_lease_renewal_failure=False): # Slot acquisition requires the limit to exist (strict=True), # but transient lease renewal failures won't terminate execution. long_running_operation()
Prevent exhausting your database connection pool by limiting concurrent queries:
from prefect import flow, task, concurrencyfrom myproject import db@taskdef database_query(query): # Here we request a single slot on the 'database' concurrency limit. This # will block in the case that all of the database connections are in use # ensuring that we never exceed the maximum number of database connections. with concurrency("database", occupy=1): result = db.execute(query) return result@flowdef my_flow(): queries = ["SELECT * FROM table1", "SELECT * FROM table2", "SELECT * FROM table3"] for query in queries: database_query.submit(query)if __name__ == "__main__": my_flow()
For more information about how global concurrency limits work and when to use them versus other concurrency controls, see the global concurrency limits concept page.
Was this page helpful?
Assistant
Responses are generated using AI and may contain mistakes.