This page requires JavaScript to display.
K
Unpacking...
← Data Engineering
Data Engineering

Kafka & Kinesis Reference

Streams, shards, topics, partitions, and the producer/consumer code for both platforms.

Contents
1. Streaming Concepts 2. Amazon Kinesis 3. Kinesis with boto3 4. Apache Kafka 5. Kafka with Python 6. Kinesis vs Kafka 7. Offsets & Delivery Guarantees 8. Scaling: Resharding & Rebalancing 9. Firehose Transforms & ksqlDB 10. Monitoring & Lag 11. Apache NiFi 12. Kafka or NiFi as a Spark Source? 13. Kafka CLI & Web UIs 14. Lambda Architecture: Flink + Databricks 15. Kappa vs Lambda 16. Watermarks in Flink & Spark 17. The Kafka Data Engineer Role

1. Streaming Concepts

Stream processing handles unbounded, continuously arriving data — clickstreams, IoT telemetry, transactions — instead of a fixed batch. A producer writes records; a broker/service durably stores and orders them; a consumer reads and processes them, tracking its own position so it can resume after a restart.

2. Amazon Kinesis

ServicePurpose
Kinesis Data StreamsCustom real-time processing; you manage shards, consumers read with the KCL/SDK
Kinesis Data FirehoseFully managed delivery straight to S3, Redshift, OpenSearch, with optional transform
Kinesis Data AnalyticsSQL or Apache Flink applications running directly over a stream

A stream is divided into shards, each providing 1MB/sec (or 1,000 records/sec) write and 2MB/sec read. A record's partition key is hashed to pick its shard — records with the same key always land on the same shard, preserving their relative order.

3. Kinesis with boto3

import boto3, json

kinesis = boto3.client("kinesis", region_name="us-east-1")

# Producer: put a single record
kinesis.put_record(
    StreamName="orders-stream",
    Data=json.dumps({"order_id": 123, "amount": 49.99}).encode("utf-8"),
    PartitionKey="order_123"
)

# Producer: batch put
kinesis.put_records(
    StreamName="orders-stream",
    Records=[
        {"Data": json.dumps({"order_id": i}).encode(), "PartitionKey": str(i)}
        for i in range(10)
    ]
)

# Consumer: read from a shard
shard_id = kinesis.describe_stream(StreamName="orders-stream")[
    "StreamDescription"]["Shards"][0]["ShardId"]

shard_iterator = kinesis.get_shard_iterator(
    StreamName="orders-stream", ShardId=shard_id,
    ShardIteratorType="TRIM_HORIZON"
)["ShardIterator"]

response = kinesis.get_records(ShardIterator=shard_iterator, Limit=100)
for record in response["Records"]:
    print(json.loads(record["Data"]))

4. Apache Kafka

Kafka is a distributed log: topics are split into partitions spread across brokers. Each partition is an ordered, append-only log; order is guaranteed only within a partition. Consumer groups let multiple consumers split a topic's partitions between them for parallel processing, with Kafka tracking each group's committed offset per partition.

5. Kafka with Python

from kafka import KafkaProducer, KafkaConsumer
import json

# Producer
producer = KafkaProducer(
    bootstrap_servers="localhost:9092",
    value_serializer=lambda v: json.dumps(v).encode("utf-8")
)
producer.send("orders", key=b"order_123", value={"order_id": 123, "amount": 49.99})
producer.flush()

# Consumer, as part of a group
consumer = KafkaConsumer(
    "orders",
    bootstrap_servers="localhost:9092",
    group_id="orders-processor",
    value_deserializer=lambda v: json.loads(v.decode("utf-8")),
    auto_offset_reset="earliest"
)
for message in consumer:
    print(message.partition, message.offset, message.value)

6. Kinesis vs Kafka

