AWS Kinesis · 2026-10-30 · Kinesis Analytics Advanced arc
Managed Flink Advanced Patterns for MCP Server Probe Pipelines: Checkpointing, VPC Connectivity, KPU Scaling, and Connector Semantics
Amazon Managed Apache Flink looks like a managed streaming platform until you run a production MCP probe analytics pipeline on it and discover four independent trap doors that each fail in ways that surface as data loss, cost explosions, or silent incorrect results rather than the Flink configuration problems they actually are. Checkpointing is how Managed Flink guarantees recovery from failures — but the default MemoryStateBackend silently fails when keyed state grows past a few hundred MB (a 24-hour session window over 5,000 MCP servers × 50 KB per server = 250 MB of state, which exhausts JVM heap and causes the application to crash in a restart loop with no clear error about the state backend being undersized); RocksDB with incremental checkpointing writes only the changed SST files per checkpoint interval (100 MB vs 10 GB for a 1% write rate on 10 GB state), but assigning stable operator UIDs before the first savepoint is the single most commonly skipped step — a topology change without UIDs causes savepoint restore to silently drop all keyed state for the affected operators, producing a running application that emits correct-looking output aggregated from zero prior state. VPC deployment for Managed Flink sounds like a checkbox — pick subnets, pick security groups, done — until the application starts successfully (healthy in the console) but cannot read from Kinesis (no VPC endpoint for kinesis-streams, Flink resolves to the public endpoint IP and times out from a private subnet with no NAT) or cannot write checkpoints to S3 (no S3 Gateway endpoint, all S3 traffic routing to the public internet from a subnet with no NAT gateway); the fix for S3 is free (S3 Gateway endpoints cost nothing), but the Kinesis Interface endpoint costs ~$7/month per AZ and must have private_dns_enabled=true — without this flag, Flink still resolves kinesis.region.amazonaws.com to the public IP even with the endpoint created. KPU scaling for AliveMCP probe analytics has a non-obvious capacity planning trap: the average probe load is low (5,000 endpoints × 1 probe/minute = 83 events/second, handled by 1 KPU), but every 6 hours when the registry scanner re-probes all endpoints simultaneously the input rate spikes to 60× above average for 2–3 minutes; autoscaling triggers on cpuUtilization > 80% with a 3-minute cooldown and then restarts the application from checkpoint in 60–180 seconds — the restart happens during the burst, the application catches up from the Kinesis backlog over several minutes, and the window aggregations fall behind, producing late-event alerts that fire 10–15 minutes after the actual endpoint state change; the correct fix is sizing the minimum KPU count for the burst, not the average, and disabling autoscaling for predictable workloads. Connector exactly-once guarantees in Managed Flink are per-connector, not per-application: the Kinesis source commits shard offsets atomically with checkpoints (exactly-once), the S3 FileSink with BulkFormat (Parquet) renames in-progress files only on checkpoint commit (exactly-once), but the KinesisStreamsSink uses PutRecords with at-least-once retries (duplicate records on recovery guaranteed), the OpenSearch sink uses bulk API retries with no transaction support (at-least-once), and Kafka exactly-once requires explicitly setting DeliveryGuarantee.EXACTLY_ONCE with a unique transactional ID prefix — a second deployment with the same prefix fences the first producer with ProducerFencedException, which is the correct behavior for ordered handover but causes unexpected failures if two application versions overlap during a rolling upgrade. This guide synthesises all four Managed Flink advanced patterns. Pattern 1 covers checkpointing — the checkpoint vs savepoint operational distinction, MemoryStateBackend vs RocksDB state backend selection, exactly-once semantics with the Kinesis source, operator UIDs for savepoint migration safety, and CloudWatch checkpoint monitoring — synthesised from Managed Flink Checkpointing for MCP Servers. Pattern 2 covers VPC connectivity — VPC requirements, security group rules for MSK and RDS, VPC endpoint configuration for Kinesis and S3, MSK authentication (TLS, SASL/SCRAM, IAM), and cross-VPC peering constraints — synthesised from Managed Flink VPC Connectivity for MCP Servers. Pattern 3 covers KPU scaling — the KPU-to-parallelism relationship, operator-level parallelism overrides, autoscaling behavior, manual scaling via savepoints, and capacity planning for MCP probe workloads — synthesised from Managed Flink Parallelism and KPU Scaling for MCP Servers. Pattern 4 covers connectors — Kinesis source and sink, S3 FileSink, Kafka/MSK connector with IAM auth, OpenSearch sink, and the connector exactly-once support matrix — synthesised from Managed Flink Connectors for MCP Servers.
TL;DR
- Checkpointing — use RocksDB with incremental checkpoints for any keyed state above ~100 MB; assign operator UIDs before the first savepoint: Set checkpoint interval to 60,000 ms. Use
EmbeddedRocksDBStateBackend(true)for applications with session windows or multi-day aggregation state. Assign stable.uid("your-operator-name")to every stateful operator before the first deployment — changing topology without UIDs silently drops keyed state on the next savepoint restore. Alarm onnumberOfFailedCheckpoints > 0andlastCheckpointDuration > (interval × 0.8). SetStateTtlConfigon all keyed state descriptors to prevent unbounded state growth. - VPC connectivity — create S3 Gateway endpoint (free) and Kinesis Interface endpoint (~$7/AZ) before deploying Flink in a private subnet: Use at least 2 private subnets in different AZs. Create the S3 Gateway endpoint and add it to the route tables for all Managed Flink subnets. Create Kinesis Interface endpoint with
private_dns_enabled = true— without this, Flink resolves to the public endpoint even with the Interface endpoint in place. For MSK: use security group ID references (not CIDR) for same-VPC rules; switch to CIDR for cross-VPC peering. IAM authentication (port 9098) is the recommended MSK auth for new clusters — no credential rotation, integrates with CloudTrail. - KPU scaling — set source parallelism to match shard count; size for burst load, not average; prefer fixed KPU over autoscaling for predictable hourly bursts: Application parallelism = KPU count (default 1 slot/KPU). Match source parallelism to Kinesis shard count — extra parallel source tasks sit idle. For the AliveMCP registry-scan burst (60× average for 2–3 minutes every 6 hours), set minimum KPU to handle the burst rather than letting autoscaling trigger — autoscaling restarts the application in 60–180 seconds, which falls within the burst window and causes window lag. For large-state applications (RocksDB >500 MB), use manual savepoint-based scaling to control timing.
- Connectors — know the exactly-once guarantee of every connector before claiming end-to-end exactly-once: Kinesis source: exactly-once ✓. KinesisStreamsSink: at-least-once only — deduplicate on probeId downstream. S3 FileSink with BulkFormat (Parquet): exactly-once ✓ — use
OnCheckpointRollingPolicy, not time-based rolling. KafkaSink: exactly-once only ifDeliveryGuarantee.EXACTLY_ONCE+ uniquetransactionalIdPrefix. OpenSearch sink: at-least-once — use deterministic document IDs. A Kinesis → window → KinesisStreamsSink pipeline is at-least-once end-to-end regardless of checkpoint mode.
Pattern 1: Checkpointing — RocksDB, savepoints, and operator UIDs
Checkpointing is the mechanism by which Managed Flink guarantees that a failed application resumes without re-processing events or losing accumulated window state. Every checkpoint writes a consistent snapshot of application state — window accumulators, aggregation buffers, Kinesis shard sequence numbers — to S3. On restart, Flink replays from the last successful checkpoint rather than from stream beginning. Without checkpointing enabled, a Managed Flink application running a 15-minute tumbling window over MCP probe results loses all in-window data on every restart, including transient TaskManager failures that would otherwise be invisible to users.
Checkpoint vs savepoint: the operational distinction
The most common checkpoint conceptual error in Managed Flink deployments is treating savepoints and checkpoints as interchangeable. They serve different purposes with different lifecycle management:
| Property | Checkpoint | Savepoint |
|---|---|---|
| Who triggers it | Flink runtime automatically on your configured interval | You manually via console, API, or CLI |
| Retention | Deleted when a newer checkpoint completes | Kept indefinitely until explicitly deleted |
| Purpose | Automatic recovery from transient failures | Planned upgrade, topology change, version migration |
| Operator UID requirement | Not required for simple topologies | Required for safe restore after topology changes |
| Creation time | Milliseconds to seconds (incremental with RocksDB) | Seconds to minutes (always full snapshot) |
For production MCP probe analytics, the operational pattern is: checkpoints every 60 seconds for automatic failure recovery; savepoints taken manually before any application version deployment, window size change, or Kinesis stream migration. If a savepoint restore fails — typically because operator UIDs were not assigned — Flink falls back to the latest checkpoint. If the checkpoint is also incompatible with the new topology, Flink starts from LATEST_TRIM_HORIZON on the Kinesis source, replaying all available shard history.
State backend selection: MemoryStateBackend vs RocksDB
The state backend determines where keyed state (window accumulators, deduplication sets, server-slug aggregation buffers) lives during normal operation. The default MemoryStateBackend stores all state in JVM heap — fast, but hard-limited by TaskManager heap size (~100 MB practical max before GC pressure causes checkpoint timeouts). For AliveMCP probe analytics, state size depends on window configuration:
// Choosing the right state backend for MCP probe analytics:
// MemoryStateBackend — appropriate when:
// 15-min tumbling window × 5,000 server slugs × 200 bytes/server = ~1 MB keyed state
// Well within TaskManager heap; checkpoint overhead negligible
// (Default — no explicit configuration needed for small-state applications)
// EmbeddedRocksDBStateBackend — required when:
// 24-hour session window × 5,000 server slugs × 50 KB/server = ~250 MB keyed state
// Exceeds JVM heap; RocksDB stores state on TaskManager disk with block cache
import org.apache.flink.contrib.streaming.state.EmbeddedRocksDBStateBackend;
EmbeddedRocksDBStateBackend rocksDB = new EmbeddedRocksDBStateBackend(
true // enableIncrementalCheckpointing — write only changed SST files per checkpoint interval
// For 10 GB state with 1% write rate: 100 MB per checkpoint instead of 10 GB
);
env.setStateBackend(rocksDB);
Incremental checkpointing with RocksDB is the single highest-impact configuration change for long-running analytics applications. A full checkpoint of 10 GB state takes 5–10 minutes and produces significant S3 write costs per interval. Incremental checkpoints write only the SST files that changed since the last checkpoint — at 1% write rate, that is 100 MB per checkpoint instead of 10 GB, a 100× reduction in checkpoint I/O and S3 cost.
Operator UIDs: the savepoint migration safety requirement
Every stateful operator must have a stable UID assigned before the first savepoint is taken. Flink auto-generates UIDs from the topology hash, but any topology change (adding an operator, reordering operators, changing operator names) changes the hash and makes auto-generated UIDs invalid for restoration. This is silent — the application starts, but keyed state for operators with mismatched UIDs is dropped:
// Correct pattern: explicit UIDs on all stateful operators
DataStream<ProbeResult> source = env
.addSource(new FlinkKinesisConsumer<>(...))
.uid("kinesis-source") // NEVER change this after first savepoint
.name("Kinesis Probe Source");
DataStream<ServerUptimeWindow> windows = source
.keyBy(ProbeResult::getServerSlug)
.window(TumblingEventTimeWindows.of(Time.minutes(15)))
.aggregate(new UptimeAggregator())
.uid("uptime-window-aggregator"); // Must be stable across deployments
windows
.addSink(new FlinkKinesisProducer<>(...))
.uid("kinesis-results-sink");
The rule: assign UIDs to every stateful operator (sources, window operators, sinks with transactional state) before the first deployment. Never change a UID after a savepoint has been taken from that operator. Stateless map/filter operators don't strictly require UIDs but benefit from them for future-proofing. See the full checkpointing guide at Managed Flink Checkpointing for MCP Servers for the complete CloudWatch alarm set and failure mode reference.
Pattern 2: VPC connectivity — MSK, private subnets, and endpoint configuration
Managed Flink can run without VPC configuration (using the Managed Flink service network, which reaches public endpoints) or inside your VPC (for private MSK brokers, RDS databases, and OpenSearch domains). The critical constraint for VPC deployment is that Managed Flink creates ENIs (Elastic Network Interfaces) in the subnets you specify — one per TaskManager per AZ — and all Flink-to-service communication traverses those ENIs. An application in a private subnet without a NAT gateway starts successfully in the Managed Flink console but fails silently on all data operations.
VPC endpoint requirements
Before deploying Managed Flink in private subnets, create VPC endpoints for every AWS service the application needs to reach:
| AWS Service | Endpoint type | Cost | Critical setting |
|---|---|---|---|
| Amazon S3 (checkpoints + JAR) | Gateway endpoint | Free | Must be added to route tables for all Flink subnets |
| Amazon Kinesis Data Streams | Interface endpoint | ~$7/month per AZ | private_dns_enabled = true — required or Flink resolves to public IP |
| Managed Flink control plane | Interface endpoint | ~$7/month per AZ | Required for JobManager ↔ service status updates |
| CloudWatch | Interface endpoint | ~$7/month per AZ | Required for metrics emission from private subnet |
| Secrets Manager | Interface endpoint | ~$7/month per AZ | Required for MSK SASL credential fetch at startup |
// Terraform: S3 Gateway endpoint (free — no Interface endpoint needed for S3)
resource "aws_vpc_endpoint" "s3" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.region}.s3"
vpc_endpoint_type = "Gateway"
route_table_ids = var.private_route_table_ids // Must include Flink subnet route tables
}
// Terraform: Kinesis Interface endpoint — private_dns_enabled is non-negotiable
resource "aws_vpc_endpoint" "kinesis_streams" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.region}.kinesis-streams"
vpc_endpoint_type = "Interface"
subnet_ids = var.private_subnet_ids
security_group_ids = [aws_security_group.vpc_endpoints.id]
private_dns_enabled = true // Overrides public DNS for kinesis.{region}.amazonaws.com
}
MSK authentication: TLS, SASL/SCRAM, and IAM
When Managed Flink connects to MSK as a Kafka source or sink, the port and authentication properties must match the MSK cluster's listener configuration. IAM authentication (port 9098) is recommended for new MSK clusters — it eliminates credential rotation and uses the Managed Flink application's execution role for access control:
// MSK IAM authentication (recommended for MSK Serverless and new provisioned clusters)
Properties kafkaProps = new Properties();
kafkaProps.setProperty("bootstrap.servers",
"b-1.msk-cluster.kafka.us-east-1.amazonaws.com:9098");
kafkaProps.setProperty("security.protocol", "SASL_SSL");
kafkaProps.setProperty("sasl.mechanism", "AWS_MSK_IAM");
kafkaProps.setProperty("sasl.jaas.config",
"software.amazon.msk.auth.iam.IAMLoginModule required;");
kafkaProps.setProperty("sasl.client.callback.handler.class",
"software.amazon.msk.auth.iam.IAMClientCallbackHandler");
// IAM auth uses the Managed Flink execution role — no credentials to rotate
For cross-VPC connectivity (Managed Flink in one VPC, MSK in another), security group rule references only work within the same VPC. Replace security group ID references with CIDR ranges for MSK broker subnets in cross-VPC rules. Also note that private DNS does not cross VPC peering boundaries by default — configure Route 53 Resolver forwarding rules or use explicit broker IP addresses in bootstrap.servers. Full VPC configuration reference and failure modes at Managed Flink VPC Connectivity for MCP Servers.
Pattern 3: KPU scaling — parallelism, autoscaling, and burst capacity
A Kinesis Processing Unit (KPU) is Managed Flink's compute unit: 1 KPU = 1 vCPU + 4 GB RAM. By default, each KPU provides one task slot, so application parallelism equals KPU count. Managed Flink bills KPU-hours regardless of whether the application is actively processing records — the scaling question is not just peak throughput, but minimum KPU count that doesn't fall behind during burst events without triggering expensive autoscaling restarts during those same events.
Operator-level parallelism and source shard alignment
The most common KPU waste in MCP probe analytics pipelines is setting source parallelism higher than the Kinesis shard count. Flink assigns shards to parallel source tasks round-robin — with 4 shards and 8 parallel source tasks, 4 tasks read one shard each and 4 tasks are idle, consuming KPU-hours without throughput benefit:
// Correct: source parallelism = shard count; aggregation at full parallelism
DataStream<String> source = env
.addSource(new FlinkKinesisConsumer<>(...))
.uid("kinesis-source")
.setParallelism(4); // Match shard count — not application parallelism
DataStream<ServerUptimeWindow> windows = source
.keyBy(ProbeResult::getServerSlug)
.window(TumblingEventTimeWindows.of(Time.minutes(15)))
.aggregate(new UptimeAggregator())
.uid("uptime-window-aggregator");
// Inherits application-level parallelism (4) — window keyed by serverSlug
// distributes evenly across all 4 parallel tasks via hash
windows
.map(new WindowSerializer())
.addSink(new KinesisFirehoseSink<>(...))
.uid("firehose-sink")
.setParallelism(1); // Reduce sink parallelism for low-throughput Firehose output
Capacity planning for the registry-scan burst
AliveMCP probes every registered endpoint every 60 seconds, but re-scans the full registry every 6 hours — producing a 60× burst above average throughput for 2–3 minutes. Autoscaling triggers at cpuUtilization > 80% with a 3-minute scale-out cooldown, then restarts the application in 60–180 seconds. A restart during the burst window causes window watermarks to stall and late-event alarms to fire minutes after actual endpoint state changes:
| Scale | Probe events/min | Recommended KPUs | Kinesis shards | Monthly KPU cost |
|---|---|---|---|---|
| 5,000 endpoints | 5,000/min = 83/s | 1 KPU | 1 shard | ~$53/month |
| 20,000 endpoints | 20,000/min = 333/s | 2 KPUs | 2 shards | ~$115/month |
| 50,000 endpoints | 50,000/min = 833/s | 4 KPUs | 4 shards | ~$230/month |
| 200,000 endpoints | 200,000/min = 3,333/s | 8 KPUs | 8 shards | ~$461/month |
For large-state applications where autoscaling restarts take 5–20 minutes (RocksDB state restoration), use manual savepoint-based scaling to control the timing: take a savepoint, update the KPU configuration, then restart from the savepoint during a low-traffic window. This avoids state restoration during the burst while giving you a rollback path if the new configuration is incorrect. Full capacity planning formulas and autoscaling failure modes at Managed Flink Parallelism and KPU Scaling for MCP Servers.
Pattern 4: Connector semantics — exactly-once support matrix
Managed Flink provides end-to-end exactly-once guarantees only when every connector in the pipeline participates in the checkpoint protocol. A Kinesis source feeding a window aggregation feeding a KinesisStreamsSink is at-least-once end-to-end regardless of checkpoint mode, because the KinesisStreamsSink uses PutRecords with at-least-once retry semantics. Knowing the guarantee of each connector is prerequisite to designing correct MCP probe analytics pipelines.
Kinesis source: exactly-once via shard sequence number checkpointing
import org.apache.flink.connector.kinesis.source.KinesisStreamsSource;
// New KinesisStreamsSource (Flink 1.15+) — recommended for new applications
// Exactly-once: shard sequence numbers checkpointed atomically with application state
KinesisStreamsSource<String> kdsSource = KinesisStreamsSource.<String>builder()
.setStreamArn("arn:aws:kinesis:us-east-1:123456789012:stream/alivemcp-probe-events")
.setDeserializationSchema(new SimpleStringSchema())
.setKinesisConsumerConfig(kinesisConsumerConfig)
.setStartingPosition(StartingPosition.fromTrimHorizon())
// Enhanced fan-out: dedicated 2 MB/s per shard — required during registry-scan burst
// when multiple Flink applications consume the same stream
.build();
S3 FileSink: exactly-once Parquet output via two-phase file naming
import org.apache.flink.connector.file.sink.FileSink;
import org.apache.flink.formats.parquet.avro.ParquetAvroWriters;
import org.apache.flink.streaming.api.functions.sink.filesystem.rollingpolicies.OnCheckpointRollingPolicy;
// S3 FileSink with Parquet BulkFormat — exactly-once when checkpointing is enabled
// In-progress files named .part-{taskId}-{checkpointId}.inprogress during write
// Renamed to final name only when checkpoint commits — partial files discarded on failure
FileSink<ProbeUptimeWindow> s3Sink = FileSink
.forBulkFormat(
new Path("s3://alivemcp-probe-analytics/uptime-windows/"),
ParquetAvroWriters.forReflectRecord(ProbeUptimeWindow.class)
)
.withBucketAssigner(new DateTimeBucketAssigner<>("'year='yyyy/'month='MM/'day='dd/'hour='HH"))
.withRollingPolicy(OnCheckpointRollingPolicy.build()) // Roll on checkpoint, not time
.build();
Use OnCheckpointRollingPolicy, not time-based rolling. Time-based rolling creates files that span checkpoint boundaries — a file rolled in the middle of a checkpoint interval may contain records from both before and after the checkpoint commit point, violating exactly-once guarantees. The in-progress/committed two-phase naming protocol only works when files roll at checkpoint boundaries.
Connector exactly-once support matrix
| Connector | Direction | Exactly-once? | Prerequisite |
|---|---|---|---|
| KinesisStreamsSource | Source | Yes | Flink 1.15+; checkpointing enabled |
| KinesisStreamsSink | Sink | No (at-least-once) | Deduplicate on probeId downstream |
| KafkaSource | Source | Yes | Checkpointing enabled |
| KafkaSink (EXACTLY_ONCE) | Sink | Yes | Kafka 0.11+; unique transactionalIdPrefix; transaction.timeout.ms > checkpoint interval |
| FileSink (BulkFormat / Parquet) | Sink | Yes | Checkpointing enabled; OnCheckpointRollingPolicy |
| FileSink (RowFormat) | Sink | At-least-once | Use BulkFormat for exactly-once |
| OpenSearch sink | Sink | No (at-least-once) | Use deterministic document IDs for idempotent indexing |
| JDBC sink | Sink | At-least-once | Use UPSERT on primary key for idempotency |
Kafka exactly-once requires setting DeliveryGuarantee.EXACTLY_ONCE with a unique transactionalIdPrefix per application deployment. If two application versions run with the same transactional ID prefix (e.g., during a deployment), the new producer fences the old producer with ProducerFencedException — this is the correct exactly-once handover behavior for upgrades. Avoid prefixes that embed run timestamps; use a fixed prefix per application name instead. Full connector configuration and failure modes at Managed Flink Connectors for MCP Servers.
Composing the four patterns: complete production checklist
The four patterns compose into a complete operational posture for Managed Flink running AliveMCP probe analytics. Failure in any one of them degrades the guarantees of the others — a correctly configured RocksDB checkpoint cannot protect against data loss if the KinesisStreamsSink has at-least-once semantics, and a correctly configured Kinesis Interface endpoint in one AZ fails half the time if the Interface endpoint was not created in the second AZ where Managed Flink places TaskManagers.
| Area | Critical configuration | Silent failure mode without it |
|---|---|---|
| Checkpointing | RocksDB + incremental + stable operator UIDs | MemoryStateBackend OOM crash loop; savepoint restore silently drops keyed state |
| VPC | S3 Gateway endpoint + Kinesis Interface endpoint (private_dns_enabled=true) in all AZs | Application starts successfully, reads 0 records, checkpoint writes fail |
| KPU scaling | Source parallelism = shard count; minimum KPU sized for burst | Idle KPU-hours from over-provisioned source tasks; window lag during autoscaling restart in burst window |
| Connectors | FileSink BulkFormat + OnCheckpointRollingPolicy for S3; deduplicate KinesisStreamsSink output | Duplicate probe results in downstream Athena queries; partial Parquet files from time-based rolling |
For new AliveMCP Managed Flink deployments, the deployment order that minimizes risk: configure VPC endpoints first (the application cannot function without them), then checkpoint configuration (RocksDB and operator UIDs must be set before the first run to avoid state loss on the first restart), then connector configuration (exactly-once semantics at sinks are easier to change before the first checkpoint has been taken), then capacity planning (autoscaling parameters can be tuned after observing production load for one week, using the initial CloudWatch metrics as the baseline).