DEV Community

Cover image for Understanding Apache Airflow 3.3: Key Improvements, Features Every Data Engineer Should Know, and Real-World Use Cases.
Gacheri-Mutua
Gacheri-Mutua

Posted on

Understanding Apache Airflow 3.3: Key Improvements, Features Every Data Engineer Should Know, and Real-World Use Cases.

It is relatively straightforward to design a data pipeline that works under the perfect conditions of responsive APIs, flawless networks and where credentials do not expire. The real engineering challenge lies in building resilience. Anticipating, reacting to and mitigating failures is a task much time and energy is spent on and having an orchestrator built to be resilient and observable makes our work easier. Airflow 3.3 introduces architectural changes specifically engineered to make managing failures easier and have a fault-tolerant pipeline.

1. The Task and Asset State Stores

A common architectural pattern in data engineering is triggering an external, long-running process eg. an API bulk export and polling an endpoint until it completes. In older versions of Airflow, if the worker running that task died mid-polling, the task would retry. However, the external system had no idea the Airflow worker died; the original job kept running in the background. On retry, Airflow would blindly spin up a duplicate external job, leading to wasted compute resources until an engineer manually intervened.

With the Task State Store Airflow 3.3 utilizes tasks to save pieces of metadata that persist across retries. Instead of starting over from scratch, a subsequent attempt of the same task can fetch the metadata, reconnect to the running external job. Unlike XComs which are cleared immediately when a task retries, the Task State Store remains intact.

This state can be accessed through the task context using the TaskFlow API:

@task(retries=5, retry_delay=timedelta(minutes=2))
def fetch_paginated_records(**context):
    # Access the state store from the execution context
    tss = context["task_state_store"]
    current_cursor = tss.get("api_pagination_cursor")

    if current_cursor is None:
        print("Starting fresh ingestion from page 1...")
        current_cursor = "START"
    else:
        print(f"Resuming ingestion from saved cursor: {current_cursor}")

Enter fullscreen mode Exit fullscreen mode

Additionally, Asset State Store attaches persistent state to an Airflow data asset rather than a specific task instance. This is ideal for tracking long-term data watermarks or Kafka offsets that multiple downstream DAGs need to read from. Both stores are fully visible inside the Airflow UI.

2. Asset partitioning and runtime mapping
Modern data pipelines must scale horizontally based on the structural realities of the data source. Airflow 3.3 advances the dynamic task mapping engine by introducing sophisticated partition mappers—such as RollupMapper, FanOutMapper, and FixedKeyMapper.Data engineers can assign operational parameters at runtime using the .add_partitions method on the execution context to allow Airflow to scale out hundreds of parallel worker threads to handle dynamic payloads, and then cleanly map those dependencies back into compressed downstream steps without manual code intervention.

3. Modular retry strategy
In earlier versions, configuring retries=3 meant Airflow would blindly hammer a failing endpoint regardless of why it failed. If an external API returned an unauthorized token error, Airflow would retry anyway, wasting worker capacity.
Retry policies give engineers control over how tasks respond to specific exceptions. Custom logic can be written to fail a task immediately on fatal exceptions.

import requests
from airflow.providers.standard.operators.python import task
from airflow.retry_policies import BaseRetryPolicy

class MyRetry(BaseRetryPolicy):

    def should_retry(self, exception, try_number):
        if isinstance(exception, PermissionError):
            return False

        if isinstance(exception, requests.exceptions.HTTPError):
            response = exception.response

            if response is not None and 500 <= response.status_code < 600:
                return try_number <= 5

            if response is not None and 400 <= response.status_code < 500:
                return False

        return try_number <= 3
Enter fullscreen mode Exit fullscreen mode

Scenario: E-commerce Transaction

The BankE company processes millions of multi-currency transaction records hourly. The data team must extract these transactions from external payment APIs, normalize the data, calculate daily rollups, flag high-risk anomalies for human approval, and publish the final audit-ready tables to a downstream data warehouse for the business intelligence team.

Transactions with a variance greater than 15% must be paused for human eyes to prevent fraud. Historically, keeping a task running while waiting for a manual manager approval would park a worker for days, thus consuming system capacity.

Airflow 3.3 solution

The platform uses the native Human-in-the-Loop (HITL) framework. When an anomaly is detected, ApprovalOperator pauses the pipeline and generates an actionable link is generated. Crucially, the task scales to zero inside the infrastructure, freeing up all worker resources while waiting for the response. Once approved, the scheduler instantly wakes up the task to execute the final publication.

In conclusion...
Apache Airflow 3.3 removes the architectural friction points in building pipelines by elevating task memory, retry rules into the platform capabilities therefore eliminating significant engineering maintenance debt, protects cloud compute budgets, and ensures your data infrastructure remains completely resilient when pipelines face unpredictable failures.

Top comments (0)