KafkaIQ

1.1 Introduction to Kafka Streams & Processing Model

Module Overview

Goal: Build the mental model of stream processing before APIs.

What You'll Learn:

  • What Kafka Streams is (library, not a cluster)
  • Stream processing vs batch processing
  • Record-at-a-time processing paradigm
  • Relation to Kafka topics and partitions
  • High-level lifecycle of a Streams application
  • When to use Kafka Streams

What's NOT Covered Here:

  • DSL operations (covered in Modules 4 and 5)
  • State stores (covered in Module 6)
  • Exactly-once semantics (covered in Module 9)
  • Specific APIs and code (covered in Module 3)

Key Insight: After this module, you'll understand what Kafka Streams does — not how to code it. That comes later.


1. What is Kafka Streams?

Definition

Kafka Streams is a client library for building real-time stream processing applications on top of Apache Kafka.

Kafka Streams is:
├─ A Java/Scala library
├─ Embedded in your application
├─ Reads from Kafka topics
├─ Processes records in real-time
└─ Writes back to Kafka topics

What Kafka Streams is NOT

❌ NOT a cluster (like Kafka itself)
❌ NOT a separate service (like Spark)
❌ NOT a broker component
❌ NOT a heavyweight framework

Key Point: Kafka Streams runs inside your application as a library dependency. You deploy it like any other JVM application.


Kafka Streams vs Kafka Connect vs Kafka Consumer API

Before diving deeper, let's clarify how Kafka Streams differs from other Kafka tools:

┌─────────────────┬───────────────────────────────┬─────────────────────────────┬──────────────────────────────┐
│ Feature         │ Kafka Streams                 │ Kafka Connect               │ Kafka Consumer API           │
├─────────────────┼───────────────────────────────┼─────────────────────────────┼──────────────────────────────┤
│ Purpose         │ Real-time process/transform   │ Moving data in/out of Kafka │ Consuming messages manually  │
│                 │ data                          │                             │ from topics                  │
├─────────────────┼───────────────────────────────┼─────────────────────────────┼──────────────────────────────┤
│ Infrastructure  │ None (library inside app)     │ Needs Kafka Connect cluster │ None (library inside app)    │
│ Needed          │                               │                             │                              │
├─────────────────┼───────────────────────────────┼─────────────────────────────┼──────────────────────────────┤
│ Use Case        │ Aggregate sales by region     │ Pull DB data into Kafka     │ Build a custom consumer      │
│ Example         │ live                          │                             │                              │
├─────────────────┼───────────────────────────────┼─────────────────────────────┼──────────────────────────────┤
│ Complexity      │ Medium (DSL + state mgmt)     │ Low (configuration-based)   │ Low to High (you write all)  │
├─────────────────┼───────────────────────────────┼─────────────────────────────┼──────────────────────────────┤
│ State           │ Built-in state management     │ Stateless                   │ You manage state manually    │
│ Management      │                               │                             │                              │
└─────────────────┴───────────────────────────────┴─────────────────────────────┴──────────────────────────────┘

When to use each:

  • Kafka Streams: Real-time computation on streaming data (filtering, aggregations, joins, windowing)
  • Kafka Connect: Moving data between Kafka and external systems (databases, S3, Elasticsearch)
  • Kafka Consumer API: Building custom consumers with specific logic not covered by Streams or Connect

Comparison: Library vs Cluster

Traditional Stream Processing (Flink, Spark Streaming):
┌──────────────────────────────────────────┐
│     Separate Cluster                     │
│  ┌─────┐  ┌─────┐  ┌─────┐              │
│  │Node1│  │Node2│  │Node3│              │
│  └─────┘  └─────┘  └─────┘              │
│     ↑        ↑        ↑                  │
│     └────────┴────────┘                  │
│    Your app submits jobs here            │
└──────────────────────────────────────────┘

Kafka Streams (Library):
┌──────────────────────────────────────────┐
│  Your Application (JAR)                  │
│  ┌────────────────────────────────────┐  │
│  │  Business Logic                    │  │
│  │  + Kafka Streams Library           │  │
│  └────────────────────────────────────┘  │
│     ↓ reads from / writes to ↓          │
│        Kafka Cluster                     │
└──────────────────────────────────────────┘

Implications:

  • ✅ No separate cluster to manage
  • ✅ Deploy as microservice (Docker, K8s, etc.)
  • ✅ Simpler operations
  • ⚠️ Scaling = deploying more app instances

2. Stream Processing vs Batch Processing

The Paradigm Difference

BATCH PROCESSING:
├─ Collect data for time period
├─ Process entire dataset at once
├─ Wait for results
└─ Repeat

STREAM PROCESSING:
├─ Process data as it arrives
├─ Continuous, never-ending
├─ Results available immediately
└─ Always running

