Dian Fu created FLINK-41102:
-------------------------------

             Summary: Keyed state clear operations are lost on key changes when 
python.state.cache-size is zero
                 Key: FLINK-41102
                 URL: https://issues.apache.org/jira/browse/FLINK-41102
             Project: Flink
          Issue Type: Bug
          Components: API / Python
            Reporter: Dian Fu


When python.state.cache-size is set to 0, a pending clear() operation can be 
lost if the current key changes before the state backend commits. Switching 
back to the original key can then make the previously cleared state visible 
again.

This issue is found during https://github.com/apache/flink/pull/29576.

Reproduction scenario

Configure python.state.cache-size=0 and use a keyed Python operator with 
ValueState:

1. Store 100 for key A and complete a bundle so that the value is persisted.
2. In a subsequent bundle, process a record for key A and call state.clear().
3. Process a record for key B before the bundle is committed.
4. Process another record for key A and call state.value().

Steps 2–4 must execute without an intervening state commit.

Expected result

state.value() returns None because the state for key A was explicitly cleared.

Actual result

state.value() returns the previously persisted value, 100.

Reading the state immediately after clear(), before switching keys, correctly 
returns None. The deletion is lost during the key change.

Root cause

SynchronousValueRuntimeState.clear() delegates to Beam's 
SynchronousBagRuntimeState.clear(), which only updates local state:

    self._cleared = True
    self._added_elements = []

The remote deletion is deferred until commit().

However, RemoteKeyedStateBackend.set_current_key() only retains the previous 
internal state object when the state cache size is greater than zero:

    if self._state_cache_size > 0:
        self.cache_internal_state(encoded_old_key, state_obj)

    state_obj.namespace = None
    state_obj._internal_state = None

With a zero-sized cache, the previous object is discarded without being 
committed. Its pending clear marker is therefore lost. Switching back creates a 
new internal state object, which reads the unchanged value from the remote 
backend.

Ordinary bag-state writes handle this configuration through 
_maybe_clear_write_cache(), which commits immediately when the cache size is 
zero. clear() does not invoke that path.

Affected code is in flink-python/pyflink/fn_execution/state_impl.py.

The same lifecycle problem affects other keyed state implementations with 
deferred clear operations, including ListState, ReducingState, 
AggregatingState, and MapState.

Verification

Confirmed through source inspection and deterministic Python-level 
reproductions using the real state implementations with modeled remote state 
I/O.

A JVM integration regression test should cover clearing state and switching 
keys within the same bundle with python.state.cache-size=0.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to