Piotr Nowojski created FLINK-41256:
--------------------------------------

             Summary: Operators changing the current key out of order silently 
lose state in BATCH execution mode
                 Key: FLINK-41256
                 URL: https://issues.apache.org/jira/browse/FLINK-41256
             Project: Flink
          Issue Type: Bug
          Components: API / DataStream, Runtime / Task
    Affects Versions: 2.1.3, 2.2.1, 2.3.0
            Reporter: Piotr Nowojski
            Assignee: Piotr Nowojski


In BATCH execution mode, keyed operators run on BatchExecutionKeyedStateBackend 
with key-sorted inputs. The backend keeps the state of only one key at a time: 
whenever the current key changes, it fires all timers of the outgoing key and 
clears all of its state.

This assumes the current key only ever advances with the sorted input. Nothing 
enforces that: an operator can call setCurrentKey(...) itself 
(AbstractStreamOperator#setCurrentKey is public and reachable from custom 
operators added via DataStream#transform). An operator that buffers records of 
several keys and later processes them key by key goes back to keys that the 
input has already left. That is correct in STREAMING, but in BATCH it silently:
* reads empty state for the revisited key, as its state was already dropped,
* fires the outgoing key's timers early, before all of that key's records were 
processed.

The job finishes successfully and produces wrong results.

h3. Reproduction

A minimal custom keyed operator: it buffers 2 records of any keys, then sums 
each buffered record into a ValueState after setCurrentKey(record key), and 
also flushes on endInput.

Input (keyed by f0): (a,1), (a,1), (a,1), (b,1)

* STREAMING: a=3, b=1 (correct)
* BATCH: a=1, b=1

In BATCH, a1 and a2 are flushed into a's state (a=2) and a3 stays buffered. 
When b1 arrives, the backend switches to b and clears a's state. The flush of 
[a3, b1] then switches back to a, reads empty state and computes a=1.

Python operators already work around this in BATCH: 
AbstractPythonFunctionOperator#setCurrentKey flushes its bundle before the key 
changes.

h3. Proposed fix

Make the problem fail loudly and give operators a hook to flush before the key 
changes:
* Add KeyContext#moveToNextKey and KeyedStateBackend#moveToNextKey, which must 
never go back to a previous key. They default to setCurrentKey, so streaming 
backends are unaffected. Add KeyedStateBackend#requiresKeyOrder, true only for 
the batch backend.
* The keyed input path of AbstractStreamOperator(V2) uses moveToNextKey when 
the backend requires key order.
* BatchExecutionKeyedStateBackend#setCurrentKey throws for any key other than 
the current one.
* Operators that buffer across keys override moveToNextKey to flush first. The 
Python operators and the State Processor API bootstrap wrapper hook into 
moveToNextKey instead of setCurrentKey.



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

Reply via email to