Knowledge / Artificial Intelligence and Data Science / Data pipelines: Airflow and Kafka

Data pipelines: Airflow and Kafka

Artificial Intelligence and Data Science (B.E./B.Tech) · Data engineering and deployment · free study notes for exams, placements and interviews

In short

A data pipeline moves data from where it is produced to where it is analysed or served, applying extraction, transformation, validation and loading on the way. Apache Airflow is the orchestrator — it schedules and monitors *batch* workflows written as code (a DAG of tasks) and retries, alerts and backfills them. Apache Kafka is the opposite half — a distributed, durable, partitioned log that carries *streaming* events between many producers and consumers at high throughput. Almost every Indian data-engineering interview (Flipkart, Swiggy, Zomato, PhonePe, Paytm, Razorpay, Jio, Zerodha, Dream11, plus the service firms TCS, Infosys, Wipro, Accenture, Deloitte) asks about DAG scheduling, idempotency, partitions, consumer groups and delivery semantics; university syllabi cover them under Big Data Analytics / Data Engineering labs, and GATE DA touches the batch-versus-stream and data-movement concepts rather than the tool APIs.

Explained

Pipelines: batch, streaming and the standard architectures

Airflow: architecture and core objects

Airflow: scheduling, dependencies and reliability

  1. The scheduler re-parses each DAG file at most once every min_file_process_interval (new files in the folder are picked up each dag_dir_list_interval) and serialises them to the DB.
  2. For each DAG it creates a DAG run for the next data interval; the run fires at the *end* of its interval — a daily DAG for 4 November runs just after midnight on 5 November. This off-by-one is the most misunderstood point in Airflow.
  3. Eligible task instances are queued; the executor dispatches them to workers subject to parallelism, max_active_tasks_per_dag, max_active_runs_per_dag and pools (named concurrency limits, e.g. 5 slots on a fragile legacy database).
  4. Failures retry retries times with retry_delay (optionally exponential); then the TI is failed and on_failure_callback / alerting fires.
  5. Trigger rules: all_success (default), all_failed, one_success, one_failed, none_failed_min_one_success, all_done — needed for cleanup tasks and for branching joins.
  6. Branching: BranchPythonOperator / @task.branch picks one path; unchosen tasks are skipped, which is why a downstream join usually needs none_failed_min_one_success.
  7. Catchup and backfill: with catchup=True and a past start_date, the scheduler creates every missed interval; catchup=False creates only the latest. A backfill is a deliberate re-run of a historical date range.
  8. Idempotency is mandatory: a task must produce the same result when re-run for the same logical date. Achieved by deriving the partition from data_interval_start, writing with delete-insert / INSERT OVERWRITE / upsert on a key, and never using datetime.now() to decide which data to process.
  9. SLA and timeouts: sla flags a task that finishes later than allowed; execution_timeout kills a hung task; dagrun_timeout caps a whole run. (SLA handling has changed across versions — verify in your release.)
  10. Limitations: Airflow is not a data-processing engine (do not pull a crore of rows into the worker); not a sub-minute scheduler; the metadata DB is the scaling bottleneck; heavy top-level code in a DAG file slows every parse.

Kafka: architecture, topics, partitions and offsets

Kafka: producers, consumers, delivery semantics and ecosystem

Design, operations and Indian compliance

Key points

Formulas and examples

Partitions needed P = ceil( max( T / p_producer , T / p_consumer ) ) × (1 + h)
T = target throughput (messages/s or MB/s); p = per-partition or per-consumer capacity (same units); h = headroom fraction
Storage S = R × V × E × r
R = retention in days; V = events/day; E = average event size after compression (bytes); r = replication factor. Add 20-35% for indexes, segment overhead and free space.
Consumer lag L(t) = log_end_offset − committed_offset (records)
Catch-up time t = L / (C − A), valid only if C > A; C = consumption rate, A = arrival rate (records/s)
Little's law: L = λ × W
L = records in flight, λ = arrival rate (records/s), W = mean time in system (s)
Backfill wall-clock ≈ ceil(N / max_active_runs) × runtime_per_run
N = number of DAG runs in the range
Effective parallelism = min(partitions, consumers in group); Pipeline slack = SLA window − total runtime

1. Sizing a Kafka topic (order events). Peak 1,20,000 msg/s. One producer thread writes 20,000 msg/s to a partition; one consumer instance processes 15,000 msg/s.

2. Storage and broker count. 50 crore (5 × 10⁸) events/day, 1.2 KB each after compression, retention 7 days, RF = 3, six brokers.

3. Consumer lag recovery. After a 45-second deploy pause, lag = 9,00,000 records. Arrival 20,000/s; the restarted group consumes 25,000/s.

4. Airflow backfill. A daily DAG is backfilled for 1 Jan 2026 to 31 Mar 2026. Each run takes 6 minutes; max_active_runs = 4.

5. SLA slack. A nightly pipeline starts 02:30 and must publish by 06:00 → window = 210 min. Extract 40 + transform 95 + load 35 = 170 min → slack 40 min (19%).

AspectAirflowKafka
PurposeOrchestrating scheduled workflowsDurable transport of event streams
Data modelTasks and dependencies (control flow)Partitioned append-only log (data flow)
LatencyMinutes to hoursMilliseconds to seconds
State storeMetadata databaseLog segments on broker disks
Scaling unitWorkers / podsPartitions and brokers
Failure handlingRetries, trigger rules, backfillReplication, ISR, offset replay
Typical useNightly warehouse load, ML retrainingClickstream, payments, CDC, telemetry
SettingFavours latencyFavours throughput/durability
linger.ms020-100
batch.sizesmall64-256 KB
acks1all with min.insync.replicas=2
compression.typenonelz4 / zstd
from airflow.decorators import dag, task
import pendulum

@dag(dag_id="daily_orders", schedule="0 2 * * *",
     start_date=pendulum.datetime(2026, 1, 1, tz="Asia/Kolkata"),
     catchup=False, default_args={"retries": 3})
def daily_orders():
    @task
    def extract(**context):
        day = context["data_interval_start"].to_date_string()   # idempotent key
        return f"s3://raw/orders/dt={day}/"

    @task
    def load(path: str):
        # DELETE WHERE dt = day, then INSERT  -> safe to re-run
        print("loading", path)

    load(extract())

daily_orders()
kafka-topics.sh --create --topic orders --partitions 12 \
  --replication-factor 3 --config min.insync.replicas=2 --bootstrap-server b1:9092
kafka-consumer-groups.sh --describe --group billing --bootstrap-server b1:9092

Where it is used

Interview questions

Common mistakes

Related topics

Big data: Hadoop and Spark · Data warehousing and ETL · Data lakes and lakehouses · Cloud data platforms: BigQuery, Snowflake and Databricks · MLOps and model deployment · AI regulation and governance

Open in the Knowledge reader →Find jobs →