Yaoxuan Wu created FLINK-41249:
----------------------------------

             Summary: Async-state streaming join returns rows joined with 
another key's match when the join key contains the unique key (ForSt)
                 Key: FLINK-41249
                 URL: https://issues.apache.org/jira/browse/FLINK-41249
             Project: Flink
          Issue Type: Bug
          Components: Table SQL / Runtime
            Reporter: Yaoxuan Wu


{code:python}
from pyflink.common import Configuration
from pyflink.table import EnvironmentSettings, TableEnvironment, DataTypes as D

c = Configuration()
c.set_string('parallelism.default', '1')
c.set_string('table.exec.async-state.enabled', 'true')
c.set_string('state.backend.type', 'forst')
t = 
TableEnvironment.create(EnvironmentSettings.new_instance().in_streaming_mode().with_configuration(c).build())
t.create_temporary_view('t', t.from_elements([(i, 'v%d' % i) for i in range(7)],
    D.ROW([D.FIELD('k', D.INT()), D.FIELD('s', D.STRING())])))

rows = t.execute_sql("""SELECT a.k FROM (SELECT DISTINCT k FROM t) a
                        JOIN (SELECT DISTINCT k FROM t) b ON a.k = 
b.k""").collect()
print(sorted(r[0] for r in rows))
# expected: [0, 1, 2, 3, 4, 5, 6]
# actual (varies between runs): [6, 6, 6, 6, 6, 6, 6], [0, 0, 0, 0, 0, 0, 0], 
...
{code}
Correct with synchronous state (on hashmap or ForSt) and with async state on 
the hashmap backend; wrong in every run with async state on ForSt.

Cause: when the join key contains the input's unique key, 
{{JoinRecordAsyncStateViews.JoinKeyContainsUniqueKey}} keeps one 
{{List<OuterRecord> reusedList}} per view and returns it from every 
{{findMatchedRecords}} call ({{reusedList.clear(); reusedList.add(...); return 
reusedList;}}). {{AssociatedRecords.fromAsyncStateView}} wraps that list lazily 
({{.thenApply(AssociatedRecords::new)}}), and 
{{AsyncStateStreamingJoinOperator.doProcessElement}} iterates it later in 
{{.thenAccept(... joinHelper.processJoin(...))}}. With a truly asynchronous 
backend (ForSt) these stages of different in-flight records interleave, so 
another record's lookup clears and refills the list before the first record's 
{{processJoin}} reads it: the record is joined with the other key's match (its 
join condition was never checked against it). 
{{OuterJoinRecordAsyncStateViews.JoinKeyContainsUniqueKey}} has the same shared 
{{reusedList}}. The synchronous views are safe because each record finishes 
before the next starts. Allocating the (at most one-element) list per call 
would fix it.

Reproduces on 2.0.1, 2.2.1 and 2.3.0; master has the same code.




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

Reply via email to