Nicholas Pini
10/31/2025, 8:41 AMsqlalchemy.exc.IntegrityError: (sqlalchemy.dialects.postgresql.asyncpg.IntegrityError) <class 'asyncpg.exceptions.UniqueViolationError'>: duplicate key value violates unique constraint "uq_task_run__flow_run_id_task_key_dynamic_key"
DETAIL: Key (flow_run_id, task_key, dynamic_key)=(c1349853-d476-41ae-8bab-4b32af3daec3, compute_model_lst-18df0754, 00cf3f29-f2d4-4b16-9ab5-c234790523a0) already exists.
what is it?Marvin
10/31/2025, 8:41 AMMarvin
10/31/2025, 8:51 AMuq_task_run__flow_run_id_task_key_dynamic_key ensures there’s only one task run per tuple of (flow_run_id, task_key, dynamic_key). The dynamic_key disambiguates dynamic/mapped tasks.
- Under concurrency, two server workers can occasionally attempt to insert the same task run simultaneously (e.g., the task run recorder handling duplicate/late events, retries after a transient DB error, or multiple API replicas). One insert wins; the other hits the unique constraint and logs this IntegrityError. The existing row remains—no data loss.
- If you only see this intermittently and your flows/tasks are not failing, you can consider it noise.
Why you might be seeing this
- Concurrency in the task-run recorder and API leads to rare races.
- Database backpressure/latency can amplify timing windows, causing duplicate attempts.
- Running multiple API pods without a durable messaging backend increases the risk of duplicate event handling.
The warnings you shared
- Warnings like CancellationCleanup took 50–70s … longer than its loop interval and similar RecentDeploymentsScheduler messages mean those background services are running slower than expected—commonly due to DB latency, lock contention, or insufficient server resources. This can correlate with the duplicate insert races you’re seeing.
What you can do
1) Upgrade to the latest 3.4.x (or newer)
We’ve made ongoing improvements to server services and DB interaction. If you can, upgrade and monitor whether the frequency drops. Releases: Prefect releases
2) If you run multiple API replicas, use Redis-backed messaging
The in-memory broker is fine for a single API process but can lead to duplicate event handling in multi-replica setups. With Redis, delivery and consumer coordination are more robust.
- Install prefect-redis in your server image
- Configure:
PREFECT_SERVER_EVENTS_MESSAGING_BROKER=prefect_redis.messaging
PREFECT_SERVER_EVENTS_MESSAGING_CACHE=prefect_redis.messaging
Docs: prefect-redis integration
3) Tune your DB connection pool for your load
If the API is waiting on a small connection pool, background services will slip past their loop intervals and increase contention windows.
- Consider setting (example values, adjust for your Postgres capacity):
PREFECT_SERVER_DATABASE_SQLALCHEMY_POOL_SIZE=20
PREFECT_SERVER_DATABASE_SQLALCHEMY_MAX_OVERFLOW=20
PREFECT_SERVER_DATABASE_SQLALCHEMY_POOL_TIMEOUT=30
PREFECT_SERVER_DATABASE_SQLALCHEMY_POOL_RECYCLE=3600
Settings reference: Prefect settings reference
4) Check Postgres health
- Monitor lock waits, slow queries, CPU/IO pressure.
- Ensure regular VACUUM/ANALYZE and appropriate instance sizing.
Related issue for context
- There’s active work/discussion around DB contention in server services; see this thread for deadlock symptoms in the task-run recorder: prefecthq/prefect#17767
A few quick questions to help tailor advice:
- Are you running more than one API/server replica?
- What Postgres version and instance size are you using?
- Do you see any task/flow failures, or is this only a log noise issue?
- Roughly how often do these unique constraint logs appear?