Skill 1.1.4: Use AWS Streaming Data Sources to Ingest Data
Task 1.1: Data Preparation for ML and AI — Collect and store data
Objective: Land continuously arriving records into a durable, replayable, queryable store so ML features, training sets, and real-time inference consume fresh data — without reprocessing history or corrupting point-in-time correctness.
1. Service Selection Matrix (highest-yield decision)
| Service | Role in the pipeline | Replay / multiple consumers | Ordering | Latency | Choose when |
|---|---|---|---|---|---|
| Kinesis Data Streams (KDS) | Durable AWS-native record buffer; consumers pull | Yes — retention 24h → 365d, many consumers | Per shard, by partition key | ~70 ms–200 ms | You need replay, fan-out, custom consumers, or KCL/Lambda processing |
| Kinesis Data Firehose | Managed delivery service to S3/Redshift/OpenSearch/Splunk | No — not a stream, no replay, no pull | Best-effort; at-least-once | 60 s+ buffering | Straight "stream → storage/lake" with zero consumer code |
| Amazon MSK (provisioned) | Managed Apache Kafka; Kafka API/topic/consumer-group compatible | Yes — topic retention, log compaction, replay | Per partition | Low ms | Org standardized on Kafka, existing producers/Connect connectors |
| MSK Serverless | Same Kafka semantics, no broker/partition capacity management | Yes | Per partition | Low ms | Kafka compatibility + unpredictable throughput; less tuning control |
| Managed Service for Apache Flink | Stateful processor (not a buffer) — windowing, joins, enrichment | Reads from KDS/MSK/S3; sinks to KDS/Firehose/S3/OpenSearch | Event-time semantics | Sub-second | You need windowed aggregates, stream-stream joins, exactly-once state |
| AWS Glue Streaming ETL | Serverless micro-batch Spark over KDS/MSK | Checkpointed micro-batch | Micro-batch | Seconds | Reuse existing Glue/Spark jobs, no cluster ops |
| Lambda + event source mapping | Serverless consumer for KDS/MSK | Checkpointed by service | Per shard/partition | Sub-second | Light per-record transform, low volume, spiky |
| MSK Connect | Managed Kafka Connect workers | Kafka offsets | Per partition | Low ms | Sink/source connectors (S3, OpenSearch, JDBC) without running Connect |
| Kinesis Video Streams | Time-indexed media ingest | Yes (fragments) | Per fragment | Seconds | Video/audio for Rekognition Video or SageMaker CV pipelines |
Trap: Firehose cannot be read by consumers and cannot be an event source for Lambda. If the scenario says "replay," "reprocess," "second consumer," or "backfill," the answer is KDS or MSK — never Firehose.
Trap: Kinesis Data Analytics was renamed Managed Service for Apache Flink. Same service, new name in distractors.
2. Capacity, Scaling & Throughput
Kinesis Data Streams — per-shard hard limits (memorize):
- Write: 1 MB/s and 1,000 records/s per shard.
- Read (shared): 2 MB/s per shard shared across all consumers → 5 consumers polling = 400 KB/s each.
- Enhanced fan-out: 2 MB/s per registered consumer, dedicated HTTP/2 push (no polling contention).
- Record size: 1 MB max (includes partition key); batch put up to 500 records / 5 MB per call.
- Retention: 24 h default → up to 365 days (long retention = replay/backfill without re-ingest).
- Provisioned mode: manual shard count; scale via split/merge/update-shard-count; cost per shard-hour + PUT payload units.
- On-demand mode: auto-scales, default 4 MB/s write / 200 MB/s read per stream, doubles on sustained >50% utilization; no shard math but a throughput ceiling.
- Partition key = shard assignment. Low-cardinality or skewed keys create hot shards while others idle → provision more shards but throughput does not rise.
MSK / Kafka:
- Partition = unit of parallelism (and ordering). A topic with N partitions caps a consumer group at N effective consumers.
- Replication factor 3 across AZs for durability; min.insync.replicas governs the durability/latency trade-off (
acks=all= strongest). - Retention by time or size; log compaction keeps the latest value per key (useful for feature snapshots).
- Consumer group offsets live in
__consumer_offsets— this is what makes replay and backfill possible. - Over-partitioning raises broker memory, replication cost, and rebalance time; under-partitioning caps throughput permanently.
Flink:
- KPU = 1 vCPU + 4 GB; 1 KPU minimum; scales parallelism of the job graph.
- Checkpoints to S3 = exactly-once state; checkpoint interval trades recovery time vs overhead.
- Watermarks define how long to wait for late events before closing a window.
Default quotas are soft and region-dependent (e.g., KDS shards per region, on-demand streams per account, Firehose ~5 MB/s or 5,000 records/s per delivery stream). Exam answers usually target the architecture pattern, not the exact number — but per-shard 1 MB/s write / 2 MB/s read is testable.
3. Delivery Semantics, Ordering & Reprocessing
| Concern | KDS | MSK | Firehose | Flink |
|---|---|---|---|---|
| Ordering | Per shard only | Per partition only | None guaranteed | Per key (keyed state) |
| Default delivery | At-least-once | At-least-once (configurable) | At-least-once | Exactly-once w/ checkpoints |
| Replay | Yes (retention window) | Yes (retention/compaction) | No | Via source replay |
| Consumer coordination | KCL lease table (DynamoDB) + checkpoints | Consumer group + offsets | N/A | Checkpointed state |
| Scaling the consumer | Add consumers up to shard count; KCL rebalances | Add consumers up to partition count | N/A | Raise parallelism/KPUs |
Key rules:
- Global ordering does not exist in KDS or Kafka. Any answer promising total ordering across a stream is wrong.
- Deduplication is the consumer's job on at-least-once sources (idempotency key, or Flink exactly-once sinks).
- Lambda event source mapping: tune batch size, batch window, parallelization factor (per-shard concurrency, breaks strict order), and bisect-on-error to isolate poison records. On-failure destinations (SQS/SNS) prevent silent loss.
- Checkpointing ≠ replay. Checkpoints resume processing; retention enables reprocessing.
4. Processing Patterns for ML (Flink / Kafka)
- Tumbling/hopping/sliding windows → rolling aggregates (avg spend last 5 min) written to an online feature store.
- Event time vs processing time: event time + watermarks handles out-of-order mobile/IoT data; processing time is simpler but wrong for late arrivals.
- Stream–stream joins (e.g., clicks ⋈ impressions) with a bounded state TTL — unbounded state is the classic Flink OOM.
- Stream enrichment: join the live stream against a static/dimension source (S3 or DynamoDB) before writing features.
- Late data: allowed lateness + side output, or route to a dead-letter path rather than dropping silently.
Gotcha: The narrowest bottleneck sets pipeline throughput. If the sink (OpenSearch, DynamoDB, Redshift) is saturated, adding consumers/KPUs only moves the queue. Diagnose upstream limit vs downstream block before scaling.
5. Landing Zone: Formats, Buffering & ML Targets
- Columnar for the curated layer. Convert streaming JSON to Parquet/ORC (Firehose record-format conversion using a Glue Data Catalog table) so Athena/Spark/SageMaker training read only needed columns.
- Small-file problem: high-frequency delivery creates millions of tiny S3 objects → slow crawls and slow training. Mitigate with larger buffer size/interval and periodic compaction.
- Firehose buffering hints (size/interval) are the main latency-vs-efficiency dial; dynamic partitioning writes Hive-style prefixes for partition pruning.
- Streaming → SageMaker Feature Store: stream processors write the online store for low-latency inference lookups while the same feature group's offline store accumulates history for training — one definition, no training–serving skew.
- Streaming → vector store: Flink/Glue generate embeddings and upsert to OpenSearch, pgvector, or S3 vector indexes for retrieval in generative AI apps.
- Near-real-time retraining loop: stream → S3 (Parquet) → Glue Catalog → SageMaker training job; batch backfill and stream must write the same schema and partition layout.
- Diverse data types: text/images/audio land in S3 as objects with metadata; streams carry the reference + metadata, not the payload, when payloads exceed the 1 MB record limit.
Gotcha: Preserve event timestamps end-to-end. Using ingestion time as the event time silently corrupts point-in-time feature joins and produces leakage in training sets.
6. Security, Networking & Compliance
| Layer | Controls |
|---|---|
| KDS | IAM identity policies + resource-based policies; KMS SSE at rest; TLS in transit; VPC interface endpoints (PrivateLink) for private producers/consumers; CloudTrail for control-plane and optional data-plane events |
| MSK | KMS at rest; TLS in transit; IAM access control or SASL/SCRAM; security groups + private subnets; PrivateLink; broker logs to CloudWatch/S3/Firehose |
| Firehose | IAM service role to S3/OpenSearch/Redshift; KMS on the destination bucket; Lambda/OpenSearch sinks can run in-VPC; no public endpoint exposure required |
| Flink / Glue Streaming | Runs inside your VPC with an execution IAM role; KMS for state/checkpoints; CloudWatch Logs for job telemetry |
| Compliance drivers | Residency/AZ placement, retention length (KDS retention vs Kafka retention vs S3 lifecycle), and who may decrypt (KMS key policy) are configured at the storage layer, not in training code |
Gotcha: Enabling SSE-KMS on a stream/topic requires producers and consumers to hold
kms:Decrypt/kms:GenerateDataKey— an "AccessDenied" after enabling encryption is a permissions problem, not a stream problem. Per-request KMS calls also add cost and latency.
7. Troubleshooting Matrix
| Symptom | Likely cause | Fix |
|---|---|---|
| Consumer lag grows linearly, never recovers | Incoming rate > consumer capacity or blocked sink | Measure sink latency first; scale sink, then consumers |
| WriteProvisionedThroughputExceeded | Shard exceeded 1 MB/s or 1,000 rec/s | Add shards, aggregate records (KPL-style batching), or switch to on-demand |
| One shard hot, others idle | Low-cardinality / skewed partition key | High-cardinality key (add salt/random suffix) |
| Multiple consumers each see throttling | 2 MB/s per shard shared read | Enable enhanced fan-out for dedicated 2 MB/s per consumer |
| Records out of order downstream | Parallel consumers or partitioned write target | Partition sink by the same key; serialize per shard if order is required |
| Kafka consumers idle though lag exists | Consumer group size > partition count | Add partitions (irreversible) or accept the parallelism cap |
| Job restarts from scratch after failure | Checkpointing not enabled / state backend misconfigured | Enable checkpointing to S3; raise checkpoint interval tolerance |
| Duplicate rows in the lake | At-least-once delivery (Firehose, non-idempotent sink) | Idempotent upsert, dedupe key, or exactly-once sink semantics |
| Training set has look-ahead leakage | Event time replaced by ingestion time | Carry and join on the source event timestamp |
8. Anti-Patterns & Distractor Traps
- Firehose as a queue. No replay, no pull consumers, no backfill. Use KDS/MSK.
- Scaling consumers into a blocked sink. Moves the queue; does not drain it.
- Assuming stream-wide ordering. Ordering is per shard/partition only.
- Raw JSON forever. Row-oriented, uncompressed files inflate every downstream query and training read; convert to columnar in the curated layer.
- Ignoring the small-file problem. Thousands of tiny S3 objects cripple Glue crawls and training throughput.
- Unbounded Flink state. Joins/keys without TTL exhaust task memory.
- Self-managed Kafka on EC2 when MSK fits. Operations burden with no functional gain; MSK Serverless if capacity is unpredictable.
- Under-partitioned topic + "add consumers." Parallelism is capped by partition count — a permanent ceiling.
- One feature definition per environment. Divergent train/serve logic is the root cause of training–serving skew; centralize in Feature Store.
- Streaming everything. If the SLA is hourly or daily, micro-batch/Glue/Step Functions is cheaper and simpler than always-on Flink KPUs.
Cost rule of thumb: KDS charges per shard-hour + per PUT payload unit (so batching/aggregation directly cuts cost); MSK charges per broker-hour + storage (so right-sizing brokers and partitions matters); Flink charges per KPU-hour (so idle always-on jobs are the most common cost mistake).