# Flink 2.x Migration Guide
## Overview
This guide covers migrating Flink applications from 1.x to 2.x. Key changes: Java 17 minimum (Java 8 and 11 no longer supported), major API removals, and serialization breaking changes affecting state compatibility.
**CRITICAL**: State migration from 1.x to 2.x fails for applications using Kryo or POJOs with collection fields. See [State Compatibility](#state-compatibility) before planning migrations.
## Major Breaking Changes Summary
| Category | Change | Impact |
|----------|--------|--------|
| Java | Java 8 and 11 removed, Java 17 default, Java 21 experimental (not supported in MSF) | Must upgrade runtime |
| Source API | `SourceFunction` removed | Must migrate to new Source API |
| Sink API | `SinkFunction`, `SinkV1` removed | Must migrate to Sink V2 API |
| Config | `flink-conf.yaml` removed | Must use `config.yaml` (standard YAML) |
| Time API | `Time` class deprecated | Use `java.time.Duration` |
| Serialization | Kryo 2.x → 5.x, new collection serializers | State incompatibility (see below) |
| DataSet API | Entire DataSet API removed | Migrate to DataStream or Table API/SQL |
| Scala API | Scala API removed entirely | Use Java API (callable from Scala) |
| Python | Python 3.8 removed, Python 3.12 default | Update Python runtime |
| DataStream | `IterativeStream`, `TimeCharacteristic` removed | Refactor required |
## Dependency Changes
### Version Updates Required
```xml
2.2.0
17
17
17
4.0.1-2.0
6.0.0-2.0
2.3.5
1.12.677
2.15.2
1.18.36
2.23.1
3.11.0
```
### Logging Changes
Flink 2.x uses `log4j-slf4j-impl` instead of `slf4j-log4j12`.
> **Note:** If migrating from Flink 1.20, you likely already use `log4j-slf4j-impl`. This change only applies when migrating from Flink versions older than ~1.15 that used `slf4j-log4j12`.
```xml
org.apache.logging.log4j
log4j-slf4j-impl
${log4j.version}
org.apache.logging.log4j
log4j-api
${log4j.version}
org.apache.logging.log4j
log4j-core
${log4j.version}
```
### Glue Schema Registry Conflict
Exclude old flink-avro from Glue Schema Registry until it is updated for Flink 2.x:
```xml
org.apache.flink
flink-avro
${flink.version}
software.amazon.glue
schema-registry-flink-serde
1.1.15
org.apache.flink
flink-avro
```
### Scope Changes for Standalone Deployment
For non-Managed Service for Apache Flink deployment, change scope from `provided` to `compile`:
```xml
org.apache.flink
flink-streaming-java
${flink.version}
compile
```
## Code Changes
### Time API Migration
Replace `org.apache.flink.streaming.api.windowing.time.Time` with `java.time.Duration`:
```java
// Before (1.x)
import org.apache.flink.streaming.api.windowing.time.Time;
.window(TumblingProcessingTimeWindows.of(Time.seconds(10)))
.window(SlidingProcessingTimeWindows.of(Time.minutes(1), Time.seconds(1)))
.window(EventTimeSessionWindows.withGap(Time.seconds(30)))
.within(Time.seconds(10))
// After (2.x)
import java.time.Duration;
.window(TumblingProcessingTimeWindows.of(Duration.ofSeconds(10)))
.window(SlidingProcessingTimeWindows.of(Duration.ofMinutes(1), Duration.ofSeconds(1)))
.window(EventTimeSessionWindows.withGap(Duration.ofSeconds(30)))
.within(Duration.ofSeconds(10))
```
### Configuration API Migration
Replace string-based config with type-safe ConfigOptions:
```java
// Before (1.x)
config.setInteger("rest.port", 8081);
config.setBoolean("web.submit.enable", true);
// After (2.x)
import org.apache.flink.configuration.RestOptions;
import org.apache.flink.configuration.WebOptions;
config.set(RestOptions.PORT, 8081);
config.set(WebOptions.SUBMIT_ENABLE, true);
```
### Function Lifecycle Changes
`open()` method signature changed from `Configuration` to `OpenContext`:
```java
// Before (1.x)
@Override
public void open(Configuration parameters) {
// initialization
}
// After (2.x)
@Override
public void open(org.apache.flink.api.common.functions.OpenContext openContext) throws Exception {
// initialization
}
```
This change applies to every `RichFunction` subclass — `RichMapFunction`, `RichFlatMapFunction`, `RichFilterFunction`, `KeyedProcessFunction`, `BroadcastProcessFunction`, `KeyedBroadcastProcessFunction`, `ProcessWindowFunction`, async I/O `RichAsyncFunction`, etc. Any code that overrides `open(Configuration)` will fail to compile against Flink 2.2. `OpenContext` does not carry the legacy `Configuration` key/value bag — read runtime properties via the `KinesisAnalyticsRuntime.getApplicationProperties()` flow or pass them through your function's constructor.
The `open()` change is one of several Flink 2.x breaking API changes you'll likely hit during the same migration. See [Removed APIs and Migration Paths](#removed-apis-and-migration-paths) for the full table; the headline removals are:
- `SourceFunction` and `SinkFunction` are removed in favor of `Source` (FLIP-27) and `Sink` (FLIP-143) — `env.addSource()` / `stream.addSink()` no longer compile.
- `org.apache.flink.api.common.time.Time` is deprecated in favor of `java.time.Duration`. Anything that took `Time` (TTL, idleness, async I/O timeout) now takes `Duration`.
- `TimeCharacteristic` is removed — event-time is the only mode and `setStreamTimeCharacteristic()` is gone.
- `enableForceAvro()` and the convenience Kryo registration methods on `StreamExecutionEnvironment` are removed; use `env.getConfig()` equivalents.
### CEP Pattern Type Information
CEP requires explicit TypeInformation for pattern output:
```java
// Before (1.x)
CEP.pattern(stream, pattern)
.inEventTime()
.select(this::extractResult);
// After (2.x)
import org.apache.flink.api.common.typeinfo.TypeHint;
import org.apache.flink.api.common.typeinfo.TypeInformation;
CEP.pattern(stream, pattern)
.inEventTime()
.select(
this::extractResult,
TypeInformation.of(new TypeHint() {})
);
```
### POJO Requirements
POJOs must implement Serializable with no-args constructor:
```java
// Before (1.x) - might work without these
@Data
@Builder
public class Event {
private String id;
private long timestamp;
}
// After (2.x) - required for proper serialization
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Event implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private long timestamp;
}
```
### Removed Classes
- `org.apache.flink.streaming.api.TimeCharacteristic` - removed, event time is default
- `org.apache.flink.api.java.typeutils.runtime.kryo.Serializers` - Kryo serializer classes removed
- `flink-java` module removed entirely
## State Compatibility
### Breaking Changes Summary
Three serialization incompatibilities prevent state migration from 1.x to 2.x:
| Issue | Root Cause | Affected Patterns | Error Signature |
|-------|-----------|-------------------|-----------------|
| Kryo reference tracking | Kryo 2.x → 5.x upgrade | `registerTypeWithKryoSerializer()` | `IndexOutOfBoundsException: Index 116 out of bounds for length 1` |
| Kryo CollectionSerializer | Kryo's internal collection format changed | Generic collections: `List`, `Map`, `Set` in state | `ClassNotFoundException: value2` (data misinterpreted as class names) |
| PojoSerializer collection handling | TypeExtractor selects different serializers (FLINK-34037) | POJO fields: List, Map, Set, Collection, Queue, Deque | `StateMigrationException: PojoSerializer@8bf85b5d incompatible with @3282ee3` |
### Compatible Patterns (State Migrates Successfully)
**Serialization Methods:**
- **Avro serialization** with explicit `AvroTypeInfo` - schema-based, independent of Flink's type system
- **Protobuf serialization** - schema-based, bypasses TypeExtractor
- **Custom TypeSerializer** implementations - user-controlled serialization
- **Simple POJOs** without collection fields - no TypeExtractor collection handling
- **Flink Tuples** - direct field access, no reflection
- **Primitive types** - fastest, no serialization changes
**State Types (all compatible with above serializers):**
- ValueState, MapState, ListState, ReducingState, AggregatingState
- BroadcastState with control streams
- Operator state (even-split and union redistribution)
- Window state (tumbling, sliding, session windows)
- Timer state (event-time and processing-time)
**Connectors:**
- Kinesis connector state (v5.0+ only — see note below; default polling and Enhanced Fan-Out)
- Kafka connector state (offsets, partition tracking)
**CRITICAL — Kinesis Connector Version Prerequisite:** KDS connector versions below 5.0 maintain state that is incompatible with the Flink 2.2 Kinesis connector (v6.0.0-2.0). You must migrate to connector v5.0+ on Flink 1.x before upgrading to Flink 2.x. See `kinesis-connector-guide.md` for migration paths and the [AWS blog post](https://aws.amazon.com/blogs/big-data/introducing-the-new-amazon-kinesis-source-connector-for-apache-flink/) for details.
**Table API/SQL (with caveat):**
- All tested patterns compatible: GROUP BY, window aggregations (TUMBLE, HOP, SESSION)
- Stream joins (INNER, LEFT OUTER), Top-N, deduplication
- DISTINCT aggregations, OVER windows
- Table API provides alternative migration path avoiding DataStream serialization issues
- **Caveat:** Apache Flink does not guarantee state compatibility between major versions for Table API applications. Always test in a non-production environment first.
### Incompatible Patterns (State Migration Fails)
**Direct Kryo Usage:**
```java
// INCOMPATIBLE - Kryo 2.x → 5.x reference tracking changed
env.getConfig().registerTypeWithKryoSerializer(MyType.class, MyKryoSerializer.class);
```
**Generic Collections in State:**
```java
// INCOMPATIBLE - Kryo CollectionSerializer format changed
ValueState> listState;
ValueState