Mugdha Sonare
12/10/2025, 4:23 PMMarvin
12/10/2025, 4:23 PMMarvin
12/10/2025, 4:28 PM{
"name": "Run B when A starts (deduped)",
"enabled": true,
"trigger": {
"type": "event",
"posture": "Reactive",
"expect": ["prefect.flow-run.Scheduled"], // or Pending/Running based on your need
"match": {
"prefect.resource.id": "<deployment-A-id>"
},
"threshold": 1,
"within": 30
},
"actions": [
{
"type": "run-deployment",
"source": "selected",
"deployment_id": "<deployment-B-id>"
}
]
}
- Multiple A “Scheduled” events that arrive within 30 seconds cause a single “Run deployment B” action when the window closes.
- Docs: Event triggers and Creating automations
Complementary: limit B so duplicates are dropped or queued
- Set a deployment-level concurrency limit on B to 1 and choose the collision strategy you prefer:
- ENQUEUE: only one B runs; the rest wait in a queue
- CANCEL_NEW: only one B runs; additional triggers are immediately canceled
- You can set this when calling `deploy()`:
from prefect.client.schemas.objects import ConcurrencyLimitConfig
from prefect import flow
@flow
def b():
...
b.deploy(
name="B",
concurrency_limit=ConcurrencyLimitConfig(
limit=1,
# choose one:
# collision_strategy="ENQUEUE",
# or
# collision_strategy="CANCEL_NEW",
grace_period_seconds=300
),
)
- Docs: Deployments (concurrency limiting)
Advanced (if you need a global gate)
- Use a global concurrency limit (name-based) to ensure only one B’s critical section runs at a time, regardless of trigger source:
from prefect.concurrency.sync import concurrency
from prefect import flow
@flow
def b():
with concurrency("run-b-once", occupy=1):
# guarded work here
...
- Docs: Global concurrency limits
Notes
- The “Run deployment” action itself is idempotent only when the same idempotency key is reused; separate automation invocations won’t share a key by default. That’s why the threshold/within window is the best way to collapse duplicate triggers up front. Docs on run_deployment: Run deployments
Quick check to tailor this:
- Which state are you listening for on A (Scheduled, Pending, Running)?
- Do you want extra triggers to be dropped, or queued to run later?Mugdha Sonare
12/10/2025, 4:30 PMMarvin
12/10/2025, 4:30 PMMarvin
12/10/2025, 4:30 PMfrom prefect import flow
from prefect.client.schemas.objects import ConcurrencyLimitConfig
@flow
def my_flow():
...
my_flow.deploy(
name="my-deployment",
concurrency_limit=ConcurrencyLimitConfig(
limit=1,
collision_strategy="CANCEL_NEW", # or "ENQUEUE"
grace_period_seconds=300 # optional
),
)
Notes
- Using an integer shorthand like concurrency_limit=1 defaults to ENQUEUE. If you want duplicates dropped, specify CANCEL_NEW.
- This works regardless of why the deployment was triggered (manual, schedule, automation, API).
- If you also want to collapse multiple events into a single action before creating extra runs, add a small “within” window on your Automation trigger; but deployment concurrency = 1 is the universal guardrail.
Docs
- Deployments (concurrency limiting): Deployments
- Automations and event triggers (threshold/within): Event triggers
Want me to suggest exact settings for your deployment(s) and whether to use ENQUEUE or CANCEL_NEW based on your workflow?Mugdha Sonare
12/10/2025, 4:51 PMMarvin
12/10/2025, 4:51 PMMarvin
12/10/2025, 4:52 PMAwaitingConcurrencySlot and will run after the first finishes.
- CANCEL_NEW: one run acquires the slot and starts; the other is immediately canceled.
“Exactly at the same time” still resolves to one winner — the server serializes the slot claim in a transaction so they won’t both run.
If you do not set a concurrency limit on the deployment, both runs will start.
Optional extra protection: add a small within window on your Automation trigger to collapse back-to-back events into a single action, but the deployment concurrency limit is the definitive guardrail.
Docs:
- Deployment concurrency limits
- Automation event triggers (threshold/within)