The v1 slice scheduling model was one of the most frequently misunderstood aspects of ADF. It was powerful once you internalized it, but it required internalization — you couldn't just look at it and understand what it was doing. ADF v2 replaces it with an explicit trigger model. Three trigger types, each with a clear purpose and a clear JSON definition.
Let's go through each one with actual configuration examples and the production scenarios where each type belongs.
Schedule Trigger
The schedule trigger is the simplest: run this pipeline on this cron-style schedule. It fires at a specified time and runs the pipeline. No time window is passed to the pipeline. The pipeline just runs.
{
"name": "DailyIngestTrigger",
"type": "ScheduleTrigger",
"typeProperties": {
"recurrence": {
"frequency": "Day",
"interval": 1,
"startTime": "2017-01-01T06:00:00Z",
"timeZone": "UTC",
"schedule": {
"hours": [6],
"minutes": [0]
}
}
},
"pipelines": [{
"pipelineReference": { "name": "DailyIngestPipeline" },
"parameters": {
"RunDate": "@trigger().scheduledTime"
}
}]
}
The schedule trigger exposes @trigger().scheduledTime — the time the trigger was supposed to fire, not the time it actually fired. This is the right value to use for run-date labeling because it's deterministic. If the trigger fires 2 minutes late due to service load, the scheduled time is still what you want in your data.
Use the schedule trigger when: you need a simple recurring pipeline that doesn't require time window awareness — daily report refresh, hourly cache invalidation, weekly data quality check.
Don't use the schedule trigger when: you need backfill handling, you need the pipeline to know which time window it's processing, or you need dependency tracking between time windows.
Tumbling Window Trigger
The tumbling window trigger is the schedule trigger with intelligence added. It fires on a schedule, but it carries the window start and end times as parameters, and it handles backfill automatically.
{
"name": "HourlyWindowTrigger",
"type": "TumblingWindowTrigger",
"typeProperties": {
"frequency": "Hour",
"interval": 1,
"startTime": "2017-01-01T00:00:00Z",
"endTime": "2099-12-31T23:59:59Z",
"delay": "00:05:00",
"maxConcurrency": 4,
"retryPolicy": {
"count": 3,
"intervalInSeconds": 30
}
},
"pipeline": {
"pipelineReference": { "name": "HourlyPartitionLoad" },
"parameters": {
"WindowStart": "@trigger().outputs.windowStartTime",
"WindowEnd": "@trigger().outputs.windowEndTime"
}
}
}
The startTime on a tumbling window trigger is meaningful in a way it isn't for a schedule trigger: the tumbling window trigger will generate a window for every period from startTime to now when it's first created or first enabled. Set startTime to today if you don't want backfill. Set it to last week if you want to backfill a week of hourly windows.
maxConcurrency controls how many windows run simultaneously during backfill. Set it high enough to complete the backfill in a reasonable time, but not so high that you saturate your source system or your data warehouse.
delay adds a wait between when the window closes and when the trigger fires. Useful for source systems that are slightly behind real time — a 5-minute delay on an hourly window gives the source system time to finish writing the hour's data before ADF starts reading it.
Use the tumbling window trigger when: you're loading time-partitioned data (daily partitions, hourly partitions), you need backfill capability, or your pipeline logic depends on knowing which time window it's processing. This is the right trigger for most analytics load patterns.
The dependency you can't do yet: ADF v2 doesn't have a native cross-pipeline dependency trigger — "run pipeline B when pipeline A completes successfully." You handle this inside a single pipeline using dependsOn between activities, or you use a Logic App / Azure Function to chain pipelines. This is a gap that the v1 slice model handled through dataset dependency propagation. V2 doesn't have an equivalent yet.
Event-Based Trigger
The event trigger fires when a blob is created or deleted in a specified Azure Blob Storage container and path pattern.
{
"name": "FileArrivalTrigger",
"type": "BlobEventsTrigger",
"typeProperties": {
"blobPathBeginsWith": "/raw-drop/blobs/vendor-files/",
"blobPathEndsWith": ".csv",
"events": ["Microsoft.Storage.BlobCreated"],
"scope": "/subscriptions/{subId}/resourceGroups/{rg}/providers/Microsoft.Storage/storageAccounts/{storageAccount}"
},
"pipelines": [{
"pipelineReference": { "name": "ProcessVendorFile" },
"parameters": {
"FileName": "@trigger().outputs.body.fileName",
"FolderPath": "@trigger().outputs.body.folderPath",
"StorageAccount": "@trigger().outputs.body.url"
}
}]
}
The event trigger passes the blob name, folder path, and full URL to the pipeline as parameters. Your pipeline knows exactly which file arrived — you don't need to write polling code or do a directory listing to find it.
This is the right answer for file-arrival-driven processing. In v1, the pattern was: configure a source dataset pointing at the folder, ADF polls for new files on the slice schedule. In v2 with event triggers: a blob lands, the trigger fires immediately, the pipeline processes that specific file. Lower latency, no polling overhead, no file discovery logic.
Watch out for: if files arrive in rapid succession (bulk uploads), you get one trigger execution per file. For a hundred files landing simultaneously, you get a hundred pipeline runs. Configure maxConcurrency carefully and make sure your downstream systems can handle the parallel load.
Side-by-Side Comparison
| Feature | Schedule | Tumbling Window | Event |
|---|---|---|---|
| Fires on schedule | Yes | Yes | No |
| Passes window times | No | Yes | No |
| Backfill support | No | Yes | No |
| Fires on file arrival | No | No | Yes |
| Retry on failure | Via pipeline | Built-in | Via pipeline |
Bottom Line
The v2 trigger model is more explicit and easier to reason about than the v1 slice model. The slice model was powerful but required internalization. Any engineer can read a schedule trigger definition and understand what it does. That matters for teams where multiple people maintain pipelines.
The missing piece is cross-pipeline dependency triggering. In v1 the slice model provided this through dataset availability propagation. In v2 you either keep dependent activities in a single pipeline (fine for most cases) or you build the coordination yourself. I expect Microsoft to address this — it's a real gap for complex orchestration patterns. For now, design your pipelines to be self-contained where possible and document the cases where you've built manual coordination. I'm here to help.