Aaron He created FLINK-41093:
--------------------------------
Summary: AsyncKeyedStateBackendAdaptor ignores the TTL config of
State V2 descriptors
Key: FLINK-41093
URL: https://issues.apache.org/jira/browse/FLINK-41093
Project: Flink
Issue Type: Bug
Components: Runtime / State Backends
Reporter: Aaron He
h2. Problem
When the State V2 (async state) API runs on a state backend without native
async state support, that is HashMap or RocksDB, Flink wraps the keyed state
backend in AsyncKeyedStateBackendAdaptor. Keyed state created through this
adaptor ignores the TTL configured with StateDescriptor#enableTimeToLive.
Values never expire and are never cleaned up.
SQL operators with table.exec.async-state.enabled set a state TTL this way, for
example AsyncStateGroupAggFunction. On HashMap or RocksDB they keep state
beyond the configured retention, so state can grow without bound, and their
results differ from the sync operators once the TTL has passed. ForSt
implements State V2 natively and is not affected.
h2. Cause
AsyncKeyedStateBackendAdaptor#createStateInternal converts the V2 descriptor
with StateDescriptorUtils#transformFromV2ToV1 and passes the result to
KeyedStateBackend#getPartitionedState. The conversion copies only the state
name and the serializer or type information, so the V1 descriptor has TTL
disabled and TtlStateFactory never wraps the state. This holds for value, list,
map, reducing and aggregating state.
h2. Proposed fix
Copy the V2 descriptor's StateTtlConfig onto the converted V1 descriptor in
AsyncKeyedStateBackendAdaptor. Both descriptor versions use the same
StateTtlConfig class, and the V1 TTL wrappers implement every method the V2
state adaptors call.
StateDescriptorUtils stays unchanged. Its other production caller,
DefaultOperatorStateBackend, creates operator state, which does not support TTL.
h2. Reproduction
# Create an AsyncKeyedStateBackendAdaptor over a HeapKeyedStateBackend that
uses a controllable TtlTimeProvider.
# Create value, list, map, reducing and aggregating state from V2 descriptors
with a 10 ms TTL.
# Write one entry to each state at time 0, then advance the TTL time to 10 ms.
# All five states still return their values.
h2. Planned work
I have prepared the one-line fix with a deterministic regression test covering
all five state types. Two existing tests asserted the no-expiry behavior for
async state on the HashMap backend: RowTimeDeduplicateFunctionTest and
GroupAggregateHarnessTest#testAggregateWithRetraction. Their async expectations
now match the sync ones. StreamingJoinOperatorTest skipped its four state
retention tests for async state because of this issue; they now run and pass.
No upstream PR has been opened yet.
h2. Compatibility
Existing async state jobs on HashMap or RocksDB with a configured TTL start
expiring state after the upgrade.
A checkpoint or savepoint written before the fix stores this state without TTL.
Restoring it with TTL now enabled uses the existing TTL state migration of the
HashMap and RocksDB backends, covered by
StateBackendMigrationTestBase#testStateMigrationAfterChangingTTLFromDisablingToEnabling.
Migrated entries are timestamped at restore time, so pre-existing state
expires one TTL after the restore. Reducing and aggregating state use the same
migration code as value state.
Unlike the native ForSt path, the adaptor path applies the V1 TTL rules,
including the V1 cleanup strategies and update types.
Note on documentation: the State V2 page
(docs/content/docs/dev/datastream/fault-tolerance/state_v2.md) says that
restoring state with TTL enabled when it was written without TTL, or the
reverse, fails with StateMigrationException. That describes native State V2
backends. On the adaptor path (HashMap and RocksDB) the V1 TTL migration
applies instead. The proposed fix does not change the documentation; a
clarification could follow separately.
h2. Proposed release note
Async state (the State V2 API, including SQL operators with
table.exec.async-state.enabled) on the HashMap and RocksDB state backends now
honors the configured state TTL. Previously such state never expired. After
restoring from an earlier checkpoint or savepoint, existing entries are treated
as written at restore time and expire one TTL later.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)