Triggers in ADF: Schedule, Tumbling Window and Event

The four Azure Data Factory trigger types — schedule, tumbling window, storage event and custom event — compared on what they guarantee, with a decision table, trigger JSON you can adapt, and the settings that cause missed or duplicate runs.

GloryTecks Team

Training & Placement Faculty

24 May 2026 7 min read

Last updated: 24 September 2026

Share:

A pipeline in Azure Data Factory does nothing until something starts it. Apart from a manual run, that something is a trigger, and the trigger type decides more than timing. It decides whether missed periods are ever processed, whether failed runs are retried, and how many runs can overlap. ADF has three families of trigger: schedule, tumbling window and event-based, with event-based split into storage events and custom events (Microsoft Learn: pipeline execution and triggers).

The four trigger types

TriggerStarts a run when…Pipelines per triggerTypical use
ScheduleA wall-clock time arrives (every 15 minutes, daily at 06:00, Mondays and Thursdays…)Many — and one pipeline can have many triggersJobs that only need to run "now, on a timetable"
Tumbling windowA fixed-size, non-overlapping time window closesExactly oneLoading data period by period, where every period must be processed once
Storage eventA blob is created or deleted in a storage accountManyProcessing files as they land
Custom eventAn event arrives on an Event Grid custom topicManyStarting a pipeline from another application or system

Schedule vs tumbling window: the difference that matters

Both fire on a clock, so they look interchangeable in the UI. They are not. A schedule trigger is fire-and-forget: its run is marked successful as soon as the pipeline run starts, whatever happens next. A tumbling window trigger run waits for the pipeline and takes on its outcome, so a failed or cancelled pipeline shows as a failed or cancelled window.

BehaviourTumbling windowSchedule
Past periods (backfill)Supported: set a start time in the past and every missed window runsNot supported: only the current time and the future
GapsNone: every window from the start date is scheduledPossible
RetriesConfigurable retry policy (default 0)None
ConcurrencyYou set it: 1 to 50 simultaneous windowsNot configurable
Window boundaries in the pipelinetrigger().outputs.windowStartTime and windowEndTimeOnly trigger().scheduledTime and trigger().startTime

