Yaoxuan Wu created FLINK-41250:
----------------------------------

             Summary: Wrong result with auto-parallelism: a KEEP_INPUT_AS_IS 
exchange is replaced by a hash partitioner on duplicated keys [a, a]
                 Key: FLINK-41250
                 URL: https://issues.apache.org/jira/browse/FLINK-41250
             Project: Flink
          Issue Type: Bug
          Components: Runtime / Coordination, Table SQL / Planner
    Affects Versions: 2.2.1, 2.3.0, 2.0.1
            Reporter: Yaoxuan Wu


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

c = Configuration()
for k, v in [('parallelism.default', '-1'),
             ('execution.batch.adaptive.auto-parallelism.enabled', 'true'),
             ('execution.batch.adaptive.auto-parallelism.min-parallelism', '2'),
             ('execution.batch.adaptive.auto-parallelism.max-parallelism', 
'4')]:
    c.set_string(k, v)
t = 
TableEnvironment.create(EnvironmentSettings.new_instance().in_batch_mode().with_configuration(c).build())
t.create_temporary_view('t', t.from_elements(
    [(1, True, None), (2, False, True), (3, False, True), (4, True, None), (5, 
True, False),
     (6, True, True), (7, True, False), (8, True, True), (9, True, False), (10, 
True, False)],
    D.ROW([D.FIELD('id', D.INT()), D.FIELD('a', D.BOOLEAN()), D.FIELD('b', 
D.BOOLEAN())])))

rows = t.execute_sql("SELECT id, a IN (SELECT i.a FROM t i WHERE i.b IS NOT 
DISTINCT FROM o.a) AS m FROM t o").collect()
print(sorted(tuple(r) for r in rows))
# expected (and with any fixed parallelism): m = TRUE for 8 rows, FALSE for ids 
2 and 3
# actual: m = FALSE for all 10 rows
{code}
Correct with a fixed parallelism (1, 2 or 4), with auto-parallelism and 
max-parallelism 1, and with {{=}} instead of {{IS NOT DISTINCT FROM}}.

The decorrelated plan has an inner join whose right input is kept as is but 
declared as hashed on a duplicated key:
{code}
HashJoin(joinType=[InnerJoin], where=[(IS NOT DISTINCT FROM(b, a0) AND (a0 = 
a))], ...)
:- Exchange(distribution=[hash[a]])
+- Exchange(distribution=[keep_input_as_is[hash[a, a]]])
   +- HashAggregate(isMerge=[true], groupBy=[a], select=[a])
      +- Exchange(distribution=[hash[a]])
{code}
Cause: {{BatchExecExchange}} translates the non-strict {{KEEP_INPUT_AS_IS}} 
into {{ForwardForConsecutiveHashPartitioner(createHashPartitioner(hash[a, 
a]))}}. With the adaptive batch scheduler the stream graph is dynamic, and 
{{StreamingJobGraphGenerator.tryConvertPartitionerForDynamicGraph}} replaces 
this non-chainable edge by {{getHashPartitioner()}}, i.e. a 
{{BinaryHashPartitioner}} over the keys {{[a, a]}}. {{HashCodeGenerator}} 
hashes {{[a, a]}} as {{hash(a) * HASH_SALT(1) + hash(a)}}, not {{hash(a)}}, so 
the right input is distributed differently from the left input ({{hash[a]}}), 
matching rows never meet, and the join is empty. With a fixed parallelism the 
edge stays a forward edge and the data keeps its {{hash[a]}} placement. The 
planner should not record (or accept) {{hash[a, a]}} for an input that is 
hashed on {{[a]}}, e.g. by deduplicating the hash keys.

Reproduces on 2.0.1, 2.2.1, 2.3.0 and master.




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

Reply via email to