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

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:

PropertyCheckpointSavepoint
Who triggers itFlink runtime automatically on your configured intervalYou manually via console, API, or CLI
RetentionDeleted when a newer checkpoint completesKept indefinitely until explicitly deleted
PurposeAutomatic recovery from transient failuresPlanned upgrade, topology change, version migration
Operator UID requirementNot required for simple topologiesRequired for safe restore after topology changes
Creation timeMilliseconds 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 ServiceEndpoint typeCostCritical setting
Amazon S3 (checkpoints + JAR)Gateway endpointFreeMust be added to route tables for all Flink subnets
Amazon Kinesis Data StreamsInterface endpoint~$7/month per AZprivate_dns_enabled = true — required or Flink resolves to public IP
Managed Flink control planeInterface endpoint~$7/month per AZRequired for JobManager ↔ service status updates
CloudWatchInterface endpoint~$7/month per AZRequired for metrics emission from private subnet
Secrets ManagerInterface endpoint~$7/month per AZRequired 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:

ScaleProbe events/minRecommended KPUsKinesis shardsMonthly KPU cost
5,000 endpoints5,000/min = 83/s1 KPU1 shard~$53/month
20,000 endpoints20,000/min = 333/s2 KPUs2 shards~$115/month
50,000 endpoints50,000/min = 833/s4 KPUs4 shards~$230/month
200,000 endpoints200,000/min = 3,333/s8 KPUs8 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

ConnectorDirectionExactly-once?Prerequisite
KinesisStreamsSourceSourceYesFlink 1.15+; checkpointing enabled
KinesisStreamsSinkSinkNo (at-least-once)Deduplicate on probeId downstream
KafkaSourceSourceYesCheckpointing enabled
KafkaSink (EXACTLY_ONCE)SinkYesKafka 0.11+; unique transactionalIdPrefix; transaction.timeout.ms > checkpoint interval
FileSink (BulkFormat / Parquet)SinkYesCheckpointing enabled; OnCheckpointRollingPolicy
FileSink (RowFormat)SinkAt-least-onceUse BulkFormat for exactly-once
OpenSearch sinkSinkNo (at-least-once)Use deterministic document IDs for idempotent indexing
JDBC sinkSinkAt-least-onceUse 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.

AreaCritical configurationSilent failure mode without it
CheckpointingRocksDB + incremental + stable operator UIDsMemoryStateBackend OOM crash loop; savepoint restore silently drops keyed state
VPCS3 Gateway endpoint + Kinesis Interface endpoint (private_dns_enabled=true) in all AZsApplication starts successfully, reads 0 records, checkpoint writes fail
KPU scalingSource parallelism = shard count; minimum KPU sized for burstIdle KPU-hours from over-provisioned source tasks; window lag during autoscaling restart in burst window
ConnectorsFileSink BulkFormat + OnCheckpointRollingPolicy for S3; deduplicate KinesisStreamsSink outputDuplicate 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).