Comparison from Microsoft Learn: trigger type comparison. The practical rule: if your pipeline loads "the data for a period" (yesterday's orders, the last hour of logs), use a tumbling window and pass the window boundaries into the query. If it simply needs to run at a time, and a skipped run does not leave a hole in your data, a schedule trigger is enough.

Passing the window into the pipeline

A tumbling window trigger maps its window boundaries onto pipeline parameters. The pipeline then filters its source on exactly that range, so re-running a window re-loads the same slice instead of "whatever arrived since the last run":

json
{
  "name": "HourlyOrdersWindow",
  "properties": {
    "type": "TumblingWindowTrigger",
    "typeProperties": {
      "frequency": "Hour",
      "interval": 1,
      "startTime": "2026-09-01T00:00:00Z",
      "delay": "00:10:00",
      "maxConcurrency": 4,
      "retryPolicy": { "count": 2, "intervalInSeconds": 300 }
    },
    "pipeline": {
      "pipelineReference": { "type": "PipelineReference", "referenceName": "LoadOrders" },
      "parameters": {
        "windowStart": { "type": "Expression", "value": "@{trigger().outputs.windowStartTime}" },
        "windowEnd": { "type": "Expression", "value": "@{trigger().outputs.windowEndTime}" }
      }
    }
  }
}

Inside LoadOrders, the source query uses those parameters, for example WHERE order_ts >= '@{pipeline().parameters.windowStart}' AND order_ts < '@{pipeline().parameters.windowEnd}'. The half-open range (>= start, < end) stops a row on the boundary being loaded twice. delay holds each window back for ten minutes after it closes, which gives late-arriving rows time to land (Microsoft Learn: tumbling window trigger).

Decide the window size before you create the trigger

The frequency and interval of a tumbling window trigger cannot be changed after it is created. Frequency can be minutes, hours or months. And a start time in the past produces a backfill: the trigger generates one run per missed window, in parallel up to maxConcurrency, before any future windows. A start date a year back on an hourly trigger is almost 9,000 pipeline runs.

Backfill ordering, dependencies between tumbling window triggers and self-dependency (a window waiting for the previous one) are covered in tumbling window triggers: backfills and dependencies.

Schedule triggers and time zones

A schedule trigger can express calendars a tumbling window cannot: specific hours, weekdays, month days, or "the first Monday of the month". The part that trips people up is time zones (Microsoft Learn: schedule trigger):

  • In UTC, start and end times need the Z suffix (2026-09-01T00:30:00Z); without it, activating the trigger fails.
  • In any other time zone, they are written without the Z (2026-09-01T06:00:00) and interpreted in the zone you name.
  • Changing the time zone setting does not change the start date for you.
  • Daylight-saving adjustment only applies to recurrences of a day or longer. India Standard Time has no daylight saving, so an IST schedule never shifts.
json
{
  "name": "DailyFinanceExtract",
  "properties": {
    "type": "ScheduleTrigger",
    "typeProperties": {
      "recurrence": {
        "frequency": "Day",
        "interval": 1,
        "startTime": "2026-09-01T00:00:00",
        "timeZone": "India Standard Time",
        "schedule": { "hours": [6], "minutes": [30] }
      }
    },
    "pipelines": [
      { "pipelineReference": { "type": "PipelineReference", "referenceName": "ExtractFinance" } }
    ]
  }
}

This runs ExtractFinance every day at 06:30 IST. Note the pipelines array: a schedule trigger can start several pipelines, and a pipeline can be attached to several schedule triggers.

Event triggers: storage and custom

A storage event trigger subscribes, through Azure Event Grid, to "blob created" and/or "blob deleted" events on a storage account (Microsoft Learn: storage event trigger). Filters on the blob path (Blob path begins with, Blob path ends with) narrow it to one folder or file type, and ignoreEmptyBlobs stops zero-byte files from starting runs. The pipeline receives the arriving file's location in @triggerBody().folderPath and @triggerBody().fileName, which you pass on as parameters so the pipeline processes exactly that file.

  • The subscription must be registered with the Event Grid resource provider, and whoever creates the trigger needs permission to create Event Grid subscriptions on the storage account (Owner or Contributor on the account, or the specific Microsoft.EventGrid/EventSubscriptions/Write permission).
  • A storage account supports at most 500 storage event triggers, an Event Grid limit.
  • Stopping and restarting an event trigger resumes its old pattern and can fire unwanted runs; Microsoft recommends deleting it and creating a new one instead.

A custom event trigger listens to an Event Grid custom topic that your own applications publish to, and filters on the event subject and type, with up to 5 advanced filters and 25 filter values per trigger (Microsoft Learn: custom event trigger). It suits "start this pipeline when that system says so", without polling. Setting up Event Grid for both kinds is walked through in event-based triggers with Azure Event Grid and ADF.

Choosing a trigger

  1. Does each run process a specific time period, and must every period be processed exactly once? Tumbling window.
  2. Does the work start when a file lands? Storage event, with the file path passed to the pipeline.
  3. Does another system decide when the work starts? Custom event.
  4. Otherwise, for jobs that just run on a timetable (a nightly refresh, a weekly export), use a schedule trigger.

Mistakes that cause missed or duplicate runs

  • Saving a trigger but not publishing it: the trigger takes effect only after you publish, not when you save it in the UI.
  • Loading "everything since the last run" from a schedule trigger. One failed run, and the next run cannot tell what was missed. Use a tumbling window with explicit boundaries.
  • Using a closed range (<= end) with window parameters, so boundary rows load twice.
  • A storage event trigger on a folder that also receives temporary or zero-byte files. Filter by extension and ignore empty blobs.

Can one ADF pipeline have more than one trigger?

Yes, with schedule and event triggers: the relationship is many-to-many. A tumbling window trigger is the exception, and starts exactly one pipeline.

How do I reprocess a failed hour?

With a tumbling window trigger, rerun that window from the Monitor view. Because the pipeline reads its window boundaries from the trigger, the rerun loads the same slice. Schedule triggers have no equivalent.

Why did my new tumbling window trigger start hundreds of runs?

Its start time is in the past, so it backfills every window between then and now, in parallel up to maxConcurrency. Set the start time to the first window you actually need.

Which storage accounts support storage event triggers?

Only Azure Data Lake Storage Gen2 and general-purpose v2 storage accounts (Microsoft Learn: storage event trigger). The subscription must also be registered with the Event Grid resource provider.

Where to go next

If you have not built the pipeline yet, start with ADF pipelines explained: building your first pipeline. Azure Data Factory is part of the Azure module in our Data Engineering course.

Sources

GloryTecks Team

Training & Placement Faculty

The GloryTecks faculty team mentors learners across Data Science, AI and Data Engineering, with a focus on real-time projects and placement readiness in Hyderabad.

Want to master Azure Data Factory?

Join GloryTecks’ expert-led Data Engineering training in Hyderabad with real-time projects and 100% placement support.

Explore course