KinesisKafka
Operations modelFully managed AWS serviceSelf-managed cluster (or managed via MSK/Confluent)
Unit of parallelismShardPartition
Retention default24 hours (extendable to 365 days)Configurable, commonly 7 days or "forever"
Ecosystem fitTight AWS integration (Firehose → S3/Redshift)Cloud-agnostic, huge connector ecosystem (Kafka Connect)

7. Offsets & Delivery Guarantees

An offset is a record's position within its partition/shard. Consumers track the offset they've processed up to, so restarting resumes correctly instead of reprocessing or skipping.

consumer = KafkaConsumer(
    "orders", bootstrap_servers="localhost:9092",
    group_id="orders-processor", enable_auto_commit=False
)
for message in consumer:
    process(message.value)
    consumer.commit()  # only after processing succeeds

8. Scaling: Resharding & Rebalancing

9. Firehose Transforms & ksqlDB

Kinesis Data Firehose can invoke a Lambda function to transform each record before delivery (e.g. flattening JSON, filtering bad records) without standing up a separate consumer:

import base64, json

def handler(event, context):
    output = []
    for record in event["records"]:
        payload = json.loads(base64.b64decode(record["data"]))
        payload["processed_at"] = context.aws_request_id
        output.append({
            "recordId": record["recordId"],
            "result": "Ok",
            "data": base64.b64encode(json.dumps(payload).encode()).decode()
        })
    return {"records": output}

Kafka's equivalent for lightweight stream SQL is ksqlDB — declares continuous queries over topics without writing a full consumer application:

CREATE STREAM high_value_orders AS
  SELECT * FROM orders
  WHERE amount > 1000
  EMIT CHANGES;

10. Monitoring & Lag

Consumer lag — the gap between the latest offset written and the offset a consumer has processed — is the single most important streaming health metric: growing lag means consumers can't keep up with producers.

11. Apache NiFi

Apache NiFi moves data between systems. You build a flow visually by wiring together processors, each doing one step. It is built for getting data from A to B and reshaping it on the way, not for heavy analytics.

Connectors out of the box
Files, S3, HTTP APIs, databases, FTP/SFTP, Kafka, JMS, MQTT, email, HDFS and more.
Light transformations
CSV → JSON, split/merge files, filter, route by content, rename fields, mask values.
Guaranteed delivery
Every record is tracked and buffered on disk; if Kafka is down, NiFi queues and retries.
Data provenance
Click any record to see where it came from, what happened to it and where it went.
Backpressure
When a downstream system slows, queues fill and upstream processors pause automatically.
Live changes
Edit, pause or reroute a flow in the UI without redeploying code.

A minimal flow: CSV file to Kafka

CSV dropped in input/
  → GetFile              (picks up the file)
  → PublishKafkaRecord   (parses CSV, converts each row to JSON, sends to topic "orders")
NiFiSpark
JobGetting data in: collect, convert, deliverProcessing: joins, validation, aggregations
Works onOne record or file at a timeWhole datasets and streams, in parallel
Built withDrag and drop in a web UICode (PySpark, Scala, SQL)

Do you need NiFi if Spark already reads Kafka?

No. If data already arrives in Kafka, Spark reads it directly and NiFi is one more system to run. NiFi earns its place in front of Kafka, for sources that cannot publish to Kafka themselves.

SourcePublishes to Kafka itself?
Your own apps and microservicesYes, via a Kafka client library
Debezium change-data-capture feedsYes
Partner files on SFTP or S3No: needs NiFi, Kafka Connect or a script
REST APIs you must pollNo
Legacy databases, JMS, MQTT, syslog, emailNo

Alternatives to NiFi: Kafka Connect (ready-made connectors for S3, JDBC, MongoDB and more, configured in JSON; the usual choice in Kafka-centric stacks) or small producer services for one or two simple sources.

NiFi + Spark integration patterns