Visual Comparison

Batch Processing:
┌─────────────────────────────────────────────┐
│ Day 1 Data                                  │
│ [Record 1] [Record 2] ... [Record 1000]    │
└─────────────────────────────────────────────┘
            ↓ (process at end of day)
      [Batch Job Runs]
            ↓
      Results at 11:59 PM


Stream Processing:
[Record 1] → [Process] → [Result 1]
[Record 2] → [Process] → [Result 2]
[Record 3] → [Process] → [Result 3]
    ...         ...         ...
(continuous, real-time, never stops)

Latency Comparison

Batch Processing:
Event happens → Wait hours/days → See result
Example: Daily sales report

Stream Processing:
Event happens → Process immediately → See result
Example: Fraud detection (milliseconds)
Aspect Batch Stream
Latency Hours to days Milliseconds to seconds
Data scope Bounded (finite) Unbounded (infinite)
Processing model Process once Process continuously
Use case Historical analysis Real-time reactions

3. Record-at-a-Time Processing

The Core Mental Model

Kafka Streams processes one record at a time as they flow through the pipeline.

Topic: "orders"
[Order 1] → [Process] → [Transformed Order 1] → Output
[Order 2] → [Process] → [Transformed Order 2] → Output
[Order 3] → [Process] → [Transformed Order 3] → Output

Not like this (batch):

[Order 1, Order 2, Order 3] → [Process All] → [Results]

The Infinite Stream

Time →
┌────────────────────────────────────────────────→
│
│ [Event 1] [Event 2] [Event 3] ... [Event N] ... ∞
│
│ Your Kafka Streams app processes this continuous flow
│
└────────────────────────────────────────────────→

Key Concepts:

  1. Stream Never Ends

    • Kafka topics are unbounded
    • Your app runs continuously
    • No concept of "done"
  2. Process as Data Arrives

    • Don't wait for "all data"
    • React to each record
    • Low latency by design
  3. State Accumulates Over Time

    • Can maintain aggregations
    • Running totals, counts, etc.
    • State persists between records

4. Relation to Kafka Topics & Partitions

Input and Output

Kafka Cluster:
┌──────────────────────────────────────────────┐
│  Topic: "input-orders"                       │
│  ├─ Partition 0: [Record][Record][Record]   │
│  ├─ Partition 1: [Record][Record][Record]   │
│  └─ Partition 2: [Record][Record][Record]   │
└──────────────────────────────────────────────┘
            ↓ reads from
┌──────────────────────────────────────────────┐
│  Your Kafka Streams Application             │
│  (processes records)                         │
└──────────────────────────────────────────────┘
            ↓ writes to
┌──────────────────────────────────────────────┐
│  Topic: "output-processed-orders"            │
│  ├─ Partition 0: [Result][Result][Result]   │
│  ├─ Partition 1: [Result][Result][Result]   │
│  └─ Partition 2: [Result][Result][Result]   │
└──────────────────────────────────────────────┘

Key Points:

  • Kafka Streams reads from topics (just like a consumer)
  • Kafka Streams writes to topics (just like a producer)
  • It's a producer + consumer + processing logic combined

Partition-Level Parallelism

Topic with 3 partitions:

Partition 0 → [Streams Task 0] → Process independently
Partition 1 → [Streams Task 1] → Process independently
Partition 2 → [Streams Task 2] → Process independently

Each task is single-threaded
Parallelism = partition count

Critical Understanding:

Important: If you process data from a topic with 10 partitions, the number of tasks will be equal to the number of partitions in the source topic, with each task processing messages from exactly one partition.

Tasks = Partitions (1:1 relationship)

Example:
├─ Topic with 10 partitions → 10 tasks created
├─ Topic with 3 partitions  → 3 tasks created
└─ Each task handles exactly 1 partition

This means:
├─ Max parallelism = number of partitions
├─ Can't have more tasks than partitions
└─ Adding more app instances beyond partition count = idle instances

Critical Understanding:

More partitions = More parallelism
└─ Each partition processed by one task
└─ Tasks can run across multiple instances
└─ Ordering preserved within partition

Scaling Model

Scenario: 6 partitions, 2 app instances

Instance 1:
├─ Task 0 → Partition 0
├─ Task 1 → Partition 1
└─ Task 2 → Partition 2

Instance 2:
├─ Task 3 → Partition 3
├─ Task 4 → Partition 4
└─ Task 5 → Partition 5

Each instance processes 3 partitions

Scaling Rules:

  • Max parallelism = number of input topic partitions
  • Adding more instances than partitions = idle instances
  • Kafka Streams handles task distribution automatically

5. High-Level Lifecycle of a Streams Application

The Five Phases

1. DEFINE TOPOLOGY
   ├─ Describe processing logic
   ├─ Input topics
   ├─ Transformations
   └─ Output topics