PatternFlowExample
1. NiFi → Kafka → Spark Streaming (most common)Kafka buffers between ingestion and processingCard transactions from ATMs, POS and partner banks normalised to JSON; Spark scores them for fraud
2. NiFi → data lake → Spark batchNiFi converts to Parquet and partitions by date in S3500 stores upload daily sales CSVs; a 2 AM Spark job builds summaries
3. NiFi triggers Spark jobsNew file → NiFi submits a job (Livy, EMR, Databricks API)Partner claims file lands; NiFi starts the dedup-and-score job and routes failures to an error queue
4. Spark → NiFi distributionSpark writes results; NiFi delivers to many targetsMonthly bills sent to a billing API, partners over SFTP, and an S3 archive
IoT with MiNiFiLightweight agent filters at the edge → NiFi → Kafka → Spark10,000 machine sensors over MQTT; Spark detects vibration anomalies

Rule of thumb: NiFi connects systems and moves data; Spark does the heavy computation. Skip NiFi when every source can already write to Kafka.

12. Kafka or NiFi as a Spark Source?

Kafka, clearly. Spark should always read from Kafka; NiFi is not an alternative streaming source, at most it feeds Kafka.

RequirementKafka → SparkNiFi → Spark directly
Built-in sourceYes: spark.readStream.format("kafka")No Structured Streaming source; the old DStream receiver is unmaintained
Replay after failureSpark checkpoints offsets and resumes exactlyPushed data can be lost or duplicated
Exactly-onceOffsets + idempotent sinkNot achievable end to end
ParallelismPartitions map to Spark tasksSingle receiver bottleneck
Reprocessing historyRewind offsetsOnce delivered, gone
Multiple consumersMany groups read the same topicFlow duplicated per consumer
[sources] → (NiFi / Kafka Connect, optional) → Kafka → Spark Structured Streaming → sink
              ingestion                       buffer     processing

13. Kafka CLI & Web UIs

Kafka ships shell tools in its bin/ directory. In Docker, run them with docker exec against the internal listener (e.g. kafka:29092); from the host use the external one (e.g. localhost:9092). In Git Bash on Windows, prefix with MSYS_NO_PATHCONV=1 to stop path rewriting.

# topics
kafka-topics.sh --bootstrap-server kafka:29092 --list
kafka-topics.sh --bootstrap-server kafka:29092 --describe --topic txn.upi
kafka-get-offsets.sh --bootstrap-server kafka:29092 --topic txn.upi

# consume from the beginning, showing keys and offsets
kafka-console-consumer.sh --bootstrap-server kafka:29092 --topic txn.upi \
  --from-beginning --property print.key=true --property print.offset=true

# produce (add these properties to send KEY|{json})
kafka-console-producer.sh --bootstrap-server kafka:29092 --topic txn.upi \
  --property parse.key=true --property key.separator="|"

Kafka web UIs

Kafbat UI (Apache 2.0) began as Provectus's "UI for Apache Kafka" and was forked by its core maintainers in 2023. It is a Spring Boot (WebFlux) backend with a React/TypeScript frontend, and is itself just a Kafka client:

Alternatives: Redpanda Console, AKHQ, Confluent Control Center, Conduktor.

14. Lambda Architecture: Flink + Databricks

Lambda runs two pipelines over the same raw data: a speed layer for immediate results and a batch layer that recomputes complete, corrected history. A common banking setup uses Flink for speed and PySpark on Databricks for batch:

REAL-TIME EVENTS                         FILE DROPS (S3 landing, business_date=…)
cards · UPI · ATM · app                  core-banking EOD · settlement · masters
        │                                                   │
        ▼                                                   │
      Kafka ──► FLINK speed layer (ms) ──► serving DB / alerts topic
        │            ▲ reference data (compacted topic / Redis)
        └─► S3 raw sink                                     │
                │                                           ▼
                └──────► DATABRICKS batch layer (nightly job)
                         BRONZE → SILVER → GOLD
                         reconciliation · Customer 360 · regulatory reports
                         → publishes reference data back to Flink
Speed layer (Flink)Batch layer (Databricks)
PurposeImmediate decisions and alertsComplete, auditable history and analytics
LatencyMillisecondsMinutes to hours
CorrectnessBest effort for late dataSource of truth; recomputes from full history

Design points

Where a nightly S3 file job fits

Files arrive with a contract and a control file (row count, totals, checksum). The job: check arrivals (SLA alert) → validate control totals and schema, quarantine bad files → Auto Loader availableNow into bronze → silver MERGE / SCD2 → join with the day's events → reconcile real-time vs settled → data-quality gate → gold → publish reference data to Flink → archive and notify. Keep it idempotent per business_date.

15. Kappa vs Lambda

LAMBDA:
Sources ─┬─► BATCH layer (complete) ──► batch views ─────┐
         └─► SPEED layer (fast)     ──► real-time views ─┴─► SERVING (merge)

KAPPA:
Sources → Kafka (replayable log) ──► ONE stream processor ──► serving tables
reprocess = run v2 from offset 0 into new tables, then switch readers (blue/green)
LambdaKappa
PipelinesTwo (batch + stream)One (stream)
Business logicOften duplicatedWritten once
ConsistencyResults can diverge; must reconcileOne code path
ReprocessingRerun batch over historyReplay the log (needs retention and capacity)
File sourcesNatural in batchMust be streamed (e.g. Auto Loader)
OperationsTwo engines and skill setsOne engine

Kappa needs a replayable source (long Kafka retention, tiered storage, or a Delta bronze table), event-time processing, idempotent or versioned sinks, and deterministic logic.

SituationChoose
Millisecond decisions + analytics/ML on another platformLambda with reconciliation
Many authoritative file/batch sourcesLambda or lakehouse hybrid
Mostly events, one team, consistency mattersKappa
One platform, seconds of latency acceptableKappa-style lakehouse (Structured Streaming / declarative pipelines)

Lakehouses blur the line: a Delta or Iceberg table can be read as a batch or a stream, so the same code does incremental and full-refresh processing.

16. Watermarks in Flink & Spark

A watermark says how far event time has progressed: "all events up to T have probably arrived". With 5 seconds of allowed out-of-orderness, watermark = max event time seen − 5 s. It decides when windows emit, which late events are dropped, and when state is cleaned up.

Event time arrivesWatermarkEffect on window 10:00–10:01
10:01:0210:00:57Still open
10:00:59 (late, within 5 s)10:00:57Counted
10:01:0610:01:01Closes and emits
10:00:55 (too late)10:01:01Dropped
-- Flink SQL
WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND

SELECT window_start, product, COUNT(*), SUM(amount)
FROM TABLE(TUMBLE(TABLE orders, DESCRIPTOR(event_time), INTERVAL '1' MINUTE))
GROUP BY window_start, window_end, product;

# PyFlink DataStream
WatermarkStrategy.for_bounded_out_of_orderness(Duration.of_seconds(5)) \
    .with_timestamp_assigner(OrderTime()) \
    .with_idleness(Duration.of_seconds(30))

# Spark Structured Streaming
df.withWatermark("event_time", "5 seconds")

The cost of dropped events

Late events are dropped only from event-time operators; they still sit in Kafka and in row-by-row paths. The failure is silent: windowed totals undercount, a windowed fraud rule may see 4 transactions instead of 5, and late data is often biased toward specific sources.

For fraud detection, never put watermark-dropping operators on the raw transaction path: every transaction must be scored and stored. Use watermarked windows only for metrics.

17. The Kafka Data Engineer Role

AreaDay-to-day work
IngestionApps, databases (CDC), files and APIs into Kafka
Topic designNames, partitions, keys, retention, compaction
Data contractsAvro / Protobuf schemas and their evolution in a Schema Registry
Stream processingClean, join, enrich, aggregate (Spark, Flink, Kafka Streams)
SinksWarehouses, lakes and databases
ReliabilityNo loss or duplicates, DLQs, recovery
Monitoring and operationsConsumer lag, throughput, incidents
Security and governanceAuthentication, ACLs, PII masking, audit

Key skills

A typical integration task