2. BUILD APPLICATION
   ├─ Create KafkaStreams instance
   ├─ Configure properties
   └─ Wire everything together

3. START PROCESSING
   ├─ Call streams.start()
   ├─ Join consumer group
   └─ Begin consuming

4. PROCESS RECORDS
   ├─ Read → Transform → Write
   ├─ Continuous loop
   └─ Runs until stopped

5. SHUT DOWN
   ├─ Graceful cleanup
   ├─ Commit state
   └─ Close resources

Conceptual Code Flow

// 1. Define Topology (what to do)
StreamsBuilder builder = new StreamsBuilder();
KStream<String, String> input = builder.stream("input-topic");

input
  .filter((key, value) -> value.contains("important"))
  .mapValues(value -> value.toUpperCase())
  .to("output-topic");

// 2. Build Application
Topology topology = builder.build();
KafkaStreams streams = new KafkaStreams(topology, props);

// 3. Start Processing
streams.start();

// 4. Processing happens continuously...
// (Kafka Streams runs in background)

// 5. Shut Down (when needed)
Runtime.getRuntime().addShutdownHook(new Thread(streams::close));

Don't worry about the code yet! This is just to show the flow. We'll dive into APIs in Module 3.


State Transitions

┌─────────────────────────────────────────────┐
│  State: CREATED                             │
│  (Topology defined, not started yet)        │
└─────────────────────────────────────────────┘
            ↓ streams.start()
┌─────────────────────────────────────────────┐
│  State: REBALANCING                         │
│  (Joining consumer group, assigning tasks)  │
└─────────────────────────────────────────────┘
            ↓
┌─────────────────────────────────────────────┐
│  State: RUNNING                             │
│  (Processing records continuously)          │
└─────────────────────────────────────────────┘
            ↓ streams.close() or failure
┌─────────────────────────────────────────────┐
│  State: PENDING_SHUTDOWN                    │
│  (Cleaning up, committing state)            │
└─────────────────────────────────────────────┘
            ↓
┌─────────────────────────────────────────────┐
│  State: NOT_RUNNING                         │
│  (Application stopped)                      │
└─────────────────────────────────────────────┘

6. Data Flow Architecture

From Kafka to Kafka

┌────────────────────────────────────────────────────┐
│              KAFKA CLUSTER                         │
│                                                    │
│  Input Topics        Processing        Output     │
│  ┌──────────┐       ┌─────────┐      ┌────────┐  │
│  │ orders   │───→   │ Streams │ ───→ │results │  │
│  │ users    │───→   │   App   │ ───→ │metrics │  │
│  │ products │───→   └─────────┘ ───→ └────────┘  │
│  └──────────┘                                      │
│      ↑                                      │      │
│      │                                      ↓      │
│   Producers                            Consumers   │
│                                                    │
└────────────────────────────────────────────────────┘

Kafka Streams sits in the middle:

  • Consumes from input topics
  • Processes in real-time
  • Produces to output topics
  • Other apps can consume the results

Example: Order Processing Pipeline

Step 1: Raw Orders Arrive
Topic: "raw-orders"
[{"id": 1, "amount": 100}]
[{"id": 2, "amount": 50}]

        ↓ (Streams App 1: Validation)

Step 2: Validated Orders
Topic: "valid-orders"
[{"id": 1, "amount": 100, "valid": true}]
[{"id": 2, "amount": 50, "valid": true}]

        ↓ (Streams App 2: Enrichment)

Step 3: Enriched Orders
Topic: "enriched-orders"
[{"id": 1, "amount": 100, "customer": "Alice"}]
[{"id": 2, "amount": 50, "customer": "Bob"}]

        ↓ (Streams App 3: Aggregation)

Step 4: Order Totals
Topic: "order-totals"
[{"customer": "Alice", "total": 100}]
[{"customer": "Bob", "total": 50}]

Key Pattern: Chain of Streams applications, each reading and writing topics.


7. Processing Guarantees (High-Level)

Three Levels

Kafka Streams supports three processing guarantees:

1. At-Most-Once
   └─ Messages may be lost, never duplicated

2. At-Least-Once (default)
   └─ Messages may be duplicated, never lost

3. Exactly-Once (configurable)
   └─ Each message processed exactly one time

Note: Exactly-once semantics will be covered in detail in Module 9. For now, just know it's possible.


Fault Tolerance

Scenario: Streams app crashes

Without Kafka Streams:
├─ Lose progress
├─ Lose state
└─ Manual recovery

With Kafka Streams:
├─ State backed up to Kafka
├─ Automatic recovery
├─ Resume from last checkpoint
└─ No data loss

Key Point: Kafka Streams provides built-in fault tolerance through Kafka itself.


8. When to Use Kafka Streams

Ideal Use Cases

✅ Real-time transformations
   Example: Convert JSON to Avro

✅ Filtering and routing
   Example: Route high-value orders to special topic

✅ Aggregations and analytics
   Example: Count events by user per minute

✅ Enrichment
   Example: Join orders with user data

✅ Stateful processing
   Example: Maintain running totals

✅ Event-driven microservices
   Example: React to events, emit new events

When NOT to Use Kafka Streams

❌ Batch processing (use Spark, Hadoop)
   └─ If you need to process historical data once

❌ Complex ML pipelines (use Flink, Spark)
   └─ If you need advanced ML libraries

❌ Low-latency microsecond processing (use custom code)
   └─ Kafka Streams is milliseconds, not microseconds

❌ Non-Kafka data sources (use Flink, Spark)
   └─ Kafka Streams only reads from Kafka topics

❌ Extremely high throughput (millions/sec per partition)
   └─ Consider custom consumers

Kafka Streams vs Alternatives

Feature Kafka Streams Kafka Consumers Flink Spark Streaming
Deployment Library (embedded) Library (embedded) Cluster Cluster
State management Built-in Manual Built-in Built-in
Exactly-once Yes Manual Yes Yes
Operations Simple Simple Complex Complex
Learning curve Moderate Low Steep Steep
Best for Kafka-centric apps Simple consumption Complex CEP Batch + Stream

9. The Kafka Streams Philosophy

Core Principles

1. Kafka-Native
   └─ Built for Kafka, leverages Kafka's strengths

2. Library, Not Framework
   └─ You control the main() method

3. Elastic Scalability
   └─ Add instances = more throughput

4. Stateful by Design
   └─ State is first-class citizen

5. Fault-Tolerant
   └─ Uses Kafka for durability

6. No External Dependencies
   └─ Only needs Kafka cluster

Design Philosophy

┌──────────────────────────────────────────────┐
│  "If you have Kafka, you have all you need" │
│                                              │
│  ├─ Message transport: Kafka                │
│  ├─ State storage: Kafka                    │
│  ├─ Fault tolerance: Kafka                  │
│  └─ Coordination: Kafka                     │
│                                              │
│  No external databases, caches, or systems  │
└──────────────────────────────────────────────┘

This is powerful: Your entire stateful stream processing stack is just Kafka + your app.


10. Mental Model Summary

Think of Kafka Streams as...

A library that:
├─ Reads from Kafka topics (like a consumer)
├─ Processes records one-at-a-time
├─ Can maintain state (aggregations, joins)
├─ Writes back to Kafka topics (like a producer)
├─ Scales by adding more instances
└─ Handles failures automatically

The Processing Model

CONTINUOUS LOOP:

1. Poll record from input topic
2. Process it (transform, aggregate, etc.)
3. Update state (if needed)
4. Produce result to output topic
5. Commit progress
6. Go to step 1

This happens for every record, forever.

Key Characteristics

REAL-TIME:
└─ Process as data arrives, not in batches

STATEFUL:
└─ Can remember things across records

FAULT-TOLERANT:
└─ Crashes don't lose data or state

SCALABLE:
└─ Add instances to process more partitions

KAFKA-CENTRIC:
└─ Input and output are Kafka topics

11. Questions You Should Be Able to Answer

After this module, you should be able to answer:

✅ What is Kafka Streams? (library, not cluster)
✅ How does stream processing differ from batch processing?
✅ What does "record-at-a-time" processing mean?
✅ How does Kafka Streams relate to Kafka topics and partitions?
✅ What is the high-level lifecycle of a Streams application?
✅ When should I use Kafka Streams vs alternatives?
✅ How does Kafka Streams scale?
✅ What guarantees does Kafka Streams provide?


12. What's Next?

Now that you understand the mental model, the next modules will teach you how to build Streams applications:

  • Module 2.1: Core abstractions (KStream, KTable, GlobalKTable) — the building blocks
  • Module 3.1: Building a Streams application — actual code structure
  • Module 4.1: Stateless operations — your first transformations
  • Module 5: Stateful processing — aggregations and windows

Summary

You learned:

  • ✅ Kafka Streams is a library that runs embedded in your application
  • ✅ Stream processing is continuous and real-time, not batch
  • ✅ Kafka Streams processes records one-at-a-time from Kafka topics
  • ✅ Parallelism comes from partitions (more partitions = more parallelism)
  • ✅ Applications have a lifecycle: define → build → start → process → shutdown
  • ✅ Kafka Streams is fault-tolerant and stateful by design
  • ✅ Use it for real-time Kafka-centric stream processing

Next up: Module 2.1 — Core Abstractions (KStream, KTable & GlobalKTable) — Learn the fundamental building blocks for stream processing.