diff --git a/.github/workflows/docs.sh b/.github/workflows/docs.sh index b8a088d31d1c9..1d08bbbafef26 100755 --- a/.github/workflows/docs.sh +++ b/.github/workflows/docs.sh @@ -22,6 +22,8 @@ mvn --version java -version javadoc -J-version +# workaround for a git security patch +git config --global --add safe.directory /root/flink git submodule update --init --recursive cd docs diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 1d17d0057a076..26754d2c1e6f6 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -43,12 +43,12 @@ jobs: if [ "${currentBranch}" = "master" ]; then echo "flink_alias=release-1.19" >> ${GITHUB_ENV} - elif [ "${currentBranch}" = "release-1.17" ]; then + elif [ "${currentBranch}" = "release-1.18" ]; then echo "flink_alias=stable" >> ${GITHUB_ENV} fi - name: Build documentation run: | - docker run --rm --volume "$PWD:/root/flink" chesnay/flink-ci:java_8_11 bash -c "cd /root/flink && ./.github/workflows/docs.sh" + docker run --rm --volume "$PWD:/root/flink" chesnay/flink-ci:java_8_11_17_maven_386_v2 bash -c "cd /root/flink && ./.github/workflows/docs.sh" - name: Upload documentation uses: burnett01/rsync-deployments@5.2 with: diff --git a/README.md b/README.md index b2691132ffa24..a510cc8996216 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Prerequisites for building Flink: * Unix-like environment (we use Linux, Mac OS X, Cygwin, WSL) * Git -* Maven (we recommend version 3.8.6 and require at least 3.1.1) +* Maven (we require version 3.8.6) * Java 8 or 11 (Java 9 or 10 may work) ``` @@ -80,9 +80,6 @@ cd flink Flink is now installed in `build-target`. -*NOTE: Maven 3.3.x can build Flink, but will not properly shade away certain dependencies. Maven 3.1.1 creates the libraries properly. -To build unit tests with Java 8, use Java 8u51 or above to prevent failures in unit tests that use the PowerMock runner.* - ## Developing Flink The Flink committers use IntelliJ IDEA to develop the Flink codebase. diff --git a/docs/content.zh/_index.md b/docs/content.zh/_index.md index 6ce0a0ab09180..f25de48eb0f8a 100644 --- a/docs/content.zh/_index.md +++ b/docs/content.zh/_index.md @@ -85,7 +85,8 @@ under the License. For some reason Hugo will only allow linking to the release notes if there is a leading '/' and file extension. --> -请参阅 [Flink 1.17]({{< ref "/release-notes/flink-1.17.md" >}}), +请参阅 [Flink 1.18]({{< ref "/release-notes/flink-1.18.md" >}}), +[Flink 1.17]({{< ref "/release-notes/flink-1.17.md" >}}), [Flink 1.16]({{< ref "/release-notes/flink-1.16.md" >}}), [Flink 1.15]({{< ref "/release-notes/flink-1.15.md" >}}), [Flink 1.14]({{< ref "/release-notes/flink-1.14.md" >}}), diff --git a/docs/content.zh/docs/connectors/table/kafka.md b/docs/content.zh/docs/connectors/table/kafka.md deleted file mode 100644 index 635c5e14dabe1..0000000000000 --- a/docs/content.zh/docs/connectors/table/kafka.md +++ /dev/null @@ -1,630 +0,0 @@ ---- -title: Kafka -weight: 3 -type: docs -aliases: - - /zh/dev/table/connectors/kafka.html ---- - - -# Apache Kafka SQL 连接器 - -{{< label "Scan Source: Bounded" >}} -{{< label "Scan Source: Unbounded" >}} -{{< label "Sink: Streaming Append Mode" >}} - -Kafka 连接器提供从 Kafka topic 中消费和写入数据的能力。 - -依赖 ------------- - -{{< sql_download_table "kafka" >}} - -Kafka 连接器目前并不包含在 Flink 的二进制发行版中,请查阅[这里]({{< ref "docs/dev/configuration/overview" >}})了解如何在集群运行中引用 Kafka 连接器。 - -如何创建 Kafka 表 ----------------- - -以下示例展示了如何创建 Kafka 表: - -```sql -CREATE TABLE KafkaTable ( - `user_id` BIGINT, - `item_id` BIGINT, - `behavior` STRING, - `ts` TIMESTAMP_LTZ(3) METADATA FROM 'timestamp' -) WITH ( - 'connector' = 'kafka', - 'topic' = 'user_behavior', - 'properties.bootstrap.servers' = 'localhost:9092', - 'properties.group.id' = 'testGroup', - 'scan.startup.mode' = 'earliest-offset', - 'format' = 'csv' -) -``` - -可用的元数据 ------------------- - -以下的连接器元数据可以在表定义中通过元数据列的形式获取。 - -`R/W` 列定义了一个元数据是可读的(`R`)还是可写的(`W`)。 -只读列必须声明为 `VIRTUAL` 以在 `INSERT INTO` 操作中排除它们。 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
数据类型描述R/W
topicSTRING NOT NULLKafka 记录的 Topic 名。R
partitionINT NOT NULLKafka 记录的 partition ID。R
headersMAP NOT NULL二进制 Map 类型的 Kafka 记录头(Header)。R/W
leader-epochINT NULLKafka 记录的 Leader epoch(如果可用)。R
offsetBIGINT NOT NULLKafka 记录在 partition 中的 offset。R
timestampTIMESTAMP_LTZ(3) NOT NULLKafka 记录的时间戳。R/W
timestamp-typeSTRING NOT NULLKafka 记录的时间戳类型。可能的类型有 "NoTimestampType", - "CreateTime"(会在写入元数据时设置),或 "LogAppendTime"。R
- -以下扩展的 `CREATE TABLE` 示例展示了使用这些元数据字段的语法: - -```sql -CREATE TABLE KafkaTable ( - `event_time` TIMESTAMP_LTZ(3) METADATA FROM 'timestamp', - `partition` BIGINT METADATA VIRTUAL, - `offset` BIGINT METADATA VIRTUAL, - `user_id` BIGINT, - `item_id` BIGINT, - `behavior` STRING -) WITH ( - 'connector' = 'kafka', - 'topic' = 'user_behavior', - 'properties.bootstrap.servers' = 'localhost:9092', - 'properties.group.id' = 'testGroup', - 'scan.startup.mode' = 'earliest-offset', - 'format' = 'csv' -); -``` - -**格式元信息** - -连接器可以读出消息格式的元数据。格式元数据的配置键以 `'value.'` 作为前缀。 - -以下示例展示了如何获取 Kafka 和 Debezium 的元数据字段: - -```sql -CREATE TABLE KafkaTable ( - `event_time` TIMESTAMP_LTZ(3) METADATA FROM 'value.source.timestamp' VIRTUAL, -- from Debezium format - `origin_table` STRING METADATA FROM 'value.source.table' VIRTUAL, -- from Debezium format - `partition_id` BIGINT METADATA FROM 'partition' VIRTUAL, -- from Kafka connector - `offset` BIGINT METADATA VIRTUAL, -- from Kafka connector - `user_id` BIGINT, - `item_id` BIGINT, - `behavior` STRING -) WITH ( - 'connector' = 'kafka', - 'topic' = 'user_behavior', - 'properties.bootstrap.servers' = 'localhost:9092', - 'properties.group.id' = 'testGroup', - 'scan.startup.mode' = 'earliest-offset', - 'value.format' = 'debezium-json' -); -``` - -连接器参数 ----------------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
参数是否必选默认值数据类型描述
connector
必选(无)String指定使用的连接器,Kafka 连接器使用 'kafka'
topic
required for sink(无)String当表用作 source 时读取数据的 topic 名。亦支持用分号间隔的 topic 列表,如 'topic-1;topic-2'。注意,对 source 表而言,'topic' 和 'topic-pattern' 两个选项只能使用其中一个。当表被用作 sink 时,该配置表示写入的 topic 名。注意 sink 表不支持 topic 列表。
topic-pattern
可选(无)String匹配读取 topic 名称的正则表达式。在作业开始运行时,所有匹配该正则表达式的 topic 都将被 Kafka consumer 订阅。注意,对 source 表而言,'topic' 和 'topic-pattern' 两个选项只能使用其中一个。
properties.bootstrap.servers
必选(无)String逗号分隔的 Kafka broker 列表。
properties.group.id
对 source 可选,不适用于 sink(无)StringKafka source 的消费组 id。如果未指定消费组 ID,则会使用自动生成的 "KafkaSource-{tableIdentifier}" 作为消费组 ID。
properties.*
可选(无)String - 可以设置和传递任意 Kafka 的配置项。后缀名必须匹配在 Kafka 配置文档 中定义的配置键。Flink 将移除 "properties." 配置键前缀并将变换后的配置键和值传入底层的 Kafka 客户端。例如,你可以通过 'properties.allow.auto.create.topics' = 'false' 来禁用 topic 的自动创建。但是某些配置项不支持进行配置,因为 Flink 会覆盖这些配置,例如 'key.deserializer''value.deserializer'。 -
format
必选(无)String用来序列化或反序列化 Kafka 消息的格式。 - 请参阅 }}">格式 页面以获取更多关于格式的细节和相关配置项。 - 注意:该配置项和 'value.format' 二者必需其一。 -
key.format
可选(无)String用来序列化和反序列化 Kafka 消息键(Key)的格式。 - 请参阅 }}">格式 页面以获取更多关于格式的细节和相关配置项。 - 注意:如果定义了键格式,则配置项 'key.fields' 也是必需的。 - 否则 Kafka 记录将使用空值作为键。 -
key.fields
可选[]List<String>表结构中用来配置消息键(Key)格式数据类型的字段列表。默认情况下该列表为空,因此消息键没有定义。 - 列表格式为 'field1;field2'。 -
key.fields-prefix
可选(无)String为所有消息键(Key)格式字段指定自定义前缀,以避免与消息体(Value)格式字段重名。默认情况下前缀为空。 - 如果定义了前缀,表结构和配置项 'key.fields' 都需要使用带前缀的名称。 - 当构建消息键格式字段时,前缀会被移除,消息键格式将会使用无前缀的名称。 - 请注意该配置项要求必须将 'value.fields-include' 配置为 'EXCEPT_KEY'。 -
value.format
必选(无)String序列化和反序列化 Kafka 消息体时使用的格式。 - 请参阅 }}">格式 页面以获取更多关于格式的细节和相关配置项。 - 注意:该配置项和 'format' 二者必需其一。 -
value.fields-include
可选ALL

枚举类型

可选值:[ALL, EXCEPT_KEY]
定义消息体(Value)格式如何处理消息键(Key)字段的策略。 - 默认情况下,表结构中 'ALL' 即所有的字段都会包含在消息体格式中,即消息键字段在消息键和消息体格式中都会出现。 -
scan.startup.mode
可选group-offsetsEnumKafka consumer 的启动模式。有效值为:'earliest-offset''latest-offset''group-offsets''timestamp''specific-offsets'。 - 请参阅下方 起始消费位点 以获取更多细节。
scan.startup.specific-offsets
可选(无)String在使用 'specific-offsets' 启动模式时为每个 partition 指定 offset,例如 'partition:0,offset:42;partition:1,offset:300'。 -
scan.startup.timestamp-millis
可选(无)Long在使用 'timestamp' 启动模式时指定启动的时间戳(单位毫秒)。
scan.bounded.mode
optionalunboundedEnumBounded mode for Kafka consumer, valid values are 'latest-offset', 'group-offsets', 'timestamp' and 'specific-offsets'. - See the following Bounded Ending Position for more details.
scan.bounded.specific-offsets
optionalyes(none)StringSpecify offsets for each partition in case of 'specific-offsets' bounded mode, e.g. 'partition:0,offset:42;partition:1,offset:300'. If an offset - for a partition is not provided it will not consume from that partition.. -
scan.bounded.timestamp-millis
optionalyes(none)LongEnd at the specified epoch timestamp (milliseconds) used in case of 'timestamp' bounded mode.
scan.topic-partition-discovery.interval
可选(无)DurationConsumer 定期探测动态创建的 Kafka topic 和 partition 的时间间隔。
sink.partitioner
可选'default'StringFlink partition 到 Kafka partition 的分区映射关系,可选值有: -
    -
  • default:使用 Kafka 默认的分区器对消息进行分区。
  • -
  • fixed:每个 Flink partition 最终对应最多一个 Kafka partition。
  • -
  • round-robin:Flink partition 按轮循(round-robin)的模式对应到 Kafka partition。只有当未指定消息的消息键时生效。
  • -
  • 自定义 FlinkKafkaPartitioner 的子类:例如 'org.mycompany.MyPartitioner'
  • -
- 请参阅下方 Sink 分区 以获取更多细节。 -
sink.semantic
可选at-least-onceString定义 Kafka sink 的语义。有效值为 'at-least-once''exactly-once''none'。请参阅 一致性保证 以获取更多细节。
sink.parallelism
可选(无)Integer定义 Kafka sink 算子的并行度。默认情况下,并行度由框架定义为与上游串联的算子相同。
- -特性 ----------------- - -### 消息键(Key)与消息体(Value)的格式 - -Kafka 消息的消息键和消息体部分都可以使用某种 [格式]({{< ref "docs/connectors/table/formats/overview" >}}) 来序列化或反序列化成二进制数据。 - -**消息体格式** - -由于 Kafka 消息中消息键是可选的,以下语句将使用消息体格式读取和写入消息,但不使用消息键格式。 -`'format'` 选项与 `'value.format'` 意义相同。 -所有的格式配置使用格式识别符作为前缀。 - -```sql -CREATE TABLE KafkaTable ( - `ts` TIMESTAMP_LTZ(3) METADATA FROM 'timestamp', - `user_id` BIGINT, - `item_id` BIGINT, - `behavior` STRING -) WITH ( - 'connector' = 'kafka', - ... - - 'format' = 'json', - 'json.ignore-parse-errors' = 'true' -) -``` - -消息体格式将配置为以下的数据类型: - -```text -ROW<`user_id` BIGINT, `item_id` BIGINT, `behavior` STRING> -``` - -**消息键和消息体格式** - -以下示例展示了如何配置和使用消息键和消息体格式。 -格式配置使用 `'key'` 或 `'value'` 加上格式识别符作为前缀。 - -```sql -CREATE TABLE KafkaTable ( - `ts` TIMESTAMP_LTZ(3) METADATA FROM 'timestamp', - `user_id` BIGINT, - `item_id` BIGINT, - `behavior` STRING -) WITH ( - 'connector' = 'kafka', - ... - - 'key.format' = 'json', - 'key.json.ignore-parse-errors' = 'true', - 'key.fields' = 'user_id;item_id', - - 'value.format' = 'json', - 'value.json.fail-on-missing-field' = 'false', - 'value.fields-include' = 'ALL' -) -``` - -消息键格式包含了在 `'key.fields'` 中列出的字段(使用 `';'` 分隔)和字段顺序。 -因此将配置为以下的数据类型: - -```text -ROW<`user_id` BIGINT, `item_id` BIGINT> -``` - -由于消息体格式配置为 `'value.fields-include' = 'ALL'`,所以消息键字段也会出现在消息体格式的数据类型中: - -```text -ROW<`user_id` BIGINT, `item_id` BIGINT, `behavior` STRING> -``` - -**重名的格式字段** - -如果消息键字段和消息体字段重名,连接器无法根据表结构信息将这些列区分开。 -`'key.fields-prefix'` 配置项可以在表结构中为消息键字段指定一个唯一名称,并在配置消息键格式的时候保留原名。 - -以下示例展示了在消息键和消息体中同时包含 `version` 字段的情况: - -```sql -CREATE TABLE KafkaTable ( - `k_version` INT, - `k_user_id` BIGINT, - `k_item_id` BIGINT, - `version` INT, - `behavior` STRING -) WITH ( - 'connector' = 'kafka', - ... - - 'key.format' = 'json', - 'key.fields-prefix' = 'k_', - 'key.fields' = 'k_version;k_user_id;k_item_id', - - 'value.format' = 'json', - 'value.fields-include' = 'EXCEPT_KEY' -) -``` - -消息体格式必须配置为 `'EXCEPT_KEY'` 模式。格式将被配置为以下的数据类型: - -```text -消息键格式: -ROW<`version` INT, `user_id` BIGINT, `item_id` BIGINT> - -消息体格式: -ROW<`version` INT, `behavior` STRING> -``` - -### Topic 和 Partition 的探测 - -`topic` 和 `topic-pattern` 配置项决定了 source 消费的 topic 或 topic 的匹配规则。`topic` 配置项可接受使用分号间隔的 topic 列表,例如 `topic-1;topic-2`。 -`topic-pattern` 配置项使用正则表达式来探测匹配的 topic。例如 `topic-pattern` 设置为 `test-topic-[0-9]`,则在作业启动时,所有匹配该正则表达式的 topic(以 `test-topic-` 开头,以一位数字结尾)都将被 consumer 订阅。 - -为允许 consumer 在作业启动之后探测到动态创建的 topic,请将 `scan.topic-partition-discovery.interval` 配置为一个非负值。这将使 consumer 能够探测匹配名称规则的 topic 中新的 partition。 - -请参阅 [Kafka DataStream 连接器文档]({{< ref "docs/connectors/datastream/kafka" >}}#kafka-consumer-topic-和分区发现) 以获取更多关于 topic 和 partition 探测的信息。 - -注意 topic 列表和 topic 匹配规则只适用于 source。对于 sink 端,Flink 目前只支持单一 topic。 - -### 起始消费位点 - -`scan.startup.mode` 配置项决定了 Kafka consumer 的启动模式。有效值为: - -* `group-offsets`:从 Zookeeper/Kafka 中某个指定的消费组已提交的偏移量开始。 -* `earliest-offset`:从可能的最早偏移量开始。 -* `latest-offset`:从最末尾偏移量开始。 -* `timestamp`:从用户为每个 partition 指定的时间戳开始。 -* `specific-offsets`:从用户为每个 partition 指定的偏移量开始。 - -默认值 `group-offsets` 表示从 Zookeeper/Kafka 中最近一次已提交的偏移量开始消费。 - -如果使用了 `timestamp`,必须使用另外一个配置项 `scan.startup.timestamp-millis` 来指定一个从格林尼治标准时间 1970 年 1 月 1 日 00:00:00.000 开始计算的毫秒单位时间戳作为起始时间。 - -如果使用了 `specific-offsets`,必须使用另外一个配置项 `scan.startup.specific-offsets` 来为每个 partition 指定起始偏移量, -例如,选项值 `partition:0,offset:42;partition:1,offset:300` 表示 partition `0` 从偏移量 `42` 开始,partition `1` 从偏移量 `300` 开始。 - -### Bounded Ending Position - -The config option `scan.bounded.mode` specifies the bounded mode for Kafka consumer. The valid enumerations are: - - -If config option value `scan.bounded.mode` is not set the default is an unbounded table. - -If `timestamp` is specified, another config option `scan.bounded.timestamp-millis` is required to specify a specific bounded timestamp in milliseconds since January 1, 1970 00:00:00.000 GMT. - -If `specific-offsets` is specified, another config option `scan.bounded.specific-offsets` is required to specify specific bounded offsets for each partition, -e.g. an option value `partition:0,offset:42;partition:1,offset:300` indicates offset `42` for partition `0` and offset `300` for partition `1`. If an offset for a partition is not provided it will not consume from that partition. - -### CDC 变更日志(Changelog) Source - -Flink 原生支持使用 Kafka 作为 CDC 变更日志(changelog) source。如果 Kafka topic 中的消息是通过变更数据捕获(CDC)工具从其他数据库捕获的变更事件,则你可以使用 CDC 格式将消息解析为 Flink SQL 系统中的插入(INSERT)、更新(UPDATE)、删除(DELETE)消息。 - -在许多情况下,变更日志(changelog) source 都是非常有用的功能,例如将数据库中的增量数据同步到其他系统,审核日志,数据库的物化视图,时态表关联数据库表的更改历史等。 - -Flink 提供了几种 CDC 格式: - -* [debezium]({{< ref "docs/connectors/table/formats/debezium.md" >}}) -* [canal]({{< ref "docs/connectors/table/formats/canal.md" >}}) -* [maxwell]({{< ref "docs/connectors/table/formats/maxwell.md" >}}) - -### Sink 分区 - -配置项 `sink.partitioner` 指定了从 Flink 分区到 Kafka 分区的映射关系。 -默认情况下,Flink 使用 [Kafka 默认分区器](https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/clients/producer/internals/DefaultPartitioner.java) 来对消息分区。默认分区器对没有消息键的消息使用 [粘性分区策略(sticky partition strategy)](https://www.confluent.io/blog/apache-kafka-producer-improvements-sticky-partitioner/) 进行分区,对含有消息键的消息使用 murmur2 哈希算法计算分区。 - -为了控制数据行到分区的路由,也可以提供一个自定义的 sink 分区器。'fixed' 分区器会将同一个 Flink 分区中的消息写入同一个 Kafka 分区,从而减少网络连接的开销。 - -### 一致性保证 - -默认情况下,如果查询在 [启用 checkpoint]({{< ref "docs/dev/datastream/fault-tolerance/checkpointing" >}}#enabling-and-configuring-checkpointing) 模式下执行时,Kafka sink 按照至少一次(at-lease-once)语义保证将数据写入到 Kafka topic 中。 - -当 Flink checkpoint 启用时,`kafka` 连接器可以提供精确一次(exactly-once)的语义保证。 - -除了启用 Flink checkpoint,还可以通过传入对应的 `sink.semantic` 选项来选择三种不同的运行模式: - - * `none`:Flink 不保证任何语义。已经写出的记录可能会丢失或重复。 - * `at-least-once` (默认设置):保证没有记录会丢失(但可能会重复)。 - * `exactly-once`:使用 Kafka 事务提供精确一次(exactly-once)语义。当使用事务向 Kafka 写入数据时,请将所有从 Kafka 中消费记录的应用中的 `isolation.level` 配置项设置成实际所需的值(`read_committed` 或 `read_uncommitted`,后者为默认值)。 - -请参阅 [Kafka 文档]({{< ref "docs/connectors/datastream/kafka" >}}#kafka-producers-和容错) 以获取更多关于语义保证的信息。 - -### Source 按分区 Watermark - -Flink 对于 Kafka 支持发送按分区的 watermark。Watermark 在 Kafka consumer 中生成。 -按分区 watermark 的合并方式和在流 shuffle 时合并 Watermark 的方式一致。 -Source 输出的 watermark 由读取的分区中最小的 watermark 决定。 -如果 topic 中的某些分区闲置,watermark 生成器将不会向前推进。 -你可以在表配置中设置 [`'table.exec.source.idle-timeout'`]({{< ref "docs/dev/table/config" >}}#table-exec-source-idle-timeout) 选项来避免上述问题。 - -请参阅 [Kafka watermark 策略]({{< ref "docs/dev/datastream/event-time/generating_watermarks" >}}#watermark-策略和-kafka-连接器) 以获取更多细节。 - -### 安全 -要启用加密和认证相关的安全配置,只需将安全配置加上 "properties." 前缀配置在 Kafka 表上即可。下面的代码片段展示了如何配置 Kafka 表以使用 -PLAIN 作为 SASL 机制并提供 JAAS 配置: -```sql -CREATE TABLE KafkaTable ( - `user_id` BIGINT, - `item_id` BIGINT, - `behavior` STRING, - `ts` TIMESTAMP_LTZ(3) METADATA FROM 'timestamp' -) WITH ( - 'connector' = 'kafka', - ... - 'properties.security.protocol' = 'SASL_PLAINTEXT', - 'properties.sasl.mechanism' = 'PLAIN', - 'properties.sasl.jaas.config' = 'org.apache.kafka.common.security.plain.PlainLoginModule required username=\"username\" password=\"password\";' -) -``` -另一个更复杂的例子,使用 SASL_SSL 作为安全协议并使用 SCRAM-SHA-256 作为 SASL 机制: -```sql -CREATE TABLE KafkaTable ( - `user_id` BIGINT, - `item_id` BIGINT, - `behavior` STRING, - `ts` TIMESTAMP_LTZ(3) METADATA FROM 'timestamp' -) WITH ( - 'connector' = 'kafka', - ... - 'properties.security.protocol' = 'SASL_SSL', - /* SSL 配置 */ - /* 配置服务端提供的 truststore (CA 证书) 的路径 */ - 'properties.ssl.truststore.location' = '/path/to/kafka.client.truststore.jks', - 'properties.ssl.truststore.password' = 'test1234', - /* 如果要求客户端认证,则需要配置 keystore (私钥) 的路径 */ - 'properties.ssl.keystore.location' = '/path/to/kafka.client.keystore.jks', - 'properties.ssl.keystore.password' = 'test1234', - /* SASL 配置 */ - /* 将 SASL 机制配置为 as SCRAM-SHA-256 */ - 'properties.sasl.mechanism' = 'SCRAM-SHA-256', - /* 配置 JAAS */ - 'properties.sasl.jaas.config' = 'org.apache.kafka.common.security.scram.ScramLoginModule required username=\"username\" password=\"password\";' -) -``` - -如果在作业 JAR 中 Kafka 客户端依赖的类路径被重置了(relocate class),登录模块(login module)的类路径可能会不同,因此请根据登录模块在 -JAR 中实际的类路径来改写以上配置。例如在 SQL client JAR 中,Kafka client 依赖被重置在了 `org.apache.flink.kafka.shaded.org.apache.kafka` 路径下, -因此 plain 登录模块的类路径应写为 `org.apache.flink.kafka.shaded.org.apache.kafka.common.security.plain.PlainLoginModule`。 - -关于安全配置的详细描述,请参阅 Apache Kafka 文档中的"安全"一节。 - -数据类型映射 ----------------- - -Kafka 将消息键值以二进制进行存储,因此 Kafka 并不存在 schema 或数据类型。Kafka 消息使用格式配置进行序列化和反序列化,例如 csv,json,avro。 -因此,数据类型映射取决于使用的格式。请参阅 [格式]({{< ref "docs/connectors/table/formats/overview" >}}) 页面以获取更多细节。 - -{{< top >}} diff --git a/docs/content.zh/docs/deployment/elastic_scaling.md b/docs/content.zh/docs/deployment/elastic_scaling.md index 2b7475a1af001..d9b09ba7e8ca2 100644 --- a/docs/content.zh/docs/deployment/elastic_scaling.md +++ b/docs/content.zh/docs/deployment/elastic_scaling.md @@ -25,17 +25,91 @@ under the License. # 弹性扩缩容 -在 Apache Flink 中,可以通过手动停止 Job,然后从停止时创建的 Savepoint 恢复,最后重新指定并行度的方式来重新扩缩容 Job。 +Historically, the parallelism of a job has been static throughout its lifecycle and defined once during its submission. Batch jobs couldn't be rescaled at all, while Streaming jobs could have been stopped with a savepoint and restarted with a different parallelism. -这个文档描述了那些可以使 Flink 自动调整并行度的选项。 +This page describes a new class of schedulers that allow Flink to adjust job's parallelism at runtime, which pushes Flink one step closer to a truly cloud-native stream processor. The new schedulers are [Adaptive Scheduler](#adaptive-scheduler) (streaming) and [Adaptive Batch Scheduler](#adaptive-batch-scheduler) (batch). -## Reactive 模式 +## Adaptive 调度器 + +The Adaptive Scheduler can adjust the parallelism of a job based on available slots. It will automatically reduce the parallelism if not enough slots are available to run the job with the originally configured parallelism; be it due to not enough resources being available at the time of submission, or TaskManager outages during the job execution. If new slots become available the job will be scaled up again, up to the configured parallelism. + +In Reactive Mode (see below) the configured parallelism is ignored and treated as if it was set to infinity, letting the job always use as many resources as possible. + +One benefit of the Adaptive Scheduler over the default scheduler is that it can handle TaskManager losses gracefully, since it would just scale down in these cases. + +{{< img src="/fig/adaptive_scheduler.png" >}} + +Adaptive Scheduler builds on top of a feature called [Declarative Resource Management](https://cwiki.apache.org/confluence/display/FLINK/FLIP-138%3A+Declarative+Resource+management). As you can see, instead of asking for the exact number of slots, JobMaster declares its desired resources (for reactive mode the maximum is set to infinity) to the ResourceManager, which then tries to fulfill those resources. + +{{< img src="/fig/adaptive_scheduler_rescale.png" >}} + +When JobMaster gets more resources during the runtime, it will automatically rescale the job using the latest available savepoint, eliminating the need for an external orchestration. + +Starting from **Flink 1.18.x**, you can re-declare the resource requirements of a running job using [Externalized Declarative Resource Management](#externalized-declarative-resource-management), otherwise the Adaptive Scheduler won't be able to handle cases where the job needs to be rescaled due to a change in the input rate, or a change in the performance of the workload. + +### Externalized Declarative Resource Management + +{{< hint warning >}} +Externalized Declarative Resource Management is an MVP ("minimum viable product") feature. The Flink community is actively looking for feedback by users through our mailing lists. Please check the limitations listed on this page. +{{< /hint >}} + +{{< hint info >}} +You can use Externalized Declarative Resource Management with the [Apache Flink Kubernetes operator](https://nightlies.apache.org/flink/flink-kubernetes-operator-docs-release-1.6/docs/custom-resource/autoscaler/#flink-118-and-in-place-scaling-support) for a fully-fledged auto-scaling experience. +{{< /hint >}} + +Externalized Declarative Resource Management aims to address two deployment scenarios: +1. Adaptive Scheduler on Session Cluster, where multiple jobs can compete for resources, and you need a finer-grained control over the distribution of resources between jobs. +2. Adaptive Scheduler on Application Cluster in combination with Active Resource Manager (e.g. [Native Kubernetes]({{< ref "docs/deployment/resource-providers/native_kubernetes" >}})), where you rely on Flink to "greedily" spawn new TaskManagers, but you still want to leverage rescaling capabilities as with [Reactive Mode](#reactive-mode). + +by introducing a new [REST API endpoint]({{< ref "docs/ops/rest_api" >}}#jobs-jobid-resource-requirements-1), that allows you to re-declare resource requirements of a running job, by setting per-vertex parallelism boundaries. + +``` +PUT /jobs//resource-requirements + +REQUEST BODY: +{ + "": { + "parallelism": { + "lowerBound": 3, + "upperBound": 5 + } + }, + "": { + "parallelism": { + "lowerBound": 2, + "upperBound": 3 + } + } +} +``` + +To a certain extent, the above endpoint could be thought about as a "re-scaling endpoint" and it introduces an important building block for building an auto-scaling experience for Flink. + +You can manually try this feature out, by navigating the Job overview in the Flink UI and using up-scale/down-scale buttons in the task list. + +### Usage {{< hint info >}} -Reactive 模式是一个 MVP (minimum viable product,最小可行产品)特性。目前 Flink 社区正在积极地从邮件列表中获取用户的使用反馈。请注意文中列举的一些局限性。 +If you are using Adaptive Scheduler on a [session cluster]({{< ref "docs/deployment/overview" >}}/#session-mode), there are no guarantees regarding the distribution of slots between multiple running jobs in the same session, in case the cluster doesn't have enough resources. The [External Declarative Resource Management](#externalized-declarative-resource-management) can partially mitigate this issue, but it is still recommended to use Adaptive Scheduler on a [application cluster]({{< ref "docs/deployment/overview" >}}/#application-mode). {{< /hint >}} -在 Reactive 模式下,Job 会使用集群中所有的资源。当增加 TaskManager 时,Job 会自动扩容。当删除时,就会自动缩容。Flink 会管理 Job 的并行度,始终会尽可能地使用最大值。 +The `jobmanager.scheduler` needs to be set to on the cluster level for the adaptive scheduler to be used instead of default scheduler. + +```yaml +jobmanager.scheduler: adaptive +``` + +The behavior of Adaptive Scheduler is configured by [all configuration options prefixed with `jobmanager.adaptive-scheduler`]({{< ref "docs/deployment/config">}}#advanced-scheduling-options) in their name. + +### Limitations + +- **Streaming jobs only**: The Adaptive Scheduler runs with streaming jobs only. When submitting a batch job, Flink will use the default scheduler of batch jobs, i.e. [Adaptive Batch Scheduler](#adaptive-batch-scheduler) +- **No support for partial failover**: Partial failover means that the scheduler is able to restart parts ("regions" in Flink's internals) of a failed job, instead of the entire job. This limitation impacts only recovery time of embarrassingly parallel jobs: Flink's default scheduler can restart failed parts, while Adaptive Scheduler will restart the entire job. +- Scaling events trigger job and task restarts, which will increase the number of Task attempts. +- +## Reactive 模式 + +Reactive Mode is a special mode for Adaptive Scheduler, that assumes a single job per-cluster (enforced by the [Application Mode]({{< ref "docs/deployment/overview" >}}#application-mode)). Reactive Mode configures a job so that it always uses all resources available in the cluster. Adding a TaskManager will scale up your job, removing resources will scale it down. Flink will manage the parallelism of the job, always setting it to the highest possible values. 当发生扩缩容时,Job 会被重启,并且会从最新的 Checkpoint 中恢复。这就意味着不需要花费额外的开销去创建 Savepoint。当然,所需要重新处理的数据量取决于 Checkpoint 的间隔时长,而恢复的时间取决于状态的大小。 @@ -116,35 +190,6 @@ cp ./examples/streaming/TopSpeedWindowing.jar lib/ [Adaptive 调度器的局限性](#limitations-1) 同样也适用于 Reactive 模式. -## Adaptive 调度器 - -{{< hint warning >}} -只推荐高级用户直接使用 Adaptive 调度器(而不是通过 Reactive 模式使用),因为在一个 Session 集群中对于多个 Job 的 Slot 的分配行为是不确定的。 -{{< /hint >}} - -Adaptive 调度器可以基于现有的 Slot 调整 Job 的并行度。它会在 Slot 数目不足时,自动减少并行度。这种情况包括在提交时资源不够,或者在 Job 运行时 TaskManager 不可用。当有新的 Slot 加入时,Job 将会自动扩容至配置的并行度。 -在 Reactive 模式下(详见上文),并行度配置会被忽略,即无限大,使得 Job 尽可能地使用资源。 -你也可以不使用 Reactive 模式而仅使用 Adaptive 调度器,但这种情况会有如下的局限性: -- 如果你在 Session 集群上使用 Adaptive 调度器,在这个集群中运行的多个 Job,他们间 Slot 的分布是无法保证的。 - -相比默认的调度器,Adaptive 调度器其中一个优势在于,它能够优雅地处理 TaskManager 丢失所造成的问题,因为对它来说就仅仅是缩容。 - -### 用法 - -需要设置如下的配置参数: - -- `jobmanager.scheduler: adaptive`:将默认的调度器换成 Adaptive。 - -Adaptive 调度器可以通过[所有在名字包含 `adaptive-scheduler` 的配置]({{< ref "docs/deployment/config">}}#advanced-scheduling-options)修改其行为。 - - - -### 局限性 - -- **只支持流式 Job**:Adaptive 调度器仅支持流式 Job。当提交的是一个批处理 Job 时,Flink 会自动使用批处理 Job 的默认调度器,即 Adaptive Batch Scheduler。 -- **不支持部分故障恢复**: 部分故障恢复意味着调度器可以只重启失败 Job 其中某一部分(在 Flink 的内部结构中被称之为 Region)而不是重启整个 Job。这个限制只会影响那些独立并行(Embarrassingly Parallel)Job的恢复时长,默认的调度器可以重启失败的部分,然而 Adaptive 将需要重启整个 Job。 -- 扩缩容事件会触发 Job 和 Task 重启,Task 重试的次数也会增加。 - ## Adaptive Batch Scheduler Adaptive Batch Scheduler 是一种可以自动调整执行计划的批作业调度器。它目前支持自动推导算子并行度,如果算子未设置并行度,调度器将根据其消费的数据量的大小来推导其并行度。这可以带来诸多好处: diff --git a/docs/content.zh/docs/dev/datastream/dataset_migration.md b/docs/content.zh/docs/dev/datastream/dataset_migration.md new file mode 100644 index 0000000000000..25d1cb8f9a26b --- /dev/null +++ b/docs/content.zh/docs/dev/datastream/dataset_migration.md @@ -0,0 +1,774 @@ +--- +title: "How to Migrate from DataSet to DataStream" +weight: 302 +type: docs +--- + + +# How to Migrate from DataSet to DataStream + +The DataSet API has been formally deprecated and will no longer receive active maintenance and support. It will be removed in the +Flink 2.0 version. Flink users are recommended to migrate from the DataSet API to the DataStream API, Table API and SQL for their +data processing requirements. + +Noticed that APIs in DataStream do not always match those in DataSet exactly. The purpose of this document is to help users understand +how to achieve the same data processing behaviors with DataStream APIs as using DataSet APIs. + +According to the changes in coding and execution efficiency that are required for migration, we categorized DataSet APIs into 4 categories: + +- Category 1: APIs that have exact equivalent in DataStream, which requires barely any changes to migrate. + +- Category 2: APIs whose behavior can be achieved by other APIs with different semantics in DataStream, which might require some code changes for +migration but will result in the same execution efficiency. + +- Category 3: APIs whose behavior can be achieved by other APIs with different semantics in DataStream, with potentially additional cost in execution efficiency. + +- Category 4: APIs whose behaviors are not supported by DataStream API. + +The subsequent sections will first introduce how to set the execution environment and source/sink, then provide detailed explanations on how to migrate +each category of DataSet APIs to the DataStream APIs, highlighting the specific considerations and challenges associated with each +category. + + +## Setting the execution environment + +The first step of migrating an application from DataSet API to DataStream API is to replace `ExecutionEnvironment` with `StreamExecutionEnvironment`. + + + + + + + + + + + + + + +
DataSetDataStream
+ {{< highlight "java" >}} +// Create the execution environment +ExecutionEnvironment.getExecutionEnvironment(); +// Create the local execution environment +ExecutionEnvironment.createLocalEnvironment(); +// Create the collection environment +new CollectionEnvironment(); +// Create the remote environment +ExecutionEnvironment.createRemoteEnvironment(String host, int port, String... jarFiles); + {{< /highlight >}} + + {{< highlight "java" >}} +// Create the execution environment +StreamExecutionEnvironment.getExecutionEnvironment(); +// Create the local execution environment +StreamExecutionEnvironment.createLocalEnvironment(); +// The collection environment is not supported. +// Create the remote environment +StreamExecutionEnvironment.createRemoteEnvironment(String host, int port, String... jarFiles); + {{< /highlight >}} +
+ +Unlike DataSet, DataStream supports processing on both bounded and unbounded data streams. Thus, user needs to explicitly set the execution mode +to `RuntimeExecutionMode.BATCH` if that is expected. + +```java +StreamExecutionEnvironment executionEnvironment = // [...]; +executionEnvironment.setRuntimeMode(RuntimeExecutionMode.BATCH); +``` + +## Using the streaming sources and sinks + +### Sources + +The DataStream API uses `DataStreamSource` to read records from external system, while the DataSet API uses the `DataSource`. + + + + + + + + + + + + + + +
DataSetDataStream
+ {{< highlight "java" >}} +// Read data from file +DataSource<> source = ExecutionEnvironment.readFile(inputFormat, filePath); +// Read data from collection +DataSource<> source = ExecutionEnvironment.fromCollection(data); +// Read data from inputformat +DataSource<> source = ExecutionEnvironment.createInput(inputFormat) + {{< /highlight >}} + + {{< highlight "java" >}} +// Read data from file +DataStreamSource<> source = StreamExecutionEnvironment.readFile(inputFormat, filePath); +// Read data from collection +DataStreamSource<> source = StreamExecutionEnvironment.fromCollection(data); +// Read data from inputformat +DataStreamSource<> source = StreamExecutionEnvironment.createInput(inputFormat) + {{< /highlight >}} +
+ +### Sinks + +The DataStream API uses `DataStreamSink` to write records to external system, while the +DataSet API uses the `DataSink`. + + + + + + + + + + + + + + +
DataSetDataStream
+ {{< highlight "java" >}} +// Write to outputformat +DataSink<> sink = dataSet.output(outputFormat); +// Write to csv file +DataSink<> sink = dataSet.writeAsCsv(filePath); +// Write to text file +DataSink<> sink = dataSet.writeAsText(filePath); + {{< /highlight >}} + + {{< highlight "java" >}} +// Write to sink +DataStreamSink<> sink = dataStream.sinkTo(sink) +// Write to csv file +DataStreamSink<> sink = dataStream.writeAsCsv(path); +// Write to text file +DataStreamSink<> sink = dataStream.writeAsText(path); + {{< /highlight >}} +
+ +If you are looking for pre-defined source and sink connectors of DataStream, please check the [Connector Docs]({{< ref "docs/connectors/datastream/overview" >}}) + +## Migrating DataSet APIs + +### Category 1 + +For Category 1, these DataSet APIs have exact equivalent in DataStream, which requires barely any changes to migrate. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperationsDataSetDataStream
Map + {{< highlight "java" >}} +dataSet.map(new MapFunction<>(){ +// implement user-defined map logic +}); + {{< /highlight >}} + + {{< highlight "java" >}} +dataStream.map(new MapFunction<>(){ +// implement user-defined map logic +}); + {{< /highlight >}} +
FlatMap + {{< highlight "java" >}} +dataSet.flatMap(new FlatMapFunction<>(){ +// implement user-defined flatmap logic +}); + {{< /highlight >}} + + {{< highlight "java" >}} +dataStream.flatMap(new FlatMapFunction<>(){ +// implement user-defined flatmap logic +}); + {{< /highlight >}} +
Filter + {{< highlight "java" >}} +dataSet.filter(new FilterFunction<>(){ +// implement user-defined filter logic +}); + {{< /highlight >}} + + {{< highlight "java" >}} +dataStream.filter(new FilterFunction<>(){ +// implement user-defined filter logic +}); + {{< /highlight >}} +
Union + {{< highlight "java" >}} +dataSet1.union(dataSet2); + {{< /highlight >}} + + {{< highlight "java" >}} +dataStream1.union(dataStream2); + {{< /highlight >}} +
Rebalance + {{< highlight "java" >}} +dataSet.rebalance(); + {{< /highlight >}} + + {{< highlight "java" >}} +dataStream.rebalance(); + {{< /highlight >}} +
Project + {{< highlight "java" >}} +DataSet> dataSet = // [...] +dataSet.project(2,0); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream = // [...] +dataStream.project(2,0); + {{< /highlight >}} +
Reduce on Grouped DataSet + {{< highlight "java" >}} +DataSet> dataSet = // [...] +dataSet.groupBy(value -> value.f0) + .reduce(new ReduceFunction<>(){ + // implement user-defined reduce logic + }); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream = // [...] +dataStream.keyBy(value -> value.f0) + .reduce(new ReduceFunction<>(){ + // implement user-defined reduce logic + }); + {{< /highlight >}} +
Aggregate on Grouped DataSet + {{< highlight "java" >}} +DataSet> dataSet = // [...] +// compute sum of the second field +dataSet.groupBy(value -> value.f0) + .aggregate(SUM, 1); +// compute min of the second field +dataSet.groupBy(value -> value.f0) + .aggregate(MIN, 1); +// compute max of the second field +dataSet.groupBy(value -> value.f0) + .aggregate(MAX, 1); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream = // [...] +// compute sum of the second field +dataStream.keyBy(value -> value.f0) + .sum(1); +// compute min of the second field +dataStream.keyBy(value -> value.f0) + .min(1); +// compute max of the second field +dataStream.keyBy(value -> value.f0) + .max(1); + {{< /highlight >}} +
+ +### Category 2 + +For category 2, the behavior of these DataSet APIs can be achieved by other APIs with different semantics in DataStream, which might require some code changes for +migration but will result in the same execution efficiency. + +Operations on a full DataSet correspond to the global window aggregation in DataStream with a custom window that is triggered at the end of the inputs. The `EndOfStreamWindows` +in the [Appendix]({{< ref "docs/dev/datastream/dataset_migration#endofstreamwindows" >}}) shows how such a window can be implemented. We will reuse it in the rest of this document. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperationsDataSetDataStream
Distinct + {{< highlight "java" >}} +DataSet dataSet = // [...] +dataSet.distinct(); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream dataStream = // [...] +dataStream.keyBy(value -> value) + .reduce((value1, value2) -> value1); + {{< /highlight >}} +
Hash-Partition + {{< highlight "java" >}} +DataSet> dataSet = // [...] +dataSet.partitionByHash(value -> value.f0); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream = // [...] +// partition by the hashcode of key +dataStream.partitionCustom( + (key, numSubpartition) -> key.hashCode() % numSubpartition, + value -> value.f0); + {{< /highlight >}} +
Reduce on Full DataSet + {{< highlight "java" >}} +DataSet dataSet = // [...] +dataSet.reduce(new ReduceFunction<>(){ + // implement user-defined reduce logic + }); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream dataStream = // [...] +dataStream.windowAll(EndOfStreamWindows.get()) + .reduce(new ReduceFunction<>(){ + // implement user-defined reduce logic + }); + {{< /highlight >}} +
Aggregate on Full DataSet + {{< highlight "java" >}} +DataSet> dataSet = // [...] +// compute sum of the second field +dataSet.aggregate(SUM, 1); +// compute min of the second field +dataSet.aggregate(MIN, 1); +// compute max of the second field +dataSet.aggregate(MAX, 1); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream = // [...] +// compute sum of the second field +dataStream.windowAll(EndOfStreamWindows.get()) + .sum(1); +// compute min of the second field +dataStream.windowAll(EndOfStreamWindows.get()) + .min(1); +// compute max of the second field +dataStream.windowAll(EndOfStreamWindows.get()) + .max(1); + {{< /highlight >}} +
GroupReduce on Full DataSet + {{< highlight "java" >}} +DataSet dataSet = // [...] +dataSet.reduceGroup(new GroupReduceFunction<>(){ + // implement user-defined group reduce logic + }); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream dataStream = // [...] +dataStream.windowAll(EndOfStreamWindows.get()) + .apply(new WindowFunction<>(){ + // implement user-defined group reduce logic + }); + {{< /highlight >}} +
GroupReduce on Grouped DataSet + {{< highlight "java" >}} +DataSet> dataSet = // [...] +dataSet.groupBy(value -> value.f0) + .reduceGroup(new GroupReduceFunction<>(){ + // implement user-defined group reduce logic + }); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream = // [...] +dataStream.keyBy(value -> value.f0) + .window(EndOfStreamWindows.get()) + .apply(new WindowFunction<>(){ + // implement user-defined group reduce logic + }); + {{< /highlight >}} +
First-n + {{< highlight "java" >}} +dataSet.first(n) + {{< /highlight >}} + + {{< highlight "java" >}} +dataStream.windowAll(EndOfStreamWindows.get()) + .apply(new AllWindowFunction<>(){ + // implement first-n logic + }); + {{< /highlight >}} +
Join + {{< highlight "java" >}} +DataSet> dataSet1 = // [...] +DataSet> dataSet2 = // [...] +dataSet1.join(dataSet2) + .where(value -> value.f0) + .equalTo(value -> value.f0) + .with(new JoinFunction<>(){ + // implement user-defined join logic + }); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream1 = // [...] +DataStream> dataStream2 = // [...] +dataStream1.join(dataStream2) + .where(value -> value.f0) + .equalTo(value -> value.f0) + .window(EndOfStreamWindows.get())) + .apply(new JoinFunction<>(){ + // implement user-defined join logic + }); + {{< /highlight >}} +
CoGroup + {{< highlight "java" >}} +DataSet> dataSet1 = // [...] +DataSet> dataSet2 = // [...] +dataSet1.coGroup(dataSet2) + .where(value -> value.f0) + .equalTo(value -> value.f0) + .with(new CoGroupFunction<>(){ + // implement user-defined co group logic + }); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream1 = // [...] +DataStream> dataStream2 = // [...] +dataStream1.coGroup(dataStream2) + .where(value -> value.f0) + .equalTo(value -> value.f0) + .window(EndOfStreamWindows.get())) + .apply(new CoGroupFunction<>(){ + // implement user-defined co group logic + }); + {{< /highlight >}} +
OuterJoin + {{< highlight "java" >}} +DataSet> dataSet1 = // [...] +DataSet> dataSet2 = // [...] +// left outer join +dataSet1.leftOuterJoin(dataSet2) + .where(dataSet1.f0) + .equalTo(dataSet2.f0) + .with(new JoinFunction<>(){ + // implement user-defined left outer join logic + }); +// right outer join +dataSet1.rightOuterJoin(dataSet2) + .where(dataSet1.f0) + .equalTo(dataSet2.f0) + .with(new JoinFunction<>(){ + // implement user-defined right outer join logic + }); + {{< /highlight >}} + + {{< highlight "java" >}} + DataStream> dataStream1 = // [...] + DataStream> dataStream2 = // [...] + // left outer join + dataStream1.coGroup(dataStream2) + .where(value -> value.f0) + .equalTo(value -> value.f0) + .window(EndOfStreamWindows.get()) + .apply((leftIterable, rightInterable, collector) -> { + if(!rightInterable.iterator().hasNext()){ + // implement user-defined left outer join logic + } + }); + // right outer join + dataStream1.coGroup(dataStream2) + .where(value -> value.f0) + .equalTo(value -> value.f0) + .window(EndOfStreamWindows.get()) + .apply((leftIterable, rightInterable, collector) -> { + if(!leftIterable.iterator().hasNext()){ + // implement user-defined right outer join logic + } + }); + {{< /highlight >}} +
+ +### Category 3 + +For category 3, the behavior of these DataSet APIs can be achieved by other APIs with different semantics in DataStream, with potentially additional cost in execution efficiency. + +Currently, DataStream API does not directly support aggregations on non-keyed streams (subtask-scope aggregations). In order to do so, we need to first assign the subtask id +to the records, then turn the stream into a keyed stream. The `AddSubtaskIdMapFunction` in the [Appendix]({{< ref "docs/dev/datastream/dataset_migration#addsubtaskidmapfunction" >}}) shows how +to do that, and we will reuse it in the rest of this document. + + + + + + + + + + + + + + + + + + + + + +
OperationsDataSetDataStream
MapPartition/SortPartition + {{< highlight "java" >}} +DataSet dataSet = // [...] +// MapPartition +dataSet.mapPartition(new MapPartitionFunction<>(){ + // implement user-defined map partition logic + }); +// SortPartition +dataSet.sortPartition(0, Order.ASCENDING); +dataSet.sortPartition(0, Order.DESCENDING); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream dataStream = // [...] +// assign subtask ID to all records +DataStream> dataStream1 = dataStream.map(new AddSubtaskIDMapFunction()); +dataStream1.keyBy(value -> value.f0) + .window(EndOfStreamWindows.get()) + .apply(new WindowFunction<>(){ + // implement user-defined map partition or sort partition logic + }); + {{< /highlight >}} +
Cross + {{< highlight "java" >}} +DataSet dataSet1 = // [...] +DataSet dataSet2 = // [...] +// Cross +dataSet1.cross(dataSet2) + .with(new CrossFunction<>(){ + // implement user-defined cross logic + }) + {{< /highlight >}} + + {{< highlight "java" >}} +// the parallelism of dataStream1 and dataStream2 should be same +DataStream dataStream1 = // [...] +DataStream dataStream2 = // [...] +DataStream> datastream3 = dataStream1.broadcast().map(new AddSubtaskIDMapFunction()); +DataStream> datastream4 = dataStream2.map(new AddSubtaskIDMapFunction()); +// join the two streams according to the subtask ID +dataStream3.join(dataStream4) + .where(value -> value.f0) + .equalTo(value -> value.f0) + .window(EndOfStreamWindows.get()) + .apply(new JoinFunction<>(){ + // implement user-defined cross logic + }) + {{< /highlight >}} +
+ +### Category 4 + +The behaviors of the following DataSet APIs are not supported by DataStream. + +* RangePartition +* GroupCombine + + +## Appendix + +#### EndOfStreamWindows + +The following code shows the example of `EndOfStreamWindows`. + +```java +public class EndOfStreamWindows extends WindowAssigner { + private static final long serialVersionUID = 1L; + + private static final EndOfStreamWindows INSTANCE = new EndOfStreamWindows(); + + private static final TimeWindow TIME_WINDOW_INSTANCE = + new TimeWindow(Long.MIN_VALUE, Long.MAX_VALUE); + + private EndOfStreamWindows() {} + + public static EndOfStreamWindows get() { + return INSTANCE; + } + + @Override + public Collection assignWindows( + Object element, long timestamp, WindowAssignerContext context) { + return Collections.singletonList(TIME_WINDOW_INSTANCE); + } + + @Override + public Trigger getDefaultTrigger(StreamExecutionEnvironment env) { + return new EndOfStreamTrigger(); + } + + @Override + public String toString() { + return "EndOfStreamWindows()"; + } + + @Override + public TypeSerializer getWindowSerializer(ExecutionConfig executionConfig) { + return new TimeWindow.Serializer(); + } + + @Override + public boolean isEventTime() { + return true; + } + + @Internal + public static class EndOfStreamTrigger extends Trigger { + @Override + public TriggerResult onElement( + Object element, long timestamp, TimeWindow window, TriggerContext ctx) + throws Exception { + return TriggerResult.CONTINUE; + } + + @Override + public TriggerResult onEventTime(long time, TimeWindow window, TriggerContext ctx) { + return time == window.maxTimestamp() ? TriggerResult.FIRE : TriggerResult.CONTINUE; + } + + @Override + public void clear(TimeWindow window, TriggerContext ctx) throws Exception {} + + @Override + public TriggerResult onProcessingTime(long time, TimeWindow window, TriggerContext ctx) { + return TriggerResult.CONTINUE; + } + } +} +``` + +#### AddSubtaskIDMapFunction + +The following code shows the example of `AddSubtaskIDMapFunction`. +```java +public static class AddSubtaskIDMapFunction extends RichMapFunction> { + @Override + public Tuple2 map(T value) { + return Tuple2.of(String.valueOf(getRuntimeContext().getIndexOfThisSubtask()), value); + } +} +``` + +{{< top >}} diff --git a/docs/content.zh/docs/dev/table/data_stream_api.md b/docs/content.zh/docs/dev/table/data_stream_api.md index 1b1b958cfa0d3..968066c13a7e3 100644 --- a/docs/content.zh/docs/dev/table/data_stream_api.md +++ b/docs/content.zh/docs/dev/table/data_stream_api.md @@ -107,9 +107,9 @@ resultStream.print(); env.execute(); // prints: -// +I[Alice] -// +I[Bob] -// +I[John] +// +I[ALICE] +// +I[BOB] +// +I[JOHN] ``` {{< /tab >}} {{< tab "Scala" >}} @@ -140,9 +140,9 @@ resultStream.print() env.execute() // prints: -// +I[Alice] -// +I[Bob] -// +I[John] +// +I[ALICE] +// +I[BOB] +// +I[JOHN] ``` {{< /tab >}} {{< tab "Python" >}} @@ -173,9 +173,9 @@ res_ds.print() env.execute() # prints: -# +I[Alice] -# +I[Bob] -# +I[John] +# +I[ALICE] +# +I[BOB] +# +I[JOHN] ``` {{< /tab >}} {{< /tabs >}} diff --git a/docs/content.zh/docs/dev/table/olap_quickstart.md b/docs/content.zh/docs/dev/table/olap_quickstart.md index e0b3afba2bc57..e109dfe71eebd 100644 --- a/docs/content.zh/docs/dev/table/olap_quickstart.md +++ b/docs/content.zh/docs/dev/table/olap_quickstart.md @@ -1,5 +1,5 @@ --- -title: "Quickstart for Flink OLAP" +title: "OLAP Quickstart" weight: 91 type: docs aliases: @@ -24,64 +24,73 @@ specific language governing permissions and limitations under the License. --> -# Introduction +# OLAP 搭建指南 -Flink OLAP has already added to [Apache Flink Roadmap](https://flink.apache.org/roadmap/). It means Flink can not only support streaming and batch computing, but also support OLAP(On-Line Analytical Processing). This page will show how to quickly set up a Flink OLAP service, and will introduce some best practices. +OLAP(OnLine Analysis Processing)是数据分析领域的一项关键技术,通常被用来对较大的数据集进行秒级的复杂查询分析。Flink 作为一款流批一体的计算引擎,现在也同样支持用户将其作为一个 OLAP 计算服务来部署。本文将会帮助你在本地快速搭建起一个 Flink OLAP 集群并试用。同时,也会介绍一些在实际生产环境中使用 Flink 作为 OLAP 计算服务的实践。 -## Architecture +## 架构介绍 -The Flink OLAP service consists of three parts: Client, Flink SQL Gateway, Flink Session Cluster. +本章节将会介绍 Flink 作为一个 OLAP 服务的总体架构及其在使用上的优势。 -* **Client**: Could be any client that can interact with Flink SQL Gateway, such as SQL client, Flink JDBC driver and so on. -* **Flink SQL Gateway**: The SQL Gateway provides an easy way to submit the Flink Job, look up the metadata, and analyze table stats. -* **Flink Session Cluster**: We choose session clusters to run OLAP queries, mainly to avoid the overhead of cluster startup. +### 架构 -## Advantage +Flink OLAP 服务整体由3个部分组成,包括:客户端,Flink SQL Gateway 和 Flink Session Cluster。 -* **Massively Parallel Processing** - * Flink OLAP runs naturally as an MPP(Massively Parallel Processing) system, which supports low-latency ad-hoc queries -* **Reuse Connectors** - * Flink OLAP can reuse rich connectors in Flink ecosystem. -* **Unified Engine** - * Unified computing engine for Streaming/Batch/OLAP. +* **客户端**: 可以是任何可以和 [Flink SQL Gateway]({{< ref "docs/dev/table/sql-gateway/overview" >}}) 交互的客户端,包括:[SQL Client]({{< ref "docs/dev/table/sqlClient" >}}),[Flink JDBC Driver]({{< ref "docs/dev/table/jdbcDriver" >}}) 等等; +* **Flink SQL Gateway**: Flink SQL Gateway 服务主要用作 SQL 解析、元数据获取、统计信息分析、Plan 优化和集群作业提交; +* **Flink Session Cluster**: OLAP 查询建议运行在 [Session 集群]({{< ref "/docs/deployment/resource-providers/native_kubernetes#starting-a-flink-session-on-kubernetes" >}})上,主要是可以减少集群启动时的额外开销; -# Deploying in Local Mode +{{< img src="/fig/olap-architecture.svg" alt="Illustration of Flink OLAP Architecture" width="85%" >}} -## Downloading Flink +### 优势 -The same as [Local Installation]({{< ref "docs/try-flink/local_installation" >}}). Flink runs on all UNIX-like environments, i.e. Linux, Mac OS X, and Cygwin (for Windows). We need to have at least Java 11 installed, Java 17 is more recommended in OLAP scenario. To check the Java version installed, type in your terminal: +* **并行计算架构** + * Flink 是天然的并行计算架构,执行 OLAP 查询时可以方便的通过调整并发来满足不同数据规模下的低延迟查询性能要求 +* **弹性资源管理** + * Flink 的集群资源具有良好的 Min、Max 扩缩容能力,可以根据集群负载动态调整所使用的资源 +* **生态丰富** + * Flink OLAP 可以复用 Flink 生态中丰富的 [连接器]({{< ref "docs/connectors/table/overview" >}}) +* **统一引擎** + * 支持流 / 批 / OLAP 的统一计算引擎 + +## 本地运行 +本章将指导用户如何在本地试用 Flink OLAP 服务。 + +### 下载 Flink + +这里的方法和[本地安装]({{< ref "docs/try-flink/local_installation" >}})中记录的步骤类似。Flink 可以运行在任何类 UNIX 的操作系统下面, 例如:Linux, Mac OS X 和 Cygwin (for Windows)。你需要在本地安装好 __Java 11__,可以通过下述命令行的方式检查安装好的 Java 版本: ``` java -version ``` -Next, [Download](https://flink.apache.org/downloads/) the latest binary release of Flink, then extract the archive: +下一步, [下载](https://flink.apache.org/downloads/) Flink 最新的二进制包并进行解压: ``` tar -xzf flink-*.tgz ``` -## Starting a local cluster +### 启动本地集群 -To start a local cluster, run the bash script that comes with Flink: +运行下述脚本,即可在本地启动集群: ``` ./bin/start-cluster.sh ``` -You should be able to navigate to the web UI at localhost:8081 to view the Flink dashboard and see that the cluster is up and running. +用户可以导航到本地的 Web UI(http://localhost:8081)来查看 Flink Dashboard 并检查集群是否已拉起和正在运行。 -## Start a SQL Client CLI +### 启动 SQL Client -You can start the CLI with an embedded gateway by calling: +用户可以通过运行下述命令,用命令行启动内嵌了 Gateway 的 SQL Client: ``` ./bin/sql-client.sh ``` -## Running Queries +### 运行 SQL 查询 -You could simply execute queries in CLI and retrieve the results. +通过命令行,用户可以方便的提交查询并获取结果: ``` SET 'sql-client.execution.result-mode' = 'tableau'; @@ -102,98 +111,98 @@ GROUP BY buyer ORDER BY total_cost LIMIT 3; ``` -And then you could find job detail information in web UI at localhost:8081. +具体的作业运行信息你可以通过访问本地的 Web UI(http://localhost:8081)来获取。 -# Deploying in Production +## 生产环境部署 -This section guides you through setting up a production ready Flink OLAP service. +这个章节会向你介绍一些在生产环境中使用 Flink OLAP 服务的建议。 -## Cluster Deployment +### 客户端 -In production, we recommend to use Flink Session Cluster, Flink SQL Gateway and Flink JDBC Driver to build an OLAP service. +#### Flink JDBC Driver -### Session Cluster +Flink JDBC Driver 提供了底层的连接管理能力,方便用户使用并向 SQL Gateway 提交查询请求。在实际的生产使用中,用户需要注意如何复用 JDBC 连接,来避免 Gateway 频繁的执行 Session 相关的创建及关闭操作,从而减少端到端的作业耗时。详细信息可以参考文档 [Flink JDBC Driver]({{ }})。 -For Flink Session Cluster, we recommend to deploy Flink on native Kubernetes using session mode. Kubernetes is a popular container-orchestration system for automating computer application deployment, scaling, and management. By deploying on native Kubernetes, Flink Session Cluster is able to dynamically allocate and de-allocate TaskManagers. For more information, please refer to [Native Kubernetes]({{< ref "docs/deployment/resource-providers/native_kubernetes">}}). +### 集群部署 -### SQL Gateway +在生产环境中,建议使用 Flink Session 集群、Flink SQL Gateway 来搭建 OLAP 服务。 -For Flink SQL Gateway, we recommend deploying it as a stateless microservice and register this on the service discovery component. For more information, please refer to the [SQL Gateway Overview]({{< ref "docs/dev/table/sql-gateway/overview">}}). +#### Session Cluster -### Flink JDBC Driver +Flink Session 集群建议搭建在 Native Kubernetes 环境下,使用 Session 模式运行。K8S 作为一个流行的容器编排系统可以自动化的支持不同计算程序的部署、扩展和管理。通过将集群部署在 Native Kubernetes 上,Flink Session 集群支持动态的增减 TaskManagers。详细信息可以参考 [Native Kubernetes]({{< ref "docs/deployment/resource-providers/native_kubernetes">}})。同时,你可以在 Session 集群中配置 [slotmanager.number-of-slots.min]({{< ref "docs/deployment/config#slotmanager-number-of-slots-min" >}}),这个可以帮助你显著减少 OLAP 查询执行的冷启动时间,详情请参阅 [FLIP-362](https://cwiki.apache.org/confluence/display/FLINK/FLIP-362%3A+Support+minimum+resource+limitation)。 -When submitting queries to SQL Gateway, we recommend using Flink JDBC Driver since it provides low-level connection management. When used in production, we need to pay attention to reuse the JDBC connection to avoid frequently creating/closing sessions in the Gateway. For more information, please refer to the [Flink JDBC Driver]({{{}}}). +#### Flink SQL Gateway -## Datasource Configurations +对于 Flink SQL Gateway,用户可以将其部署为无状态的微服务并注册到服务发现的组件上来对外提供服务,方便客户端可以进行负载均衡。详细信息可以参考 [SQL Gateway Overview]({{< ref "docs/dev/table/sql-gateway/overview">}})。 -### Catalogs +### 数据源配置 -In OLAP scenario, we recommend using FileCatalogStore in the catalog configuration introduced in [FLIP-295](https://cwiki.apache.org/confluence/display/FLINK/FLIP-295%3A+Support+lazy+initialization+of+catalogs+and+persistence+of+catalog+configurations). As a long running service, Flink OLAP cluster's catalog information will not change frequently and can be re-used cross sessions. For more information, please refer to the [Catalog Store]({{< ref "docs/dev/table/catalogs#catalog-store">}}). +#### Catalogs -### Connectors +在 OLAP 场景下,集群建议配置 [Catalogs]({{< ref "docs/dev/table/catalogs">}}) 中提供的 FileCatalogStore 作为 Catalog 选项。作为一个常驻服务,Flink OLAP 集群的元信息通常不会频繁变更而且需要支持跨 Session 的复用,这样可以减少元信息加载的冷启动时间。详细信息可以参考文档 [Catalog Store]({{< ref "docs/dev/table/catalogs#catalog-store">}})。 -Both Session Cluster and SQL Gateway rely on connectors to analyze table stats and read data from the configured data source. To add connectors, please refer to the [Connectors and Formats]({{< ref "docs/connectors/table/overview">}}). +#### 连接器 -## Cluster Configurations +Session Cluster 和 SQL Gateway 都依赖连接器来获取表的元信息同时从配置好的数据源读取数据,详细信息可以参考文档 [连接器]({{< ref "docs/connectors/table/overview" >}})。 -In OLAP scenario, we picked out a few configurations that can help improve user usability and query performance. +### 推荐参数配置 -### SQL&Table Options +对于 OLAP 场景,合理的参数配置可以帮助用户较大的提升服务总体的可用性和查询性能,下面列了一些生产环境建议的参数配置。 -| Parameters | Default | Recommended | -|:-------------------------------------|:--------|:------------| -| table.optimizer.join-reorder-enabled | false | true | -| pipeline.object-reuse | false | true | +#### SQL&Table 参数 -### Runtime Options +| 参数名称 | 默认值 | 推荐值 | +|:---------------------------------------------------------------------------------------------------------------|:------|:-----| +| [table.optimizer.join-reorder-enabled]({{}}) | false | true | +| [pipeline.object-reuse]({{< ref "docs/deployment/config#pipeline-object-reuse" >}}) | false | true | -| Parameters | Default | Recommended | -|:-----------------------------|:-----------------------|:------------------------------------------------------------------------------------------------------------------------------------------| -| execution.runtime-mode | STREAMING | BATCH | -| execution.batch-shuffle-mode | ALL_EXCHANGES_BLOCKING | ALL_EXCHANGES_PIPELINED | -| env.java.opts.all | {default value} | {default value} -XX:PerMethodRecompilationCutoff=10000 -XX:PerBytecodeRecompilationCutoff=10000-XX:ReservedCodeCacheSize=512M -XX:+UseZGC | -| JDK Version | 11 | 17 | +#### Runtime 参数 -We strongly recommend using JDK17 with ZGC in OLAP scenario in order to provide zero gc stw and solve the issue described in [FLINK-32746](https://issues.apache.org/jira/browse/FLINK-32746). +| 参数名称 | 默认值 | 推荐值 | +|:---------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------|:------------------------------------------------------------------------------------------------------------------------------------------| +| [execution.runtime-mode]({{< ref "docs/deployment/config#execution-runtime-mode" >}}) | STREAMING | BATCH | +| [execution.batch-shuffle-mode]({{< ref "docs/deployment/config#execution-batch-shuffle-mode" >}}) | ALL_EXCHANGES_BLOCKING | ALL_EXCHANGES_PIPELINED | +| [env.java.opts.all]({{< ref "docs/deployment/config#env-java-opts-all" >}}) | {default value} | {default value} -XX:PerMethodRecompilationCutoff=10000 -XX:PerBytecodeRecompilationCutoff=10000-XX:ReservedCodeCacheSize=512M -XX:+UseZGC | +| JDK Version | 11 | 17 | -### Scheduling Options +推荐在 OLAP 生产环境中使用 JDK17 和 ZGC,ZGC 可以优化 Metaspace 区垃圾回收的问题,详见 [FLINK-32746](https://issues.apache.org/jira/browse/FLINK-32746)。同时 ZGC 在堆内内存垃圾回收时可以提供接近0毫秒的应用程序暂停时间。OLAP 查询在执行时需要使用批模式,因为 OLAP 查询的执行计划中可能同时出现 Pipelined 和 Blocking 属性的边。批模式下的调度器支持对作业分阶段调度,可以避免出现调度死锁问题。 -| Parameters | Default | Recommended | -|:---------------------------------------------------------|:------------------|:------------------| -| jobmanager.scheduler | Default | Default | -| jobmanager.execution.failover-strategy | region | full | -| restart-strategy.type | (none) | disable | -| jobstore.type | File | Memory | -| jobstore.max-capacity | Integer.MAX_VALUE | 500 | +#### Scheduling 参数 -We would like to highlight the usage of `PipelinedRegionSchedulingStrategy`. Since many OLAP queries will have blocking edges in their jobGraph. +| 参数名称 | 默认值 | 推荐值 | +|:------------------------------------------------------------------------------------------------------------------------|:------------------|:--------| +| [jobmanager.scheduler]({{< ref "docs/deployment/config#jobmanager-scheduler" >}}) | Default | Default | +| [jobmanager.execution.failover-strategy]({{< ref "docs/deployment/config#jobmanager-execution-failover-strategy-1" >}}) | region | full | +| [restart-strategy.type]({{< ref "docs/deployment/config#restart-strategy-type" >}}) | (none) | disable | +| [jobstore.type]({{< ref "docs/deployment/config#jobstore-type" >}}) | File | Memory | +| [jobstore.max-capacity]({{< ref "docs/deployment/config#jobstore-max-capacity" >}}) | Integer.MAX_VALUE | 500 | -### Network Options -| Parameters | Default | Recommended | -|:------------------------------------|:-----------|:---------------| -| rest.server.numThreads | 4 | 32 | -| web.refresh-interval | 3000 | 300000 | -| pekko.framesize | 10485760b | 104857600b | +#### 网络参数 -### ResourceManager Options +| 参数名称 | 默认值 | 推荐值 | +|:--------------------------------------------------------------------------------------|:----------|:-----------| +| [rest.server.numThreads]({{< ref "docs/deployment/config#rest-server-numthreads" >}}) | 4 | 32 | +| [web.refresh-interval]({{< ref "docs/deployment/config#web-refresh-interval" >}}) | 3000 | 300000 | +| [pekko.framesize]({{< ref "docs/deployment/config#pekko-framesize" >}}) | 10485760b | 104857600b | -| Parameters | Default | Recommended | -|:-------------------------------------|:----------|:---------------| -| kubernetes.jobmanager.replicas | 1 | 2 | -| kubernetes.jobmanager.cpu.amount | 1.0 | 16.0 | -| jobmanager.memory.process.size | (none) | 65536m | -| jobmanager.memory.jvm-overhead.max | 1g | 6144m | -| kubernetes.taskmanager.cpu.amount | (none) | 16 | -| taskmanager.numberOfTaskSlots | 1 | 32 | -| taskmanager.memory.process.size | (none) | 65536m | -| taskmanager.memory.managed.size | (none) | 65536m | +#### 资源管理参数 -We prefer to use large taskManager pods in OLAP since this can put more computation in local and reduce network/deserialization/serialization overhead. Meanwhile, since JobManager is a single point of calculation in OLAP scenario, we also prefer large pod. +| 参数名称 | 默认值 | 推荐值 | +|:--------------------------------------------------------------------------------------------------------------|:-------|:----------------------------------------| +| [kubernetes.jobmanager.replicas]({{< ref "docs/deployment/config#kubernetes-jobmanager-replicas" >}}) | 1 | 2 | +| [kubernetes.jobmanager.cpu.amount]({{< ref "docs/deployment/config#kubernetes-jobmanager-cpu-amount" >}}) | 1.0 | 16.0 | +| [jobmanager.memory.process.size]({{< ref "docs/deployment/config#jobmanager-memory-process-size" >}}) | (none) | 32g | +| [jobmanager.memory.jvm-overhead.max]({{< ref "docs/deployment/config#jobmanager-memory-jvm-overhead-max" >}}) | 1g | 3g | +| [kubernetes.taskmanager.cpu.amount]({{< ref "docs/deployment/config#kubernetes-taskmanager-cpu-amount" >}}) | (none) | 16 | +| [taskmanager.numberOfTaskSlots]({{< ref "docs/deployment/config#taskmanager-numberoftaskslots" >}}) | 1 | 32 | +| [taskmanager.memory.process.size]({{< ref "docs/deployment/config#taskmanager-memory-process-size" >}}) | (none) | 65536m | +| [taskmanager.memory.managed.size]({{< ref "docs/deployment/config#taskmanager-memory-managed-size" >}}) | (none) | 16384m | +| [slotmanager.number-of-slots.min]({{< ref "docs/deployment/config#slotmanager-number-of-slots-min" >}}) | 0 | {taskManagerNumber * numberOfTaskSlots} | -# Future Work -There is a big margin for improvement in Flink OLAP, both in usability and query performance, and we trace all of them in underlying tickets. +用户可以根据实际的生产情况把 `slotmanager.number-of-slots.min` 配置为一个合理值,并将其用作集群的预留资源池从而支持 OLAP 查询。在 OLAP 场景下,TaskManager 建议配置为较大的资源规格,因为这样可以把更多的计算放到本地从而减少网络 / 序列化 / 反序列化的开销。JobManager 因为是 OLAP 场景下的计算单点,也建议使用较大的资源规格。 + +## Future Work +作为 [Apache Flink Roadmap](https://flink.apache.org/what-is-flink/roadmap/) 的一部分,社区将会持续优化 Flink 在 OLAP 场景下易用性与可用性,提升查询性能与集群承载能力。相关的工作我们都会通过下面的 jira 追踪进展: - https://issues.apache.org/jira/browse/FLINK-25318 - https://issues.apache.org/jira/browse/FLINK-32898 - -Furthermore, we are adding relevant OLAP benchmarks to the Flink repository such as [flink-benchmarks](https://github.com/apache/flink-benchmarks). \ No newline at end of file diff --git a/docs/content.zh/docs/dev/table/overview.md b/docs/content.zh/docs/dev/table/overview.md index 05f758ac98cf4..13d6dc88d73c5 100644 --- a/docs/content.zh/docs/dev/table/overview.md +++ b/docs/content.zh/docs/dev/table/overview.md @@ -52,8 +52,8 @@ and later use the DataStream API to build alerting based on the matched patterns * [SQL]({{< ref "docs/dev/table/sql/overview" >}}): SQL 支持的操作和语法。 * [内置函数]({{< ref "docs/dev/table/functions/systemFunctions" >}}): Table API 和 SQL 中的内置函数。 * [SQL Client]({{< ref "docs/dev/table/sqlClient" >}}): 不用编写代码就可以尝试 Flink SQL,可以直接提交 SQL 任务到集群上。 +* [SQL Gateway]({{< ref "docs/dev/table/sql-gateway/overview" >}}): SQL 提交服务,支持多个客户端从远端并发提交 SQL 任务。 +* [OLAP Quickstart]({{< ref "docs/dev/table/olap_quickstart" >}}): Flink OLAP 服务搭建指南。 * [SQL Jdbc Driver]({{< ref "docs/dev/table/jdbcDriver" >}}): 标准JDBC Driver,可以提交Flink SQL作业到Sql Gateway。 -* [OLAP Quickstart]({{< ref "docs/dev/table/olap_quickstart" >}}): Flink OLAP服务搭建指南. - {{< top >}} diff --git a/docs/content.zh/docs/dev/table/sql/queries/window-tvf.md b/docs/content.zh/docs/dev/table/sql/queries/window-tvf.md index 8af3015fd2220..986aab222c200 100644 --- a/docs/content.zh/docs/dev/table/sql/queries/window-tvf.md +++ b/docs/content.zh/docs/dev/table/sql/queries/window-tvf.md @@ -312,13 +312,13 @@ CUMULATE(TABLE data, DESCRIPTOR(timecol), step, size) Flink SQL> SELECT * FROM TABLE( TUMBLE(TABLE Bid, DESCRIPTOR(bidtime), INTERVAL '10' MINUTES, INTERVAL '1' MINUTES)); -- or with the named params --- note: the DATA param must be the first +-- note: the DATA param must be the first and `OFFSET` should be wrapped with double quotes Flink SQL> SELECT * FROM TABLE( TUMBLE( DATA => TABLE Bid, TIMECOL => DESCRIPTOR(bidtime), SIZE => INTERVAL '10' MINUTES, - OFFSET => INTERVAL '1' MINUTES)); + `OFFSET` => INTERVAL '1' MINUTES)); +------------------+-------+------+------------------+------------------+-------------------------+ | bidtime | price | item | window_start | window_end | window_time | +------------------+-------+------+------------------+------------------+-------------------------+ diff --git a/docs/content.zh/docs/dev/table/sql/show.md b/docs/content.zh/docs/dev/table/sql/show.md index c6464b7081597..ca004ab19307e 100644 --- a/docs/content.zh/docs/dev/table/sql/show.md +++ b/docs/content.zh/docs/dev/table/sql/show.md @@ -608,7 +608,7 @@ show tables; ## SHOW CREATE TABLE ```sql -SHOW CREATE TABLE [catalog_name.][db_name.]table_name +SHOW CREATE TABLE [[catalog_name.]db_name.]table_name ``` 展示创建指定表的 create 语句。 diff --git a/docs/content.zh/release-notes/flink-1.18.md b/docs/content.zh/release-notes/flink-1.18.md new file mode 100644 index 0000000000000..0dc95975fcc0e --- /dev/null +++ b/docs/content.zh/release-notes/flink-1.18.md @@ -0,0 +1,151 @@ +--- +title: "Release Notes - Flink 1.18" +--- + + +# Release notes - Flink 1.18 + +These release notes discuss important aspects, such as configuration, behavior or dependencies, +that changed between Flink 1.17 and Flink 1.18. Please read these notes carefully if you are +planning to upgrade your Flink version to 1.18. + + +### Build System + +#### Support Java 17 (LTS) + +##### [FLINK-15736](https://issues.apache.org/jira/browse/FLINK-15736) +Apache Flink was made ready to compile and run with Java 17 (LTS). This feature is still in beta mode. +Issues should be reported in Flink's bug tracker. + + +### Table API & SQL + +#### Unified the max display column width for SQL Client and Table APi in both Streaming and Batch execution Mode + +##### [FLINK-30025](https://issues.apache.org/jira/browse/FLINK-30025) +Introduction of the new ConfigOption `DISPLAY_MAX_COLUMN_WIDTH` (`table.display.max-column-width`) +in the TableConfigOptions class is now in place. +This option is utilized when displaying table results through the Table API and SQL Client. +As SQL Client relies on the Table API underneath, and both SQL Client and the Table API serve distinct +and isolated scenarios, it is a rational choice to maintain a centralized configuration. +This approach also simplifies matters for users, as they only need to manage one ConfigOption for display control. + +During the migration phase, while `sql-client.display.max-column-width` is deprecated, +any changes made to `sql-client.display.max-column-width` will be automatically transferred to `table.display.max-column-width`. +Caution is advised when using the CLI, as it is not recommended to switch back and forth between these two options. + +#### Introduce Flink JDBC Driver For SQL Gateway +##### [FLINK-31496](https://issues.apache.org/jira/browse/FLINK-31496) +Apache Flink now supports JDBC driver to access SQL Gateway, you can use the driver in any cases that +support standard JDBC extension to connect to Flink cluster. + +#### Extend watermark-related features for SQL +##### [FLINK-31535](https://issues.apache.org/jira/browse/FLINK-31535) +Flink now enables user config watermark emit strategy/watermark alignment/watermark idle-timeout +in Flink SQL job with dynamic table options and 'OPTIONS' hint. + +#### Support configuring CatalogStore in Table API +##### [FLINK-32431](https://issues.apache.org/jira/browse/FLINK-32431) +Support lazy initialization of catalog and persistence of catalog configuration. + +#### Deprecate ManagedTable related APIs +##### [FLINK-32656](https://issues.apache.org/jira/browse/FLINK-32656) +ManagedTable related APIs are deprecated and will be removed in a future major release. + +### Connectors & Libraries + +#### SplitReader implements AutoCloseable instead of providing its own close method +##### [FLINK-31015](https://issues.apache.org/jira/browse/FLINK-31015) +SplitReader interface now extends `AutoCloseable` instead of providing its own method signature. + +#### JSON format supports projection push down +##### [FLINK-32610](https://issues.apache.org/jira/browse/FLINK-32610) +The JSON format introduced JsonParser as a new default way to deserialize JSON data. +JsonParser is a Jackson JSON streaming API to read JSON data which is much faster +and consumes less memory compared to the previous JsonNode approach. +This should be a compatible change, if you encounter any issues after upgrading, +you can fallback to the previous JsonNode approach by setting `json.decode.json-parser.enabled` to `false`. + + + +### Runtime & Coordination + +#### Unifying the Implementation of SlotManager +##### [FLINK-31439](https://issues.apache.org/jira/browse/FLINK-31439) +Fine-grained resource management are now enabled by default. You can use it by specifying the resource requirement. +More details can be found at https://nightlies.apache.org/flink/flink-docs-master/docs/deployment/finegrained_resource/#usage. + +#### Watermark aggregation performance is poor when watermark alignment is enabled and parallelism is high +##### [FLINK-32420](https://issues.apache.org/jira/browse/FLINK-32420) +This performance improvement would be good to mention in the release blog post. + +As proven by the micro benchmarks (screenshots attached in the ticket), with 5000 subtasks, +the time to calculate the watermark alignment on the JobManager by a factor of 76x (7664%). +Previously such large jobs were actually at large risk of overloading JobManager, now that's far less likely to happen. + +#### Replace Akka by Pekko +##### [FLINK-32468](https://issues.apache.org/jira/browse/32468) +Flink's RPC framework is now based on Apache Pekko instead of Akka. Any Akka dependencies were removed. + +#### Introduce Runtime Filter for Flink Batch Jobs +##### [FLINK-32486](https://issues.apache.org/jira/browse/FLINK-32486) +We introduced a runtime filter for batch jobs in 1.18, which is designed to improve join performance. +It will dynamically generate filter conditions for certain Join queries at runtime to reduce the amount of scanned or shuffled data, +avoid unnecessary I/O and network transmission, and speed up the query. +Its working principle is building a filter(e.g. bloom filter) based on the data on the small table side(build side) first, +then passing this filter to the large table side(probe side) to filter the irrelevant data on it, +this can reduce the data reaching the join and improve performance. + +#### Make watermark alignment ready for production use +##### [FLINK-32548](https://issues.apache.org/jira/browse/FLINK-32548) +The watermark alignment is ready for production since Flink 1.18, +which completed a series of bug fixes and improvements related to watermark alignment. +Please refer to [FLINK-32420](https://issues.apache.org/jira/browse/FLINK-32420) for more information. + +#### Redundant TaskManagers should always be fulfilled in FineGrainedSlotManager +##### [FLINK-32880](https://issues.apache.org/jira/browse/FLINK-32880) +Fix the issue that redundant TaskManagers will not be fulfilled in FineGrainedSlotManager periodically. + +#### RestClient can deadlock if request made after Netty event executor terminated +##### [FLINK-32583](https://issues.apache.org/jira/browse/FLINK-32583) +Fix a bug in the RestClient where making a request after the client was closed returns a future that never completes. + +#### Deprecate Queryable State +##### [FLINK-32559](https://issues.apache.org/jira/browse/FLINK-32559) +The Queryable State feature is formally deprecated. It will be removed in future major version bumps. + + +### SDK + +#### Properly deprecate DataSet API +##### [FLINK-32558](https://issues.apache.org/jira/browse/FLINK-32558) +DataSet API is formally deprecated, and will be removed in the next major release. + + +### Dependency upgrades + +#### Upgrade Calcite version to 1.32.0 +##### [FLINK-29319](https://issues.apache.org/jira/browse/FLINK-29319) and related tickets [FLINK-27998](https://issues.apache.org/jira/browse/FLINK-27998), [FLINK-28744](https://issues.apache.org/jira/browse/FLINK-28744) + +Due to CALCITE-4861 (Optimization of chained CAST calls can lead to unexpected behavior), +also Flink's casting behavior has slightly changed. Some corner cases might behave differently now: For example, +casting from `FLOAT`/`DOUBLE` 9234567891.12 to `INT`/`BIGINT` has now Java behavior for overflows. + diff --git a/docs/content/_index.md b/docs/content/_index.md index 30e64fe67fbeb..1e6e1ab0751ad 100644 --- a/docs/content/_index.md +++ b/docs/content/_index.md @@ -86,6 +86,7 @@ For some reason Hugo will only allow linking to the release notes if there is a leading '/' and file extension. --> See the release notes for +[Flink 1.18]({{< ref "/release-notes/flink-1.18.md" >}}), [Flink 1.17]({{< ref "/release-notes/flink-1.17.md" >}}), [Flink 1.16]({{< ref "/release-notes/flink-1.16.md" >}}), [Flink 1.15]({{< ref "/release-notes/flink-1.15.md" >}}), diff --git a/docs/content/docs/connectors/table/kafka.md b/docs/content/docs/connectors/table/kafka.md deleted file mode 100644 index 1bdf3ca8c566e..0000000000000 --- a/docs/content/docs/connectors/table/kafka.md +++ /dev/null @@ -1,688 +0,0 @@ - ---- -title: Kafka -weight: 3 -type: docs -aliases: - - /dev/table/connectors/kafka.html ---- - - -# Apache Kafka SQL Connector - -{{< label "Scan Source: Bounded" >}} -{{< label "Scan Source: Unbounded" >}} -{{< label "Sink: Streaming Append Mode" >}} - -The Kafka connector allows for reading data from and writing data into Kafka topics. - -Dependencies ------------- - -{{< sql_download_table "kafka" >}} - -The Kafka connector is not part of the binary distribution. -See how to link with it for cluster execution [here]({{< ref "docs/dev/configuration/overview" >}}). - -How to create a Kafka table ----------------- - -The example below shows how to create a Kafka table: - -```sql -CREATE TABLE KafkaTable ( - `user_id` BIGINT, - `item_id` BIGINT, - `behavior` STRING, - `ts` TIMESTAMP_LTZ(3) METADATA FROM 'timestamp' -) WITH ( - 'connector' = 'kafka', - 'topic' = 'user_behavior', - 'properties.bootstrap.servers' = 'localhost:9092', - 'properties.group.id' = 'testGroup', - 'scan.startup.mode' = 'earliest-offset', - 'format' = 'csv' -) -``` - -Available Metadata ------------------- - -The following connector metadata can be accessed as metadata columns in a table definition. - -The `R/W` column defines whether a metadata field is readable (`R`) and/or writable (`W`). -Read-only columns must be declared `VIRTUAL` to exclude them during an `INSERT INTO` operation. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyData TypeDescriptionR/W
topicSTRING NOT NULLTopic name of the Kafka record.R
partitionINT NOT NULLPartition ID of the Kafka record.R
headersMAP NOT NULLHeaders of the Kafka record as a map of raw bytes.R/W
leader-epochINT NULLLeader epoch of the Kafka record if available.R
offsetBIGINT NOT NULLOffset of the Kafka record in the partition.R
timestampTIMESTAMP_LTZ(3) NOT NULLTimestamp of the Kafka record.R/W
timestamp-typeSTRING NOT NULLTimestamp type of the Kafka record. Either "NoTimestampType", - "CreateTime" (also set when writing metadata), or "LogAppendTime".R
- -The extended `CREATE TABLE` example demonstrates the syntax for exposing these metadata fields: - -```sql -CREATE TABLE KafkaTable ( - `event_time` TIMESTAMP_LTZ(3) METADATA FROM 'timestamp', - `partition` BIGINT METADATA VIRTUAL, - `offset` BIGINT METADATA VIRTUAL, - `user_id` BIGINT, - `item_id` BIGINT, - `behavior` STRING -) WITH ( - 'connector' = 'kafka', - 'topic' = 'user_behavior', - 'properties.bootstrap.servers' = 'localhost:9092', - 'properties.group.id' = 'testGroup', - 'scan.startup.mode' = 'earliest-offset', - 'format' = 'csv' -); -``` - -**Format Metadata** - -The connector is able to expose metadata of the value format for reading. Format metadata keys -are prefixed with `'value.'`. - -The following example shows how to access both Kafka and Debezium metadata fields: - -```sql -CREATE TABLE KafkaTable ( - `event_time` TIMESTAMP_LTZ(3) METADATA FROM 'value.source.timestamp' VIRTUAL, -- from Debezium format - `origin_table` STRING METADATA FROM 'value.source.table' VIRTUAL, -- from Debezium format - `partition_id` BIGINT METADATA FROM 'partition' VIRTUAL, -- from Kafka connector - `offset` BIGINT METADATA VIRTUAL, -- from Kafka connector - `user_id` BIGINT, - `item_id` BIGINT, - `behavior` STRING -) WITH ( - 'connector' = 'kafka', - 'topic' = 'user_behavior', - 'properties.bootstrap.servers' = 'localhost:9092', - 'properties.group.id' = 'testGroup', - 'scan.startup.mode' = 'earliest-offset', - 'value.format' = 'debezium-json' -); -``` - -Connector Options ----------------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionRequiredForwardedDefaultTypeDescription
connector
requiredno(none)StringSpecify what connector to use, for Kafka use 'kafka'.
topic
required for sinkyes(none)StringTopic name(s) to read data from when the table is used as source. It also supports topic list for source by separating topic by semicolon like 'topic-1;topic-2'. Note, only one of "topic-pattern" and "topic" can be specified for sources. When the table is used as sink, the topic name is the topic to write data to. Note topic list is not supported for sinks.
topic-pattern
optionalyes(none)StringThe regular expression for a pattern of topic names to read from. All topics with names that match the specified regular expression will be subscribed by the consumer when the job starts running. Note, only one of "topic-pattern" and "topic" can be specified for sources.
properties.bootstrap.servers
requiredyes(none)StringComma separated list of Kafka brokers.
properties.group.id
optional for source, not applicable for sinkyes(none)StringThe id of the consumer group for Kafka source. If group ID is not specified, an automatically generated id "KafkaSource-{tableIdentifier}" will be used.
properties.*
optionalno(none)String - This can set and pass arbitrary Kafka configurations. Suffix names must match the configuration key defined in Kafka Configuration documentation. Flink will remove the "properties." key prefix and pass the transformed key and values to the underlying KafkaClient. For example, you can disable automatic topic creation via 'properties.allow.auto.create.topics' = 'false'. But there are some configurations that do not support to set, because Flink will override them, e.g. 'key.deserializer' and 'value.deserializer'. -
format
requiredno(none)StringThe format used to deserialize and serialize the value part of Kafka messages. - Please refer to the }}">formats page for - more details and more format options. - Note: Either this option or the 'value.format' option are required. -
key.format
optionalno(none)StringThe format used to deserialize and serialize the key part of Kafka messages. - Please refer to the }}">formats page - for more details and more format options. Note: If a key format is defined, the 'key.fields' - option is required as well. Otherwise the Kafka records will have an empty key. -
key.fields
optionalno[]List<String>Defines an explicit list of physical columns from the table schema that configure the data - type for the key format. By default, this list is empty and thus a key is undefined. - The list should look like 'field1;field2'. -
key.fields-prefix
optionalno(none)StringDefines a custom prefix for all fields of the key format to avoid name clashes with fields - of the value format. By default, the prefix is empty. If a custom prefix is defined, both the - table schema and 'key.fields' will work with prefixed names. When constructing the - data type of the key format, the prefix will be removed and the non-prefixed names will be used - within the key format. Please note that this option requires that 'value.fields-include' - must be set to 'EXCEPT_KEY'. -
value.format
requiredno(none)StringThe format used to deserialize and serialize the value part of Kafka messages. - Please refer to the }}">formats page - for more details and more format options. - Note: Either this option or the 'format' option are required. -
value.fields-include
optionalnoALL

Enum

Possible values: [ALL, EXCEPT_KEY]
Defines a strategy how to deal with key columns in the data type of the value format. By - default, 'ALL' physical columns of the table schema will be included in the value - format which means that key columns appear in the data type for both the key and value format. -
scan.startup.mode
optionalyesgroup-offsetsEnumStartup mode for Kafka consumer, valid values are 'earliest-offset', 'latest-offset', 'group-offsets', 'timestamp' and 'specific-offsets'. - See the following Start Reading Position for more details.
scan.startup.specific-offsets
optionalyes(none)StringSpecify offsets for each partition in case of 'specific-offsets' startup mode, e.g. 'partition:0,offset:42;partition:1,offset:300'. -
scan.startup.timestamp-millis
optionalyes(none)LongStart from the specified epoch timestamp (milliseconds) used in case of 'timestamp' startup mode.
scan.bounded.mode
optionalunboundedEnumBounded mode for Kafka consumer, valid values are 'latest-offset', 'group-offsets', 'timestamp' and 'specific-offsets'. - See the following Bounded Ending Position for more details.
scan.bounded.specific-offsets
optionalyes(none)StringSpecify offsets for each partition in case of 'specific-offsets' bounded mode, e.g. 'partition:0,offset:42;partition:1,offset:300'. If an offset - for a partition is not provided it will not consume from that partition.. -
scan.bounded.timestamp-millis
optionalyes(none)LongEnd at the specified epoch timestamp (milliseconds) used in case of 'timestamp' bounded mode.
scan.topic-partition-discovery.interval
optionalyes(none)DurationInterval for consumer to discover dynamically created Kafka topics and partitions periodically.
sink.partitioner
optionalyes'default'StringOutput partitioning from Flink's partitions into Kafka's partitions. Valid values are -
    -
  • default: use the kafka default partitioner to partition records.
  • -
  • fixed: each Flink partition ends up in at most one Kafka partition.
  • -
  • round-robin: a Flink partition is distributed to Kafka partitions sticky round-robin. It only works when record's keys are not specified.
  • -
  • Custom FlinkKafkaPartitioner subclass: e.g. 'org.mycompany.MyPartitioner'.
  • -
- See the following Sink Partitioning for more details. -
sink.semantic
optionalnoat-least-onceStringDeprecated: Please use sink.delivery-guarantee.
sink.delivery-guarantee
optionalnoat-least-onceStringDefines the delivery semantic for the Kafka sink. Valid enumerationns are 'at-least-once', 'exactly-once' and 'none'. See Consistency guarantees for more details.
sink.transactional-id-prefix
optionalyes(none)StringIf the delivery guarantee is configured as 'exactly-once' this value must be set and is used a prefix for the identifier of all opened Kafka transactions.
sink.parallelism
optionalno(none)IntegerDefines the parallelism of the Kafka sink operator. By default, the parallelism is determined by the framework using the same parallelism of the upstream chained operator.
- -Features ----------------- - -### Key and Value Formats - -Both the key and value part of a Kafka record can be serialized to and deserialized from raw bytes using -one of the given [formats]({{< ref "docs/connectors/table/formats/overview" >}}). - -**Value Format** - -Since a key is optional in Kafka records, the following statement reads and writes records with a configured -value format but without a key format. The `'format'` option is a synonym for `'value.format'`. All format -options are prefixed with the format identifier. - -```sql -CREATE TABLE KafkaTable ( - `ts` TIMESTAMP_LTZ(3) METADATA FROM 'timestamp', - `user_id` BIGINT, - `item_id` BIGINT, - `behavior` STRING -) WITH ( - 'connector' = 'kafka', - ... - - 'format' = 'json', - 'json.ignore-parse-errors' = 'true' -) -``` - -The value format will be configured with the following data type: - -```text -ROW<`user_id` BIGINT, `item_id` BIGINT, `behavior` STRING> -``` - -**Key and Value Format** - -The following example shows how to specify and configure key and value formats. The format options are -prefixed with either the `'key'` or `'value'` plus format identifier. - -```sql -CREATE TABLE KafkaTable ( - `ts` TIMESTAMP_LTZ(3) METADATA FROM 'timestamp', - `user_id` BIGINT, - `item_id` BIGINT, - `behavior` STRING -) WITH ( - 'connector' = 'kafka', - ... - - 'key.format' = 'json', - 'key.json.ignore-parse-errors' = 'true', - 'key.fields' = 'user_id;item_id', - - 'value.format' = 'json', - 'value.json.fail-on-missing-field' = 'false', - 'value.fields-include' = 'ALL' -) -``` - -The key format includes the fields listed in `'key.fields'` (using `';'` as the delimiter) in the same -order. Thus, it will be configured with the following data type: - -```text -ROW<`user_id` BIGINT, `item_id` BIGINT> -``` - -Since the value format is configured with `'value.fields-include' = 'ALL'`, key fields will also end up in -the value format's data type: - -```text -ROW<`user_id` BIGINT, `item_id` BIGINT, `behavior` STRING> -``` - -**Overlapping Format Fields** - -The connector cannot split the table's columns into key and value fields based on schema information -if both key and value formats contain fields of the same name. The `'key.fields-prefix'` option allows -to give key columns a unique name in the table schema while keeping the original names when configuring -the key format. - -The following example shows a key and value format that both contain a `version` field: - -```sql -CREATE TABLE KafkaTable ( - `k_version` INT, - `k_user_id` BIGINT, - `k_item_id` BIGINT, - `version` INT, - `behavior` STRING -) WITH ( - 'connector' = 'kafka', - ... - - 'key.format' = 'json', - 'key.fields-prefix' = 'k_', - 'key.fields' = 'k_version;k_user_id;k_item_id', - - 'value.format' = 'json', - 'value.fields-include' = 'EXCEPT_KEY' -) -``` - -The value format must be configured in `'EXCEPT_KEY'` mode. The formats will be configured with -the following data types: - -```text -key format: -ROW<`version` INT, `user_id` BIGINT, `item_id` BIGINT> - -value format: -ROW<`version` INT, `behavior` STRING> -``` - -### Topic and Partition Discovery - -The config option `topic` and `topic-pattern` specifies the topics or topic pattern to consume for source. The config option `topic` can accept topic list using semicolon separator like 'topic-1;topic-2'. -The config option `topic-pattern` will use regular expression to discover the matched topic. For example, if the `topic-pattern` is `test-topic-[0-9]`, then all topics with names that match the specified regular expression (starting with `test-topic-` and ending with a single digit)) will be subscribed by the consumer when the job starts running. - -To allow the consumer to discover dynamically created topics after the job started running, set a non-negative value for `scan.topic-partition-discovery.interval`. This allows the consumer to discover partitions of new topics with names that also match the specified pattern. - -Please refer to [Kafka DataStream Connector documentation]({{< ref "docs/connectors/datastream/kafka" >}}#kafka-consumers-topic-and-partition-discovery) for more about topic and partition discovery. - -Note that topic list and topic pattern only work in sources. In sinks, Flink currently only supports a single topic. - -### Start Reading Position - -The config option `scan.startup.mode` specifies the startup mode for Kafka consumer. The valid enumerations are: - -* `group-offsets`: start from committed offsets in ZK / Kafka brokers of a specific consumer group. -* `earliest-offset`: start from the earliest offset possible. -* `latest-offset`: start from the latest offset. -* `timestamp`: start from user-supplied timestamp for each partition. -* `specific-offsets`: start from user-supplied specific offsets for each partition. - -The default option value is `group-offsets` which indicates to consume from last committed offsets in ZK / Kafka brokers. - -If `timestamp` is specified, another config option `scan.startup.timestamp-millis` is required to specify a specific startup timestamp in milliseconds since January 1, 1970 00:00:00.000 GMT. - -If `specific-offsets` is specified, another config option `scan.startup.specific-offsets` is required to specify specific startup offsets for each partition, -e.g. an option value `partition:0,offset:42;partition:1,offset:300` indicates offset `42` for partition `0` and offset `300` for partition `1`. - -### Bounded Ending Position - -The config option `scan.bounded.mode` specifies the bounded mode for Kafka consumer. The valid enumerations are: -
    -
  • `group-offsets`: bounded by committed offsets in ZooKeeper / Kafka brokers of a specific consumer group. This is evaluated at the start of consumption from a given partition.
  • -
  • `latest-offset`: bounded by latest offsets. This is evaluated at the start of consumption from a given partition.
  • -
  • `timestamp`: bounded by a user-supplied timestamp.
  • -
  • `specific-offsets`: bounded by user-supplied specific offsets for each partition.
  • -
- -If config option value `scan.bounded.mode` is not set the default is an unbounded table. - -If `timestamp` is specified, another config option `scan.bounded.timestamp-millis` is required to specify a specific bounded timestamp in milliseconds since January 1, 1970 00:00:00.000 GMT. - -If `specific-offsets` is specified, another config option `scan.bounded.specific-offsets` is required to specify specific bounded offsets for each partition, -e.g. an option value `partition:0,offset:42;partition:1,offset:300` indicates offset `42` for partition `0` and offset `300` for partition `1`. If an offset for a partition is not provided it will not consume from that partition. - -### CDC Changelog Source - -Flink natively supports Kafka as a CDC changelog source. If messages in a Kafka topic are change event captured from other databases using a CDC tool, you can use the corresponding Flink CDC format to interpret the messages as INSERT/UPDATE/DELETE statements into a Flink SQL table. - -The changelog source is a very useful feature in many cases, such as synchronizing incremental data from databases to other systems, auditing logs, materialized views on databases, temporal join changing history of a database table and so on. - -Flink provides several CDC formats: - -* [debezium]({{< ref "docs/connectors/table/formats/debezium.md" >}}) -* [canal]({{< ref "docs/connectors/table/formats/canal.md" >}}) -* [maxwell]({{< ref "docs/connectors/table/formats/maxwell.md" >}}) - -### Sink Partitioning - -The config option `sink.partitioner` specifies output partitioning from Flink's partitions into Kafka's partitions. -By default, Flink uses the [Kafka default partitioner](https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/clients/producer/internals/DefaultPartitioner.java) to partition records. It uses the [sticky partition strategy](https://www.confluent.io/blog/apache-kafka-producer-improvements-sticky-partitioner/) for records with null keys and uses a murmur2 hash to compute the partition for a record with the key defined. - -In order to control the routing of rows into partitions, a custom sink partitioner can be provided. The 'fixed' partitioner will write the records in the same Flink partition into the same Kafka partition, which could reduce the cost of the network connections. - -### Consistency guarantees - -By default, a Kafka sink ingests data with at-least-once guarantees into a Kafka topic if the query is executed with [checkpointing enabled]({{< ref "docs/dev/datastream/fault-tolerance/checkpointing" >}}#enabling-and-configuring-checkpointing). - -With Flink's checkpointing enabled, the `kafka` connector can provide exactly-once delivery guarantees. - -Besides enabling Flink's checkpointing, you can also choose three different modes of operating chosen by passing appropriate `sink.delivery-guarantee` option: - - * `none`: Flink will not guarantee anything. Produced records can be lost or they can be duplicated. - * `at-least-once` (default setting): This guarantees that no records will be lost (although they can be duplicated). - * `exactly-once`: Kafka transactions will be used to provide exactly-once semantic. Whenever you write - to Kafka using transactions, do not forget about setting desired `isolation.level` (`read_committed` - or `read_uncommitted` - the latter one is the default value) for any application consuming records - from Kafka. - -Please refer to [Kafka documentation]({{< ref "docs/connectors/datastream/kafka" >}}#kafka-producers-and-fault-tolerance) for more caveats about delivery guarantees. - -### Source Per-Partition Watermarks - -Flink supports to emit per-partition watermarks for Kafka. Watermarks are generated inside the Kafka -consumer. The per-partition watermarks are merged in the same way as watermarks are merged during streaming -shuffles. The output watermark of the source is determined by the minimum watermark among the partitions -it reads. If some partitions in the topics are idle, the watermark generator will not advance. You can -alleviate this problem by setting the [`'table.exec.source.idle-timeout'`]({{< ref "docs/dev/table/config" >}}#table-exec-source-idle-timeout) -option in the table configuration. - -Please refer to [Kafka watermark strategies]({{< ref "docs/dev/datastream/event-time/generating_watermarks" >}}#watermark-strategies-and-the-kafka-connector) -for more details. - -### Security -In order to enable security configurations including encryption and authentication, you just need to setup security -configurations with "properties." prefix in table options. The code snippet below shows configuring Kafka table to -use PLAIN as SASL mechanism and provide JAAS configuration: -```sql -CREATE TABLE KafkaTable ( - `user_id` BIGINT, - `item_id` BIGINT, - `behavior` STRING, - `ts` TIMESTAMP_LTZ(3) METADATA FROM 'timestamp' -) WITH ( - 'connector' = 'kafka', - ... - 'properties.security.protocol' = 'SASL_PLAINTEXT', - 'properties.sasl.mechanism' = 'PLAIN', - 'properties.sasl.jaas.config' = 'org.apache.kafka.common.security.plain.PlainLoginModule required username=\"username\" password=\"password\";' -) -``` -For a more complex example, use SASL_SSL as the security protocol and use SCRAM-SHA-256 as SASL mechanism: -```sql -CREATE TABLE KafkaTable ( - `user_id` BIGINT, - `item_id` BIGINT, - `behavior` STRING, - `ts` TIMESTAMP_LTZ(3) METADATA FROM 'timestamp' -) WITH ( - 'connector' = 'kafka', - ... - 'properties.security.protocol' = 'SASL_SSL', - /* SSL configurations */ - /* Configure the path of truststore (CA) provided by the server */ - 'properties.ssl.truststore.location' = '/path/to/kafka.client.truststore.jks', - 'properties.ssl.truststore.password' = 'test1234', - /* Configure the path of keystore (private key) if client authentication is required */ - 'properties.ssl.keystore.location' = '/path/to/kafka.client.keystore.jks', - 'properties.ssl.keystore.password' = 'test1234', - /* SASL configurations */ - /* Set SASL mechanism as SCRAM-SHA-256 */ - 'properties.sasl.mechanism' = 'SCRAM-SHA-256', - /* Set JAAS configurations */ - 'properties.sasl.jaas.config' = 'org.apache.kafka.common.security.scram.ScramLoginModule required username=\"username\" password=\"password\";' -) -``` - -Please note that the class path of the login module in `sasl.jaas.config` might be different if you relocate Kafka -client dependencies, so you may need to rewrite it with the actual class path of the module in the JAR. -For example if you are using SQL client JAR, which has relocate Kafka client dependencies to `org.apache.flink.kafka.shaded.org.apache.kafka`, -the path of plain login module should be `org.apache.flink.kafka.shaded.org.apache.kafka.common.security.plain.PlainLoginModule` instead. - -For detailed explanations of security configurations, please refer to -the "Security" section in Apache Kafka documentation. - -Data Type Mapping ----------------- - -Kafka stores message keys and values as bytes, so Kafka doesn't have schema or data types. The Kafka messages are deserialized and serialized by formats, e.g. csv, json, avro. -Thus, the data type mapping is determined by specific formats. Please refer to [Formats]({{< ref "docs/connectors/table/formats/overview" >}}) pages for more details. - -{{< top >}} diff --git a/docs/content/docs/deployment/elastic_scaling.md b/docs/content/docs/deployment/elastic_scaling.md index 5259c1066fa08..f342f05ffb9c3 100644 --- a/docs/content/docs/deployment/elastic_scaling.md +++ b/docs/content/docs/deployment/elastic_scaling.md @@ -25,22 +25,96 @@ under the License. # Elastic Scaling -Apache Flink allows you to rescale your jobs. You can do this manually by stopping the job and restarting from the savepoint created during shutdown with a different parallelism. +Historically, the parallelism of a job has been static throughout its lifecycle and defined once during its submission. Batch jobs couldn't be rescaled at all, while Streaming jobs could have been stopped with a savepoint and restarted with a different parallelism. -This page describes options where Flink automatically adjusts the parallelism instead. +This page describes a new class of schedulers that allow Flink to adjust job's parallelism at runtime, which pushes Flink one step closer to a truly cloud-native stream processor. The new schedulers are [Adaptive Scheduler](#adaptive-scheduler) (streaming) and [Adaptive Batch Scheduler](#adaptive-batch-scheduler) (batch). -## Reactive Mode +## Adaptive Scheduler + +The Adaptive Scheduler can adjust the parallelism of a job based on available slots. It will automatically reduce the parallelism if not enough slots are available to run the job with the originally configured parallelism; be it due to not enough resources being available at the time of submission, or TaskManager outages during the job execution. If new slots become available the job will be scaled up again, up to the configured parallelism. + +In Reactive Mode (see below) the configured parallelism is ignored and treated as if it was set to infinity, letting the job always use as many resources as possible. + +One benefit of the Adaptive Scheduler over the default scheduler is that it can handle TaskManager losses gracefully, since it would just scale down in these cases. + +{{< img src="/fig/adaptive_scheduler.png" >}} + +Adaptive Scheduler builds on top of a feature called [Declarative Resource Management](https://cwiki.apache.org/confluence/display/FLINK/FLIP-138%3A+Declarative+Resource+management). As you can see, instead of asking for the exact number of slots, JobMaster declares its desired resources (for reactive mode the maximum is set to infinity) to the ResourceManager, which then tries to fulfill those resources. + +{{< img src="/fig/adaptive_scheduler_rescale.png" >}} + +When JobMaster gets more resources during the runtime, it will automatically rescale the job using the latest available savepoint, eliminating the need for an external orchestration. + +Starting from **Flink 1.18.x**, you can re-declare the resource requirements of a running job using [Externalized Declarative Resource Management](#externalized-declarative-resource-management), otherwise the Adaptive Scheduler won't be able to handle cases where the job needs to be rescaled due to a change in the input rate, or a change in the performance of the workload. + +### Externalized Declarative Resource Management + +{{< hint warning >}} +Externalized Declarative Resource Management is an MVP ("minimum viable product") feature. The Flink community is actively looking for feedback by users through our mailing lists. Please check the limitations listed on this page. +{{< /hint >}} {{< hint info >}} -Reactive mode is an MVP ("minimum viable product") feature. The Flink community is actively looking for feedback by users through our mailing lists. Please check the limitations listed on this page. +You can use Externalized Declarative Resource Management with the [Apache Flink Kubernetes operator](https://nightlies.apache.org/flink/flink-kubernetes-operator-docs-release-1.6/docs/custom-resource/autoscaler/#flink-118-and-in-place-scaling-support) for a fully-fledged auto-scaling experience. {{< /hint >}} -Reactive Mode configures a job so that it always uses all resources available in the cluster. Adding a TaskManager will scale up your job, removing resources will scale it down. Flink will manage the parallelism of the job, always setting it to the highest possible values. +Externalized Declarative Resource Management aims to address two deployment scenarios: +1. Adaptive Scheduler on Session Cluster, where multiple jobs can compete for resources, and you need a finer-grained control over the distribution of resources between jobs. +2. Adaptive Scheduler on Application Cluster in combination with Active Resource Manager (e.g. [Native Kubernetes]({{< ref "docs/deployment/resource-providers/native_kubernetes" >}})), where you rely on Flink to "greedily" spawn new TaskManagers, but you still want to leverage rescaling capabilities as with [Reactive Mode](#reactive-mode). -Reactive Mode restarts a job on a rescaling event, restoring it from the latest completed checkpoint. This means that there is no overhead of creating a savepoint (which is needed for manually rescaling a job). Also, the amount of data that is reprocessed after rescaling depends on the checkpointing interval, and the restore time depends on the state size. +by introducing a new [REST API endpoint]({{< ref "docs/ops/rest_api" >}}#jobs-jobid-resource-requirements-1), that allows you to re-declare resource requirements of a running job, by setting per-vertex parallelism boundaries. -The Reactive Mode allows Flink users to implement a powerful autoscaling mechanism, by having an external service monitor certain metrics, such as consumer lag, aggregate CPU utilization, throughput or latency. As soon as these metrics are above or below a certain threshold, additional TaskManagers can be added or removed from the Flink cluster. This could be implemented through changing the [replica factor](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#replicas) of a Kubernetes deployment, or an [autoscaling group](https://docs.aws.amazon.com/autoscaling/ec2/userguide/AutoScalingGroup.html) on AWS. This external service only needs to handle the resource allocation and deallocation. Flink will take care of keeping the job running with the resources available. +``` +PUT /jobs//resource-requirements +REQUEST BODY: +{ + "": { + "parallelism": { + "lowerBound": 3, + "upperBound": 5 + } + }, + "": { + "parallelism": { + "lowerBound": 2, + "upperBound": 3 + } + } +} +``` + +To a certain extent, the above endpoint could be thought about as a "re-scaling endpoint" and it introduces an important building block for building an auto-scaling experience for Flink. + +You can manually try this feature out, by navigating the Job overview in the Flink UI and using up-scale/down-scale buttons in the task list. + +### Usage + +{{< hint info >}} +If you are using Adaptive Scheduler on a [session cluster]({{< ref "docs/deployment/overview" >}}/#session-mode), there are no guarantees regarding the distribution of slots between multiple running jobs in the same session, in case the cluster doesn't have enough resources. The [External Declarative Resource Management](#externalized-declarative-resource-management) can partially mitigate this issue, but it is still recommended to use Adaptive Scheduler on a [application cluster]({{< ref "docs/deployment/overview" >}}/#application-mode). +{{< /hint >}} + +The `jobmanager.scheduler` needs to be set to on the cluster level for the adaptive scheduler to be used instead of default scheduler. + +```yaml +jobmanager.scheduler: adaptive +``` + +The behavior of Adaptive Scheduler is configured by [all configuration options prefixed with `jobmanager.adaptive-scheduler`]({{< ref "docs/deployment/config">}}#advanced-scheduling-options) in their name. + +### Limitations + +- **Streaming jobs only**: The Adaptive Scheduler runs with streaming jobs only. When submitting a batch job, Flink will use the default scheduler of batch jobs, i.e. [Adaptive Batch Scheduler](#adaptive-batch-scheduler) +- **No support for partial failover**: Partial failover means that the scheduler is able to restart parts ("regions" in Flink's internals) of a failed job, instead of the entire job. This limitation impacts only recovery time of embarrassingly parallel jobs: Flink's default scheduler can restart failed parts, while Adaptive Scheduler will restart the entire job. +- Scaling events trigger job and task restarts, which will increase the number of Task attempts. + +## Reactive Mode + +Reactive Mode is a special mode for Adaptive Scheduler, that assumes a single job per-cluster (enforced by the [Application Mode]({{< ref "docs/deployment/overview" >}}#application-mode)). Reactive Mode configures a job so that it always uses all resources available in the cluster. Adding a TaskManager will scale up your job, removing resources will scale it down. Flink will manage the parallelism of the job, always setting it to the highest possible values. + +Reactive Mode restarts a job on a rescaling event, restoring it from the latest completed checkpoint. This means that there is no overhead of creating a savepoint (which is needed for manually rescaling a job). Also, the amount of data that is reprocessed after rescaling depends on the checkpointing interval, and the restore time depends on the state size. + +The Reactive Mode allows Flink users to implement a powerful autoscaling mechanism, by having an external service monitor certain metrics, such as consumer lag, aggregate CPU utilization, throughput or latency. As soon as these metrics are above or below a certain threshold, additional TaskManagers can be added or removed from the Flink cluster. This could be implemented through changing the [replica factor](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#replicas) of a Kubernetes deployment, or an [autoscaling group](https://docs.aws.amazon.com/autoscaling/ec2/userguide/AutoScalingGroup.html) on AWS. This external service only needs to handle the resource allocation and deallocation. Flink will take care of keeping the job running with the resources available. + ### Getting started If you just want to try out Reactive Mode, follow these instructions. They assume that you are deploying Flink on a single machine. @@ -100,6 +174,11 @@ With Reactive Mode enabled, the [`jobmanager.adaptive-scheduler.resource-stabili In scenarios where TaskManagers are not connecting at the same time, but slowly one after another, this behavior leads to a job restart whenever a TaskManager connects. Increase this configuration value if you want to wait for the resources to stabilize before scheduling the job. Additionally, one can configure [`jobmanager.adaptive-scheduler.min-parallelism-increase`]({{< ref "docs/deployment/config">}}#jobmanager-adaptive-scheduler-min-parallelism-increase): This configuration option specifics the minimum amount of additional, aggregate parallelism increase before triggering a scale-up. For example if you have a job with a source (parallelism=2) and a sink (parallelism=2), the aggregate parallelism is 4. By default, the configuration key is set to 1, so any increase in the aggregate parallelism will trigger a restart. +One can force scaling operations to happen by setting [`jobmanager.adaptive-scheduler.scaling-interval.max`]({{< ref "docs/deployment/config">}}#jobmanager-adaptive-scheduler-scaling-interval-max). It is disabled by default. If set, then when new resources are added to the cluster, a rescale is scheduled after [`jobmanager.adaptive-scheduler.scaling-interval.max`]({{< ref "docs/deployment/config">}}#jobmanager-adaptive-scheduler-scaling-interval-max) even if [`jobmanager.adaptive-scheduler.min-parallelism-increase`]({{< ref "docs/deployment/config">}}#jobmanager-adaptive-scheduler-min-parallelism-increase) is not satisfied. + +To avoid too frequent scaling operations, one can configure [`jobmanager.adaptive-scheduler.scaling-interval.min`]({{< ref "docs/deployment/config">}}#jobmanager-adaptive-scheduler-scaling-interval-min) to set the minimum time between 2 scaling operations. The default is 30s. + + #### Recommendations - **Configure periodic checkpointing for stateful jobs**: Reactive mode restores from the latest completed checkpoint on a rescale event. If no periodic checkpointing is enabled, your program will lose its state. Checkpointing also configures a **restart strategy**. Reactive Mode will respect the configured restarting strategy: If no restarting strategy is configured, reactive mode will fail your job, instead of scaling it. @@ -113,40 +192,12 @@ Additionally, one can configure [`jobmanager.adaptive-scheduler.min-parallelism- Since Reactive Mode is a new, experimental feature, not all features supported by the default scheduler are also available with Reactive Mode (and its adaptive scheduler). The Flink community is working on addressing these limitations. -- **Deployment is only supported as a standalone application deployment**. Active resource providers (such as native Kubernetes, YARN) are explicitly not supported. Standalone session clusters are not supported either. The application deployment is limited to single job applications. +- **Deployment is only supported as a standalone application deployment**. Active resource providers (such as native Kubernetes, YARN) are explicitly not supported. Standalone session clusters are not supported either. The application deployment is limited to single job applications. The only supported deployment options are [Standalone in Application Mode]({{< ref "docs/deployment/resource-providers/standalone/overview" >}}#application-mode) ([described](#getting-started) on this page), [Docker in Application Mode]({{< ref "docs/deployment/resource-providers/standalone/docker" >}}#application-mode-on-docker) and [Standalone Kubernetes Application Cluster]({{< ref "docs/deployment/resource-providers/standalone/kubernetes" >}}#deploy-application-cluster). The [limitations of Adaptive Scheduler](#limitations-1) also apply to Reactive Mode. - -## Adaptive Scheduler - -{{< hint warning >}} -Using Adaptive Scheduler directly (not through Reactive Mode) is only advised for advanced users because slot allocation on a session cluster with multiple jobs is not defined. -{{< /hint >}} - -The Adaptive Scheduler can adjust the parallelism of a job based on available slots. It will automatically reduce the parallelism if not enough slots are available to run the job with the originally configured parallelism; be it due to not enough resources being available at the time of submission, or TaskManager outages during the job execution. If new slots become available the job will be scaled up again, up to the configured parallelism. -In Reactive Mode (see above) the configured parallelism is ignored and treated as if it was set to infinity, letting the job always use as many resources as possible. -You can also use Adaptive Scheduler without Reactive Mode, but there are some practical limitations: -- If you are using Adaptive Scheduler on a session cluster, there are no guarantees regarding the distribution of slots between multiple running jobs in the same session. - -One benefit of the Adaptive Scheduler over the default scheduler is that it can handle TaskManager losses gracefully, since it would just scale down in these cases. - -### Usage - -The following configuration parameter need to be set: - -- `jobmanager.scheduler: adaptive`: Change from the default scheduler to adaptive scheduler - -The behavior of Adaptive Scheduler is configured by [all configuration options containing `adaptive-scheduler`]({{< ref "docs/deployment/config">}}#advanced-scheduling-options) in their name. - -### Limitations - -- **Streaming jobs only**: The Adaptive Scheduler runs with streaming jobs only. When submitting a batch job, Flink will use the default scheduler of batch jobs, i.e. Adaptive Batch Scheduler. -- **No support for partial failover**: Partial failover means that the scheduler is able to restart parts ("regions" in Flink's internals) of a failed job, instead of the entire job. This limitation impacts only recovery time of embarrassingly parallel jobs: Flink's default scheduler can restart failed parts, while Adaptive Scheduler will restart the entire job. -- Scaling events trigger job and task restarts, which will increase the number of Task attempts. - ## Adaptive Batch Scheduler The Adaptive Batch Scheduler is a batch job scheduler that can automatically adjust the execution plan. It currently supports automatically deciding parallelisms of operators for batch jobs. If an operator is not set with a parallelism, the scheduler will decide parallelism for it according to the size of its consumed datasets. This can bring many benefits: diff --git a/docs/content/docs/dev/datastream/dataset_migration.md b/docs/content/docs/dev/datastream/dataset_migration.md new file mode 100644 index 0000000000000..25d1cb8f9a26b --- /dev/null +++ b/docs/content/docs/dev/datastream/dataset_migration.md @@ -0,0 +1,774 @@ +--- +title: "How to Migrate from DataSet to DataStream" +weight: 302 +type: docs +--- + + +# How to Migrate from DataSet to DataStream + +The DataSet API has been formally deprecated and will no longer receive active maintenance and support. It will be removed in the +Flink 2.0 version. Flink users are recommended to migrate from the DataSet API to the DataStream API, Table API and SQL for their +data processing requirements. + +Noticed that APIs in DataStream do not always match those in DataSet exactly. The purpose of this document is to help users understand +how to achieve the same data processing behaviors with DataStream APIs as using DataSet APIs. + +According to the changes in coding and execution efficiency that are required for migration, we categorized DataSet APIs into 4 categories: + +- Category 1: APIs that have exact equivalent in DataStream, which requires barely any changes to migrate. + +- Category 2: APIs whose behavior can be achieved by other APIs with different semantics in DataStream, which might require some code changes for +migration but will result in the same execution efficiency. + +- Category 3: APIs whose behavior can be achieved by other APIs with different semantics in DataStream, with potentially additional cost in execution efficiency. + +- Category 4: APIs whose behaviors are not supported by DataStream API. + +The subsequent sections will first introduce how to set the execution environment and source/sink, then provide detailed explanations on how to migrate +each category of DataSet APIs to the DataStream APIs, highlighting the specific considerations and challenges associated with each +category. + + +## Setting the execution environment + +The first step of migrating an application from DataSet API to DataStream API is to replace `ExecutionEnvironment` with `StreamExecutionEnvironment`. + + + + + + + + + + + + + + +
DataSetDataStream
+ {{< highlight "java" >}} +// Create the execution environment +ExecutionEnvironment.getExecutionEnvironment(); +// Create the local execution environment +ExecutionEnvironment.createLocalEnvironment(); +// Create the collection environment +new CollectionEnvironment(); +// Create the remote environment +ExecutionEnvironment.createRemoteEnvironment(String host, int port, String... jarFiles); + {{< /highlight >}} + + {{< highlight "java" >}} +// Create the execution environment +StreamExecutionEnvironment.getExecutionEnvironment(); +// Create the local execution environment +StreamExecutionEnvironment.createLocalEnvironment(); +// The collection environment is not supported. +// Create the remote environment +StreamExecutionEnvironment.createRemoteEnvironment(String host, int port, String... jarFiles); + {{< /highlight >}} +
+ +Unlike DataSet, DataStream supports processing on both bounded and unbounded data streams. Thus, user needs to explicitly set the execution mode +to `RuntimeExecutionMode.BATCH` if that is expected. + +```java +StreamExecutionEnvironment executionEnvironment = // [...]; +executionEnvironment.setRuntimeMode(RuntimeExecutionMode.BATCH); +``` + +## Using the streaming sources and sinks + +### Sources + +The DataStream API uses `DataStreamSource` to read records from external system, while the DataSet API uses the `DataSource`. + + + + + + + + + + + + + + +
DataSetDataStream
+ {{< highlight "java" >}} +// Read data from file +DataSource<> source = ExecutionEnvironment.readFile(inputFormat, filePath); +// Read data from collection +DataSource<> source = ExecutionEnvironment.fromCollection(data); +// Read data from inputformat +DataSource<> source = ExecutionEnvironment.createInput(inputFormat) + {{< /highlight >}} + + {{< highlight "java" >}} +// Read data from file +DataStreamSource<> source = StreamExecutionEnvironment.readFile(inputFormat, filePath); +// Read data from collection +DataStreamSource<> source = StreamExecutionEnvironment.fromCollection(data); +// Read data from inputformat +DataStreamSource<> source = StreamExecutionEnvironment.createInput(inputFormat) + {{< /highlight >}} +
+ +### Sinks + +The DataStream API uses `DataStreamSink` to write records to external system, while the +DataSet API uses the `DataSink`. + + + + + + + + + + + + + + +
DataSetDataStream
+ {{< highlight "java" >}} +// Write to outputformat +DataSink<> sink = dataSet.output(outputFormat); +// Write to csv file +DataSink<> sink = dataSet.writeAsCsv(filePath); +// Write to text file +DataSink<> sink = dataSet.writeAsText(filePath); + {{< /highlight >}} + + {{< highlight "java" >}} +// Write to sink +DataStreamSink<> sink = dataStream.sinkTo(sink) +// Write to csv file +DataStreamSink<> sink = dataStream.writeAsCsv(path); +// Write to text file +DataStreamSink<> sink = dataStream.writeAsText(path); + {{< /highlight >}} +
+ +If you are looking for pre-defined source and sink connectors of DataStream, please check the [Connector Docs]({{< ref "docs/connectors/datastream/overview" >}}) + +## Migrating DataSet APIs + +### Category 1 + +For Category 1, these DataSet APIs have exact equivalent in DataStream, which requires barely any changes to migrate. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperationsDataSetDataStream
Map + {{< highlight "java" >}} +dataSet.map(new MapFunction<>(){ +// implement user-defined map logic +}); + {{< /highlight >}} + + {{< highlight "java" >}} +dataStream.map(new MapFunction<>(){ +// implement user-defined map logic +}); + {{< /highlight >}} +
FlatMap + {{< highlight "java" >}} +dataSet.flatMap(new FlatMapFunction<>(){ +// implement user-defined flatmap logic +}); + {{< /highlight >}} + + {{< highlight "java" >}} +dataStream.flatMap(new FlatMapFunction<>(){ +// implement user-defined flatmap logic +}); + {{< /highlight >}} +
Filter + {{< highlight "java" >}} +dataSet.filter(new FilterFunction<>(){ +// implement user-defined filter logic +}); + {{< /highlight >}} + + {{< highlight "java" >}} +dataStream.filter(new FilterFunction<>(){ +// implement user-defined filter logic +}); + {{< /highlight >}} +
Union + {{< highlight "java" >}} +dataSet1.union(dataSet2); + {{< /highlight >}} + + {{< highlight "java" >}} +dataStream1.union(dataStream2); + {{< /highlight >}} +
Rebalance + {{< highlight "java" >}} +dataSet.rebalance(); + {{< /highlight >}} + + {{< highlight "java" >}} +dataStream.rebalance(); + {{< /highlight >}} +
Project + {{< highlight "java" >}} +DataSet> dataSet = // [...] +dataSet.project(2,0); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream = // [...] +dataStream.project(2,0); + {{< /highlight >}} +
Reduce on Grouped DataSet + {{< highlight "java" >}} +DataSet> dataSet = // [...] +dataSet.groupBy(value -> value.f0) + .reduce(new ReduceFunction<>(){ + // implement user-defined reduce logic + }); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream = // [...] +dataStream.keyBy(value -> value.f0) + .reduce(new ReduceFunction<>(){ + // implement user-defined reduce logic + }); + {{< /highlight >}} +
Aggregate on Grouped DataSet + {{< highlight "java" >}} +DataSet> dataSet = // [...] +// compute sum of the second field +dataSet.groupBy(value -> value.f0) + .aggregate(SUM, 1); +// compute min of the second field +dataSet.groupBy(value -> value.f0) + .aggregate(MIN, 1); +// compute max of the second field +dataSet.groupBy(value -> value.f0) + .aggregate(MAX, 1); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream = // [...] +// compute sum of the second field +dataStream.keyBy(value -> value.f0) + .sum(1); +// compute min of the second field +dataStream.keyBy(value -> value.f0) + .min(1); +// compute max of the second field +dataStream.keyBy(value -> value.f0) + .max(1); + {{< /highlight >}} +
+ +### Category 2 + +For category 2, the behavior of these DataSet APIs can be achieved by other APIs with different semantics in DataStream, which might require some code changes for +migration but will result in the same execution efficiency. + +Operations on a full DataSet correspond to the global window aggregation in DataStream with a custom window that is triggered at the end of the inputs. The `EndOfStreamWindows` +in the [Appendix]({{< ref "docs/dev/datastream/dataset_migration#endofstreamwindows" >}}) shows how such a window can be implemented. We will reuse it in the rest of this document. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperationsDataSetDataStream
Distinct + {{< highlight "java" >}} +DataSet dataSet = // [...] +dataSet.distinct(); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream dataStream = // [...] +dataStream.keyBy(value -> value) + .reduce((value1, value2) -> value1); + {{< /highlight >}} +
Hash-Partition + {{< highlight "java" >}} +DataSet> dataSet = // [...] +dataSet.partitionByHash(value -> value.f0); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream = // [...] +// partition by the hashcode of key +dataStream.partitionCustom( + (key, numSubpartition) -> key.hashCode() % numSubpartition, + value -> value.f0); + {{< /highlight >}} +
Reduce on Full DataSet + {{< highlight "java" >}} +DataSet dataSet = // [...] +dataSet.reduce(new ReduceFunction<>(){ + // implement user-defined reduce logic + }); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream dataStream = // [...] +dataStream.windowAll(EndOfStreamWindows.get()) + .reduce(new ReduceFunction<>(){ + // implement user-defined reduce logic + }); + {{< /highlight >}} +
Aggregate on Full DataSet + {{< highlight "java" >}} +DataSet> dataSet = // [...] +// compute sum of the second field +dataSet.aggregate(SUM, 1); +// compute min of the second field +dataSet.aggregate(MIN, 1); +// compute max of the second field +dataSet.aggregate(MAX, 1); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream = // [...] +// compute sum of the second field +dataStream.windowAll(EndOfStreamWindows.get()) + .sum(1); +// compute min of the second field +dataStream.windowAll(EndOfStreamWindows.get()) + .min(1); +// compute max of the second field +dataStream.windowAll(EndOfStreamWindows.get()) + .max(1); + {{< /highlight >}} +
GroupReduce on Full DataSet + {{< highlight "java" >}} +DataSet dataSet = // [...] +dataSet.reduceGroup(new GroupReduceFunction<>(){ + // implement user-defined group reduce logic + }); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream dataStream = // [...] +dataStream.windowAll(EndOfStreamWindows.get()) + .apply(new WindowFunction<>(){ + // implement user-defined group reduce logic + }); + {{< /highlight >}} +
GroupReduce on Grouped DataSet + {{< highlight "java" >}} +DataSet> dataSet = // [...] +dataSet.groupBy(value -> value.f0) + .reduceGroup(new GroupReduceFunction<>(){ + // implement user-defined group reduce logic + }); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream = // [...] +dataStream.keyBy(value -> value.f0) + .window(EndOfStreamWindows.get()) + .apply(new WindowFunction<>(){ + // implement user-defined group reduce logic + }); + {{< /highlight >}} +
First-n + {{< highlight "java" >}} +dataSet.first(n) + {{< /highlight >}} + + {{< highlight "java" >}} +dataStream.windowAll(EndOfStreamWindows.get()) + .apply(new AllWindowFunction<>(){ + // implement first-n logic + }); + {{< /highlight >}} +
Join + {{< highlight "java" >}} +DataSet> dataSet1 = // [...] +DataSet> dataSet2 = // [...] +dataSet1.join(dataSet2) + .where(value -> value.f0) + .equalTo(value -> value.f0) + .with(new JoinFunction<>(){ + // implement user-defined join logic + }); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream1 = // [...] +DataStream> dataStream2 = // [...] +dataStream1.join(dataStream2) + .where(value -> value.f0) + .equalTo(value -> value.f0) + .window(EndOfStreamWindows.get())) + .apply(new JoinFunction<>(){ + // implement user-defined join logic + }); + {{< /highlight >}} +
CoGroup + {{< highlight "java" >}} +DataSet> dataSet1 = // [...] +DataSet> dataSet2 = // [...] +dataSet1.coGroup(dataSet2) + .where(value -> value.f0) + .equalTo(value -> value.f0) + .with(new CoGroupFunction<>(){ + // implement user-defined co group logic + }); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream> dataStream1 = // [...] +DataStream> dataStream2 = // [...] +dataStream1.coGroup(dataStream2) + .where(value -> value.f0) + .equalTo(value -> value.f0) + .window(EndOfStreamWindows.get())) + .apply(new CoGroupFunction<>(){ + // implement user-defined co group logic + }); + {{< /highlight >}} +
OuterJoin + {{< highlight "java" >}} +DataSet> dataSet1 = // [...] +DataSet> dataSet2 = // [...] +// left outer join +dataSet1.leftOuterJoin(dataSet2) + .where(dataSet1.f0) + .equalTo(dataSet2.f0) + .with(new JoinFunction<>(){ + // implement user-defined left outer join logic + }); +// right outer join +dataSet1.rightOuterJoin(dataSet2) + .where(dataSet1.f0) + .equalTo(dataSet2.f0) + .with(new JoinFunction<>(){ + // implement user-defined right outer join logic + }); + {{< /highlight >}} + + {{< highlight "java" >}} + DataStream> dataStream1 = // [...] + DataStream> dataStream2 = // [...] + // left outer join + dataStream1.coGroup(dataStream2) + .where(value -> value.f0) + .equalTo(value -> value.f0) + .window(EndOfStreamWindows.get()) + .apply((leftIterable, rightInterable, collector) -> { + if(!rightInterable.iterator().hasNext()){ + // implement user-defined left outer join logic + } + }); + // right outer join + dataStream1.coGroup(dataStream2) + .where(value -> value.f0) + .equalTo(value -> value.f0) + .window(EndOfStreamWindows.get()) + .apply((leftIterable, rightInterable, collector) -> { + if(!leftIterable.iterator().hasNext()){ + // implement user-defined right outer join logic + } + }); + {{< /highlight >}} +
+ +### Category 3 + +For category 3, the behavior of these DataSet APIs can be achieved by other APIs with different semantics in DataStream, with potentially additional cost in execution efficiency. + +Currently, DataStream API does not directly support aggregations on non-keyed streams (subtask-scope aggregations). In order to do so, we need to first assign the subtask id +to the records, then turn the stream into a keyed stream. The `AddSubtaskIdMapFunction` in the [Appendix]({{< ref "docs/dev/datastream/dataset_migration#addsubtaskidmapfunction" >}}) shows how +to do that, and we will reuse it in the rest of this document. + + + + + + + + + + + + + + + + + + + + + +
OperationsDataSetDataStream
MapPartition/SortPartition + {{< highlight "java" >}} +DataSet dataSet = // [...] +// MapPartition +dataSet.mapPartition(new MapPartitionFunction<>(){ + // implement user-defined map partition logic + }); +// SortPartition +dataSet.sortPartition(0, Order.ASCENDING); +dataSet.sortPartition(0, Order.DESCENDING); + {{< /highlight >}} + + {{< highlight "java" >}} +DataStream dataStream = // [...] +// assign subtask ID to all records +DataStream> dataStream1 = dataStream.map(new AddSubtaskIDMapFunction()); +dataStream1.keyBy(value -> value.f0) + .window(EndOfStreamWindows.get()) + .apply(new WindowFunction<>(){ + // implement user-defined map partition or sort partition logic + }); + {{< /highlight >}} +
Cross + {{< highlight "java" >}} +DataSet dataSet1 = // [...] +DataSet dataSet2 = // [...] +// Cross +dataSet1.cross(dataSet2) + .with(new CrossFunction<>(){ + // implement user-defined cross logic + }) + {{< /highlight >}} + + {{< highlight "java" >}} +// the parallelism of dataStream1 and dataStream2 should be same +DataStream dataStream1 = // [...] +DataStream dataStream2 = // [...] +DataStream> datastream3 = dataStream1.broadcast().map(new AddSubtaskIDMapFunction()); +DataStream> datastream4 = dataStream2.map(new AddSubtaskIDMapFunction()); +// join the two streams according to the subtask ID +dataStream3.join(dataStream4) + .where(value -> value.f0) + .equalTo(value -> value.f0) + .window(EndOfStreamWindows.get()) + .apply(new JoinFunction<>(){ + // implement user-defined cross logic + }) + {{< /highlight >}} +
+ +### Category 4 + +The behaviors of the following DataSet APIs are not supported by DataStream. + +* RangePartition +* GroupCombine + + +## Appendix + +#### EndOfStreamWindows + +The following code shows the example of `EndOfStreamWindows`. + +```java +public class EndOfStreamWindows extends WindowAssigner { + private static final long serialVersionUID = 1L; + + private static final EndOfStreamWindows INSTANCE = new EndOfStreamWindows(); + + private static final TimeWindow TIME_WINDOW_INSTANCE = + new TimeWindow(Long.MIN_VALUE, Long.MAX_VALUE); + + private EndOfStreamWindows() {} + + public static EndOfStreamWindows get() { + return INSTANCE; + } + + @Override + public Collection assignWindows( + Object element, long timestamp, WindowAssignerContext context) { + return Collections.singletonList(TIME_WINDOW_INSTANCE); + } + + @Override + public Trigger getDefaultTrigger(StreamExecutionEnvironment env) { + return new EndOfStreamTrigger(); + } + + @Override + public String toString() { + return "EndOfStreamWindows()"; + } + + @Override + public TypeSerializer getWindowSerializer(ExecutionConfig executionConfig) { + return new TimeWindow.Serializer(); + } + + @Override + public boolean isEventTime() { + return true; + } + + @Internal + public static class EndOfStreamTrigger extends Trigger { + @Override + public TriggerResult onElement( + Object element, long timestamp, TimeWindow window, TriggerContext ctx) + throws Exception { + return TriggerResult.CONTINUE; + } + + @Override + public TriggerResult onEventTime(long time, TimeWindow window, TriggerContext ctx) { + return time == window.maxTimestamp() ? TriggerResult.FIRE : TriggerResult.CONTINUE; + } + + @Override + public void clear(TimeWindow window, TriggerContext ctx) throws Exception {} + + @Override + public TriggerResult onProcessingTime(long time, TimeWindow window, TriggerContext ctx) { + return TriggerResult.CONTINUE; + } + } +} +``` + +#### AddSubtaskIDMapFunction + +The following code shows the example of `AddSubtaskIDMapFunction`. +```java +public static class AddSubtaskIDMapFunction extends RichMapFunction> { + @Override + public Tuple2 map(T value) { + return Tuple2.of(String.valueOf(getRuntimeContext().getIndexOfThisSubtask()), value); + } +} +``` + +{{< top >}} diff --git a/docs/content/docs/dev/table/data_stream_api.md b/docs/content/docs/dev/table/data_stream_api.md index a5061b5981769..cb67469f376e7 100644 --- a/docs/content/docs/dev/table/data_stream_api.md +++ b/docs/content/docs/dev/table/data_stream_api.md @@ -105,9 +105,9 @@ resultStream.print(); env.execute(); // prints: -// +I[Alice] -// +I[Bob] -// +I[John] +// +I[ALICE] +// +I[BOB] +// +I[JOHN] ``` {{< /tab >}} {{< tab "Scala" >}} @@ -138,9 +138,9 @@ resultStream.print() env.execute() // prints: -// +I[Alice] -// +I[Bob] -// +I[John] +// +I[ALICE] +// +I[BOB] +// +I[JOHN] ``` {{< /tab >}} {{< tab "Python" >}} @@ -171,9 +171,9 @@ res_ds.print() env.execute() # prints: -# +I[Alice] -# +I[Bob] -# +I[John] +# +I[ALICE] +# +I[BOB] +# +I[JOHN] ``` {{< /tab >}} {{< /tabs >}} diff --git a/docs/content/docs/dev/table/olap_quickstart.md b/docs/content/docs/dev/table/olap_quickstart.md index e0b3afba2bc57..e5084e065c203 100644 --- a/docs/content/docs/dev/table/olap_quickstart.md +++ b/docs/content/docs/dev/table/olap_quickstart.md @@ -1,5 +1,5 @@ --- -title: "Quickstart for Flink OLAP" +title: "OLAP Quickstart" weight: 91 type: docs aliases: @@ -24,32 +24,41 @@ specific language governing permissions and limitations under the License. --> -# Introduction +# OLAP Quickstart -Flink OLAP has already added to [Apache Flink Roadmap](https://flink.apache.org/roadmap/). It means Flink can not only support streaming and batch computing, but also support OLAP(On-Line Analytical Processing). This page will show how to quickly set up a Flink OLAP service, and will introduce some best practices. +OLAP (OnLine Analysis Processing) is a key technology in the field of data analysis, it is generally used to perform complex queries on large data sets with latencies in seconds. Now Flink can not only support streaming and batch computing, but also supports users to deploy it as an OLAP computing service. This page will show you how to quickly set up a local Flink OLAP service, and will also introduce some best practices helping you deploy Flink OLAP service in production. -## Architecture +## Architecture Introduction +This chapter will introduce you to the overall architecture of Flink OLAP service and the advantages of using it. -The Flink OLAP service consists of three parts: Client, Flink SQL Gateway, Flink Session Cluster. +### Architecture -* **Client**: Could be any client that can interact with Flink SQL Gateway, such as SQL client, Flink JDBC driver and so on. -* **Flink SQL Gateway**: The SQL Gateway provides an easy way to submit the Flink Job, look up the metadata, and analyze table stats. -* **Flink Session Cluster**: We choose session clusters to run OLAP queries, mainly to avoid the overhead of cluster startup. +Flink OLAP service consists of three parts: Client, Flink SQL Gateway and Flink Session Cluster. -## Advantage +* **Client**: Could be any client that can interact with [Flink SQL Gateway]({{< ref "docs/dev/table/sql-gateway/overview" >}}), such as [SQL Client]({{< ref "docs/dev/table/sqlClient" >}}), [Flink JDBC Driver]({{< ref "docs/dev/table/jdbcDriver" >}}) and so on. +* **Flink SQL Gateway**: The SQL Gateway provides an easy way to parse the sql query, look up the metadata, analyze table stats, optimize the plan and submit JobGraphs to cluster. +* **Flink Session Cluster**: OLAP queries run on [session cluster]({{< ref "/docs/deployment/resource-providers/native_kubernetes#starting-a-flink-session-on-kubernetes" >}}), mainly to avoid the overhead of cluster startup. + +{{< img src="/fig/olap-architecture.svg" alt="Illustration of Flink OLAP Architecture" width="85%" >}} + +### Advantage * **Massively Parallel Processing** - * Flink OLAP runs naturally as an MPP(Massively Parallel Processing) system, which supports low-latency ad-hoc queries + * Flink OLAP runs naturally as a massively parallel processing system, which enables planners to easily adjust the job parallelism to fulfill queries' latency requirement under different data sizes. +* **Elastic Resource Management** + * Flink's resource management supports min/max scaling, which means the session cluster can allocate the resource according to workload dynamically. * **Reuse Connectors** - * Flink OLAP can reuse rich connectors in Flink ecosystem. + * Flink OLAP can reuse the rich [Connectors]({{< ref "docs/connectors/table/overview" >}}) in Flink ecosystem. * **Unified Engine** * Unified computing engine for Streaming/Batch/OLAP. -# Deploying in Local Mode +## Deploying in Local Mode + +In this chapter, you will learn how to build Flink OLAP services locally. -## Downloading Flink +### Downloading Flink -The same as [Local Installation]({{< ref "docs/try-flink/local_installation" >}}). Flink runs on all UNIX-like environments, i.e. Linux, Mac OS X, and Cygwin (for Windows). We need to have at least Java 11 installed, Java 17 is more recommended in OLAP scenario. To check the Java version installed, type in your terminal: +The same as [Local Installation]({{< ref "docs/try-flink/local_installation" >}}). Flink runs on all UNIX-like environments, i.e. Linux, Mac OS X, and Cygwin (for Windows). User need to have at __Java 11__ installed. To check the Java version installed, user can type in the terminal: ``` java -version @@ -61,7 +70,7 @@ Next, [Download](https://flink.apache.org/downloads/) the latest binary release tar -xzf flink-*.tgz ``` -## Starting a local cluster +### Starting a local cluster To start a local cluster, run the bash script that comes with Flink: @@ -69,9 +78,9 @@ To start a local cluster, run the bash script that comes with Flink: ./bin/start-cluster.sh ``` -You should be able to navigate to the web UI at localhost:8081 to view the Flink dashboard and see that the cluster is up and running. +You should be able to navigate to the web UI at http://localhost:8081 to view the Flink dashboard and see that the cluster is up and running. -## Start a SQL Client CLI +### Start a SQL Client CLI You can start the CLI with an embedded gateway by calling: @@ -79,7 +88,7 @@ You can start the CLI with an embedded gateway by calling: ./bin/sql-client.sh ``` -## Running Queries +### Running Queries You could simply execute queries in CLI and retrieve the results. @@ -102,98 +111,98 @@ GROUP BY buyer ORDER BY total_cost LIMIT 3; ``` -And then you could find job detail information in web UI at localhost:8081. +And then you could find job detail information in web UI at http://localhost:8081. -# Deploying in Production +## Deploying in Production This section guides you through setting up a production ready Flink OLAP service. -## Cluster Deployment +### Client -In production, we recommend to use Flink Session Cluster, Flink SQL Gateway and Flink JDBC Driver to build an OLAP service. +#### Flink JDBC Driver -### Session Cluster +You should use Flink JDBC Driver when submitting queries to SQL Gateway since it provides low-level connection management. When used in production, you should pay attention to reuse the JDBC connection to avoid frequently creating/closing sessions in the Gateway and then reduce the E2E query latency. For detailed information, please refer to the [Flink JDBC Driver]({{ }}). -For Flink Session Cluster, we recommend to deploy Flink on native Kubernetes using session mode. Kubernetes is a popular container-orchestration system for automating computer application deployment, scaling, and management. By deploying on native Kubernetes, Flink Session Cluster is able to dynamically allocate and de-allocate TaskManagers. For more information, please refer to [Native Kubernetes]({{< ref "docs/deployment/resource-providers/native_kubernetes">}}). +### Cluster Deployment -### SQL Gateway +In production, you should use Flink Session Cluster, Flink SQL Gateway to build an OLAP service. -For Flink SQL Gateway, we recommend deploying it as a stateless microservice and register this on the service discovery component. For more information, please refer to the [SQL Gateway Overview]({{< ref "docs/dev/table/sql-gateway/overview">}}). +#### Session Cluster -### Flink JDBC Driver +For Flink Session Cluster, you can deploy it on Native Kubernetes using session mode. Kubernetes is a popular container-orchestration system for automating computer application deployment, scaling, and management. By deploying on Native Kubernetes, Flink Session Cluster is able to dynamically allocate and de-allocate TaskManagers. For more information, please refer to [Native Kubernetes]({{ < ref "docs/deployment/resource-providers/native_kubernetes"> }}). Furthermore, you can config the option [slotmanager.number-of-slots.min]({{< ref "docs/deployment/config#slotmanager-number-of-slots-min" >}}) in session cluster. This will help you significantly reduce the cold start time of your query. For detailed information, please refer to [FLIP-362](https://cwiki.apache.org/confluence/display/FLINK/FLIP-362%3A+Support+minimum+resource+limitation). -When submitting queries to SQL Gateway, we recommend using Flink JDBC Driver since it provides low-level connection management. When used in production, we need to pay attention to reuse the JDBC connection to avoid frequently creating/closing sessions in the Gateway. For more information, please refer to the [Flink JDBC Driver]({{{}}}). +#### SQL Gateway -## Datasource Configurations +For Flink SQL Gateway, you should deploy it as a stateless microservice and register the instance on service discovery component. Through this way, client can balance the query between instances easily. For more information, please refer to [SQL Gateway Overview]({{< ref "docs/dev/table/sql-gateway/overview">}}). -### Catalogs +### Datasource Configurations -In OLAP scenario, we recommend using FileCatalogStore in the catalog configuration introduced in [FLIP-295](https://cwiki.apache.org/confluence/display/FLINK/FLIP-295%3A+Support+lazy+initialization+of+catalogs+and+persistence+of+catalog+configurations). As a long running service, Flink OLAP cluster's catalog information will not change frequently and can be re-used cross sessions. For more information, please refer to the [Catalog Store]({{< ref "docs/dev/table/catalogs#catalog-store">}}). +#### Catalogs -### Connectors +In OLAP scenario, you should configure `FileCatalogStore` provided by [Catalogs]({{< ref "docs/dev/table/catalogs">}}) as the catalog used by cluster. As a long-running service, Flink OLAP cluster's catalog information will not change frequently and should be re-used cross sessions to reduce the cold-start cost. For more information, please refer to the [Catalog Store]({{< ref "docs/dev/table/catalogs#catalog-store">}}). -Both Session Cluster and SQL Gateway rely on connectors to analyze table stats and read data from the configured data source. To add connectors, please refer to the [Connectors and Formats]({{< ref "docs/connectors/table/overview">}}). +#### Connectors -## Cluster Configurations +Both Session Cluster and SQL Gateway rely on connectors to analyze table stats and read data from the configured data source. To add connectors, please refer to the [Connectors]({{< ref "docs/connectors/table/overview">}}). -In OLAP scenario, we picked out a few configurations that can help improve user usability and query performance. +### Recommended Cluster Configurations -### SQL&Table Options +In OLAP scenario, appropriate configurations that can greatly help users improve the overall usability and query performance. Here are some recommended production configurations: -| Parameters | Default | Recommended | -|:-------------------------------------|:--------|:------------| -| table.optimizer.join-reorder-enabled | false | true | -| pipeline.object-reuse | false | true | +#### SQL&Table Options -### Runtime Options +| Parameters | Default | Recommended | +|:---------------------------------------------------------------------------------------------------------------|:--------|:------------| +| [table.optimizer.join-reorder-enabled]({{}}) | false | true | +| [pipeline.object-reuse]({{< ref "docs/deployment/config#pipeline-object-reuse" >}}) | false | true | -| Parameters | Default | Recommended | -|:-----------------------------|:-----------------------|:------------------------------------------------------------------------------------------------------------------------------------------| -| execution.runtime-mode | STREAMING | BATCH | -| execution.batch-shuffle-mode | ALL_EXCHANGES_BLOCKING | ALL_EXCHANGES_PIPELINED | -| env.java.opts.all | {default value} | {default value} -XX:PerMethodRecompilationCutoff=10000 -XX:PerBytecodeRecompilationCutoff=10000-XX:ReservedCodeCacheSize=512M -XX:+UseZGC | -| JDK Version | 11 | 17 | +#### Runtime Options -We strongly recommend using JDK17 with ZGC in OLAP scenario in order to provide zero gc stw and solve the issue described in [FLINK-32746](https://issues.apache.org/jira/browse/FLINK-32746). +| Parameters | Default | Recommended | +|:--------------------------------------------------------------------------------------------------|:-----------------------|:------------------------------------------------------------------------------------------------------------------------------------------| +| [execution.runtime-mode]({{< ref "docs/deployment/config#execution-runtime-mode" >}}) | STREAMING | BATCH | +| [execution.batch-shuffle-mode]({{< ref "docs/deployment/config#execution-batch-shuffle-mode" >}}) | ALL_EXCHANGES_BLOCKING | ALL_EXCHANGES_PIPELINED | +| [env.java.opts.all]({{< ref "docs/deployment/config#env-java-opts-all" >}}) | {default value} | {default value} -XX:PerMethodRecompilationCutoff=10000 -XX:PerBytecodeRecompilationCutoff=10000-XX:ReservedCodeCacheSize=512M -XX:+UseZGC | +| JDK Version | 11 | 17 | -### Scheduling Options +Using JDK17 within ZGC can greatly help optimize the metaspace garbage collection issue, detailed information can be found in [FLINK-32746](https://issues.apache.org/jira/browse/FLINK-32746). Meanwhile, ZGC can provide close to zero application pause time when collecting garbage objects in memory. Additionally, OLAP queries need to be executed in `BATCH` mode because both `Pipelined` and `Blocking` edges may appear in the execution plan of an OLAP query. Batch scheduler allows queries to be scheduled in stages, which could avoid scheduling deadlocks in this scenario. + +#### Scheduling Options | Parameters | Default | Recommended | -|:---------------------------------------------------------|:------------------|:------------------| -| jobmanager.scheduler | Default | Default | -| jobmanager.execution.failover-strategy | region | full | -| restart-strategy.type | (none) | disable | -| jobstore.type | File | Memory | -| jobstore.max-capacity | Integer.MAX_VALUE | 500 | - -We would like to highlight the usage of `PipelinedRegionSchedulingStrategy`. Since many OLAP queries will have blocking edges in their jobGraph. - -### Network Options - -| Parameters | Default | Recommended | -|:------------------------------------|:-----------|:---------------| -| rest.server.numThreads | 4 | 32 | -| web.refresh-interval | 3000 | 300000 | -| pekko.framesize | 10485760b | 104857600b | - -### ResourceManager Options - -| Parameters | Default | Recommended | -|:-------------------------------------|:----------|:---------------| -| kubernetes.jobmanager.replicas | 1 | 2 | -| kubernetes.jobmanager.cpu.amount | 1.0 | 16.0 | -| jobmanager.memory.process.size | (none) | 65536m | -| jobmanager.memory.jvm-overhead.max | 1g | 6144m | -| kubernetes.taskmanager.cpu.amount | (none) | 16 | -| taskmanager.numberOfTaskSlots | 1 | 32 | -| taskmanager.memory.process.size | (none) | 65536m | -| taskmanager.memory.managed.size | (none) | 65536m | - -We prefer to use large taskManager pods in OLAP since this can put more computation in local and reduce network/deserialization/serialization overhead. Meanwhile, since JobManager is a single point of calculation in OLAP scenario, we also prefer large pod. - -# Future Work -There is a big margin for improvement in Flink OLAP, both in usability and query performance, and we trace all of them in underlying tickets. +|:------------------------------------------------------------------------------------------------------------------------|:------------------|:--------| +| [jobmanager.scheduler]({{< ref "docs/deployment/config#jobmanager-scheduler" >}}) | Default | Default | +| [jobmanager.execution.failover-strategy]({{< ref "docs/deployment/config#jobmanager-execution-failover-strategy-1" >}}) | region | full | +| [restart-strategy.type]({{< ref "docs/deployment/config#restart-strategy-type" >}}) | (none) | disable | +| [jobstore.type]({{< ref "docs/deployment/config#jobstore-type" >}}) | File | Memory | +| [jobstore.max-capacity]({{< ref "docs/deployment/config#jobstore-max-capacity" >}}) | Integer.MAX_VALUE | 500 | + + +#### Network Options + +| Parameters | Default | Recommended | +|:--------------------------------------------------------------------------------------|:-----------|:---------------| +| [rest.server.numThreads]({{< ref "docs/deployment/config#rest-server-numthreads" >}}) | 4 | 32 | +| [web.refresh-interval]({{< ref "docs/deployment/config#web-refresh-interval" >}}) | 3000 | 300000 | +| [pekko.framesize]({{< ref "docs/deployment/config#pekko-framesize" >}}) | 10485760b | 104857600b | + +#### ResourceManager Options + +| Parameters | Default | Recommended | +|:-------------------------------------------------------------------|:--------|:----------------------------------------| +| [kubernetes.jobmanager.replicas]({{< ref "docs/deployment/config#kubernetes-jobmanager-replicas" >}}) | 1 | 2 | +| [kubernetes.jobmanager.cpu.amount]({{< ref "docs/deployment/config#kubernetes-jobmanager-cpu-amount" >}}) | 1.0 | 16.0 | +| [jobmanager.memory.process.size]({{< ref "docs/deployment/config#jobmanager-memory-process-size" >}}) | (none) | 32g | +| [jobmanager.memory.jvm-overhead.max]({{< ref "docs/deployment/config#jobmanager-memory-jvm-overhead-max" >}}) | 1g | 3g | +| [kubernetes.taskmanager.cpu.amount]({{< ref "docs/deployment/config#kubernetes-taskmanager-cpu-amount" >}}) | (none) | 16 | +| [taskmanager.numberOfTaskSlots]({{< ref "docs/deployment/config#taskmanager-numberoftaskslots" >}}) | 1 | 32 | +| [taskmanager.memory.process.size]({{< ref "docs/deployment/config#taskmanager-memory-process-size" >}}) | (none) | 65536m | +| [taskmanager.memory.managed.size]({{< ref "docs/deployment/config#taskmanager-memory-managed-size" >}}) | (none) | 16384m | +| [slotmanager.number-of-slots.min]({{< ref "docs/deployment/config#slotmanager-number-of-slots-min" >}}) | 0 | {taskManagerNumber * numberOfTaskSlots} | + +You can configure `slotmanager.number-of-slots.min` to a proper value as the reserved resource pool serving OLAP queries. TaskManager should configure with a large resource specification in OLAP scenario since this can put more computations in local and reduce network/deserialization/serialization overhead. Meanwhile, as a single point of calculation in OLAP, JobManager also prefer large resource specification. + +## Future Work +Flink OLAP is now part of [Apache Flink Roadmap](https://flink.apache.org/what-is-flink/roadmap/), which means the community will keep putting efforts to improve Flink OLAP, both in usability and query performance. Relevant work are traced in underlying tickets: - https://issues.apache.org/jira/browse/FLINK-25318 -- https://issues.apache.org/jira/browse/FLINK-32898 - -Furthermore, we are adding relevant OLAP benchmarks to the Flink repository such as [flink-benchmarks](https://github.com/apache/flink-benchmarks). \ No newline at end of file +- https://issues.apache.org/jira/browse/FLINK-32898 \ No newline at end of file diff --git a/docs/content/docs/dev/table/overview.md b/docs/content/docs/dev/table/overview.md index 6715d064c2faf..69bc97a1ccd19 100644 --- a/docs/content/docs/dev/table/overview.md +++ b/docs/content/docs/dev/table/overview.md @@ -60,7 +60,7 @@ Where to go next? * [Built-in Functions]({{< ref "docs/dev/table/functions/systemFunctions" >}}): Supported functions in Table API and SQL. * [SQL Client]({{< ref "docs/dev/table/sqlClient" >}}): Play around with Flink SQL and submit a table program to a cluster without programming knowledge. * [SQL Gateway]({{< ref "docs/dev/table/sql-gateway/overview" >}}): A service that enables the multiple clients to execute SQL from the remote in concurrency. -* [SQL JDBC Driver]({{< ref "docs/dev/table/jdbcDriver" >}}): A JDBC Driver that submits SQL statements to sql-gateway. * [OLAP Quickstart]({{< ref "docs/dev/table/olap_quickstart" >}}): A quickstart to show how to set up a Flink OLAP service. +* [SQL JDBC Driver]({{< ref "docs/dev/table/jdbcDriver" >}}): A JDBC Driver that submits SQL statements to sql-gateway. {{< top >}} diff --git a/docs/content/docs/dev/table/sql/queries/window-tvf.md b/docs/content/docs/dev/table/sql/queries/window-tvf.md index 95cf450844164..712c27d22278b 100644 --- a/docs/content/docs/dev/table/sql/queries/window-tvf.md +++ b/docs/content/docs/dev/table/sql/queries/window-tvf.md @@ -312,13 +312,13 @@ We show an example to describe how to use offset in Tumble window in the followi Flink SQL> SELECT * FROM TABLE( TUMBLE(TABLE Bid, DESCRIPTOR(bidtime), INTERVAL '10' MINUTES, INTERVAL '1' MINUTES)); -- or with the named params --- note: the DATA param must be the first +-- note: the DATA param must be the first and `OFFSET` should be wrapped with double quotes Flink SQL> SELECT * FROM TABLE( TUMBLE( DATA => TABLE Bid, TIMECOL => DESCRIPTOR(bidtime), SIZE => INTERVAL '10' MINUTES, - OFFSET => INTERVAL '1' MINUTES)); + `OFFSET` => INTERVAL '1' MINUTES)); +------------------+-------+------+------------------+------------------+-------------------------+ | bidtime | price | item | window_start | window_end | window_time | +------------------+-------+------+------------------+------------------+-------------------------+ diff --git a/docs/content/docs/dev/table/sql/show.md b/docs/content/docs/dev/table/sql/show.md index 310027245026d..bb5d4af3e2d9e 100644 --- a/docs/content/docs/dev/table/sql/show.md +++ b/docs/content/docs/dev/table/sql/show.md @@ -608,7 +608,7 @@ show tables; ## SHOW CREATE TABLE ```sql -SHOW CREATE TABLE +SHOW CREATE TABLE [[catalog_name.]db_name.]table_name ``` Show create table statement for specified table. diff --git a/docs/content/docs/ops/metrics.md b/docs/content/docs/ops/metrics.md index 2bdb44cac9853..ac106f6e6c927 100644 --- a/docs/content/docs/ops/metrics.md +++ b/docs/content/docs/ops/metrics.md @@ -315,7 +315,7 @@ public class MyMapper extends RichMapFunction { .getMetricGroup() .histogram("myHistogram", new DropwizardHistogramWrapper(dropwizardHistogram)); } - + @Override public Long map(Long value) throws Exception { this.histogram.update(value); @@ -333,12 +333,12 @@ class MyMapper extends RichMapFunction[Long, Long] { override def open(config: Configuration): Unit = { val dropwizardHistogram = new com.codahale.metrics.Histogram(new SlidingWindowReservoir(500)) - + histogram = getRuntimeContext() .getMetricGroup() .histogram("myHistogram", new DropwizardHistogramWrapper(dropwizardHistogram)) } - + override def map(value: Long): Long = { histogram.update(value) value @@ -464,7 +464,7 @@ class MyMapper extends RichMapFunction[Long,Long] { override def open(config: Configuration): Unit = { val dropwizardMeter: com.codahale.metrics.Meter = new com.codahale.metrics.Meter() - + meter = getRuntimeContext() .getMetricGroup() .meter("myMeter", new DropwizardMeterWrapper(dropwizardMeter)) @@ -687,7 +687,7 @@ Thus, in order to infer the metric identifier: ### Memory -The memory-related metrics require Oracle's memory management (also included in OpenJDK's Hotspot implementation) to be in place. +The memory-related metrics require Oracle's memory management (also included in OpenJDK's Hotspot implementation) to be in place. Some metrics might not be exposed when using other JVM implementations (e.g. IBM's J9). @@ -715,8 +715,8 @@ Some metrics might not be exposed when using other JVM implementations (e.g. IBM @@ -829,15 +829,20 @@ Some metrics might not be exposed when using other JVM implementations (e.g. IBM - - - - + + + + + + + + + - - + + @@ -1904,7 +1909,7 @@ Please refer to [Kafka monitoring]({{< ref "docs/connectors/datastream/kafka" >} - @@ -2147,7 +2152,7 @@ Metrics below can be used to measure the effectiveness of speculative execution. - @@ -2162,7 +2167,7 @@ To enable the latency tracking you must set the `latencyTrackingInterval` to a p At the `latencyTrackingInterval`, the sources will periodically emit a special record, called a `LatencyMarker`. The marker contains a timestamp from the time when the record has been emitted at the sources. -Latency markers can not overtake regular user records, thus if records are queuing up in front of an operator, +Latency markers can not overtake regular user records, thus if records are queuing up in front of an operator, it will add to the latency tracked by the marker. Note that the latency markers are not accounting for the time user records spend in operators as they are @@ -2170,17 +2175,17 @@ bypassing them. In particular the markers are not accounting for the time record Only if operators are not able to accept new records, thus they are queuing up, the latency measured using the markers will reflect that. -The `LatencyMarker`s are used to derive a distribution of the latency between the sources of the topology and each -downstream operator. These distributions are reported as histogram metrics. The granularity of these distributions can -be controlled in the [Flink configuration]({{< ref "docs/deployment/config" >}}#metrics-latency-interval). For the highest -granularity `subtask` Flink will derive the latency distribution between every source subtask and every downstream -subtask, which results in quadratic (in the terms of the parallelism) number of histograms. +The `LatencyMarker`s are used to derive a distribution of the latency between the sources of the topology and each +downstream operator. These distributions are reported as histogram metrics. The granularity of these distributions can +be controlled in the [Flink configuration]({{< ref "docs/deployment/config" >}}#metrics-latency-interval). For the highest +granularity `subtask` Flink will derive the latency distribution between every source subtask and every downstream +subtask, which results in quadratic (in the terms of the parallelism) number of histograms. Currently, Flink assumes that the clocks of all machines in the cluster are in sync. We recommend setting up an automated clock synchronisation service (like NTP) to avoid false latency results. Warning Enabling latency metrics can significantly impact the performance -of the cluster (in particular for `subtask` granularity). It is highly recommended to only use them for debugging +of the cluster (in particular for `subtask` granularity). It is highly recommended to only use them for debugging purposes. ## State access latency tracking @@ -2194,7 +2199,7 @@ This configuration has a default value of 100. A smaller value will get more acc As the type of this latency metrics is histogram, `state.backend.latency-track.history-size` will control the maximum number of recorded values in history, which has the default value of 128. A larger value of this configuration will require more memory, but will provide a more accurate result. -Warning Enabling state-access-latency metrics may impact the performance. +Warning Enabling state-access-latency metrics may impact the performance. It is recommended to only use them for debugging purposes. ## REST API integration diff --git a/docs/content/release-notes/flink-1.18.md b/docs/content/release-notes/flink-1.18.md new file mode 100644 index 0000000000000..d7a985a678070 --- /dev/null +++ b/docs/content/release-notes/flink-1.18.md @@ -0,0 +1,151 @@ +--- +title: "Release Notes - Flink 1.18" +--- + + +# Release notes - Flink 1.18 + +These release notes discuss important aspects, such as configuration, behavior or dependencies, +that changed between Flink 1.17 and Flink 1.18. Please read these notes carefully if you are +planning to upgrade your Flink version to 1.18. + + +### Build System + +#### Support Java 17 (LTS) + +##### [FLINK-15736](https://issues.apache.org/jira/browse/FLINK-15736) +Apache Flink was made ready to compile and run with Java 17 (LTS). This feature is still in beta mode. +Issues should be reported in Flink's bug tracker. + + +### Table API & SQL + +#### Unified the max display column width for SQL Client and Table APi in both Streaming and Batch execution Mode + +##### [FLINK-30025](https://issues.apache.org/jira/browse/FLINK-30025) +Introduction of the new ConfigOption `DISPLAY_MAX_COLUMN_WIDTH` (`table.display.max-column-width`) +in the TableConfigOptions class is now in place. +This option is utilized when displaying table results through the Table API and SQL Client. +As SQL Client relies on the Table API underneath, and both SQL Client and the Table API serve distinct +and isolated scenarios, it is a rational choice to maintain a centralized configuration. +This approach also simplifies matters for users, as they only need to manage one ConfigOption for display control. + +During the migration phase, while `sql-client.display.max-column-width` is deprecated, +any changes made to `sql-client.display.max-column-width` will be automatically transferred to `table.display.max-column-width`. +Caution is advised when using the CLI, as it is not recommended to switch back and forth between these two options. + +#### Introduce Flink JDBC Driver For SQL Gateway +##### [FLINK-31496](https://issues.apache.org/jira/browse/FLINK-31496) +Apache Flink now supports JDBC driver to access SQL Gateway, you can use the driver in any cases that +support standard JDBC extension to connect to Flink cluster. + +#### Extend watermark-related features for SQL +##### [FLINK-31535](https://issues.apache.org/jira/browse/FLINK-31535) +Flink now enables user config watermark emit strategy/watermark alignment/watermark idle-timeout +in Flink SQL job with dynamic table options and 'OPTIONS' hint. + +#### Support configuring CatalogStore in Table API +##### [FLINK-32431](https://issues.apache.org/jira/browse/FLINK-32431) +Support lazy initialization of catalog and persistence of catalog configuration. + +#### Deprecate ManagedTable related APIs +##### [FLINK-32656](https://issues.apache.org/jira/browse/FLINK-32656) +ManagedTable related APIs are deprecated and will be removed in a future major release. + +### Connectors & Libraries + +#### SplitReader implements AutoCloseable instead of providing its own close method +##### [FLINK-31015](https://issues.apache.org/jira/browse/FLINK-31015) +SplitReader interface now extends `AutoCloseable` instead of providing its own method signature. + +#### JSON format supports projection push down +##### [FLINK-32610](https://issues.apache.org/jira/browse/FLINK-32610) +The JSON format introduced JsonParser as a new default way to deserialize JSON data. +JsonParser is a Jackson JSON streaming API to read JSON data which is much faster +and consumes less memory compared to the previous JsonNode approach. +This should be a compatible change, if you encounter any issues after upgrading, +you can fallback to the previous JsonNode approach by setting `json.decode.json-parser.enabled` to `false`. + + + +### Runtime & Coordination + +#### Unifying the Implementation of SlotManager +##### [FLINK-31439](https://issues.apache.org/jira/browse/FLINK-31439) +Fine-grained resource management are now enabled by default. You can use it by specifying the resource requirement. +More details can be found at https://nightlies.apache.org/flink/flink-docs-master/docs/deployment/finegrained_resource/#usage. + +#### Watermark aggregation performance is poor when watermark alignment is enabled and parallelism is high +##### [FLINK-32420](https://issues.apache.org/jira/browse/FLINK-32420) +This performance improvement would be good to mention in the release blog post. + +As proven by the micro benchmarks (screenshots attached in the ticket), with 5000 subtasks, +the time to calculate the watermark alignment on the JobManager by a factor of 76x (7664%). +Previously such large jobs were actually at large risk of overloading JobManager, now that's far less likely to happen. + +#### Replace Akka by Pekko +##### [FLINK-32468](https://issues.apache.org/jira/browse/32468) +Flink's RPC framework is now based on Apache Pekko instead of Akka. Any Akka dependencies were removed. + +#### Introduce Runtime Filter for Flink Batch Jobs +##### [FLINK-32486](https://issues.apache.org/jira/browse/FLINK-32486) +We introduced a runtime filter for batch jobs in 1.18, which is designed to improve join performance. +It will dynamically generate filter conditions for certain Join queries at runtime to reduce the amount of scanned or shuffled data, +avoid unnecessary I/O and network transmission, and speed up the query. +Its working principle is building a filter(e.g. bloom filter) based on the data on the small table side(build side) first, +then passing this filter to the large table side(probe side) to filter the irrelevant data on it, +this can reduce the data reaching the join and improve performance. + +#### Make watermark alignment ready for production use +##### [FLINK-32548](https://issues.apache.org/jira/browse/FLINK-32548) +The watermark alignment is ready for production since Flink 1.18, +which completed a series of bug fixes and improvements related to watermark alignment. +Please refer to [FLINK-32420](https://issues.apache.org/jira/browse/FLINK-32420) for more information. + +#### Redundant TaskManagers should always be fulfilled in FineGrainedSlotManager +##### [FLINK-32880](https://issues.apache.org/jira/browse/FLINK-32880) +Fix the issue that redundant TaskManagers will not be fulfilled in FineGrainedSlotManager periodically. + +#### RestClient can deadlock if request made after Netty event executor terminated +##### [FLINK-32583](https://issues.apache.org/jira/browse/FLINK-32583) +Fix a bug in the RestClient where making a request after the client was closed returns a future that never completes. + +#### Deprecate Queryable State +##### [FLINK-32559](https://issues.apache.org/jira/browse/FLINK-32559) +The Queryable State feature is formally deprecated. It will be removed in future major version bumps. + + +### SDK + +#### Properly deprecate DataSet API +##### [FLINK-32558](https://issues.apache.org/jira/browse/FLINK-32558) +DataSet API is formally deprecated, and will be removed in the next major release. + + +### Dependency upgrades + +#### Upgrade Calcite version to 1.32.0 +##### [FLINK-29319](https://issues.apache.org/jira/browse/FLINK-29319) and related tickets [FLINK-27998](https://issues.apache.org/jira/browse/FLINK-27998), [FLINK-28744](https://issues.apache.org/jira/browse/FLINK-28744) + +Due to CALCITE-4861 (Optimization of chained CAST calls can lead to unexpected behavior), +also Flink's casting behavior has slightly changed. Some corner cases might behave differently now: For example, +casting from `FLOAT`/`DOUBLE` 9234567891.12 to `INT`/`BIGINT` has now Java behavior for overflows. + diff --git a/docs/data/sql_functions_zh.yml b/docs/data/sql_functions_zh.yml index 69eeccc64b3b7..dea5b499bc1e1 100644 --- a/docs/data/sql_functions_zh.yml +++ b/docs/data/sql_functions_zh.yml @@ -339,7 +339,7 @@ string: table: STRING.rtrim() description: | 返回从 STRING 中删除右边空格的字符串。 - 例如 `'This is a test String. '.ltrim()` 返回 `'This is a test String.'`。 + 例如 `'This is a test String. '.rtrim()` 返回 `'This is a test String.'`。 - sql: REPEAT(string, int) table: STRING.repeat(INT) description: | diff --git a/docs/layouts/shortcodes/generated/all_jobmanager_section.html b/docs/layouts/shortcodes/generated/all_jobmanager_section.html index 5bcb175141750..79b0e7f804b66 100644 --- a/docs/layouts/shortcodes/generated/all_jobmanager_section.html +++ b/docs/layouts/shortcodes/generated/all_jobmanager_section.html @@ -26,6 +26,18 @@ + + + + + + + + + + + + diff --git a/docs/layouts/shortcodes/generated/all_taskmanager_network_section.html b/docs/layouts/shortcodes/generated/all_taskmanager_network_section.html index 81fd2233f4e23..d1d221664108b 100644 --- a/docs/layouts/shortcodes/generated/all_taskmanager_network_section.html +++ b/docs/layouts/shortcodes/generated/all_taskmanager_network_section.html @@ -182,17 +182,23 @@ + + + + + + - + - + diff --git a/docs/layouts/shortcodes/generated/all_taskmanager_section.html b/docs/layouts/shortcodes/generated/all_taskmanager_section.html index bde8c250bdf89..256672be9b56f 100644 --- a/docs/layouts/shortcodes/generated/all_taskmanager_section.html +++ b/docs/layouts/shortcodes/generated/all_taskmanager_section.html @@ -74,6 +74,12 @@ + + + + + + diff --git a/docs/layouts/shortcodes/generated/checkpointing_configuration.html b/docs/layouts/shortcodes/generated/checkpointing_configuration.html index b817d7802f5d1..92e2ae126a44e 100644 --- a/docs/layouts/shortcodes/generated/checkpointing_configuration.html +++ b/docs/layouts/shortcodes/generated/checkpointing_configuration.html @@ -26,6 +26,12 @@ + + + + + + diff --git a/docs/layouts/shortcodes/generated/cluster_configuration.html b/docs/layouts/shortcodes/generated/cluster_configuration.html index d734ea405f13e..49b6af5f659a0 100644 --- a/docs/layouts/shortcodes/generated/cluster_configuration.html +++ b/docs/layouts/shortcodes/generated/cluster_configuration.html @@ -8,12 +8,6 @@ - - - - - - diff --git a/docs/layouts/shortcodes/generated/common_state_backends_section.html b/docs/layouts/shortcodes/generated/common_state_backends_section.html index 84592cec94a04..383bb165c7a23 100644 --- a/docs/layouts/shortcodes/generated/common_state_backends_section.html +++ b/docs/layouts/shortcodes/generated/common_state_backends_section.html @@ -44,6 +44,12 @@ + + + + + + diff --git a/docs/layouts/shortcodes/generated/expert_scheduling_section.html b/docs/layouts/shortcodes/generated/expert_scheduling_section.html index b5a3163a32198..96fc305609914 100644 --- a/docs/layouts/shortcodes/generated/expert_scheduling_section.html +++ b/docs/layouts/shortcodes/generated/expert_scheduling_section.html @@ -8,12 +8,6 @@ - - - - - - @@ -86,6 +80,18 @@ + + + + + + + + + + + + @@ -176,5 +182,11 @@ + + + + + +
Heap.Max The maximum amount of heap memory that can be used for memory management (in bytes).
- This value might not be necessarily equal to the maximum value specified through -Xmx or - the equivalent Flink configuration parameter. Some GC algorithms allocate heap memory that won't + This value might not be necessarily equal to the maximum value specified through -Xmx or + the equivalent Flink configuration parameter. Some GC algorithms allocate heap memory that won't be available to the user code and, therefore, not being exposed through the heap metrics.
Gauge
Job-/TaskManagerStatus.JVM.GarbageCollector<GarbageCollector>.CountThe total number of collections that have occurred.Job-/TaskManagerStatus.JVM.GarbageCollector<Collector/All>.CountThe total number of collections that have occurred for the given (or all) collector.Gauge
<Collector/All>.TimeThe total time spent performing garbage collection for the given (or all) collector. Gauge
<GarbageCollector>.TimeThe total time spent performing garbage collection.<Collector/All>.TimeMsPerSecondThe time (in milliseconds) spent garbage collecting per second for the given (or all) collector. Gauge
Operator loopFrequencyHz stream, shardIdThe number of calls to getRecords in one second. + The number of calls to getRecords in one second. Gauge
numEffectiveSpeculativeExecutionsNumber of effective speculative execution attempts, i.e. speculative execution attempts which + Number of effective speculative execution attempts, i.e. speculative execution attempts which finish earlier than their corresponding original attempts. Counter
Duration The maximum time the JobManager will wait to acquire all required resources after a job submission or restart. Once elapsed it will try to run the job with a lower parallelism, or fail if the minimum amount of resources could not be acquired.
Increasing this value will make the cluster more resilient against temporary resources shortages (e.g., there is more time for a failed TaskManager to be restarted).
Setting a negative duration will disable the resource timeout: The JobManager will wait indefinitely for resources to appear.
If scheduler-mode is configured to REACTIVE, this configuration value will default to a negative value to disable the resource timeout.
jobmanager.adaptive-scheduler.scaling-interval.max
(none)DurationDetermines the maximum interval time after which a scaling operation is forced even if the jobmanager.adaptive-scheduler.min-parallelism-increase aren't met. The scaling operation will be ignored when the resource hasn't changed. This option is disabled by default.
jobmanager.adaptive-scheduler.scaling-interval.min
30 sDurationDetermines the minimum time between scaling operations.
jobmanager.archive.fs.dir
(none)String The Netty transport type, either "nio" or "epoll". The "auto" means selecting the property mode automatically based on the platform. Note that the "epoll" mode can get better performance, less GC and have more advanced features which are only available on modern Linux.
taskmanager.network.partition-request-timeout
10 sDurationTimeout for an individual partition request of remote input channels. The partition request will finally fail if the total wait time exceeds twice the value of taskmanager.network.request-backoff.max.
taskmanager.network.request-backoff.initial
100 IntegerMinimum backoff in milliseconds for partition requests of input channels.Minimum backoff in milliseconds for partition requests of local input channels.
taskmanager.network.request-backoff.max
10000 IntegerMaximum backoff in milliseconds for partition requests of input channels.Maximum backoff in milliseconds for partition requests of local input channels.
taskmanager.network.retries
Boolean Whether to kill the TaskManager when the task thread throws an OutOfMemoryError.
taskmanager.load-balance.mode
NONE

Enum

Mode for the load-balance allocation strategy across all available TaskManagers.
  • The SLOTS mode tries to spread out the slots evenly across all available TaskManagers.
  • The NONE mode is the default mode without any specified strategy.


Possible values:
  • "NONE"
  • "SLOTS"
taskmanager.memory.min-segment-size
256 bytesString The checkpoint storage implementation to be used to checkpoint state.
The implementation can be specified either via their shortcut name, or via the class name of a CheckpointStorageFactory. If a factory is specified it is instantiated via its zero argument constructor and its CheckpointStorageFactory#createFromConfig(ReadableConfig, ClassLoader) method is called.
Recognized shortcut names are 'jobmanager' and 'filesystem'.
state.checkpoint.cleaner.parallel-mode
trueBooleanOption whether to discard a checkpoint's states in parallel using the ExecutorService passed into the cleaner
state.checkpoints.dir
(none)
cluster.evenly-spread-out-slots
falseBooleanEnable the slot spread out allocation strategy. This strategy tries to spread out the slots evenly across all available TaskExecutors.
cluster.intercept-user-system-exit
DISABLEDBoolean This option configures local recovery for this state backend. By default, local recovery is deactivated. Local recovery currently only covers keyed state backends (including both the EmbeddedRocksDBStateBackend and the HashMapStateBackend).
state.checkpoint.cleaner.parallel-mode
trueBooleanOption whether to discard a checkpoint's states in parallel using the ExecutorService passed into the cleaner
state.checkpoints.num-retained
1
cluster.evenly-spread-out-slots
falseBooleanEnable the slot spread out allocation strategy. This strategy tries to spread out the slots evenly across all available TaskExecutors.
execution.batch.adaptive.auto-parallelism.avg-data-volume-per-task
16 mbDuration The maximum time the JobManager will wait to acquire all required resources after a job submission or restart. Once elapsed it will try to run the job with a lower parallelism, or fail if the minimum amount of resources could not be acquired.
Increasing this value will make the cluster more resilient against temporary resources shortages (e.g., there is more time for a failed TaskManager to be restarted).
Setting a negative duration will disable the resource timeout: The JobManager will wait indefinitely for resources to appear.
If scheduler-mode is configured to REACTIVE, this configuration value will default to a negative value to disable the resource timeout.
jobmanager.adaptive-scheduler.scaling-interval.max
(none)DurationDetermines the maximum interval time after which a scaling operation is forced even if the jobmanager.adaptive-scheduler.min-parallelism-increase aren't met. The scaling operation will be ignored when the resource hasn't changed. This option is disabled by default.
jobmanager.adaptive-scheduler.scaling-interval.min
30 sDurationDetermines the minimum time between scaling operations.
jobmanager.partition.hybrid.partition-data-consume-constraint
(none)Double The finished execution ratio threshold to calculate the slow tasks detection baseline. Given that the parallelism is N and the ratio is R, define T as the median of the first N*R finished tasks' execution time. The baseline will be T*M, where M is the multiplier of the baseline. Note that the execution time will be weighted with the task's input bytes to ensure the accuracy of the detection if data skew occurs.
taskmanager.load-balance.mode
NONE

Enum

Mode for the load-balance allocation strategy across all available TaskManagers.
  • The SLOTS mode tries to spread out the slots evenly across all available TaskManagers.
  • The NONE mode is the default mode without any specified strategy.


Possible values:
  • "NONE"
  • "SLOTS"
diff --git a/docs/layouts/shortcodes/generated/job_manager_configuration.html b/docs/layouts/shortcodes/generated/job_manager_configuration.html index 711d09020b7ed..d07705d7018c2 100644 --- a/docs/layouts/shortcodes/generated/job_manager_configuration.html +++ b/docs/layouts/shortcodes/generated/job_manager_configuration.html @@ -26,6 +26,18 @@ Duration The maximum time the JobManager will wait to acquire all required resources after a job submission or restart. Once elapsed it will try to run the job with a lower parallelism, or fail if the minimum amount of resources could not be acquired.
Increasing this value will make the cluster more resilient against temporary resources shortages (e.g., there is more time for a failed TaskManager to be restarted).
Setting a negative duration will disable the resource timeout: The JobManager will wait indefinitely for resources to appear.
If scheduler-mode is configured to REACTIVE, this configuration value will default to a negative value to disable the resource timeout. + +
jobmanager.adaptive-scheduler.scaling-interval.max
+ (none) + Duration + Determines the maximum interval time after which a scaling operation is forced even if the jobmanager.adaptive-scheduler.min-parallelism-increase aren't met. The scaling operation will be ignored when the resource hasn't changed. This option is disabled by default. + + +
jobmanager.adaptive-scheduler.scaling-interval.min
+ 30 s + Duration + Determines the minimum time between scaling operations. +
jobmanager.archive.fs.dir
(none) diff --git a/docs/layouts/shortcodes/generated/netty_shuffle_environment_configuration.html b/docs/layouts/shortcodes/generated/netty_shuffle_environment_configuration.html index 2e2ea2507688c..42419910d9b4c 100644 --- a/docs/layouts/shortcodes/generated/netty_shuffle_environment_configuration.html +++ b/docs/layouts/shortcodes/generated/netty_shuffle_environment_configuration.html @@ -170,17 +170,23 @@ String The Netty transport type, either "nio" or "epoll". The "auto" means selecting the property mode automatically based on the platform. Note that the "epoll" mode can get better performance, less GC and have more advanced features which are only available on modern Linux. + +
taskmanager.network.partition-request-timeout
+ 10 s + Duration + Timeout for an individual partition request of remote input channels. The partition request will finally fail if the total wait time exceeds twice the value of taskmanager.network.request-backoff.max. +
taskmanager.network.request-backoff.initial
100 Integer - Minimum backoff in milliseconds for partition requests of input channels. + Minimum backoff in milliseconds for partition requests of local input channels.
taskmanager.network.request-backoff.max
10000 Integer - Maximum backoff in milliseconds for partition requests of input channels. + Maximum backoff in milliseconds for partition requests of local input channels.
taskmanager.network.retries
diff --git a/docs/layouts/shortcodes/generated/rest_v1_dispatcher.html b/docs/layouts/shortcodes/generated/rest_v1_dispatcher.html index ae68e8c83838d..a96b18f5d5134 100644 --- a/docs/layouts/shortcodes/generated/rest_v1_dispatcher.html +++ b/docs/layouts/shortcodes/generated/rest_v1_dispatcher.html @@ -2524,6 +2524,9 @@ "type" : "object", "id" : "urn:jsonschema:org:apache:flink:runtime:rest:messages:JobExceptionsInfo:ExecutionExceptionInfo", "properties" : { + "endpoint" : { + "type" : "string" + }, "exception" : { "type" : "string" }, @@ -2558,6 +2561,9 @@ "type" : "object", "id" : "urn:jsonschema:org:apache:flink:runtime:rest:messages:JobExceptionsInfoWithHistory:ExceptionInfo", "properties" : { + "endpoint" : { + "type" : "string" + }, "exceptionName" : { "type" : "string" }, @@ -2585,6 +2591,9 @@ } } }, + "endpoint" : { + "type" : "string" + }, "exceptionName" : { "type" : "string" }, @@ -3635,6 +3644,9 @@ "end-time" : { "type" : "integer" }, + "endpoint" : { + "type" : "string" + }, "host" : { "type" : "string" }, @@ -4079,6 +4091,9 @@ "attempt" : { "type" : "integer" }, + "endpoint" : { + "type" : "string" + }, "host" : { "type" : "string" }, @@ -4221,6 +4236,9 @@ "end-time" : { "type" : "integer" }, + "endpoint" : { + "type" : "string" + }, "host" : { "type" : "string" }, @@ -4351,6 +4369,9 @@ "end-time" : { "type" : "integer" }, + "endpoint" : { + "type" : "string" + }, "host" : { "type" : "string" }, @@ -4621,6 +4642,9 @@ "duration" : { "type" : "integer" }, + "endpoint" : { + "type" : "string" + }, "host" : { "type" : "string" }, @@ -4729,6 +4753,9 @@ "end-time" : { "type" : "integer" }, + "endpoint" : { + "type" : "string" + }, "host" : { "type" : "string" }, diff --git a/docs/layouts/shortcodes/generated/task_manager_configuration.html b/docs/layouts/shortcodes/generated/task_manager_configuration.html index 6f2c12d5df3f2..61e01e96bba2e 100644 --- a/docs/layouts/shortcodes/generated/task_manager_configuration.html +++ b/docs/layouts/shortcodes/generated/task_manager_configuration.html @@ -56,6 +56,12 @@ Boolean Whether to kill the TaskManager when the task thread throws an OutOfMemoryError. + +
taskmanager.load-balance.mode
+ NONE +

Enum

+ Mode for the load-balance allocation strategy across all available TaskManagers.
  • The SLOTS mode tries to spread out the slots evenly across all available TaskManagers.
  • The NONE mode is the default mode without any specified strategy.


Possible values:
  • "NONE"
  • "SLOTS"
+
taskmanager.network.bind-policy
"ip" diff --git a/docs/static/fig/adaptive_scheduler.png b/docs/static/fig/adaptive_scheduler.png new file mode 100644 index 0000000000000..4962392ae5875 Binary files /dev/null and b/docs/static/fig/adaptive_scheduler.png differ diff --git a/docs/static/fig/adaptive_scheduler_rescale.png b/docs/static/fig/adaptive_scheduler_rescale.png new file mode 100644 index 0000000000000..4515a9a2be8fc Binary files /dev/null and b/docs/static/fig/adaptive_scheduler_rescale.png differ diff --git a/docs/static/fig/olap-architecture.svg b/docs/static/fig/olap-architecture.svg new file mode 100644 index 0000000000000..2b39666ab86fe --- /dev/null +++ b/docs/static/fig/olap-architecture.svg @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/docs/static/generated/rest_v1_dispatcher.yml b/docs/static/generated/rest_v1_dispatcher.yml index 4c510e9374259..62d298241c112 100644 --- a/docs/static/generated/rest_v1_dispatcher.yml +++ b/docs/static/generated/rest_v1_dispatcher.yml @@ -6,7 +6,7 @@ info: license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html - version: v1/1.18-SNAPSHOT + version: v1/1.19-SNAPSHOT paths: /cluster: delete: @@ -1819,6 +1819,10 @@ components: format: int64 discriminator: propertyName: className + mapping: + completed: '#/components/schemas/CompletedCheckpointStatistics' + failed: '#/components/schemas/FailedCheckpointStatistics' + in_progress: '#/components/schemas/PendingCheckpointStatistics' CheckpointStatisticsSummary: type: object properties: @@ -2018,6 +2022,8 @@ components: ExceptionInfo: type: object properties: + endpoint: + type: string exceptionName: type: string failureLabels: @@ -2054,6 +2060,8 @@ components: ExecutionExceptionInfo: type: object properties: + endpoint: + type: string exception: type: string location: @@ -2589,6 +2597,8 @@ components: end-time: type: integer format: int64 + endpoint: + type: string host: type: string metrics: @@ -2778,6 +2788,8 @@ components: type: array items: $ref: '#/components/schemas/ExceptionInfo' + endpoint: + type: string exceptionName: type: string failureLabels: @@ -2888,6 +2900,8 @@ components: attempt: type: integer format: int32 + endpoint: + type: string host: type: string subtask: @@ -2935,6 +2949,9 @@ components: type: string discriminator: propertyName: className + mapping: + completed: '#/components/schemas/CompletedSubtaskCheckpointStatistics' + pending: '#/components/schemas/PendingSubtaskCheckpointStatistics' SubtaskExecutionAttemptAccumulatorsInfo: type: object properties: @@ -2962,6 +2979,8 @@ components: end-time: type: integer format: int64 + endpoint: + type: string host: type: string metrics: @@ -2991,6 +3010,8 @@ components: duration: type: integer format: int64 + endpoint: + type: string host: type: string subtask: diff --git a/docs/static/generated/rest_v1_sql_gateway.yml b/docs/static/generated/rest_v1_sql_gateway.yml index d91e75cafff76..71c7db934bca7 100644 --- a/docs/static/generated/rest_v1_sql_gateway.yml +++ b/docs/static/generated/rest_v1_sql_gateway.yml @@ -6,7 +6,7 @@ info: license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html - version: v1/1.18-SNAPSHOT + version: v1/1.19-SNAPSHOT paths: /api_versions: get: diff --git a/docs/static/generated/rest_v2_sql_gateway.yml b/docs/static/generated/rest_v2_sql_gateway.yml index 42ca8d5bd599b..15008070ef94f 100644 --- a/docs/static/generated/rest_v2_sql_gateway.yml +++ b/docs/static/generated/rest_v2_sql_gateway.yml @@ -6,7 +6,7 @@ info: license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html - version: v2/1.18-SNAPSHOT + version: v2/1.19-SNAPSHOT paths: /api_versions: get: diff --git a/flink-clients/src/main/java/org/apache/flink/client/program/StreamContextEnvironment.java b/flink-clients/src/main/java/org/apache/flink/client/program/StreamContextEnvironment.java index 21ddca6169491..07f47573666e6 100644 --- a/flink-clients/src/main/java/org/apache/flink/client/program/StreamContextEnvironment.java +++ b/flink-clients/src/main/java/org/apache/flink/client/program/StreamContextEnvironment.java @@ -303,6 +303,7 @@ private void checkMainConfiguration(Configuration clusterConfigMap, List private void checkCheckpointConfig(Configuration clusterConfigMap, List errors) { CheckpointConfig expectedCheckpointConfig = new CheckpointConfig(); expectedCheckpointConfig.configure(clusterConfigMap); + configureCheckpointStorage(clusterConfigMap, expectedCheckpointConfig); checkConfigurationObject( expectedCheckpointConfig.toConfiguration(), checkpointCfg.toConfiguration(), diff --git a/flink-clients/src/test/java/org/apache/flink/client/program/StreamContextEnvironmentTest.java b/flink-clients/src/test/java/org/apache/flink/client/program/StreamContextEnvironmentTest.java index 85f6279fc584e..1af115a402ab6 100644 --- a/flink-clients/src/test/java/org/apache/flink/client/program/StreamContextEnvironmentTest.java +++ b/flink-clients/src/test/java/org/apache/flink/client/program/StreamContextEnvironmentTest.java @@ -121,6 +121,36 @@ void testDisallowCheckpointStorage( CheckpointConfig.class.getSimpleName(), "setCheckpointStorage"); } + @ParameterizedTest + @MethodSource("provideExecutors") + void testDisallowCheckpointStorageByConfiguration( + ThrowingConsumer executor) { + final Configuration clusterConfig = new Configuration(); + + Configuration jobConfig = new Configuration(); + String disallowedPath = "file:///flink/disallowed/modification"; + jobConfig.set(CheckpointingOptions.CHECKPOINT_STORAGE, "jobmanager"); + jobConfig.set(CheckpointingOptions.CHECKPOINTS_DIRECTORY, disallowedPath); + final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + final StreamContextEnvironment environment = + new StreamContextEnvironment( + new MockExecutorServiceLoader(), + clusterConfig, + jobConfig, + classLoader, + true, + true, + false, + Collections.emptyList()); + + environment.fromCollection(Collections.singleton(1)).sinkTo(new DiscardingSink<>()); + assertThatThrownBy(() -> executor.accept(environment)) + .isInstanceOf(MutatedConfigurationException.class) + .hasMessageContainingAll( + CheckpointingOptions.CHECKPOINT_STORAGE.key(), + CheckpointingOptions.CHECKPOINTS_DIRECTORY.key()); + } + @ParameterizedTest @MethodSource("provideExecutors") void testAllowCheckpointStorage( diff --git a/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/hybrid/HybridSourceSplitEnumerator.java b/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/hybrid/HybridSourceSplitEnumerator.java index c8b00b6a1f4ad..17c70ebbdc4d3 100644 --- a/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/hybrid/HybridSourceSplitEnumerator.java +++ b/flink-connectors/flink-connector-base/src/main/java/org/apache/flink/connector/base/source/hybrid/HybridSourceSplitEnumerator.java @@ -259,6 +259,7 @@ private void switchEnumerator() { if (currentEnumerator != null) { try { currentEnumerator.close(); + finishedReaders.clear(); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/flink-connectors/flink-connector-base/src/test/java/org/apache/flink/connector/base/sink/writer/TestSinkInitContext.java b/flink-connectors/flink-connector-base/src/test/java/org/apache/flink/connector/base/sink/writer/TestSinkInitContext.java index 601d4f9d427b1..1f70b03413caa 100644 --- a/flink-connectors/flink-connector-base/src/test/java/org/apache/flink/connector/base/sink/writer/TestSinkInitContext.java +++ b/flink-connectors/flink-connector-base/src/test/java/org/apache/flink/connector/base/sink/writer/TestSinkInitContext.java @@ -28,7 +28,7 @@ import org.apache.flink.metrics.groups.OperatorIOMetricGroup; import org.apache.flink.metrics.groups.SinkWriterMetricGroup; import org.apache.flink.metrics.testutils.MetricListener; -import org.apache.flink.runtime.metrics.groups.InternalSinkWriterMetricGroup; +import org.apache.flink.runtime.metrics.groups.MetricsGroupTestUtils; import org.apache.flink.runtime.metrics.groups.UnregisteredMetricGroups; import org.apache.flink.streaming.runtime.tasks.StreamTaskActionExecutor; import org.apache.flink.streaming.runtime.tasks.TestProcessingTimeService; @@ -51,7 +51,7 @@ public class TestSinkInitContext implements Sink.InitContext { private final OperatorIOMetricGroup operatorIOMetricGroup = UnregisteredMetricGroups.createUnregisteredOperatorMetricGroup().getIOMetricGroup(); private final SinkWriterMetricGroup metricGroup = - InternalSinkWriterMetricGroup.mock( + MetricsGroupTestUtils.mockWriterMetricGroup( metricListener.getMetricGroup(), operatorIOMetricGroup); private final MailboxExecutor mailboxExecutor; diff --git a/flink-connectors/flink-connector-base/src/test/java/org/apache/flink/connector/base/source/hybrid/HybridSourceSplitEnumeratorTest.java b/flink-connectors/flink-connector-base/src/test/java/org/apache/flink/connector/base/source/hybrid/HybridSourceSplitEnumeratorTest.java index fcde32811f4ca..8b068d645b6c7 100644 --- a/flink-connectors/flink-connector-base/src/test/java/org/apache/flink/connector/base/source/hybrid/HybridSourceSplitEnumeratorTest.java +++ b/flink-connectors/flink-connector-base/src/test/java/org/apache/flink/connector/base/source/hybrid/HybridSourceSplitEnumeratorTest.java @@ -252,6 +252,42 @@ public void testInterceptNoMoreSplitEvent() { assertThat(context.hasNoMoreSplits(0)).isTrue(); } + @Test + public void testMultiSubtaskSwitchEnumerator() { + context = new MockSplitEnumeratorContext<>(2); + source = + HybridSource.builder(MOCK_SOURCE) + .addSource(MOCK_SOURCE) + .addSource(MOCK_SOURCE) + .build(); + + enumerator = (HybridSourceSplitEnumerator) source.createEnumerator(context); + enumerator.start(); + + registerReader(context, enumerator, SUBTASK0); + registerReader(context, enumerator, SUBTASK1); + enumerator.handleSourceEvent(SUBTASK0, new SourceReaderFinishedEvent(-1)); + enumerator.handleSourceEvent(SUBTASK1, new SourceReaderFinishedEvent(-1)); + + assertThat(getCurrentSourceIndex(enumerator)).isEqualTo(0); + enumerator.handleSourceEvent(SUBTASK0, new SourceReaderFinishedEvent(0)); + assertThat(getCurrentSourceIndex(enumerator)).isEqualTo(0); + enumerator.handleSourceEvent(SUBTASK1, new SourceReaderFinishedEvent(0)); + assertThat(getCurrentSourceIndex(enumerator)) + .as("all reader finished source-0") + .isEqualTo(1); + + enumerator.handleSourceEvent(SUBTASK0, new SourceReaderFinishedEvent(1)); + assertThat(getCurrentSourceIndex(enumerator)) + .as( + "only reader-0 has finished reading, reader-1 is not yet done, so do not switch to the next source") + .isEqualTo(1); + enumerator.handleSourceEvent(SUBTASK1, new SourceReaderFinishedEvent(1)); + assertThat(getCurrentSourceIndex(enumerator)) + .as("all reader finished source-1") + .isEqualTo(2); + } + private static class UnderlyingEnumeratorWrapper implements SplitEnumerator { private static final MockSourceSplit SPLIT_1 = new MockSourceSplit(0, 0, 1); diff --git a/flink-connectors/flink-connector-files/src/test/java/org/apache/flink/connector/file/sink/writer/FileWriterTest.java b/flink-connectors/flink-connector-files/src/test/java/org/apache/flink/connector/file/sink/writer/FileWriterTest.java index 0149bf3e6da4d..cd0dda1d9781d 100644 --- a/flink-connectors/flink-connector-files/src/test/java/org/apache/flink/connector/file/sink/writer/FileWriterTest.java +++ b/flink-connectors/flink-connector-files/src/test/java/org/apache/flink/connector/file/sink/writer/FileWriterTest.java @@ -31,7 +31,7 @@ import org.apache.flink.metrics.groups.OperatorIOMetricGroup; import org.apache.flink.metrics.groups.SinkWriterMetricGroup; import org.apache.flink.metrics.testutils.MetricListener; -import org.apache.flink.runtime.metrics.groups.InternalSinkWriterMetricGroup; +import org.apache.flink.runtime.metrics.groups.MetricsGroupTestUtils; import org.apache.flink.runtime.metrics.groups.UnregisteredMetricGroups; import org.apache.flink.streaming.api.functions.sink.filesystem.BucketAssigner; import org.apache.flink.streaming.api.functions.sink.filesystem.OutputFileConfig; @@ -292,7 +292,7 @@ void testNumberRecordsOutCounter(@TempDir java.nio.file.Path tempDir) final OperatorIOMetricGroup operatorIOMetricGroup = UnregisteredMetricGroups.createUnregisteredOperatorMetricGroup().getIOMetricGroup(); final SinkWriterMetricGroup sinkWriterMetricGroup = - InternalSinkWriterMetricGroup.mock( + MetricsGroupTestUtils.mockWriterMetricGroup( metricListener.getMetricGroup(), operatorIOMetricGroup); Counter recordsCounter = sinkWriterMetricGroup.getIOMetricGroup().getNumRecordsOutCounter(); @@ -471,7 +471,7 @@ private FileWriter createWriter( basePath, rollingPolicy, outputFileConfig, - InternalSinkWriterMetricGroup.mock(metricListener.getMetricGroup())); + MetricsGroupTestUtils.mockWriterMetricGroup(metricListener.getMetricGroup())); } private FileWriter createWriter( @@ -484,7 +484,7 @@ private FileWriter createWriter( throws IOException { return new FileWriter<>( basePath, - InternalSinkWriterMetricGroup.mock(metricListener.getMetricGroup()), + MetricsGroupTestUtils.mockWriterMetricGroup(metricListener.getMetricGroup()), bucketAssigner, new DefaultFileWriterBucketFactory<>(), new RowWiseBucketWriter<>( diff --git a/flink-connectors/flink-connector-hive/src/test/java/org/apache/flink/connectors/hive/HiveTemporalJoinITCase.java b/flink-connectors/flink-connector-hive/src/test/java/org/apache/flink/connectors/hive/HiveTemporalJoinITCase.java index 3f1685fb0aef7..0b238aff180a3 100644 --- a/flink-connectors/flink-connector-hive/src/test/java/org/apache/flink/connectors/hive/HiveTemporalJoinITCase.java +++ b/flink-connectors/flink-connector-hive/src/test/java/org/apache/flink/connectors/hive/HiveTemporalJoinITCase.java @@ -31,13 +31,15 @@ import org.apache.flink.table.planner.utils.TableTestBase; import org.apache.flink.types.Row; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.sql.Timestamp; import java.util.Arrays; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + /** * Test Temporal join of hive tables. * @@ -45,13 +47,13 @@ * 3.1.1. To run this test, please use mvn command: mvn test -Phive-3.1.1 * -Dtest=org.apache.flink.connectors.hive.HiveTemporalJoinITCase */ -public class HiveTemporalJoinITCase extends TableTestBase { +class HiveTemporalJoinITCase extends TableTestBase { private static TableEnvironment tableEnv; private static HiveCatalog hiveCatalog; - @BeforeClass - public static void setup() { + @BeforeAll + static void setup() { if (!HiveVersionTestUtil.HIVE_310_OR_LATER) { return; } @@ -96,7 +98,7 @@ public static void setup() { } @Test - public void testProcTimeTemporalJoinHiveTable() throws Exception { + void testProcTimeTemporalJoinHiveTable() throws Exception { if (!HiveVersionTestUtil.HIVE_310_OR_LATER) { return; } @@ -104,16 +106,22 @@ public void testProcTimeTemporalJoinHiveTable() throws Exception { tableEnv.executeSql("insert into build values (1,'a',10),(2,'a',21),(2,'b',22),(3,'c',33)") .await(); - expectedException().expect(TableException.class); - expectedException().expectMessage("Processing-time temporal join is not supported yet."); tableEnv.executeSql( "select p.x, p.y, b.z from " + " default_catalog.default_database.probe as p " + " join build for system_time as of p.p as b on p.x=b.x and p.y=b.y"); + assertThatThrownBy( + () -> + tableEnv.executeSql( + "select p.x, p.y, b.z from " + + " default_catalog.default_database.probe as p " + + " join build for system_time as of p.p as b on p.x=b.x and p.y=b.y")) + .hasMessageContaining("Processing-time temporal join is not supported yet.") + .isInstanceOf(TableException.class); } @Test - public void testRowTimeTemporalJoinHiveTable() throws Exception { + void testRowTimeTemporalJoinHiveTable() throws Exception { if (!HiveVersionTestUtil.HIVE_310_OR_LATER) { return; } @@ -122,19 +130,20 @@ public void testRowTimeTemporalJoinHiveTable() throws Exception { .await(); // Streaming hive table does not support defines watermark - expectedException().expect(ValidationException.class); - expectedException() - .expectMessage( + assertThatThrownBy( + () -> + tableEnv.executeSql( + "select p.x, p.y, b.z from " + + " default_catalog.default_database.probe as p " + + " join build for system_time as of p.rowtime as b on p.x=b.x and p.y=b.y")) + .hasMessageContaining( "Event-Time Temporal Table Join requires both primary key" - + " and row time attribute in versioned table, but no row time attribute can be found."); - tableEnv.executeSql( - "select p.x, p.y, b.z from " - + " default_catalog.default_database.probe as p " - + " join build for system_time as of p.rowtime as b on p.x=b.x and p.y=b.y"); + + " and row time attribute in versioned table, but no row time attribute can be found.") + .isInstanceOf(ValidationException.class); } - @AfterClass - public static void tearDown() { + @AfterAll + static void tearDown() { if (!HiveVersionTestUtil.HIVE_310_OR_LATER) { return; } diff --git a/flink-core/pom.xml b/flink-core/pom.xml index 598f0ed4995cf..aee9443281585 100644 --- a/flink-core/pom.xml +++ b/flink-core/pom.xml @@ -34,7 +34,14 @@ under the License. jar - + + ${surefire.module.config.jdk21} --add-exports=java.rmi/sun.rmi.registry=ALL-UNNAMED + -Djava.security.manager=allow + + diff --git a/flink-core/src/main/java/org/apache/flink/api/common/ExecutionConfig.java b/flink-core/src/main/java/org/apache/flink/api/common/ExecutionConfig.java index f3fae490cf712..22aee331513cc 100644 --- a/flink-core/src/main/java/org/apache/flink/api/common/ExecutionConfig.java +++ b/flink-core/src/main/java/org/apache/flink/api/common/ExecutionConfig.java @@ -154,8 +154,6 @@ public class ExecutionConfig implements Serializable, Archiveable parameters) { + configuration.set(PipelineOptions.GLOBAL_JOB_PARAMETERS, parameters); } // -------------------------------------------------------------------------------------------- @@ -985,7 +990,6 @@ public boolean equals(Object obj) { || (null != restartStrategyConfiguration && restartStrategyConfiguration.equals( other.restartStrategyConfiguration))) - && Objects.equals(globalJobParameters, other.globalJobParameters) && registeredTypesWithKryoSerializerClasses.equals( other.registeredTypesWithKryoSerializerClasses) && defaultKryoSerializerClasses.equals(other.defaultKryoSerializerClasses) @@ -1002,7 +1006,6 @@ public int hashCode() { return Objects.hash( configuration, restartStrategyConfiguration, - globalJobParameters, registeredTypesWithKryoSerializerClasses, defaultKryoSerializerClasses, registeredKryoTypes, @@ -1018,8 +1021,6 @@ public String toString() { + executionRetryDelay + ", restartStrategyConfiguration=" + restartStrategyConfiguration - + ", globalJobParameters=" - + globalJobParameters + ", registeredTypesWithKryoSerializers=" + registeredTypesWithKryoSerializers + ", registeredTypesWithKryoSerializerClasses=" @@ -1150,7 +1151,6 @@ public void configure(ReadableConfig configuration, ClassLoader classLoader) { configuration.getOptional(PipelineOptions.FORCE_KRYO).ifPresent(this::setForceKryo); configuration .getOptional(PipelineOptions.GLOBAL_JOB_PARAMETERS) - .map(MapBasedJobParameters::new) .ifPresent(this::setGlobalJobParameters); configuration @@ -1202,7 +1202,7 @@ public void configure(ReadableConfig configuration, ClassLoader classLoader) { /** * @return A copy of internal {@link #configuration}. Note it is missing all options that are * stored as plain java fields in {@link ExecutionConfig}, for example {@link - * #registeredKryoTypes} or {@link #globalJobParameters}. + * #registeredKryoTypes}. */ @Internal public Configuration toConfiguration() { diff --git a/flink-core/src/main/java/org/apache/flink/api/common/state/AppendingState.java b/flink-core/src/main/java/org/apache/flink/api/common/state/AppendingState.java index d020c0b7dcf26..1f21427e99648 100644 --- a/flink-core/src/main/java/org/apache/flink/api/common/state/AppendingState.java +++ b/flink-core/src/main/java/org/apache/flink/api/common/state/AppendingState.java @@ -58,7 +58,8 @@ public interface AppendingState extends State { * of values. The next time {@link #get()} is called (for the same state partition) the returned * state will represent the updated list. * - *

If null is passed in, the state value will remain unchanged. + *

If null is passed in, the behaviour is undefined (implementation related). TODO: An + * unified behaviour across all sub-classes. * * @param value The new value for the state. * @throws Exception Thrown if the system cannot access the state. diff --git a/flink-core/src/main/java/org/apache/flink/api/common/state/ListState.java b/flink-core/src/main/java/org/apache/flink/api/common/state/ListState.java index 7508054f5c021..3ea5b16fb7f96 100644 --- a/flink-core/src/main/java/org/apache/flink/api/common/state/ListState.java +++ b/flink-core/src/main/java/org/apache/flink/api/common/state/ListState.java @@ -48,10 +48,13 @@ public interface ListState extends MergingState> { * given list of values. The next time {@link #get()} is called (for the same state partition) * the returned state will represent the updated list. * - *

If null or an empty list is passed in, the state value will be null. + *

If an empty list is passed in, the state value will be null. + * + *

Null value passed in or any null value in list is not allowed. * * @param values The new values for the state. - * @throws Exception The method may forward exception thrown internally (by I/O or functions). + * @throws Exception The method may forward exception thrown internally (by I/O or functions, or + * sanity check for null value). */ void update(List values) throws Exception; @@ -60,10 +63,13 @@ public interface ListState extends MergingState> { * existing list of values. The next time {@link #get()} is called (for the same state * partition) the returned state will represent the updated list. * - *

If null or an empty list is passed in, the state value remains unchanged. + *

If an empty list is passed in, the state value remains unchanged. + * + *

Null value passed in or any null value in list is not allowed. * * @param values The new values to be added to the state. - * @throws Exception The method may forward exception thrown internally (by I/O or functions). + * @throws Exception The method may forward exception thrown internally (by I/O or functions, or + * sanity check for null value). */ void addAll(List values) throws Exception; } diff --git a/flink-core/src/main/java/org/apache/flink/configuration/CheckpointingOptions.java b/flink-core/src/main/java/org/apache/flink/configuration/CheckpointingOptions.java index b95a1d97d883a..d17d12592d1a6 100644 --- a/flink-core/src/main/java/org/apache/flink/configuration/CheckpointingOptions.java +++ b/flink-core/src/main/java/org/apache/flink/configuration/CheckpointingOptions.java @@ -109,6 +109,19 @@ public class CheckpointingOptions { .defaultValue(1) .withDescription("The maximum number of completed checkpoints to retain."); + /* Option whether to clean individual checkpoint's operatorstates in parallel. If enabled, + * operator states are discarded in parallel using the ExecutorService passed to the cleaner. + * This speeds up checkpoints cleaning, but adds load to the IO. + */ + @Documentation.Section(Documentation.Sections.COMMON_STATE_BACKENDS) + public static final ConfigOption CLEANER_PARALLEL_MODE = + ConfigOptions.key("state.checkpoint.cleaner.parallel-mode") + .booleanType() + .defaultValue(true) + .withDescription( + "Option whether to discard a checkpoint's states in parallel using" + + " the ExecutorService passed into the cleaner"); + /** @deprecated Checkpoints are always asynchronous. */ @Deprecated public static final ConfigOption ASYNC_SNAPSHOTS = diff --git a/flink-core/src/main/java/org/apache/flink/configuration/ClusterOptions.java b/flink-core/src/main/java/org/apache/flink/configuration/ClusterOptions.java index 58150beb4b7f5..c0b9ca8459ef1 100644 --- a/flink-core/src/main/java/org/apache/flink/configuration/ClusterOptions.java +++ b/flink-core/src/main/java/org/apache/flink/configuration/ClusterOptions.java @@ -84,6 +84,13 @@ public class ClusterOptions { + "By default it will use 4 * the number of CPU cores (hardware contexts) that the cluster process has access to. " + "Increasing the pool size allows to run more IO operations concurrently."); + /** + * @deprecated Please use {@link TaskManagerOptions#TASK_MANAGER_LOAD_BALANCE_MODE} instead. + * Note: The 'taskmanager.load-balance.mode: SLOTS' is equal to + * 'cluster.evenly-spread-out-slots: true'. The 'taskmanager.load-balance.mode: NONE' is + * equal to 'cluster.evenly-spread-out-slots: false'. + */ + @Deprecated @Documentation.Section(Documentation.Sections.EXPERT_SCHEDULING) public static final ConfigOption EVENLY_SPREAD_OUT_SLOTS_STRATEGY = ConfigOptions.key("cluster.evenly-spread-out-slots") diff --git a/flink-core/src/main/java/org/apache/flink/configuration/JobManagerOptions.java b/flink-core/src/main/java/org/apache/flink/configuration/JobManagerOptions.java index 1da99be996670..0378e380923f2 100644 --- a/flink-core/src/main/java/org/apache/flink/configuration/JobManagerOptions.java +++ b/flink-core/src/main/java/org/apache/flink/configuration/JobManagerOptions.java @@ -507,6 +507,32 @@ public enum SchedulerType { .withDescription( "Configure the minimum increase in parallelism for a job to scale up."); + @Documentation.Section({ + Documentation.Sections.EXPERT_SCHEDULING, + Documentation.Sections.ALL_JOB_MANAGER + }) + public static final ConfigOption SCHEDULER_SCALING_INTERVAL_MIN = + key("jobmanager.adaptive-scheduler.scaling-interval.min") + .durationType() + .defaultValue(Duration.ofSeconds(30)) + // rescaling and let the user increase the value for high workloads + .withDescription("Determines the minimum time between scaling operations."); + + @Documentation.Section({ + Documentation.Sections.EXPERT_SCHEDULING, + Documentation.Sections.ALL_JOB_MANAGER + }) + public static final ConfigOption SCHEDULER_SCALING_INTERVAL_MAX = + key("jobmanager.adaptive-scheduler.scaling-interval.max") + .durationType() + .noDefaultValue() + .withDescription( + Description.builder() + .text( + "Determines the maximum interval time after which a scaling operation is forced even if the %s aren't met. The scaling operation will be ignored when the resource hasn't changed. This option is disabled by default.", + code(MIN_PARALLELISM_INCREASE.key())) + .build()); + @Documentation.Section({ Documentation.Sections.EXPERT_SCHEDULING, Documentation.Sections.ALL_JOB_MANAGER diff --git a/flink-core/src/main/java/org/apache/flink/configuration/NettyShuffleEnvironmentOptions.java b/flink-core/src/main/java/org/apache/flink/configuration/NettyShuffleEnvironmentOptions.java index 8b2099ee8e3df..aeeb4b4725793 100644 --- a/flink-core/src/main/java/org/apache/flink/configuration/NettyShuffleEnvironmentOptions.java +++ b/flink-core/src/main/java/org/apache/flink/configuration/NettyShuffleEnvironmentOptions.java @@ -21,8 +21,12 @@ import org.apache.flink.annotation.Experimental; import org.apache.flink.annotation.PublicEvolving; import org.apache.flink.annotation.docs.Documentation; +import org.apache.flink.configuration.description.Description; + +import java.time.Duration; import static org.apache.flink.configuration.ConfigOptions.key; +import static org.apache.flink.configuration.description.TextElement.code; /** The set of configuration options relating to network stack. */ @PublicEvolving @@ -585,7 +589,7 @@ public class NettyShuffleEnvironmentOptions { .defaultValue(100) .withDeprecatedKeys("taskmanager.net.request-backoff.initial") .withDescription( - "Minimum backoff in milliseconds for partition requests of input channels."); + "Minimum backoff in milliseconds for partition requests of local input channels."); /** Maximum backoff for partition requests of input channels. */ @Documentation.Section(Documentation.Sections.ALL_TASK_MANAGER_NETWORK) @@ -595,7 +599,22 @@ public class NettyShuffleEnvironmentOptions { .defaultValue(10000) .withDeprecatedKeys("taskmanager.net.request-backoff.max") .withDescription( - "Maximum backoff in milliseconds for partition requests of input channels."); + "Maximum backoff in milliseconds for partition requests of local input channels."); + + /** The timeout for partition request listener in result partition manager. */ + @Documentation.Section(Documentation.Sections.ALL_TASK_MANAGER_NETWORK) + public static final ConfigOption NETWORK_PARTITION_REQUEST_TIMEOUT = + key("taskmanager.network.partition-request-timeout") + .durationType() + .defaultValue(Duration.ofSeconds(10)) + .withDescription( + Description.builder() + .text( + "Timeout for an individual partition request of remote input channels. " + + "The partition request will finally fail if the total wait time exceeds " + + "twice the value of %s.", + code(NETWORK_REQUEST_BACKOFF_MAX.key())) + .build()); // ------------------------------------------------------------------------ diff --git a/flink-core/src/main/java/org/apache/flink/configuration/TaskManagerOptions.java b/flink-core/src/main/java/org/apache/flink/configuration/TaskManagerOptions.java index 8f4a836d4f338..0e4bc7f7d15f3 100644 --- a/flink-core/src/main/java/org/apache/flink/configuration/TaskManagerOptions.java +++ b/flink-core/src/main/java/org/apache/flink/configuration/TaskManagerOptions.java @@ -25,9 +25,12 @@ import org.apache.flink.configuration.description.Description; import org.apache.flink.util.TimeUtils; +import javax.annotation.Nonnull; + import java.time.Duration; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import static org.apache.flink.configuration.ConfigOptions.key; import static org.apache.flink.configuration.description.TextElement.code; @@ -708,6 +711,55 @@ public class TaskManagerOptions { "Time we wait for the timers in milliseconds to finish all pending timer threads" + " when the stream task is cancelled."); + @Documentation.Section({ + Documentation.Sections.EXPERT_SCHEDULING, + Documentation.Sections.ALL_TASK_MANAGER + }) + public static final ConfigOption TASK_MANAGER_LOAD_BALANCE_MODE = + ConfigOptions.key("taskmanager.load-balance.mode") + .enumType(TaskManagerLoadBalanceMode.class) + .defaultValue(TaskManagerLoadBalanceMode.NONE) + .withDescription( + Description.builder() + .text( + "Mode for the load-balance allocation strategy across all available %s.", + code("TaskManagers")) + .list( + text( + "The %s mode tries to spread out the slots evenly across all available %s.", + code(TaskManagerLoadBalanceMode.SLOTS.name()), + code("TaskManagers")), + text( + "The %s mode is the default mode without any specified strategy.", + code(TaskManagerLoadBalanceMode.NONE.name()))) + .build()); + + /** Type of {@link TaskManagerOptions#TASK_MANAGER_LOAD_BALANCE_MODE}. */ + public enum TaskManagerLoadBalanceMode { + NONE, + SLOTS; + + /** + * The method is mainly to load the {@link + * TaskManagerOptions#TASK_MANAGER_LOAD_BALANCE_MODE} from {@link Configuration}, which is + * compatible with {@link ClusterOptions#EVENLY_SPREAD_OUT_SLOTS_STRATEGY}. + */ + public static TaskManagerLoadBalanceMode loadFromConfiguration( + @Nonnull Configuration configuration) { + Optional taskManagerLoadBalanceModeOptional = + configuration.getOptional(TaskManagerOptions.TASK_MANAGER_LOAD_BALANCE_MODE); + if (taskManagerLoadBalanceModeOptional.isPresent()) { + return taskManagerLoadBalanceModeOptional.get(); + } + boolean evenlySpreadOutSlots = + configuration.getBoolean(ClusterOptions.EVENLY_SPREAD_OUT_SLOTS_STRATEGY); + + return evenlySpreadOutSlots + ? TaskManagerLoadBalanceMode.SLOTS + : TaskManagerOptions.TASK_MANAGER_LOAD_BALANCE_MODE.defaultValue(); + } + } + // ------------------------------------------------------------------------ /** Not intended to be instantiated. */ diff --git a/flink-core/src/test/java/org/apache/flink/configuration/TaskManagerLoadBalanceModeTest.java b/flink-core/src/test/java/org/apache/flink/configuration/TaskManagerLoadBalanceModeTest.java new file mode 100644 index 0000000000000..4ffae33b0808b --- /dev/null +++ b/flink-core/src/test/java/org/apache/flink/configuration/TaskManagerLoadBalanceModeTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.configuration; + +import org.junit.jupiter.api.Test; + +import static org.apache.flink.configuration.ClusterOptions.EVENLY_SPREAD_OUT_SLOTS_STRATEGY; +import static org.apache.flink.configuration.TaskManagerOptions.TASK_MANAGER_LOAD_BALANCE_MODE; +import static org.apache.flink.configuration.TaskManagerOptions.TaskManagerLoadBalanceMode; +import static org.assertj.core.api.Assertions.assertThat; + +/** Test for {@link TaskManagerLoadBalanceMode}. */ +class TaskManagerLoadBalanceModeTest { + + @Test + void testReadTaskManagerLoadBalanceMode() { + // Check for non-set 'taskmanager.load-balance.mode' and + // 'cluster.evenly-spread-out-slots: false' + Configuration conf1 = new Configuration(); + assertThat(TaskManagerLoadBalanceMode.loadFromConfiguration(conf1)) + .isEqualTo(TASK_MANAGER_LOAD_BALANCE_MODE.defaultValue()); + + // Check for non-set 'taskmanager.load-balance.mode' and + // 'cluster.evenly-spread-out-slots: true' + Configuration conf2 = new Configuration(); + conf2.set(EVENLY_SPREAD_OUT_SLOTS_STRATEGY, true); + assertThat(TaskManagerLoadBalanceMode.loadFromConfiguration(conf2)) + .isEqualTo(TaskManagerLoadBalanceMode.SLOTS); + + // Check for setting manually 'taskmanager.load-balance.mode: NONE' and + // 'cluster.evenly-spread-out-slots: false' + Configuration conf3 = new Configuration(); + conf3.set(TASK_MANAGER_LOAD_BALANCE_MODE, TaskManagerLoadBalanceMode.NONE); + assertThat(TaskManagerLoadBalanceMode.loadFromConfiguration(conf3)) + .isEqualTo(TaskManagerLoadBalanceMode.NONE); + + // Check for setting manually 'taskmanager.load-balance.mode: NONE' and + // 'cluster.evenly-spread-out-slots: true' + Configuration conf4 = new Configuration(); + conf4.set(TASK_MANAGER_LOAD_BALANCE_MODE, TaskManagerLoadBalanceMode.NONE); + conf4.set(EVENLY_SPREAD_OUT_SLOTS_STRATEGY, true); + assertThat(TaskManagerLoadBalanceMode.loadFromConfiguration(conf4)) + .isEqualTo(TaskManagerLoadBalanceMode.NONE); + + // Check for setting manually 'taskmanager.load-balance.mode: SLOTS' and + // 'cluster.evenly-spread-out-slots: false' + Configuration conf5 = new Configuration(); + conf5.set(TASK_MANAGER_LOAD_BALANCE_MODE, TaskManagerLoadBalanceMode.SLOTS); + assertThat(TaskManagerLoadBalanceMode.loadFromConfiguration(conf5)) + .isEqualTo(TaskManagerLoadBalanceMode.SLOTS); + + // Check for setting manually 'taskmanager.load-balance.mode: SLOTS' and + // 'cluster.evenly-spread-out-slots: true' + Configuration conf6 = new Configuration(); + conf6.set(TASK_MANAGER_LOAD_BALANCE_MODE, TaskManagerLoadBalanceMode.SLOTS); + conf6.set(EVENLY_SPREAD_OUT_SLOTS_STRATEGY, true); + assertThat(TaskManagerLoadBalanceMode.loadFromConfiguration(conf6)) + .isEqualTo(TaskManagerLoadBalanceMode.SLOTS); + } +} diff --git a/flink-dist/src/main/flink-bin/bin/config.sh b/flink-dist/src/main/flink-bin/bin/config.sh index f09c68d493fb9..dcd48a256f72a 100755 --- a/flink-dist/src/main/flink-bin/bin/config.sh +++ b/flink-dist/src/main/flink-bin/bin/config.sh @@ -334,6 +334,12 @@ if [ -z "${FLINK_ENV_JAVA_OPTS}" ]; then # Remove leading and ending double quotes (if present) of value FLINK_ENV_JAVA_OPTS="-XX:+IgnoreUnrecognizedVMOptions $( echo "${FLINK_ENV_JAVA_OPTS}" | sed -e 's/^"//' -e 's/"$//' )" + + JAVA_SPEC_VERSION=`${JAVA_HOME}/bin/java -XshowSettings:properties 2>&1 | grep "java.specification.version" | cut -d "=" -f 2 | tr -d '[:space:]' | rev | cut -d "." -f 1 | rev` + if [[ $(( $JAVA_SPEC_VERSION > 17 )) == 1 ]]; then + # set security manager property to allow calls to System.setSecurityManager() at runtime + FLINK_ENV_JAVA_OPTS="$FLINK_ENV_JAVA_OPTS -Djava.security.manager=allow" + fi fi if [ -z "${FLINK_ENV_JAVA_OPTS_JM}" ]; then diff --git a/flink-dist/src/main/resources/META-INF/NOTICE b/flink-dist/src/main/resources/META-INF/NOTICE index 8eb3dbc5dc798..a5358c47c3048 100644 --- a/flink-dist/src/main/resources/META-INF/NOTICE +++ b/flink-dist/src/main/resources/META-INF/NOTICE @@ -12,7 +12,7 @@ This project bundles the following dependencies under the Apache Software Licens - commons-cli:commons-cli:1.5.0 - commons-collections:commons-collections:3.2.2 - commons-io:commons-io:2.11.0 -- org.apache.commons:commons-compress:1.21 +- org.apache.commons:commons-compress:1.24.0 - org.apache.commons:commons-lang3:3.12.0 - org.apache.commons:commons-math3:3.6.1 - org.apache.commons:commons-text:1.10.0 diff --git a/flink-end-to-end-tests/test-scripts/common_docker.sh b/flink-end-to-end-tests/test-scripts/common_docker.sh index 43c387c1fd8cd..e38f20359babf 100644 --- a/flink-end-to-end-tests/test-scripts/common_docker.sh +++ b/flink-end-to-end-tests/test-scripts/common_docker.sh @@ -54,6 +54,9 @@ function build_image() { if [[ ${PROFILE} == *"jdk11"* ]]; then java_version=11 fi + if [[ ${PROFILE} == *"jdk17"* ]]; then + java_version=17 + fi cd flink-docker ./add-custom.sh -u ${file_server_address}:9999/flink.tgz -n ${image_name} -j ${java_version} diff --git a/flink-end-to-end-tests/test-scripts/common_ha.sh b/flink-end-to-end-tests/test-scripts/common_ha.sh index 23f89eea0bb42..a18bd15da46f8 100644 --- a/flink-end-to-end-tests/test-scripts/common_ha.sh +++ b/flink-end-to-end-tests/test-scripts/common_ha.sh @@ -49,7 +49,7 @@ function verify_num_occurences_in_logs() { local text="$2" local expected_no="$3" - local actual_no=$(grep -r --include "*${log_pattern}*.log*" -e "${text}" "$FLINK_LOG_DIR/" | cut -d ":" -f 1 | sed "s/\.[0-9]\{1,\}$//g" | uniq | wc -l) + local actual_no=$(grep -r --include "*${log_pattern}*.log*" -e "${text}" "$FLINK_LOG_DIR/" | cut -d ":" -f 1 | sed "s/\.[0-9]\{1,\}$//g" | sort -u | wc -l) [[ "${expected_no}" -eq "${actual_no}" ]] } diff --git a/flink-end-to-end-tests/test-scripts/common_kubernetes.sh b/flink-end-to-end-tests/test-scripts/common_kubernetes.sh index 59b4ea70f3472..c12fa071fe42d 100755 --- a/flink-end-to-end-tests/test-scripts/common_kubernetes.sh +++ b/flink-end-to-end-tests/test-scripts/common_kubernetes.sh @@ -21,10 +21,12 @@ source "$(dirname "$0")"/common.sh source "$(dirname "$0")"/common_docker.sh CONTAINER_SCRIPTS=${END_TO_END_DIR}/test-scripts/container-scripts -MINIKUBE_START_RETRIES=3 -MINIKUBE_START_BACKOFF=5 +RETRY_COUNT=3 +RETRY_BACKOFF_TIME=5 RESULT_HASH="e682ec6622b5e83f2eb614617d5ab2cf" MINIKUBE_VERSION="v1.28.0" +CRICTL_VERSION="v1.24.2" +CRI_DOCKERD_VERSION="0.2.3" NON_LINUX_ENV_NOTE="****** Please start/stop minikube manually in non-linux environment. ******" @@ -39,8 +41,9 @@ function setup_kubernetes_for_linux { if ! [ -x "$(command -v kubectl)" ]; then echo "Installing kubectl ..." local version=$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt) - curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/$version/bin/linux/$arch/kubectl && \ - chmod +x kubectl && sudo mv kubectl /usr/local/bin/ + download_kubectl_url="https://storage.googleapis.com/kubernetes-release/release/$version/bin/linux/$arch/kubectl" + retry_download "${download_kubectl_url}" + chmod +x kubectl && sudo mv kubectl /usr/local/bin/ fi # Download minikube when it is not installed beforehand. if [ -x "$(command -v minikube)" ] && [[ "$(minikube version | grep -c $MINIKUBE_VERSION)" == "0" ]]; then @@ -50,32 +53,32 @@ function setup_kubernetes_for_linux { if ! [ -x "$(command -v minikube)" ]; then echo "Installing minikube $MINIKUBE_VERSION ..." - curl -Lo minikube https://storage.googleapis.com/minikube/releases/$MINIKUBE_VERSION/minikube-linux-$arch && \ - chmod +x minikube && sudo mv minikube /usr/bin/minikube + download_minikube_url="https://storage.googleapis.com/minikube/releases/$MINIKUBE_VERSION/minikube-linux-$arch" + retry_download "${download_minikube_url}" + chmod +x "minikube-linux-$arch" && sudo mv "minikube-linux-$arch" /usr/bin/minikube fi # conntrack is required for minikube 1.9 and later sudo apt-get install conntrack # crictl is required for cri-dockerd - local crictl_version crictl_archive - crictl_version="v1.24.2" - crictl_archive="crictl-$crictl_version-linux-${arch}.tar.gz" - wget -nv "https://github.com/kubernetes-sigs/cri-tools/releases/download/${crictl_version}/${crictl_archive}" + local crictl_archive + crictl_archive="crictl-$CRICTL_VERSION-linux-${arch}.tar.gz" + download_crictl_url="https://github.com/kubernetes-sigs/cri-tools/releases/download/${CRICTL_VERSION}/${crictl_archive}" + retry_download "${download_crictl_url}" sudo tar zxvf ${crictl_archive} -C /usr/local/bin rm -f ${crictl_archive} # cri-dockerd is required to use Kubernetes 1.24+ and the none driver - local cri_dockerd_version cri_dockerd_archive cri_dockerd_binary - cri_dockerd_version="0.2.3" - cri_dockerd_archive="cri-dockerd-${cri_dockerd_version}.${arch}.tgz" + local cri_dockerd_archive cri_dockerd_binary + cri_dockerd_archive="cri-dockerd-${CRI_DOCKERD_VERSION}.${arch}.tgz" cri_dockerd_binary="cri-dockerd" - wget -nv "https://github.com/Mirantis/cri-dockerd/releases/download/v${cri_dockerd_version}/${cri_dockerd_archive}" + retry_download "https://github.com/Mirantis/cri-dockerd/releases/download/v${CRI_DOCKERD_VERSION}/${cri_dockerd_archive}" tar xzvf $cri_dockerd_archive "cri-dockerd/${cri_dockerd_binary}" --strip-components=1 sudo install -o root -g root -m 0755 "${cri_dockerd_binary}" "/usr/local/bin/${cri_dockerd_binary}" rm ${cri_dockerd_binary} - wget -nv https://raw.githubusercontent.com/Mirantis/cri-dockerd/v${cri_dockerd_version}/packaging/systemd/cri-docker.service - wget -nv https://raw.githubusercontent.com/Mirantis/cri-dockerd/v${cri_dockerd_version}/packaging/systemd/cri-docker.socket + retry_download "https://raw.githubusercontent.com/Mirantis/cri-dockerd/v${CRI_DOCKERD_VERSION}/packaging/systemd/cri-docker.service" + retry_download "https://raw.githubusercontent.com/Mirantis/cri-dockerd/v${CRI_DOCKERD_VERSION}/packaging/systemd/cri-docker.socket" sudo mv cri-docker.socket cri-docker.service /etc/systemd/system/ sudo sed -i -e "s,/usr/bin/${cri_dockerd_binary},/usr/local/bin/${cri_dockerd_binary}," /etc/systemd/system/cri-docker.service @@ -87,6 +90,21 @@ function setup_kubernetes_for_linux { sudo sysctl fs.protected_regular=0 } +function retry_download { + if [[ "$#" != 1 ]]; then + echo "Fatal error: No parameter or too many parameters passed: $@" + exit 1; + fi + + local download_url download_command + download_url="$1" + download_command="wget -nv ${download_url}" + if ! retry_times ${RETRY_COUNT} ${RETRY_BACKOFF_TIME} "${download_command}"; then + echo "ERROR: Download failed repeatedly after ${RETRY_COUNT} tries. Aborting..." + exit 1 + fi +} + function check_kubernetes_status { minikube status return $? @@ -138,7 +156,7 @@ function start_kubernetes { echo "The mounting process is running with pid $minikube_mount_pid" else setup_kubernetes_for_linux - if ! retry_times ${MINIKUBE_START_RETRIES} ${MINIKUBE_START_BACKOFF} start_kubernetes_if_not_running; then + if ! retry_times ${RETRY_COUNT} ${RETRY_BACKOFF_TIME} start_kubernetes_if_not_running; then echo "Could not start minikube. Aborting..." exit 1 fi @@ -153,7 +171,7 @@ function stop_kubernetes { else echo "Stopping minikube ..." stop_command="minikube stop" - if ! retry_times ${MINIKUBE_START_RETRIES} ${MINIKUBE_START_BACKOFF} "${stop_command}"; then + if ! retry_times ${RETRY_COUNT} ${RETRY_BACKOFF_TIME} "${stop_command}"; then echo "Could not stop minikube. Aborting..." exit 1 fi diff --git a/flink-filesystems/flink-fs-hadoop-shaded/src/main/resources/META-INF/NOTICE b/flink-filesystems/flink-fs-hadoop-shaded/src/main/resources/META-INF/NOTICE index 0236725e0a47f..c5afbb58e3690 100644 --- a/flink-filesystems/flink-fs-hadoop-shaded/src/main/resources/META-INF/NOTICE +++ b/flink-filesystems/flink-fs-hadoop-shaded/src/main/resources/META-INF/NOTICE @@ -18,7 +18,7 @@ This project bundles the following dependencies under the Apache Software Licens - commons-collections:commons-collections:3.2.2 - commons-io:commons-io:2.11.0 - commons-logging:commons-logging:1.1.3 -- org.apache.commons:commons-compress:1.21 +- org.apache.commons:commons-compress:1.24.0 - org.apache.commons:commons-configuration2:2.1.1 - org.apache.commons:commons-lang3:3.12.0 - org.apache.commons:commons-text:1.10.0 diff --git a/flink-filesystems/flink-s3-fs-hadoop/src/main/resources/META-INF/NOTICE b/flink-filesystems/flink-s3-fs-hadoop/src/main/resources/META-INF/NOTICE index c16ab1adc986d..69137435b28a7 100644 --- a/flink-filesystems/flink-s3-fs-hadoop/src/main/resources/META-INF/NOTICE +++ b/flink-filesystems/flink-s3-fs-hadoop/src/main/resources/META-INF/NOTICE @@ -24,7 +24,7 @@ This project bundles the following dependencies under the Apache Software Licens - commons-io:commons-io:2.11.0 - commons-logging:commons-logging:1.1.3 - joda-time:joda-time:2.5 -- org.apache.commons:commons-compress:1.21 +- org.apache.commons:commons-compress:1.24.0 - org.apache.commons:commons-configuration2:2.1.1 - org.apache.commons:commons-lang3:3.12.0 - org.apache.commons:commons-text:1.10.0 diff --git a/flink-filesystems/flink-s3-fs-presto/src/main/resources/META-INF/NOTICE b/flink-filesystems/flink-s3-fs-presto/src/main/resources/META-INF/NOTICE index 3356afa220538..fc834f1e4d467 100644 --- a/flink-filesystems/flink-s3-fs-presto/src/main/resources/META-INF/NOTICE +++ b/flink-filesystems/flink-s3-fs-presto/src/main/resources/META-INF/NOTICE @@ -36,7 +36,7 @@ This project bundles the following dependencies under the Apache Software Licens - io.airlift:units:1.3 - joda-time:joda-time:2.5 - org.alluxio:alluxio-shaded-client:2.7.3 -- org.apache.commons:commons-compress:1.21 +- org.apache.commons:commons-compress:1.24.0 - org.apache.commons:commons-configuration2:2.1.1 - org.apache.commons:commons-lang3:3.12.0 - org.apache.commons:commons-text:1.10.0 diff --git a/flink-formats/flink-avro/src/test/java/org/apache/flink/formats/avro/RegistryAvroDeserializationSchemaTest.java b/flink-formats/flink-avro/src/test/java/org/apache/flink/formats/avro/RegistryAvroDeserializationSchemaTest.java index c11c4bfb6b6f4..424e44817ee67 100644 --- a/flink-formats/flink-avro/src/test/java/org/apache/flink/formats/avro/RegistryAvroDeserializationSchemaTest.java +++ b/flink-formats/flink-avro/src/test/java/org/apache/flink/formats/avro/RegistryAvroDeserializationSchemaTest.java @@ -85,7 +85,7 @@ void testSpecificRecordReadMoreFieldsThanWereWritten() throws IOException { + " \"fields\": [\n" + " {\"name\": \"name\", \"type\": \"string\"}" + " ]\n" - + "}]"); + + "}"); RegistryAvroDeserializationSchema deserializer = new RegistryAvroDeserializationSchema<>( SimpleRecord.class, diff --git a/flink-formats/flink-sql-avro-confluent-registry/src/main/resources/META-INF/NOTICE b/flink-formats/flink-sql-avro-confluent-registry/src/main/resources/META-INF/NOTICE index dd601b9119f9e..2231d190de11d 100644 --- a/flink-formats/flink-sql-avro-confluent-registry/src/main/resources/META-INF/NOTICE +++ b/flink-formats/flink-sql-avro-confluent-registry/src/main/resources/META-INF/NOTICE @@ -13,8 +13,8 @@ This project bundles the following dependencies under the Apache Software Licens - io.confluent:common-config:7.2.2 - io.confluent:common-utils:7.2.2 - io.confluent:kafka-schema-registry-client:7.2.2 -- org.apache.avro:avro:1.11.1 -- org.apache.commons:commons-compress:1.21 +- org.apache.avro:avro:1.11.3 +- org.apache.commons:commons-compress:1.24.0 - org.apache.kafka:kafka-clients:7.2.2-ccs - org.glassfish.jersey.core:jersey-common:2.30 - org.xerial.snappy:snappy-java:1.1.10.4 diff --git a/flink-formats/flink-sql-avro/src/main/resources/META-INF/NOTICE b/flink-formats/flink-sql-avro/src/main/resources/META-INF/NOTICE index 21f85619d82fa..c0ca160784f8d 100644 --- a/flink-formats/flink-sql-avro/src/main/resources/META-INF/NOTICE +++ b/flink-formats/flink-sql-avro/src/main/resources/META-INF/NOTICE @@ -6,8 +6,8 @@ The Apache Software Foundation (http://www.apache.org/). This project bundles the following dependencies under the Apache Software License 2.0. (http://www.apache.org/licenses/LICENSE-2.0.txt) -- org.apache.avro:avro:1.11.1 +- org.apache.avro:avro:1.11.3 - com.fasterxml.jackson.core:jackson-core:2.14.3 - com.fasterxml.jackson.core:jackson-databind:2.14.3 - com.fasterxml.jackson.core:jackson-annotations:2.14.3 -- org.apache.commons:commons-compress:1.21 +- org.apache.commons:commons-compress:1.24.0 diff --git a/flink-libraries/flink-state-processing-api/src/main/java/org/apache/flink/state/api/input/MultiStateKeyIterator.java b/flink-libraries/flink-state-processing-api/src/main/java/org/apache/flink/state/api/input/MultiStateKeyIterator.java index ed219e69db592..d4ffbce264e7b 100644 --- a/flink-libraries/flink-state-processing-api/src/main/java/org/apache/flink/state/api/input/MultiStateKeyIterator.java +++ b/flink-libraries/flink-state-processing-api/src/main/java/org/apache/flink/state/api/input/MultiStateKeyIterator.java @@ -31,6 +31,8 @@ import java.io.IOException; import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; +import java.util.stream.Stream; /** * An iterator for reading all keys in a state backend across multiple partitioned states. @@ -46,7 +48,10 @@ public final class MultiStateKeyIterator implements CloseableIterator { private final KeyedStateBackend backend; - private final Iterator internal; + /** Avoids using Stream#flatMap due to a known flaw, see FLINK-26585 for more details. */ + private final Iterator> outerIter; + + private Iterator innerIter; private final CloseableRegistry registry; @@ -54,39 +59,43 @@ public final class MultiStateKeyIterator implements CloseableIterator { public MultiStateKeyIterator( List> descriptors, KeyedStateBackend backend) { + this.descriptors = Preconditions.checkNotNull(descriptors); this.backend = Preconditions.checkNotNull(backend); + outerIter = this.descriptors.iterator(); + innerIter = null; + this.registry = new CloseableRegistry(); - this.internal = - descriptors.stream() - .map( - descriptor -> - backend.getKeys( - descriptor.getName(), VoidNamespace.INSTANCE)) - .peek( - stream -> { - try { - registry.registerCloseable(stream::close); - } catch (IOException e) { - throw new RuntimeException( - "Failed to read keys from configured StateBackend", - e); - } - }) - .flatMap(stream -> stream) - .iterator(); } @Override public boolean hasNext() { - return internal.hasNext(); + while (innerIter == null || !innerIter.hasNext()) { + if (!outerIter.hasNext()) { + return false; + } + + StateDescriptor descriptor = outerIter.next(); + Stream stream = backend.getKeys(descriptor.getName(), VoidNamespace.INSTANCE); + innerIter = stream.iterator(); + try { + registry.registerCloseable(stream::close); + } catch (IOException e) { + throw new RuntimeException("Failed to read keys from configured StateBackend", e); + } + } + return true; } @Override public K next() { - currentKey = internal.next(); - return currentKey; + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + currentKey = this.innerIter.next(); + return currentKey; + } } /** Removes the current key from ALL known states in the state backend. */ diff --git a/flink-libraries/flink-state-processing-api/src/test/java/org/apache/flink/state/api/input/MultiStateKeyIteratorTest.java b/flink-libraries/flink-state-processing-api/src/test/java/org/apache/flink/state/api/input/MultiStateKeyIteratorTest.java index 33fcf2cea816d..e891c5583ed69 100644 --- a/flink-libraries/flink-state-processing-api/src/test/java/org/apache/flink/state/api/input/MultiStateKeyIteratorTest.java +++ b/flink-libraries/flink-state-processing-api/src/test/java/org/apache/flink/state/api/input/MultiStateKeyIteratorTest.java @@ -18,28 +18,57 @@ package org.apache.flink.state.api.input; +import org.apache.flink.api.common.ExecutionConfig; import org.apache.flink.api.common.JobID; +import org.apache.flink.api.common.state.State; import org.apache.flink.api.common.state.StateDescriptor; import org.apache.flink.api.common.state.ValueStateDescriptor; import org.apache.flink.api.common.typeinfo.Types; +import org.apache.flink.api.common.typeutils.TypeSerializer; import org.apache.flink.api.common.typeutils.base.IntSerializer; +import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.core.fs.CloseableRegistry; +import org.apache.flink.runtime.checkpoint.CheckpointOptions; +import org.apache.flink.runtime.execution.Environment; import org.apache.flink.runtime.metrics.groups.UnregisteredMetricGroups; import org.apache.flink.runtime.operators.testutils.DummyEnvironment; +import org.apache.flink.runtime.query.TaskKvStateRegistry; import org.apache.flink.runtime.state.AbstractKeyedStateBackend; +import org.apache.flink.runtime.state.CheckpointStreamFactory; import org.apache.flink.runtime.state.KeyGroupRange; +import org.apache.flink.runtime.state.KeyGroupedInternalPriorityQueue; +import org.apache.flink.runtime.state.Keyed; +import org.apache.flink.runtime.state.KeyedStateHandle; +import org.apache.flink.runtime.state.PriorityComparable; +import org.apache.flink.runtime.state.SavepointResources; +import org.apache.flink.runtime.state.SnapshotResult; +import org.apache.flink.runtime.state.StateSerializerProvider; +import org.apache.flink.runtime.state.StateSnapshotTransformer; import org.apache.flink.runtime.state.VoidNamespace; import org.apache.flink.runtime.state.VoidNamespaceSerializer; +import org.apache.flink.runtime.state.heap.HeapPriorityQueueElement; +import org.apache.flink.runtime.state.heap.InternalKeyContext; +import org.apache.flink.runtime.state.heap.InternalKeyContextImpl; +import org.apache.flink.runtime.state.metrics.LatencyTrackingStateConfig; import org.apache.flink.runtime.state.ttl.TtlTimeProvider; +import org.apache.flink.runtime.state.ttl.mock.MockRestoreOperation; import org.apache.flink.runtime.state.ttl.mock.MockStateBackend; import org.junit.Assert; import org.junit.Test; +import javax.annotation.Nonnull; + import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.RunnableFuture; +import java.util.stream.IntStream; +import java.util.stream.Stream; /** Test for the multi-state key iterator. */ public class MultiStateKeyIteratorTest { @@ -68,6 +97,37 @@ private static AbstractKeyedStateBackend createKeyedStateBackend() { new CloseableRegistry()); } + private static CountingKeysKeyedStateBackend createCountingKeysKeyedStateBackend( + Integer numKeys) { + Environment env = new DummyEnvironment(); + TypeSerializer keySerializer = IntSerializer.INSTANCE; + int numberOfKeyGroups = 129; + KeyGroupRange keyGroupRange = KeyGroupRange.of(0, 128); + TaskKvStateRegistry kvStateRegistry = null; + TtlTimeProvider ttlTimeProvider = TtlTimeProvider.DEFAULT; + @Nonnull Collection stateHandles = Collections.emptyList(); + CloseableRegistry cancelStreamRegistry = new CloseableRegistry(); + + Map>> stateValues = new HashMap<>(); + MockRestoreOperation restoreOperation = + new MockRestoreOperation<>(stateHandles, stateValues); + restoreOperation.restore(); + + StateSerializerProvider keySerializerProvider = + StateSerializerProvider.fromNewRegisteredSerializer(keySerializer); + + return new CountingKeysKeyedStateBackend( + numKeys, + kvStateRegistry, + keySerializerProvider.currentSchemaSerializer(), + env.getUserCodeClassLoader().asClassLoader(), + env.getExecutionConfig(), + ttlTimeProvider, + LatencyTrackingStateConfig.disabled(), + cancelStreamRegistry, + new InternalKeyContextImpl<>(keyGroupRange, numberOfKeyGroups)); + } + private static void setKey( AbstractKeyedStateBackend backend, ValueStateDescriptor descriptor, @@ -79,6 +139,17 @@ private static void setKey( .update(0); } + private static void clearKey( + AbstractKeyedStateBackend backend, + ValueStateDescriptor descriptor, + int key) + throws Exception { + backend.setCurrentKey(key); + backend.getPartitionedState( + VoidNamespace.INSTANCE, VoidNamespaceSerializer.INSTANCE, descriptor) + .clear(); + } + @Test public void testIteratorPullsKeyFromAllDescriptors() throws Exception { AbstractKeyedStateBackend keyedStateBackend = createKeyedStateBackend(); @@ -99,6 +170,36 @@ public void testIteratorPullsKeyFromAllDescriptors() throws Exception { Assert.assertEquals("Unexpected keys found", Arrays.asList(1, 2), keys); } + @Test + public void testIteratorSkipsEmptyDescriptors() throws Exception { + AbstractKeyedStateBackend keyedStateBackend = createKeyedStateBackend(); + + List> threeDescriptors = new ArrayList<>(3); + threeDescriptors.add(new ValueStateDescriptor<>("state-1", Types.INT)); + threeDescriptors.add(new ValueStateDescriptor<>("state-2", Types.INT)); + threeDescriptors.add(new ValueStateDescriptor<>("state-3", Types.INT)); + + setKey(keyedStateBackend, threeDescriptors.get(0), 1); + + // initializes descriptor 1, but empties it immediately after + setKey(keyedStateBackend, threeDescriptors.get(1), 12); + clearKey(keyedStateBackend, threeDescriptors.get(1), 12); + + setKey(keyedStateBackend, threeDescriptors.get(2), 2); + + MultiStateKeyIterator iterator = + new MultiStateKeyIterator<>(threeDescriptors, keyedStateBackend); + + List keys = new ArrayList<>(); + + while (iterator.hasNext()) { + keys.add(iterator.next()); + } + + Assert.assertEquals("Unexpected number of keys", 2, keys.size()); + Assert.assertEquals("Unexpected keys found", Arrays.asList(1, 2), keys); + } + @Test public void testIteratorRemovesFromAllDescriptors() throws Exception { AbstractKeyedStateBackend keyedStateBackend = createKeyedStateBackend(); @@ -125,4 +226,117 @@ public void testIteratorRemovesFromAllDescriptors() throws Exception { .count()); } } + + /** Test for lazy enumeration of inner iterators. */ + @Test + public void testIteratorPullsSingleKeyFromAllDescriptors() throws AssertionError { + CountingKeysKeyedStateBackend keyedStateBackend = + createCountingKeysKeyedStateBackend(100_000_000); + MultiStateKeyIterator testedIterator = + new MultiStateKeyIterator<>(descriptors, keyedStateBackend); + + testedIterator.hasNext(); + + Assert.assertEquals( + "Unexpected number of keys enumerated", + 1, + keyedStateBackend.numberOfKeysEnumerated); + } + + /** + * Mockup {@link AbstractKeyedStateBackend} that counts how many keys are enumerated. + * + *

Generates a configured number of integer keys, only method actually implemented is {@link + * CountingKeysKeyedStateBackend#getKeys(java.lang.String, java.lang.Object)} + */ + static class CountingKeysKeyedStateBackend extends AbstractKeyedStateBackend { + int numberOfKeysGenerated; + public long numberOfKeysEnumerated; + + public CountingKeysKeyedStateBackend( + int numberOfKeysGenerated, + TaskKvStateRegistry kvStateRegistry, + TypeSerializer keySerializer, + ClassLoader userCodeClassLoader, + ExecutionConfig executionConfig, + TtlTimeProvider ttlTimeProvider, + LatencyTrackingStateConfig latencyTrackingStateConfig, + CloseableRegistry cancelStreamRegistry, + InternalKeyContext keyContext) { + super( + kvStateRegistry, + keySerializer, + userCodeClassLoader, + executionConfig, + ttlTimeProvider, + latencyTrackingStateConfig, + cancelStreamRegistry, + keyContext); + this.numberOfKeysGenerated = numberOfKeysGenerated; + numberOfKeysEnumerated = 0; + } + + @Override + public Stream getKeys(String state, N namespace) { + return IntStream.range(0, this.numberOfKeysGenerated) + .boxed() + .peek(i -> numberOfKeysEnumerated++); + } + + @Override + public int numKeyValueStateEntries() { + return numberOfKeysGenerated; + } + + @Nonnull + @Override + public & Keyed> + KeyGroupedInternalPriorityQueue create( + @Nonnull String stateName, + @Nonnull TypeSerializer byteOrderedElementSerializer) { + throw new UnsupportedOperationException( + "Operations other than getKeys() are not supported on this testing StateBackend."); + } + + @Override + public void notifyCheckpointComplete(long checkpointId) {} + + @Nonnull + @Override + public RunnableFuture> snapshot( + long checkpointId, + long timestamp, + @Nonnull CheckpointStreamFactory streamFactory, + @Nonnull CheckpointOptions checkpointOptions) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Operations other than getKeys() are not supported on this testing StateBackend."); + } + + @Nonnull + @Override + public IS createOrUpdateInternalState( + @Nonnull TypeSerializer namespaceSerializer, + @Nonnull StateDescriptor stateDesc, + @Nonnull + StateSnapshotTransformer.StateSnapshotTransformFactory + snapshotTransformFactory) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Operations other than getKeys() are not supported on this testing StateBackend."); + } + + @Override + public Stream> getKeysAndNamespaces(String state) { + throw new UnsupportedOperationException( + "Operations other than getKeys() are not supported on this testing StateBackend."); + } + + @Nonnull + @Override + public SavepointResources savepoint() throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Operations other than getKeys() are not supported on this testing StateBackend."); + } + } } diff --git a/flink-metrics/flink-metrics-core/src/main/java/org/apache/flink/metrics/MeterView.java b/flink-metrics/flink-metrics-core/src/main/java/org/apache/flink/metrics/MeterView.java index ddb7e3c4fd30f..61a087cfde907 100644 --- a/flink-metrics/flink-metrics-core/src/main/java/org/apache/flink/metrics/MeterView.java +++ b/flink-metrics/flink-metrics-core/src/main/java/org/apache/flink/metrics/MeterView.java @@ -71,6 +71,10 @@ public MeterView(Counter counter, int timeSpanInSeconds) { this.values = new long[this.timeSpanInSeconds / UPDATE_INTERVAL_SECONDS + 1]; } + public MeterView(Gauge numberGauge) { + this(new GaugeWrapper(numberGauge)); + } + @Override public void markEvent() { this.counter.inc(); @@ -98,4 +102,39 @@ public void update() { currentRate = ((double) (values[time] - values[(time + 1) % values.length]) / timeSpanInSeconds); } + + /** Simple wrapper to expose number gauges as timers. */ + static class GaugeWrapper implements Counter { + + final Gauge numberGauge; + + GaugeWrapper(Gauge numberGauge) { + this.numberGauge = numberGauge; + } + + @Override + public void inc() { + throw new UnsupportedOperationException(); + } + + @Override + public void inc(long n) { + throw new UnsupportedOperationException(); + } + + @Override + public void dec() { + throw new UnsupportedOperationException(); + } + + @Override + public void dec(long n) { + throw new UnsupportedOperationException(); + } + + @Override + public long getCount() { + return numberGauge.getValue().longValue(); + } + } } diff --git a/flink-python/pyflink/examples/datastream/connectors/kafka_csv_format.py b/flink-python/pyflink/examples/datastream/connectors/kafka_csv_format.py index 4dbb243fcf984..39c134a8ed336 100644 --- a/flink-python/pyflink/examples/datastream/connectors/kafka_csv_format.py +++ b/flink-python/pyflink/examples/datastream/connectors/kafka_csv_format.py @@ -21,8 +21,7 @@ from pyflink.common import Types from pyflink.datastream import StreamExecutionEnvironment from pyflink.datastream.connectors.kafka import FlinkKafkaProducer, FlinkKafkaConsumer -from pyflink.datastream.formats.csv import CsvRowSerializationSchema -from pyflink.datastream.formats.json import JsonRowDeserializationSchema +from pyflink.datastream.formats.csv import CsvRowSerializationSchema, CsvRowDeserializationSchema # Make sure that the Kafka cluster is started and the topic 'test_csv_topic' is @@ -46,9 +45,8 @@ def write_to_kafka(env): def read_from_kafka(env): - deserialization_schema = JsonRowDeserializationSchema.Builder() \ - .type_info(Types.ROW([Types.INT(), Types.STRING()])) \ - .build() + type_info = Types.ROW([Types.INT(), Types.STRING()]) + deserialization_schema = CsvRowDeserializationSchema.Builder(type_info).build() kafka_consumer = FlinkKafkaConsumer( topics='test_csv_topic', diff --git a/flink-runtime-web/src/test/resources/rest_api_v1.snapshot b/flink-runtime-web/src/test/resources/rest_api_v1.snapshot index 62f8166917c1f..d6ba3eb2f5cd1 100644 --- a/flink-runtime-web/src/test/resources/rest_api_v1.snapshot +++ b/flink-runtime-web/src/test/resources/rest_api_v1.snapshot @@ -1971,6 +1971,9 @@ "location" : { "type" : "string" }, + "endpoint" : { + "type" : "string" + }, "timestamp" : { "type" : "integer" }, @@ -2014,6 +2017,9 @@ "location" : { "type" : "string" }, + "endpoint" : { + "type" : "string" + }, "taskManagerId" : { "type" : "string" }, @@ -2044,6 +2050,9 @@ "location" : { "type" : "string" }, + "endpoint" : { + "type" : "string" + }, "taskManagerId" : { "type" : "string" } @@ -2598,6 +2607,9 @@ "host" : { "type" : "string" }, + "endpoint" : { + "type" : "string" + }, "start-time" : { "type" : "integer" }, @@ -2941,6 +2953,9 @@ "host" : { "type" : "string" }, + "endpoint" : { + "type" : "string" + }, "user-accumulators" : { "type" : "array", "items" : { @@ -3033,6 +3048,9 @@ "host" : { "type" : "string" }, + "endpoint" : { + "type" : "string" + }, "start-time" : { "type" : "integer" }, @@ -3142,6 +3160,9 @@ "host" : { "type" : "string" }, + "endpoint" : { + "type" : "string" + }, "start-time" : { "type" : "integer" }, @@ -3338,6 +3359,9 @@ "host" : { "type" : "string" }, + "endpoint" : { + "type" : "string" + }, "duration" : { "type" : "integer" }, @@ -3393,6 +3417,9 @@ "host" : { "type" : "string" }, + "endpoint" : { + "type" : "string" + }, "status" : { "type" : "string", "enum" : [ "CREATED", "SCHEDULED", "DEPLOYING", "RUNNING", "FINISHED", "CANCELING", "CANCELED", "FAILED", "RECONCILING", "INITIALIZING" ] diff --git a/flink-runtime-web/web-dashboard/src/app/pages/job-manager/metrics/job-manager-metrics.component.ts b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/metrics/job-manager-metrics.component.ts index 8888fa314fe29..bb759e2694041 100644 --- a/flink-runtime-web/web-dashboard/src/app/pages/job-manager/metrics/job-manager-metrics.component.ts +++ b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/metrics/job-manager-metrics.component.ts @@ -112,7 +112,11 @@ export class JobManagerMetricsComponent implements OnInit, OnDestroy { this.listOfGCMetric = Array.from( new Set( this.listOfGCName.map(item => - item.replace('Status.JVM.GarbageCollector.', '').replace('.Count', '').replace('.Time', '') + item + .replace('Status.JVM.GarbageCollector.', '') + .replace('.Count', '') + .replace('.TimeMsPerSecond', '') + .replace('.Time', '') ) ) ).map(name => { diff --git a/flink-runtime-web/web-dashboard/src/app/pages/submit/submit.component.ts b/flink-runtime-web/web-dashboard/src/app/pages/submit/submit.component.ts index cfa74457a5455..8e14bcf82c1d4 100644 --- a/flink-runtime-web/web-dashboard/src/app/pages/submit/submit.component.ts +++ b/flink-runtime-web/web-dashboard/src/app/pages/submit/submit.component.ts @@ -78,7 +78,7 @@ export class SubmitComponent implements OnInit, OnDestroy { public validateForm: UntypedFormGroup; public planVisible = false; - @ViewChild(DagreComponent, { static: true }) private readonly dagreComponent: DagreComponent; + @ViewChild(DagreComponent) private readonly dagreComponent: DagreComponent; private readonly destroy$ = new Subject(); @@ -177,6 +177,7 @@ export class SubmitComponent implements OnInit, OnDestroy { ) .subscribe(data => { this.planVisible = true; + this.cdr.detectChanges(); this.dagreComponent.flush(data.nodes, data.links, true); }); } diff --git a/flink-runtime/pom.xml b/flink-runtime/pom.xml index 359a0e181cb7e..c6bd2e909644e 100644 --- a/flink-runtime/pom.xml +++ b/flink-runtime/pom.xml @@ -34,7 +34,14 @@ under the License. jar - + + ${surefire.module.config.jdk21} --add-opens=java.base/java.util=ALL-UNNAMED + -Djava.security.manager=allow + + diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/Checkpoint.java b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/Checkpoint.java index b8972875e2f4f..5fa6b08ff0f30 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/Checkpoint.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/Checkpoint.java @@ -17,6 +17,11 @@ package org.apache.flink.runtime.checkpoint; +import org.apache.flink.util.concurrent.FutureUtils; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + /** A checkpoint, pending or completed. */ public interface Checkpoint { DiscardObject NOOP_DISCARD_OBJECT = () -> {}; @@ -33,5 +38,9 @@ public interface Checkpoint { /** Extra interface for discarding the checkpoint. */ interface DiscardObject { void discard() throws Exception; + + default CompletableFuture discardAsync(Executor ioExecutor) { + return FutureUtils.runAsync(this::discard, ioExecutor); + } } } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/CheckpointsCleaner.java b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/CheckpointsCleaner.java index b67dfefac74f5..6d1bacaa83a66 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/CheckpointsCleaner.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/CheckpointsCleaner.java @@ -18,8 +18,9 @@ package org.apache.flink.runtime.checkpoint; +import org.apache.flink.configuration.CheckpointingOptions; import org.apache.flink.util.AutoCloseableAsync; -import org.apache.flink.util.function.RunnableWithException; +import org.apache.flink.util.concurrent.FutureUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,6 +48,7 @@ public class CheckpointsCleaner implements Serializable, AutoCloseableAsync { private static final Logger LOG = LoggerFactory.getLogger(CheckpointsCleaner.class); private static final long serialVersionUID = 2545865801947537790L; + private final boolean parallelMode; private final Object lock = new Object(); @GuardedBy("lock") @@ -60,6 +62,14 @@ public class CheckpointsCleaner implements Serializable, AutoCloseableAsync { @GuardedBy("lock") private final List subsumedCheckpoints = new ArrayList<>(); + public CheckpointsCleaner() { + this.parallelMode = CheckpointingOptions.CLEANER_PARALLEL_MODE.defaultValue(); + } + + public CheckpointsCleaner(boolean parallelMode) { + this.parallelMode = parallelMode; + } + int getNumberOfCheckpointsToClean() { synchronized (lock) { return numberOfCheckpointsToClean; @@ -71,10 +81,35 @@ public void cleanCheckpoint( boolean shouldDiscard, Runnable postCleanAction, Executor executor) { - Checkpoint.DiscardObject discardObject = - shouldDiscard ? checkpoint.markAsDiscarded() : Checkpoint.NOOP_DISCARD_OBJECT; + LOG.debug( + "Clean checkpoint {} parallel-mode={} shouldDiscard={}", + checkpoint.getCheckpointID(), + parallelMode, + shouldDiscard); + if (shouldDiscard) { + incrementNumberOfCheckpointsToClean(); + + Checkpoint.DiscardObject discardObject = checkpoint.markAsDiscarded(); + CompletableFuture discardFuture = + parallelMode + ? discardObject.discardAsync(executor) + : FutureUtils.runAsync(discardObject::discard, executor); + discardFuture.handle( + (Object outerIgnored, Throwable outerThrowable) -> { + if (outerThrowable != null) { + LOG.warn( + "Could not properly discard completed checkpoint {}.", + checkpoint.getCheckpointID(), + outerThrowable); + } - cleanup(checkpoint, discardObject::discard, postCleanAction, executor); + decrementNumberOfCheckpointsToClean(); + postCleanAction.run(); + return null; + }); + } else { + executor.execute(postCleanAction); + } } /** @@ -123,30 +158,7 @@ public void cleanSubsumedCheckpoints( public void cleanCheckpointOnFailedStoring( CompletedCheckpoint completedCheckpoint, Executor executor) { - Checkpoint.DiscardObject discardObject = completedCheckpoint.markAsDiscarded(); - cleanup(completedCheckpoint, discardObject::discard, () -> {}, executor); - } - - private void cleanup( - Checkpoint checkpoint, - RunnableWithException cleanupAction, - Runnable postCleanupAction, - Executor executor) { - incrementNumberOfCheckpointsToClean(); - executor.execute( - () -> { - try { - cleanupAction.run(); - } catch (Exception e) { - LOG.warn( - "Could not properly discard completed checkpoint {}.", - checkpoint.getCheckpointID(), - e); - } finally { - decrementNumberOfCheckpointsToClean(); - postCleanupAction.run(); - } - }); + cleanCheckpoint(completedCheckpoint, true, () -> {}, executor); } private void incrementNumberOfCheckpointsToClean() { diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/CompletedCheckpoint.java b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/CompletedCheckpoint.java index daee8986f10f6..3925f3ca2fe3a 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/CompletedCheckpoint.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/CompletedCheckpoint.java @@ -25,9 +25,12 @@ import org.apache.flink.runtime.jobgraph.RestoreMode; import org.apache.flink.runtime.state.CompletedCheckpointStorageLocation; import org.apache.flink.runtime.state.SharedStateRegistry; +import org.apache.flink.runtime.state.StateObject; import org.apache.flink.runtime.state.StateUtil; import org.apache.flink.runtime.state.StreamStateHandle; import org.apache.flink.util.ExceptionUtils; +import org.apache.flink.util.concurrent.FutureUtils; +import org.apache.flink.util.concurrent.FutureUtils.ConjunctFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,6 +46,9 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; import static org.apache.flink.util.Preconditions.checkArgument; import static org.apache.flink.util.Preconditions.checkNotNull; @@ -327,7 +333,6 @@ public String toString() { /** Implementation of {@link org.apache.flink.runtime.checkpoint.Checkpoint.DiscardObject}. */ @NotThreadSafe public class CompletedCheckpointDiscardObject implements DiscardObject { - @Override public void discard() throws Exception { LOG.trace("Executing discard procedure for {}.", this); @@ -371,5 +376,34 @@ public void discard() throws Exception { private boolean isMarkedAsDiscarded() { return completedCheckpointStats == null || completedCheckpointStats.isDiscarded(); } + + @Override + public CompletableFuture discardAsync(Executor ioExecutor) { + checkState( + isMarkedAsDiscarded(), + "Checkpoint should be marked as discarded before discard."); + + List discardables = + operatorStates.values().stream() + .flatMap(op -> op.getDiscardables().stream()) + .collect(Collectors.toList()); + discardables.add(metadataHandle); + + ConjunctFuture discardStates = + FutureUtils.completeAll( + discardables.stream() + .map( + item -> + FutureUtils.runAsync( + item::discardState, ioExecutor)) + .collect(Collectors.toList())); + + return FutureUtils.runAfterwards( + discardStates, + () -> { + operatorStates.clear(); + storageLocation.disposeStorageLocation(); + }); + } } } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/OperatorState.java b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/OperatorState.java index 462968fe160e9..2b6c4e06197c2 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/OperatorState.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/OperatorState.java @@ -21,6 +21,7 @@ import org.apache.flink.runtime.jobgraph.OperatorID; import org.apache.flink.runtime.state.CompositeStateHandle; import org.apache.flink.runtime.state.SharedStateRegistry; +import org.apache.flink.runtime.state.StateObject; import org.apache.flink.runtime.state.memory.ByteStreamStateHandle; import org.apache.flink.util.CollectionUtil; import org.apache.flink.util.Preconditions; @@ -29,8 +30,10 @@ import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; import static org.apache.flink.util.Preconditions.checkState; @@ -165,6 +168,18 @@ public OperatorState copyAndDiscardInFlightData() { return newState; } + public List getDiscardables() { + List toDispose = + operatorSubtaskStates.values().stream() + .flatMap(op -> op.getDiscardables().stream()) + .collect(Collectors.toList()); + + if (coordinatorState != null) { + toDispose.add(coordinatorState); + } + return toDispose; + } + @Override public void discardState() throws Exception { for (OperatorSubtaskState operatorSubtaskState : operatorSubtaskStates.values()) { diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/OperatorSubtaskState.java b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/OperatorSubtaskState.java index 8b0e2846e60d6..4fede9683e5bb 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/OperatorSubtaskState.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/OperatorSubtaskState.java @@ -194,22 +194,27 @@ public InflightDataRescalingDescriptor getOutputRescalingDescriptor() { return outputRescalingDescriptor; } + public List getDiscardables() { + List toDispose = + new ArrayList<>( + managedOperatorState.size() + + rawOperatorState.size() + + managedKeyedState.size() + + rawKeyedState.size() + + inputChannelState.size() + + resultSubpartitionState.size()); + toDispose.addAll(managedOperatorState); + toDispose.addAll(rawOperatorState); + toDispose.addAll(managedKeyedState); + toDispose.addAll(rawKeyedState); + toDispose.addAll(collectUniqueDelegates(inputChannelState, resultSubpartitionState)); + return toDispose; + } + @Override public void discardState() { try { - List toDispose = - new ArrayList<>( - managedOperatorState.size() - + rawOperatorState.size() - + managedKeyedState.size() - + rawKeyedState.size() - + inputChannelState.size() - + resultSubpartitionState.size()); - toDispose.addAll(managedOperatorState); - toDispose.addAll(rawOperatorState); - toDispose.addAll(managedKeyedState); - toDispose.addAll(rawKeyedState); - toDispose.addAll(collectUniqueDelegates(inputChannelState, resultSubpartitionState)); + List toDispose = getDiscardables(); StateUtil.bestEffortDiscardAllStateObjects(toDispose); } catch (Exception e) { LOG.warn("Error while discarding operator states.", e); diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/PendingCheckpoint.java b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/PendingCheckpoint.java index 9ec66bb95fe62..2f2cc6f6f6736 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/PendingCheckpoint.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/PendingCheckpoint.java @@ -29,11 +29,14 @@ import org.apache.flink.runtime.state.CheckpointMetadataOutputStream; import org.apache.flink.runtime.state.CheckpointStorageLocation; import org.apache.flink.runtime.state.CompletedCheckpointStorageLocation; +import org.apache.flink.runtime.state.StateObject; import org.apache.flink.runtime.state.StateUtil; import org.apache.flink.runtime.state.memory.ByteStreamStateHandle; import org.apache.flink.util.CollectionUtil; import org.apache.flink.util.ExceptionUtils; import org.apache.flink.util.Preconditions; +import org.apache.flink.util.concurrent.FutureUtils; +import org.apache.flink.util.concurrent.FutureUtils.ConjunctFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,6 +56,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledFuture; +import java.util.stream.Collectors; import static org.apache.flink.util.Preconditions.checkArgument; import static org.apache.flink.util.Preconditions.checkNotNull; @@ -652,5 +656,39 @@ public void discard() { operatorStates.clear(); } } + + @Override + public CompletableFuture discardAsync(Executor ioExecutor) { + synchronized (lock) { + if (discarded) { + Preconditions.checkState( + disposed, "Checkpoint should be disposed before being discarded"); + } else { + discarded = true; + } + } + List discardables = + operatorStates.values().stream() + .flatMap(op -> op.getDiscardables().stream()) + .collect(Collectors.toList()); + + ConjunctFuture discardStates = + FutureUtils.completeAll( + discardables.stream() + .map( + item -> + FutureUtils.runAsync( + item::discardState, ioExecutor)) + .collect(Collectors.toList())); + + return FutureUtils.runAfterwards( + discardStates, + () -> { + operatorStates.clear(); + if (targetLocation != null) { + targetLocation.disposeOnFailure(); + } + }); + } } } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/PrioritizedOperatorSubtaskState.java b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/PrioritizedOperatorSubtaskState.java index ef9bcd0440b12..e41bcfe73385b 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/PrioritizedOperatorSubtaskState.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/PrioritizedOperatorSubtaskState.java @@ -31,10 +31,14 @@ import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.stream.Collectors; /** * This class is a wrapper over multiple alternative {@link OperatorSubtaskState} that are (partial) @@ -286,14 +290,14 @@ public PrioritizedOperatorSubtaskState build() { } return new PrioritizedOperatorSubtaskState( - resolvePrioritizedAlternatives( + computePrioritizedAlternatives( jobManagerState.getManagedKeyedState(), managedKeyedAlternatives, - eqStateApprover(KeyedStateHandle::getKeyGroupRange)), - resolvePrioritizedAlternatives( + KeyedStateHandle::getKeyGroupRange), + computePrioritizedAlternatives( jobManagerState.getRawKeyedState(), rawKeyedAlternatives, - eqStateApprover(KeyedStateHandle::getKeyGroupRange)), + KeyedStateHandle::getKeyGroupRange), resolvePrioritizedAlternatives( jobManagerState.getManagedOperatorState(), managedOperatorAlternatives, @@ -313,22 +317,121 @@ public PrioritizedOperatorSubtaskState build() { restoredCheckpointId); } + /** + * This method creates an alternative recovery option by replacing as much job manager state + * with higher prioritized (=local) alternatives as possible. + * + * @param jobManagerState the state that the task got assigned from the job manager (this + * state lives in remote storage). + * @param alternativesByPriority local alternatives to the job manager state, ordered by + * priority. + * @param identityExtractor function to extract an identifier from a state object. + * @return prioritized state alternatives. + * @param the type of the state objects we process. + * @param the type of object that represents the id the state object type. + */ + + List> computePrioritizedAlternatives( + StateObjectCollection jobManagerState, + List> alternativesByPriority, + Function identityExtractor) { + + if (alternativesByPriority != null + && !alternativesByPriority.isEmpty() + && jobManagerState.hasState()) { + + Optional> mergedAlternative = + tryComputeMixedLocalAndRemoteAlternative( + jobManagerState, alternativesByPriority, identityExtractor); + + // Return the mix of local/remote state as first and pure remote state as second + // alternative (in case that we fail to recover from the local state, e.g. because + // of corruption). + if (mergedAlternative.isPresent()) { + return Arrays.asList(mergedAlternative.get(), jobManagerState); + } + } + + return Collections.singletonList(jobManagerState); + } + + /** + * This method creates an alternative recovery option by replacing as much job manager state + * with higher prioritized (=local) alternatives as possible. Returns empty Optional if the + * JM state is empty or nothing could be replaced. + * + * @param jobManagerState the state that the task got assigned from the job manager (this + * state lives in remote storage). + * @param alternativesByPriority local alternatives to the job manager state, ordered by + * priority. + * @param identityExtractor function to extract an identifier from a state object. + * @return A state collection where all JM state handles for which we could find local * + * alternatives are replaced by the alternative with the highest priority. Empty + * optional if no state could be replaced. + * @param the type of the state objects we process. + * @param the type of object that represents the id the state object type. + */ + static + Optional> + tryComputeMixedLocalAndRemoteAlternative( + StateObjectCollection jobManagerState, + List> alternativesByPriority, + Function identityExtractor) { + + List result = Collections.emptyList(); + + // Build hash index over ids of the JM state + Map indexById = + jobManagerState.stream() + .collect(Collectors.toMap(identityExtractor, Function.identity())); + + // Move through all alternative in order from high to low priority + for (StateObjectCollection alternative : alternativesByPriority) { + // Check all the state objects in the alternative if they can replace JM state + for (STATE_OBJ_TYPE stateHandle : alternative) { + // Remove the current state object's id from the index to check for a match + if (indexById.remove(identityExtractor.apply(stateHandle)) != null) { + if (result.isEmpty()) { + // Lazy init result collection + result = new ArrayList<>(jobManagerState.size()); + } + // If the id was still in the index, replace with higher prio alternative + result.add(stateHandle); + + // If the index is empty we are already done, all JM state was replaces with + // the best alternative. + if (indexById.isEmpty()) { + return Optional.of(new StateObjectCollection<>(result)); + } + } + } + } + + // Nothing useful to return + if (result.isEmpty()) { + return Optional.empty(); + } + + // Add all remaining JM state objects that we could not replace from the index to the + // final result + result.addAll(indexById.values()); + return Optional.of(new StateObjectCollection<>(result)); + } + /** * This helper method resolves the dependencies between the ground truth of the operator * state obtained from the job manager and potential alternatives for recovery, e.g. from a * task-local source. */ - protected - List> resolvePrioritizedAlternatives( - StateObjectCollection jobManagerState, - List> alternativesByPriority, - BiFunction approveFun) { + List> resolvePrioritizedAlternatives( + StateObjectCollection jobManagerState, + List> alternativesByPriority, + BiFunction approveFun) { // Nothing to resolve if there are no alternatives, or the ground truth has already no - // state, or if we can - // assume that a rescaling happened because we find more than one handle in the JM state - // (this is more a sanity - // check). + // state, or if we can assume that a rescaling happened because we find more than one + // handle in the JM state + // (this is more a sanity check). if (alternativesByPriority == null || alternativesByPriority.isEmpty() || !jobManagerState.hasState() @@ -347,8 +450,7 @@ List> resolvePrioritizedAlternatives( for (StateObjectCollection alternative : alternativesByPriority) { // We found an alternative to the JM state if it has state, we have a 1:1 - // relationship, and the - // approve-function signaled true. + // relationship, and the approve-function signaled true. if (alternative != null && alternative.hasState() && alternative.size() == 1 diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/metadata/MetadataV2V3SerializerBase.java b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/metadata/MetadataV2V3SerializerBase.java index db3b5dd22d0e7..bd91e8e1fbb4d 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/metadata/MetadataV2V3SerializerBase.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/checkpoint/metadata/MetadataV2V3SerializerBase.java @@ -341,7 +341,7 @@ static void serializeKeyedStateHandle(KeyedStateHandle stateHandle, DataOutputSt dos.writeInt(incrementalKeyedStateHandle.getKeyGroupRange().getNumberOfKeyGroups()); dos.writeLong(incrementalKeyedStateHandle.getCheckpointedSize()); - serializeStreamStateHandle(incrementalKeyedStateHandle.getMetaStateHandle(), dos); + serializeStreamStateHandle(incrementalKeyedStateHandle.getMetaDataStateHandle(), dos); serializeHandleAndLocalPathList(incrementalKeyedStateHandle.getSharedState(), dos); serializeHandleAndLocalPathList(incrementalKeyedStateHandle.getPrivateState(), dos); diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/deployment/InputGateDeploymentDescriptor.java b/flink-runtime/src/main/java/org/apache/flink/runtime/deployment/InputGateDeploymentDescriptor.java index 50ed98ed1dcf7..333a91e0a7320 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/deployment/InputGateDeploymentDescriptor.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/deployment/InputGateDeploymentDescriptor.java @@ -33,7 +33,7 @@ import org.apache.flink.runtime.jobgraph.DistributionPattern; import org.apache.flink.runtime.jobgraph.IntermediateDataSetID; import org.apache.flink.runtime.shuffle.ShuffleDescriptor; -import org.apache.flink.runtime.taskexecutor.ShuffleDescriptorsCache; +import org.apache.flink.runtime.util.GroupCache; import org.apache.flink.util.CompressedSerializedValue; import org.apache.flink.util.Preconditions; @@ -167,7 +167,7 @@ public ShuffleDescriptor[] getShuffleDescriptors() { public void tryLoadAndDeserializeShuffleDescriptors( @Nullable PermanentBlobService blobService, JobID jobId, - ShuffleDescriptorsCache shuffleDescriptorsCache) + GroupCache shuffleDescriptorsCache) throws IOException { if (inputChannels != null) { return; @@ -190,13 +190,14 @@ private void tryLoadAndDeserializeShuffleDescriptorGroup( @Nullable PermanentBlobService blobService, JobID jobId, MaybeOffloaded serializedShuffleDescriptors, - ShuffleDescriptorsCache shuffleDescriptorsCache) + GroupCache shuffleDescriptorsCache) throws IOException, ClassNotFoundException { if (serializedShuffleDescriptors instanceof Offloaded) { PermanentBlobKey blobKey = ((Offloaded) serializedShuffleDescriptors) .serializedValueKey; - ShuffleDescriptorGroup shuffleDescriptorGroup = shuffleDescriptorsCache.get(blobKey); + ShuffleDescriptorGroup shuffleDescriptorGroup = + shuffleDescriptorsCache.get(jobId, blobKey); if (shuffleDescriptorGroup == null) { Preconditions.checkNotNull(blobService); // NOTE: Do not delete the ShuffleDescriptor BLOBs since it may be needed again diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/deployment/TaskDeploymentDescriptor.java b/flink-runtime/src/main/java/org/apache/flink/runtime/deployment/TaskDeploymentDescriptor.java index bd3b770142c01..5684066735f03 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/deployment/TaskDeploymentDescriptor.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/deployment/TaskDeploymentDescriptor.java @@ -23,19 +23,22 @@ import org.apache.flink.runtime.blob.PermanentBlobService; import org.apache.flink.runtime.checkpoint.JobManagerTaskRestore; import org.apache.flink.runtime.clusterframework.types.AllocationID; +import org.apache.flink.runtime.deployment.TaskDeploymentDescriptorFactory.ShuffleDescriptorGroup; import org.apache.flink.runtime.executiongraph.ExecutionAttemptID; import org.apache.flink.runtime.executiongraph.JobInformation; import org.apache.flink.runtime.executiongraph.TaskInformation; -import org.apache.flink.runtime.taskexecutor.ShuffleDescriptorsCache; -import org.apache.flink.util.FileUtils; +import org.apache.flink.runtime.util.GroupCache; +import org.apache.flink.util.InstantiationUtil; import org.apache.flink.util.Preconditions; import org.apache.flink.util.SerializedValue; import javax.annotation.Nullable; +import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.Serializable; +import java.nio.file.Files; import java.util.List; /** @@ -97,11 +100,23 @@ public Offloaded(PermanentBlobKey serializedValueKey) { } } - /** Serialized job information or null if offloaded. */ - private MaybeOffloaded serializedJobInformation; + /** Serialized job information if non-offloaded or PermanentBlobKey if offloaded. */ + private final MaybeOffloaded serializedJobInformation; - /** Serialized task information or null if offloaded. */ - private MaybeOffloaded serializedTaskInformation; + /** Serialized task information if non-offloaded or PermanentBlobKey if offloaded. */ + private final MaybeOffloaded serializedTaskInformation; + + /** + * The job information, it isn't null when serializedJobInformation is offloaded and after + * {@link #loadBigData}. + */ + private transient JobInformation jobInformation; + + /** + * The task information, it isn't null when serializedTaskInformation is offloaded and after + * {@link #loadBigData}. + */ + private transient TaskInformation taskInformation; /** * The ID referencing the job this task belongs to. @@ -151,39 +166,43 @@ public TaskDeploymentDescriptor( } /** - * Return the sub task's serialized job information. + * Return the sub task's job information. * - * @return serialized job information (may throw {@link IllegalStateException} if {@link - * #loadBigData} is not called beforehand). + * @return job information (may throw {@link IllegalStateException} if {@link #loadBigData} is + * not called beforehand). * @throws IllegalStateException If job information is offloaded to BLOB store. */ - public SerializedValue getSerializedJobInformation() { + public JobInformation getJobInformation() throws IOException, ClassNotFoundException { + if (jobInformation != null) { + return jobInformation; + } if (serializedJobInformation instanceof NonOffloaded) { NonOffloaded jobInformation = (NonOffloaded) serializedJobInformation; - return jobInformation.serializedValue; - } else { - throw new IllegalStateException( - "Trying to work with offloaded serialized job information."); + return jobInformation.serializedValue.deserializeValue(getClass().getClassLoader()); } + throw new IllegalStateException( + "Trying to work with offloaded serialized job information."); } /** - * Return the sub task's serialized task information. + * Return the sub task's task information. * - * @return serialized task information (may throw {@link IllegalStateException} if {@link - * #loadBigData} is not called beforehand)). + * @return task information (may throw {@link IllegalStateException} if {@link #loadBigData} is + * not called beforehand)). * @throws IllegalStateException If job information is offloaded to BLOB store. */ - public SerializedValue getSerializedTaskInformation() { + public TaskInformation getTaskInformation() throws IOException, ClassNotFoundException { + if (taskInformation != null) { + return taskInformation; + } if (serializedTaskInformation instanceof NonOffloaded) { NonOffloaded taskInformation = (NonOffloaded) serializedTaskInformation; - return taskInformation.serializedValue; - } else { - throw new IllegalStateException( - "Trying to work with offloaded serialized job information."); + return taskInformation.serializedValue.deserializeValue(getClass().getClassLoader()); } + throw new IllegalStateException( + "Trying to work with offloaded serialized task information."); } /** @@ -242,7 +261,9 @@ public AllocationID getAllocationId() { */ public void loadBigData( @Nullable PermanentBlobService blobService, - ShuffleDescriptorsCache shuffleDescriptorsCache) + GroupCache jobInformationCache, + GroupCache taskInformationCache, + GroupCache shuffleDescriptorsCache) throws IOException, ClassNotFoundException { // re-integrate offloaded job info from blob @@ -253,13 +274,19 @@ public void loadBigData( Preconditions.checkNotNull(blobService); - final File dataFile = blobService.getFile(jobId, jobInfoKey); - // NOTE: Do not delete the job info BLOB since it may be needed again during recovery. - // (it is deleted automatically on the BLOB server and cache when the job - // enters a terminal state) - SerializedValue serializedValue = - SerializedValue.fromBytes(FileUtils.readAllBytes(dataFile.toPath())); - serializedJobInformation = new NonOffloaded<>(serializedValue); + JobInformation jobInformation = jobInformationCache.get(jobId, jobInfoKey); + if (jobInformation == null) { + final File dataFile = blobService.getFile(jobId, jobInfoKey); + // NOTE: Do not delete the job info BLOB since it may be needed again during + // recovery. (it is deleted automatically on the BLOB server and cache when the job + // enters a terminal state) + jobInformation = + InstantiationUtil.deserializeObject( + new BufferedInputStream(Files.newInputStream(dataFile.toPath())), + getClass().getClassLoader()); + jobInformationCache.put(jobId, jobInfoKey, jobInformation); + } + this.jobInformation = jobInformation.deepCopy(); } // re-integrate offloaded task info from blob @@ -269,23 +296,25 @@ public void loadBigData( Preconditions.checkNotNull(blobService); - final File dataFile = blobService.getFile(jobId, taskInfoKey); - // NOTE: Do not delete the task info BLOB since it may be needed again during recovery. - // (it is deleted automatically on the BLOB server and cache when the job - // enters a terminal state) - SerializedValue serializedValue = - SerializedValue.fromBytes(FileUtils.readAllBytes(dataFile.toPath())); - serializedTaskInformation = new NonOffloaded<>(serializedValue); + TaskInformation taskInformation = taskInformationCache.get(jobId, taskInfoKey); + if (taskInformation == null) { + final File dataFile = blobService.getFile(jobId, taskInfoKey); + // NOTE: Do not delete the task info BLOB since it may be needed again during + // recovery. (it is deleted automatically on the BLOB server and cache when the job + // enters a terminal state) + taskInformation = + InstantiationUtil.deserializeObject( + new BufferedInputStream(Files.newInputStream(dataFile.toPath())), + getClass().getClassLoader()); + taskInformationCache.put(jobId, taskInfoKey, taskInformation); + } + this.taskInformation = taskInformation.deepCopy(); } for (InputGateDeploymentDescriptor inputGate : inputGates) { inputGate.tryLoadAndDeserializeShuffleDescriptors( blobService, jobId, shuffleDescriptorsCache); } - - // make sure that the serialized job and task information fields are filled - Preconditions.checkNotNull(serializedJobInformation); - Preconditions.checkNotNull(serializedTaskInformation); } @Override diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/dispatcher/cleanup/CheckpointResourcesCleanupRunner.java b/flink-runtime/src/main/java/org/apache/flink/runtime/dispatcher/cleanup/CheckpointResourcesCleanupRunner.java index e657c8d740012..a734294ad9dee 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/dispatcher/cleanup/CheckpointResourcesCleanupRunner.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/dispatcher/cleanup/CheckpointResourcesCleanupRunner.java @@ -21,6 +21,7 @@ import org.apache.flink.api.common.JobID; import org.apache.flink.api.common.JobStatus; import org.apache.flink.api.common.time.Time; +import org.apache.flink.configuration.CheckpointingOptions; import org.apache.flink.configuration.Configuration; import org.apache.flink.runtime.checkpoint.CheckpointIDCounter; import org.apache.flink.runtime.checkpoint.CheckpointRecoveryFactory; @@ -88,7 +89,10 @@ public CheckpointResourcesCleanupRunner( this.cleanupExecutor = Preconditions.checkNotNull(cleanupExecutor); this.initializationTimestamp = initializationTimestamp; - this.checkpointsCleaner = new CheckpointsCleaner(); + this.checkpointsCleaner = + new CheckpointsCleaner( + jobManagerConfiguration.getBoolean( + CheckpointingOptions.CLEANER_PARALLEL_MODE)); this.resultFuture = new CompletableFuture<>(); this.cleanupFuture = resultFuture.thenCompose(ignored -> runCleanupAsync()); diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/JobInformation.java b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/JobInformation.java index f30bf6f32d214..5792caa271153 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/JobInformation.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/JobInformation.java @@ -21,13 +21,18 @@ import org.apache.flink.api.common.ExecutionConfig; import org.apache.flink.api.common.JobID; import org.apache.flink.configuration.Configuration; +import org.apache.flink.configuration.UnmodifiableConfiguration; import org.apache.flink.runtime.blob.PermanentBlobKey; import org.apache.flink.util.Preconditions; import org.apache.flink.util.SerializedValue; +import org.apache.flink.shaded.guava31.com.google.common.collect.ImmutableCollection; +import org.apache.flink.shaded.guava31.com.google.common.collect.ImmutableList; + import java.io.Serializable; import java.net.URL; import java.util.Collection; +import java.util.Objects; /** Container class for job information which is stored in the {@link ExecutionGraph}. */ public class JobInformation implements Serializable { @@ -44,13 +49,13 @@ public class JobInformation implements Serializable { private final SerializedValue serializedExecutionConfig; /** Configuration of the job. */ - private final Configuration jobConfiguration; + private final UnmodifiableConfiguration jobConfiguration; /** Blob keys for the required jar files. */ - private final Collection requiredJarFileBlobKeys; + private final ImmutableCollection requiredJarFileBlobKeys; /** URLs specifying the classpath to add to the class loader. */ - private final Collection requiredClasspathURLs; + private final ImmutableCollection requiredClasspathURLs; public JobInformation( JobID jobId, @@ -62,9 +67,12 @@ public JobInformation( this.jobId = Preconditions.checkNotNull(jobId); this.jobName = Preconditions.checkNotNull(jobName); this.serializedExecutionConfig = Preconditions.checkNotNull(serializedExecutionConfig); - this.jobConfiguration = Preconditions.checkNotNull(jobConfiguration); - this.requiredJarFileBlobKeys = Preconditions.checkNotNull(requiredJarFileBlobKeys); - this.requiredClasspathURLs = Preconditions.checkNotNull(requiredClasspathURLs); + this.jobConfiguration = + new UnmodifiableConfiguration(Preconditions.checkNotNull(jobConfiguration)); + this.requiredJarFileBlobKeys = + ImmutableList.copyOf(Preconditions.checkNotNull(requiredJarFileBlobKeys)); + this.requiredClasspathURLs = + ImmutableList.copyOf(Preconditions.checkNotNull(requiredClasspathURLs)); } public JobID getJobId() { @@ -79,18 +87,51 @@ public SerializedValue getSerializedExecutionConfig() { return serializedExecutionConfig; } - public Configuration getJobConfiguration() { + public UnmodifiableConfiguration getJobConfiguration() { return jobConfiguration; } - public Collection getRequiredJarFileBlobKeys() { + public ImmutableCollection getRequiredJarFileBlobKeys() { return requiredJarFileBlobKeys; } - public Collection getRequiredClasspathURLs() { + public ImmutableCollection getRequiredClasspathURLs() { return requiredClasspathURLs; } + // All fields are immutable, so return this directly. + public JobInformation deepCopy() { + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + JobInformation that = (JobInformation) o; + return Objects.equals(jobId, that.jobId) + && Objects.equals(jobName, that.jobName) + && Objects.equals(serializedExecutionConfig, that.serializedExecutionConfig) + && Objects.equals(jobConfiguration, that.jobConfiguration) + && Objects.equals(requiredJarFileBlobKeys, that.requiredJarFileBlobKeys) + && Objects.equals(requiredClasspathURLs, that.requiredClasspathURLs); + } + + @Override + public int hashCode() { + return Objects.hash( + jobId, + jobName, + serializedExecutionConfig, + jobConfiguration, + requiredJarFileBlobKeys, + requiredClasspathURLs); + } + // ------------------------------------------------------------------------ @Override diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/TaskInformation.java b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/TaskInformation.java index 6a3b1a26e7f0b..e1b59d4bfb12f 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/TaskInformation.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/executiongraph/TaskInformation.java @@ -23,6 +23,7 @@ import org.apache.flink.util.Preconditions; import java.io.Serializable; +import java.util.Objects; /** * Container class for operator/task specific information which are stored at the {@link @@ -88,4 +89,43 @@ public String getInvokableClassName() { public Configuration getTaskConfiguration() { return taskConfiguration; } + + public TaskInformation deepCopy() { + return new TaskInformation( + getJobVertexId(), + getTaskName(), + getNumberOfSubtasks(), + getMaxNumberOfSubtasks(), + getInvokableClassName(), + // Return the new Configuration to avoid shared conf being changed. + new Configuration(getTaskConfiguration())); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TaskInformation that = (TaskInformation) o; + return numberOfSubtasks == that.numberOfSubtasks + && maxNumberOfSubtasks == that.maxNumberOfSubtasks + && Objects.equals(jobVertexId, that.jobVertexId) + && Objects.equals(taskName, that.taskName) + && Objects.equals(invokableClassName, that.invokableClassName) + && Objects.equals(taskConfiguration, that.taskConfiguration); + } + + @Override + public int hashCode() { + return Objects.hash( + jobVertexId, + taskName, + numberOfSubtasks, + maxNumberOfSubtasks, + invokableClassName, + taskConfiguration); + } } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/NettyShuffleEnvironment.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/NettyShuffleEnvironment.java index 2faff9650d3e9..f3ae8ff7d2b42 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/NettyShuffleEnvironment.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/NettyShuffleEnvironment.java @@ -61,6 +61,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import static org.apache.flink.runtime.io.network.metrics.NettyShuffleMetricFactory.METRIC_GROUP_INPUT; import static org.apache.flink.runtime.io.network.metrics.NettyShuffleMetricFactory.METRIC_GROUP_OUTPUT; @@ -68,6 +69,7 @@ import static org.apache.flink.runtime.io.network.metrics.NettyShuffleMetricFactory.registerDebloatingTaskMetrics; import static org.apache.flink.runtime.io.network.metrics.NettyShuffleMetricFactory.registerInputMetrics; import static org.apache.flink.runtime.io.network.metrics.NettyShuffleMetricFactory.registerOutputMetrics; +import static org.apache.flink.util.ExecutorUtils.gracefulShutdown; import static org.apache.flink.util.Preconditions.checkArgument; import static org.apache.flink.util.Preconditions.checkNotNull; @@ -409,15 +411,15 @@ public void close() { } try { - batchShuffleReadBufferPool.destroy(); + gracefulShutdown(10, TimeUnit.SECONDS, batchShuffleReadIOExecutor); } catch (Throwable t) { - LOG.warn("Cannot shut down batch shuffle read buffer pool properly.", t); + LOG.warn("Cannot shut down batch shuffle read IO executor properly.", t); } try { - batchShuffleReadIOExecutor.shutdown(); + batchShuffleReadBufferPool.destroy(); } catch (Throwable t) { - LOG.warn("Cannot shut down batch shuffle read IO executor properly.", t); + LOG.warn("Cannot shut down batch shuffle read buffer pool properly.", t); } isClosed = true; diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/NettyShuffleServiceFactory.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/NettyShuffleServiceFactory.java index fe71f47762b08..7f708b45188cc 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/NettyShuffleServiceFactory.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/NettyShuffleServiceFactory.java @@ -43,6 +43,7 @@ import org.apache.flink.runtime.shuffle.ShuffleServiceFactory; import org.apache.flink.runtime.taskmanager.NettyShuffleEnvironmentConfiguration; import org.apache.flink.util.concurrent.ExecutorThreadFactory; +import org.apache.flink.util.concurrent.ScheduledExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -86,6 +87,7 @@ public NettyShuffleEnvironment createShuffleEnvironment( shuffleEnvironmentContext.getEventPublisher(), shuffleEnvironmentContext.getParentMetricGroup(), shuffleEnvironmentContext.getIoExecutor(), + shuffleEnvironmentContext.getScheduledExecutor(), shuffleEnvironmentContext.getNumberOfSlots(), shuffleEnvironmentContext.getTmpDirPaths()); } @@ -97,13 +99,15 @@ static NettyShuffleEnvironment createNettyShuffleEnvironment( TaskEventPublisher taskEventPublisher, MetricGroup metricGroup, Executor ioExecutor, + ScheduledExecutor scheduledExecutor, int numberOfSlots, String[] tmpDirPaths) { return createNettyShuffleEnvironment( config, taskExecutorResourceId, taskEventPublisher, - new ResultPartitionManager(), + new ResultPartitionManager( + config.getPartitionRequestListenerTimeout(), scheduledExecutor), metricGroup, ioExecutor, numberOfSlots, diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/NetworkSequenceViewReader.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/NetworkSequenceViewReader.java index fc317ef2b023a..665dc1b975009 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/NetworkSequenceViewReader.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/NetworkSequenceViewReader.java @@ -18,7 +18,11 @@ package org.apache.flink.runtime.io.network; +import org.apache.flink.runtime.io.network.partition.PartitionNotFoundException; +import org.apache.flink.runtime.io.network.partition.PartitionRequestListener; +import org.apache.flink.runtime.io.network.partition.ResultPartition; import org.apache.flink.runtime.io.network.partition.ResultPartitionID; +import org.apache.flink.runtime.io.network.partition.ResultPartitionManager; import org.apache.flink.runtime.io.network.partition.ResultPartitionProvider; import org.apache.flink.runtime.io.network.partition.ResultSubpartitionView; import org.apache.flink.runtime.io.network.partition.consumer.InputChannel.BufferAndAvailability; @@ -34,12 +38,36 @@ */ public interface NetworkSequenceViewReader { - void requestSubpartitionView( + /** + * When the netty server receives the downstream task's partition request and the upstream task + * has registered its partition, it will process the partition request immediately, otherwise it + * will create a {@link PartitionRequestListener} for given {@link ResultPartitionID} in {@link + * ResultPartitionManager} and notify the listener when the upstream task registers its + * partition. + * + * @param partitionProvider the result partition provider + * @param resultPartitionId the result partition id + * @param subPartitionIndex the sub partition index + * @throws IOException the thrown exception + */ + void requestSubpartitionViewOrRegisterListener( ResultPartitionProvider partitionProvider, ResultPartitionID resultPartitionId, int subPartitionIndex) throws IOException; + /** + * When the {@link ResultPartitionManager} registers {@link ResultPartition}, it will get the + * {@link PartitionRequestListener} via given {@link ResultPartitionID}, and create subpartition + * view reader for downstream task. + * + * @param partition the result partition + * @param subPartitionIndex the sub partition index + * @throws IOException the thrown exception + */ + void notifySubpartitionCreated(ResultPartition partition, int subPartitionIndex) + throws IOException; + @Nullable BufferAndAvailability getNextBuffer() throws IOException; @@ -91,4 +119,12 @@ void requestSubpartitionView( InputChannelID getReceiverId(); void notifyNewBufferSize(int newBufferSize); + + /** + * When the partition request from the given downstream task is timeout, it should notify the + * reader in netty server and send {@link PartitionNotFoundException} to the task. + * + * @param partitionRequestListener the timeout message of given {@link PartitionRequestListener} + */ + void notifyPartitionRequestTimeout(PartitionRequestListener partitionRequestListener); } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/netty/CreditBasedSequenceNumberingViewReader.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/netty/CreditBasedSequenceNumberingViewReader.java index 4aed5e138df2d..c12db284c13c5 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/netty/CreditBasedSequenceNumberingViewReader.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/netty/CreditBasedSequenceNumberingViewReader.java @@ -22,6 +22,8 @@ import org.apache.flink.runtime.io.network.NetworkSequenceViewReader; import org.apache.flink.runtime.io.network.buffer.Buffer; import org.apache.flink.runtime.io.network.partition.BufferAvailabilityListener; +import org.apache.flink.runtime.io.network.partition.PartitionRequestListener; +import org.apache.flink.runtime.io.network.partition.ResultPartition; import org.apache.flink.runtime.io.network.partition.ResultPartitionID; import org.apache.flink.runtime.io.network.partition.ResultPartitionProvider; import org.apache.flink.runtime.io.network.partition.ResultSubpartition.BufferAndBacklog; @@ -33,8 +35,10 @@ import javax.annotation.Nullable; import java.io.IOException; +import java.util.Optional; import static org.apache.flink.util.Preconditions.checkArgument; +import static org.apache.flink.util.Preconditions.checkState; /** * Simple wrapper for the subpartition view used in the new network credit-based mode. @@ -55,6 +59,8 @@ class CreditBasedSequenceNumberingViewReader private volatile ResultSubpartitionView subpartitionView; + private volatile PartitionRequestListener partitionRequestListener; + /** * The status indicating whether this reader is already enqueued in the pipeline for * transferring data or not. @@ -78,27 +84,47 @@ class CreditBasedSequenceNumberingViewReader } @Override - public void requestSubpartitionView( + public void requestSubpartitionViewOrRegisterListener( ResultPartitionProvider partitionProvider, ResultPartitionID resultPartitionId, int subPartitionIndex) throws IOException { - synchronized (requestLock) { - if (subpartitionView == null) { - // This call can trigger a notification we have to - // schedule a separate task at the event loop that will - // start consuming this. Otherwise the reference to the - // view cannot be available in getNextBuffer(). - this.subpartitionView = - partitionProvider.createSubpartitionView( - resultPartitionId, subPartitionIndex, this); + checkState(subpartitionView == null, "Subpartition already requested"); + checkState( + partitionRequestListener == null, "Partition request listener already created"); + partitionRequestListener = + new NettyPartitionRequestListener( + partitionProvider, this, subPartitionIndex, resultPartitionId); + // The partition provider will create subpartitionView if resultPartition is + // registered, otherwise it will register a listener of partition request to the result + // partition manager. + Optional subpartitionViewOptional = + partitionProvider.createSubpartitionViewOrRegisterListener( + resultPartitionId, subPartitionIndex, this, partitionRequestListener); + if (subpartitionViewOptional.isPresent()) { + this.subpartitionView = subpartitionViewOptional.get(); } else { - throw new IllegalStateException("Subpartition already requested"); + // If the subpartitionView is not exist, it means that the requested partition is + // not registered. + return; } } notifyDataAvailable(); + requestQueue.notifyReaderCreated(this); + } + + @Override + public void notifySubpartitionCreated(ResultPartition partition, int subPartitionIndex) + throws IOException { + synchronized (requestLock) { + checkState(subpartitionView == null, "Subpartition already requested"); + subpartitionView = partition.createSubpartitionView(subPartitionIndex, this); + } + + notifyDataAvailable(); + requestQueue.notifyReaderCreated(this); } @Override @@ -182,6 +208,12 @@ public void notifyNewBufferSize(int newBufferSize) { subpartitionView.notifyNewBufferSize(newBufferSize); } + @Override + public void notifyPartitionRequestTimeout(PartitionRequestListener partitionRequestListener) { + requestQueue.notifyPartitionRequestTimeout(partitionRequestListener); + this.partitionRequestListener = null; + } + @VisibleForTesting int getNumCreditsAvailable() { return numCreditsAvailable; @@ -226,6 +258,9 @@ public Throwable getFailureCause() { @Override public void releaseAllResources() throws IOException { + if (partitionRequestListener != null) { + partitionRequestListener.releaseListener(); + } subpartitionView.releaseAllResources(); } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/netty/NettyPartitionRequestListener.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/netty/NettyPartitionRequestListener.java new file mode 100644 index 0000000000000..b09756bf22d64 --- /dev/null +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/netty/NettyPartitionRequestListener.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.runtime.io.network.netty; + +import org.apache.flink.annotation.VisibleForTesting; +import org.apache.flink.runtime.io.network.NetworkSequenceViewReader; +import org.apache.flink.runtime.io.network.partition.PartitionRequestListener; +import org.apache.flink.runtime.io.network.partition.ResultPartition; +import org.apache.flink.runtime.io.network.partition.ResultPartitionID; +import org.apache.flink.runtime.io.network.partition.ResultPartitionProvider; +import org.apache.flink.runtime.io.network.partition.consumer.InputChannelID; + +import java.io.IOException; + +import static org.apache.flink.util.Preconditions.checkNotNull; + +/** Implementation of {@link PartitionRequestListener} for netty partition request. */ +public class NettyPartitionRequestListener implements PartitionRequestListener { + private final ResultPartitionProvider resultPartitionProvider; + private final NetworkSequenceViewReader reader; + private final int subPartitionIndex; + private final ResultPartitionID resultPartitionId; + private final long createTimestamp; + + public NettyPartitionRequestListener( + ResultPartitionProvider resultPartitionProvider, + NetworkSequenceViewReader reader, + int subPartitionIndex, + ResultPartitionID resultPartitionId) { + this( + resultPartitionProvider, + reader, + subPartitionIndex, + resultPartitionId, + System.currentTimeMillis()); + } + + @VisibleForTesting + public NettyPartitionRequestListener( + ResultPartitionProvider resultPartitionProvider, + NetworkSequenceViewReader reader, + int subPartitionIndex, + ResultPartitionID resultPartitionId, + long createTimestamp) { + this.resultPartitionProvider = resultPartitionProvider; + this.reader = reader; + this.subPartitionIndex = subPartitionIndex; + this.resultPartitionId = resultPartitionId; + this.createTimestamp = createTimestamp; + } + + @Override + public long getCreateTimestamp() { + return createTimestamp; + } + + @Override + public ResultPartitionID getResultPartitionId() { + return resultPartitionId; + } + + @Override + public NetworkSequenceViewReader getViewReader() { + return reader; + } + + @Override + public InputChannelID getReceiverId() { + return reader.getReceiverId(); + } + + @Override + public void notifyPartitionCreated(ResultPartition partition) throws IOException { + checkNotNull(partition); + reader.notifySubpartitionCreated(partition, subPartitionIndex); + } + + @Override + public void notifyPartitionCreatedTimeout() { + reader.notifyPartitionRequestTimeout(this); + } + + @Override + public void releaseListener() { + resultPartitionProvider.releasePartitionRequestListener(this); + } +} diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/netty/PartitionRequestQueue.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/netty/PartitionRequestQueue.java index 57337fdc4f7de..933e122899adf 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/netty/PartitionRequestQueue.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/netty/PartitionRequestQueue.java @@ -22,6 +22,9 @@ import org.apache.flink.runtime.io.network.NetworkSequenceViewReader; import org.apache.flink.runtime.io.network.buffer.Buffer; import org.apache.flink.runtime.io.network.netty.NettyMessage.ErrorResponse; +import org.apache.flink.runtime.io.network.partition.PartitionNotFoundException; +import org.apache.flink.runtime.io.network.partition.PartitionRequestListener; +import org.apache.flink.runtime.io.network.partition.ResultPartitionID; import org.apache.flink.runtime.io.network.partition.ResultSubpartitionView; import org.apache.flink.runtime.io.network.partition.consumer.InputChannel.BufferAndAvailability; import org.apache.flink.runtime.io.network.partition.consumer.InputChannelID; @@ -259,6 +262,26 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object msg) throws Exc if (toRelease != null) { releaseViewReader(toRelease); } + } else if (msg instanceof PartitionRequestListener) { + PartitionRequestListener partitionRequestListener = (PartitionRequestListener) msg; + + // Send partition not found message to the downstream task when the listener is timeout. + final ResultPartitionID resultPartitionId = + partitionRequestListener.getResultPartitionId(); + final InputChannelID inputChannelId = partitionRequestListener.getReceiverId(); + availableReaders.remove(partitionRequestListener.getViewReader()); + allReaders.remove(inputChannelId); + try { + ctx.writeAndFlush( + new NettyMessage.ErrorResponse( + new PartitionNotFoundException(resultPartitionId), inputChannelId)); + } catch (Exception e) { + LOG.warn( + "Write partition not found exception to {} for result partition {} fail", + inputChannelId, + resultPartitionId, + e); + } } else { ctx.fireUserEventTriggered(msg); } @@ -358,7 +381,10 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E } private void handleException(Channel channel, Throwable cause) throws IOException { - LOG.error("Encountered error while consuming partitions", cause); + LOG.error( + "Encountered error while consuming partitions (connection to {})", + channel.remoteAddress(), + cause); fatalError = true; releaseAllResources(); @@ -393,6 +419,10 @@ private void onChannelFutureFailure(ChannelFuture future) throws Exception { } } + public void notifyPartitionRequestTimeout(PartitionRequestListener partitionRequestListener) { + ctx.pipeline().fireUserEventTriggered(partitionRequestListener); + } + // This listener is called after an element of the current nonEmptyReader has been // flushed. If successful, the listener triggers further processing of the // queues. diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/netty/PartitionRequestServerHandler.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/netty/PartitionRequestServerHandler.java index ccd9b1c53a872..a0d6e25aec29f 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/netty/PartitionRequestServerHandler.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/netty/PartitionRequestServerHandler.java @@ -29,7 +29,6 @@ import org.apache.flink.runtime.io.network.netty.NettyMessage.ResumeConsumption; import org.apache.flink.runtime.io.network.netty.NettyMessage.SegmentId; import org.apache.flink.runtime.io.network.netty.NettyMessage.TaskEventRequest; -import org.apache.flink.runtime.io.network.partition.PartitionNotFoundException; import org.apache.flink.runtime.io.network.partition.ResultPartitionProvider; import org.apache.flink.runtime.io.network.partition.consumer.InputChannelID; @@ -83,19 +82,14 @@ protected void channelRead0(ChannelHandlerContext ctx, NettyMessage msg) throws LOG.debug("Read channel on {}: {}.", ctx.channel().localAddress(), request); - try { - NetworkSequenceViewReader reader; - reader = - new CreditBasedSequenceNumberingViewReader( - request.receiverId, request.credit, outboundQueue); + NetworkSequenceViewReader reader; + reader = + new CreditBasedSequenceNumberingViewReader( + request.receiverId, request.credit, outboundQueue); - reader.requestSubpartitionView( - partitionProvider, request.partitionId, request.queueIndex); + reader.requestSubpartitionViewOrRegisterListener( + partitionProvider, request.partitionId, request.queueIndex); - outboundQueue.notifyReaderCreated(reader); - } catch (PartitionNotFoundException notFound) { - respondWithError(ctx, notFound, request.receiverId); - } } // ---------------------------------------------------------------- // Task events diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/PartitionRequestListener.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/PartitionRequestListener.java new file mode 100644 index 0000000000000..82253be5256ab --- /dev/null +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/PartitionRequestListener.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.runtime.io.network.partition; + +import org.apache.flink.runtime.io.network.NetworkSequenceViewReader; +import org.apache.flink.runtime.io.network.partition.consumer.InputChannelID; + +import java.io.IOException; + +/** + * When the netty server receives a downstream task's partition request event and finds its upstream + * task doesn't register its partition yet, the netty server will construct a {@link + * PartitionRequestListener} and notify the listener when the task deploys itself and registers its + * partition to {@link ResultPartitionManager}. + */ +public interface PartitionRequestListener { + + /** + * The creation timestamp of this notifier, it's used to check whether the notifier is timeout. + * + * @return the creation timestamp + */ + long getCreateTimestamp(); + + /** + * Get the result partition id of the notifier. + * + * @return the result partition id + */ + ResultPartitionID getResultPartitionId(); + + /** + * Get the view reader of the notifier. + * + * @return the view reader + */ + NetworkSequenceViewReader getViewReader(); + + /** + * Get the input channel id of the notifier. + * + * @return the input channel id + */ + InputChannelID getReceiverId(); + + /** + * Notify the partition request listener when the given partition is registered. + * + * @param partition The registered partition. + */ + void notifyPartitionCreated(ResultPartition partition) throws IOException; + + /** + * When the partition request listener is timeout, it will be notified to send {@link + * PartitionNotFoundException}. + */ + void notifyPartitionCreatedTimeout(); + + /** Release this listener. */ + void releaseListener(); +} diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/PartitionRequestListenerManager.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/PartitionRequestListenerManager.java new file mode 100644 index 0000000000000..e296d767fb890 --- /dev/null +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/PartitionRequestListenerManager.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.runtime.io.network.partition; + +import org.apache.flink.runtime.io.network.partition.consumer.InputChannelID; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** Manages partition request listener with input channel id. */ +public class PartitionRequestListenerManager { + private final Map listeners; + + public PartitionRequestListenerManager() { + this.listeners = new HashMap<>(); + } + + public Collection getPartitionRequestListeners() { + return listeners.values(); + } + + public void remove(InputChannelID receiverId) { + listeners.remove(receiverId); + } + + public boolean isEmpty() { + return listeners.isEmpty(); + } + + public void registerListener(PartitionRequestListener listener) { + PartitionRequestListener previous = listeners.put(listener.getReceiverId(), listener); + if (previous != null) { + throw new IllegalStateException( + "Partition request listener with receiver " + + listener.getReceiverId() + + " has been registered."); + } + } + + /** + * Remove the expire partition request listener and add it to the given timeoutListeners. + * + * @param now the timestamp + * @param timeout the timeout mills + * @param timeoutListeners the expire partition request listeners + */ + public void removeExpiration( + long now, long timeout, Collection timeoutListeners) { + Iterator> iterator = + listeners.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + PartitionRequestListener partitionRequestListener = entry.getValue(); + if ((now - partitionRequestListener.getCreateTimestamp()) > timeout) { + timeoutListeners.add(partitionRequestListener); + iterator.remove(); + } + } + } +} diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/ResultPartitionManager.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/ResultPartitionManager.java index 223052681c024..7657bf538b883 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/ResultPartitionManager.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/ResultPartitionManager.java @@ -18,14 +18,26 @@ package org.apache.flink.runtime.io.network.partition; +import org.apache.flink.annotation.VisibleForTesting; import org.apache.flink.util.CollectionUtil; +import org.apache.flink.util.concurrent.ScheduledExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; + import java.io.IOException; import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import static org.apache.flink.util.Preconditions.checkState; @@ -40,9 +52,36 @@ public class ResultPartitionManager implements ResultPartitionProvider { private final Map registeredPartitions = CollectionUtil.newHashMapWithExpectedSize(16); + @GuardedBy("registeredPartitions") + private final Map listenerManagers = + new HashMap<>(); + + @Nullable private ScheduledFuture partitionListenerTimeoutChecker; + + private final int partitionListenerTimeout; + private boolean isShutdown; - public void registerResultPartition(ResultPartition partition) { + @VisibleForTesting + public ResultPartitionManager() { + this(0, null); + } + + public ResultPartitionManager( + int partitionListenerTimeout, ScheduledExecutor scheduledExecutor) { + this.partitionListenerTimeout = partitionListenerTimeout; + if (partitionListenerTimeout > 0 && scheduledExecutor != null) { + this.partitionListenerTimeoutChecker = + scheduledExecutor.scheduleWithFixedDelay( + this::checkRequestPartitionListeners, + partitionListenerTimeout, + partitionListenerTimeout, + TimeUnit.MILLISECONDS); + } + } + + public void registerResultPartition(ResultPartition partition) throws IOException { + PartitionRequestListenerManager listenerManager; synchronized (registeredPartitions) { checkState(!isShutdown, "Result partition manager already shut down."); @@ -53,8 +92,16 @@ public void registerResultPartition(ResultPartition partition) { throw new IllegalStateException("Result partition already registered."); } - LOG.debug("Registered {}.", partition); + listenerManager = listenerManagers.remove(partition.getPartitionId()); + } + if (listenerManager != null) { + for (PartitionRequestListener listener : + listenerManager.getPartitionRequestListeners()) { + listener.notifyPartitionCreated(partition); + } } + + LOG.debug("Registered {}.", partition); } @Override @@ -81,7 +128,51 @@ public ResultSubpartitionView createSubpartitionView( return subpartitionView; } + @Override + public Optional createSubpartitionViewOrRegisterListener( + ResultPartitionID partitionId, + int subpartitionIndex, + BufferAvailabilityListener availabilityListener, + PartitionRequestListener partitionRequestListener) + throws IOException { + + final ResultSubpartitionView subpartitionView; + synchronized (registeredPartitions) { + final ResultPartition partition = registeredPartitions.get(partitionId); + + if (partition == null) { + listenerManagers + .computeIfAbsent(partitionId, key -> new PartitionRequestListenerManager()) + .registerListener(partitionRequestListener); + subpartitionView = null; + } else { + + LOG.debug("Requesting subpartition {} of {}.", subpartitionIndex, partition); + + subpartitionView = + partition.createSubpartitionView(subpartitionIndex, availabilityListener); + } + } + + return subpartitionView == null ? Optional.empty() : Optional.of(subpartitionView); + } + + @Override + public void releasePartitionRequestListener(PartitionRequestListener listener) { + synchronized (registeredPartitions) { + PartitionRequestListenerManager listenerManager = + listenerManagers.get(listener.getResultPartitionId()); + if (listenerManager != null) { + listenerManager.remove(listener.getReceiverId()); + if (listenerManager.isEmpty()) { + listenerManagers.remove(listener.getResultPartitionId()); + } + } + } + } + public void releasePartition(ResultPartitionID partitionId, Throwable cause) { + PartitionRequestListenerManager listenerManager; synchronized (registeredPartitions) { ResultPartition resultPartition = registeredPartitions.remove(partitionId); if (resultPartition != null) { @@ -91,6 +182,13 @@ public void releasePartition(ResultPartitionID partitionId, Throwable cause) { partitionId.getPartitionId(), partitionId.getProducerId()); } + listenerManager = listenerManagers.remove(partitionId); + } + if (listenerManager != null && !listenerManager.isEmpty()) { + for (PartitionRequestListener listener : + listenerManager.getPartitionRequestListeners()) { + listener.notifyPartitionCreatedTimeout(); + } } } @@ -106,12 +204,61 @@ public void shutdown() { registeredPartitions.clear(); + releaseListenerManagers(); + + // stop the timeout checks for the TaskManagers + if (partitionListenerTimeoutChecker != null) { + partitionListenerTimeoutChecker.cancel(false); + partitionListenerTimeoutChecker = null; + } + isShutdown = true; LOG.debug("Successful shutdown."); } } + private void releaseListenerManagers() { + for (PartitionRequestListenerManager listenerManager : listenerManagers.values()) { + for (PartitionRequestListener listener : + listenerManager.getPartitionRequestListeners()) { + listener.notifyPartitionCreatedTimeout(); + } + } + listenerManagers.clear(); + } + + /** Check whether the partition request listener is timeout. */ + private void checkRequestPartitionListeners() { + List timeoutPartitionRequestListeners = new LinkedList<>(); + synchronized (registeredPartitions) { + if (isShutdown) { + return; + } + long now = System.currentTimeMillis(); + Iterator> iterator = + listenerManagers.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = + iterator.next(); + PartitionRequestListenerManager partitionRequestListeners = entry.getValue(); + partitionRequestListeners.removeExpiration( + now, partitionListenerTimeout, timeoutPartitionRequestListeners); + if (partitionRequestListeners.isEmpty()) { + iterator.remove(); + } + } + } + for (PartitionRequestListener partitionRequestListener : timeoutPartitionRequestListeners) { + partitionRequestListener.notifyPartitionCreatedTimeout(); + } + } + + @VisibleForTesting + public Map getListenerManagers() { + return listenerManagers; + } + // ------------------------------------------------------------------------ // Notifications // ------------------------------------------------------------------------ @@ -131,6 +278,12 @@ void onConsumedPartition(ResultPartition partition) { partitionId.getPartitionId(), partitionId.getProducerId()); } + PartitionRequestListenerManager listenerManager = + listenerManagers.remove(partition.getPartitionId()); + checkState( + listenerManager == null || listenerManager.isEmpty(), + "The partition request listeners is not empty for " + + partition.getPartitionId()); } } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/ResultPartitionProvider.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/ResultPartitionProvider.java index 0cd323a3c66d4..b95cc6f8c968d 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/ResultPartitionProvider.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/ResultPartitionProvider.java @@ -19,6 +19,7 @@ package org.apache.flink.runtime.io.network.partition; import java.io.IOException; +import java.util.Optional; /** Interface for creating result partitions. */ public interface ResultPartitionProvider { @@ -29,4 +30,29 @@ ResultSubpartitionView createSubpartitionView( int index, BufferAvailabilityListener availabilityListener) throws IOException; + + /** + * If the upstream task's partition has been registered, returns the result subpartition input + * view immediately, otherwise register the listener and return empty. + * + * @param partitionId the result partition id + * @param index the index + * @param availabilityListener the buffer availability listener + * @param partitionRequestListener the partition request listener + * @return the result subpartition view + * @throws IOException the thrown exception + */ + Optional createSubpartitionViewOrRegisterListener( + ResultPartitionID partitionId, + int index, + BufferAvailabilityListener availabilityListener, + PartitionRequestListener partitionRequestListener) + throws IOException; + + /** + * Release the given listener in this result partition provider. + * + * @param listener the given listener + */ + void releasePartitionRequestListener(PartitionRequestListener listener); } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/InputChannel.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/InputChannel.java index 1d5a444e2c4e7..f36c5d95aec56 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/InputChannel.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/InputChannel.java @@ -77,7 +77,7 @@ public abstract class InputChannel { protected final Counter numBuffersIn; /** The current backoff (in ms). */ - private int currentBackoff; + protected int currentBackoff; protected InputChannel( SingleInputGate inputGate, @@ -105,7 +105,7 @@ protected InputChannel( this.initialBackoff = initial; this.maxBackoff = max; - this.currentBackoff = initial == 0 ? -1 : 0; + this.currentBackoff = 0; this.numBytesIn = numBytesIn; this.numBuffersIn = numBuffersIn; @@ -277,12 +277,12 @@ protected int getCurrentBackoff() { */ protected boolean increaseBackoff() { // Backoff is disabled - if (currentBackoff < 0) { + if (initialBackoff == 0) { return false; } - // This is the first time backing off if (currentBackoff == 0) { + // This is the first time backing off currentBackoff = initialBackoff; return true; diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/RemoteInputChannel.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/RemoteInputChannel.java index b4fdc3e3c108c..2559e02ea8c92 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/RemoteInputChannel.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/RemoteInputChannel.java @@ -100,6 +100,9 @@ public class RemoteInputChannel extends InputChannel { /** The initial number of exclusive buffers assigned to this channel. */ private final int initialCredit; + /** The milliseconds timeout for partition request listener in result partition manager. */ + private final int partitionRequestListenerTimeout; + /** The number of available buffers that have not been announced to the producer yet. */ private final AtomicInteger unannouncedCredit = new AtomicInteger(0); @@ -124,6 +127,7 @@ public RemoteInputChannel( ConnectionManager connectionManager, int initialBackOff, int maxBackoff, + int partitionRequestListenerTimeout, int networkBuffersPerChannel, Counter numBytesIn, Counter numBuffersIn, @@ -140,6 +144,7 @@ public RemoteInputChannel( numBuffersIn); checkArgument(networkBuffersPerChannel >= 0, "Must be non-negative."); + this.partitionRequestListenerTimeout = partitionRequestListenerTimeout; this.initialCredit = networkBuffersPerChannel; this.connectionId = checkNotNull(connectionId); this.connectionManager = checkNotNull(connectionManager); @@ -201,12 +206,30 @@ void retriggerSubpartitionRequest() throws IOException { if (increaseBackoff()) { partitionRequestClient.requestSubpartition( - partitionId, consumedSubpartitionIndex, this, getCurrentBackoff()); + partitionId, consumedSubpartitionIndex, this, 0); } else { failPartitionRequest(); } } + /** + * The remote task manager creates partition request listener and returns {@link + * PartitionNotFoundException} until the listener is timeout, so the backoff should add the + * timeout milliseconds if it exists. + * + * @return true, iff the operation was successful. Otherwise, false. + */ + @Override + protected boolean increaseBackoff() { + if (partitionRequestListenerTimeout > 0) { + currentBackoff += partitionRequestListenerTimeout; + return currentBackoff < 2 * maxBackoff; + } + + // Backoff is disabled + return false; + } + @Override public Optional getNextBuffer() throws IOException { checkPartitionRequestQueueInitialized(); diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/RemoteRecoveredInputChannel.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/RemoteRecoveredInputChannel.java index 2c0efa01592ab..f63ab7564f6a2 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/RemoteRecoveredInputChannel.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/RemoteRecoveredInputChannel.java @@ -34,6 +34,7 @@ public class RemoteRecoveredInputChannel extends RecoveredInputChannel { private final ConnectionID connectionId; private final ConnectionManager connectionManager; + private final int partitionRequestListenerTimeout; RemoteRecoveredInputChannel( SingleInputGate inputGate, @@ -44,6 +45,7 @@ public class RemoteRecoveredInputChannel extends RecoveredInputChannel { ConnectionManager connectionManager, int initialBackOff, int maxBackoff, + int partitionRequestListenerTimeout, int networkBuffersPerChannel, InputChannelMetrics metrics) { super( @@ -59,6 +61,7 @@ public class RemoteRecoveredInputChannel extends RecoveredInputChannel { this.connectionId = checkNotNull(connectionId); this.connectionManager = checkNotNull(connectionManager); + this.partitionRequestListenerTimeout = partitionRequestListenerTimeout; } @Override @@ -73,6 +76,7 @@ protected InputChannel toInputChannelInternal() throws IOException { connectionManager, initialBackoff, maxBackoff, + partitionRequestListenerTimeout, networkBuffersPerChannel, numBytesIn, numBuffersIn, diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/SingleInputGateFactory.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/SingleInputGateFactory.java index de35b31bff1f8..38f6d13ebfa6b 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/SingleInputGateFactory.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/SingleInputGateFactory.java @@ -79,6 +79,8 @@ public class SingleInputGateFactory { protected final int partitionRequestMaxBackoff; + protected final int partitionRequestListenerTimeout; + @Nonnull protected final ConnectionManager connectionManager; @Nonnull protected final ResultPartitionManager partitionManager; @@ -118,6 +120,7 @@ public SingleInputGateFactory( this.taskExecutorResourceId = taskExecutorResourceId; this.partitionRequestInitialBackoff = networkConfig.partitionRequestInitialBackoff(); this.partitionRequestMaxBackoff = networkConfig.partitionRequestMaxBackoff(); + this.partitionRequestListenerTimeout = networkConfig.getPartitionRequestListenerTimeout(); this.maxRequiredBuffersPerGate = networkConfig.maxRequiredBuffersPerGate(); this.configuredNetworkBuffersPerChannel = NettyShuffleUtils.getNetworkBuffersPerInputChannel( @@ -307,6 +310,7 @@ private InputChannel createInputChannel( connectionManager, partitionRequestInitialBackoff, partitionRequestMaxBackoff, + partitionRequestListenerTimeout, buffersPerChannel, metrics); }, @@ -363,6 +367,7 @@ protected InputChannel createKnownInputChannel( connectionManager, partitionRequestInitialBackoff, partitionRequestMaxBackoff, + partitionRequestListenerTimeout, buffersPerChannel, metrics); } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/UnknownInputChannel.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/UnknownInputChannel.java index 3be987b770797..af3043f0f2d5f 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/UnknownInputChannel.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/consumer/UnknownInputChannel.java @@ -56,6 +56,8 @@ class UnknownInputChannel extends InputChannel implements ChannelStateHolder { private final int maxBackoff; + private final int partitionRequestListenerTimeout; + private final int networkBuffersPerChannel; private final InputChannelMetrics metrics; @@ -72,6 +74,7 @@ public UnknownInputChannel( ConnectionManager connectionManager, int initialBackoff, int maxBackoff, + int partitionRequestListenerTimeout, int networkBuffersPerChannel, InputChannelMetrics metrics) { @@ -91,6 +94,7 @@ public UnknownInputChannel( this.metrics = checkNotNull(metrics); this.initialBackoff = initialBackoff; this.maxBackoff = maxBackoff; + this.partitionRequestListenerTimeout = partitionRequestListenerTimeout; this.networkBuffersPerChannel = networkBuffersPerChannel; } @@ -168,6 +172,7 @@ public RemoteInputChannel toRemoteInputChannel(ConnectionID producerAddress) { connectionManager, initialBackoff, maxBackoff, + partitionRequestListenerTimeout, networkBuffersPerChannel, metrics.getNumBytesInRemoteCounter(), metrics.getNumBuffersInRemoteCounter(), diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/HsFileDataIndexImpl.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/HsFileDataIndexImpl.java index 25012ffa8f1cd..d4c0fed0a0b62 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/HsFileDataIndexImpl.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/HsFileDataIndexImpl.java @@ -160,7 +160,7 @@ private static void addInternalRegionToMap( checkArgument(firstBufferInRegion.subpartitionId == lastBufferInRegion.subpartitionId); checkArgument(firstBufferInRegion.bufferIndex <= lastBufferInRegion.bufferIndex); internalRegionsBySubpartition - .computeIfAbsent(firstBufferInRegion.subpartitionId, ArrayList::new) + .computeIfAbsent(firstBufferInRegion.subpartitionId, k -> new ArrayList<>()) .add( new InternalRegion( firstBufferInRegion.bufferIndex, diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/HsSpillingStrategy.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/HsSpillingStrategy.java index 6d3a15d427a63..70a1c0b985e05 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/HsSpillingStrategy.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/HsSpillingStrategy.java @@ -125,36 +125,48 @@ static class Builder { private Builder() {} public Builder addBufferToSpill(BufferIndexAndChannel buffer) { - bufferToSpill.computeIfAbsent(buffer.getChannel(), ArrayList::new).add(buffer); + bufferToSpill + .computeIfAbsent(buffer.getChannel(), k -> new ArrayList<>()) + .add(buffer); return this; } public Builder addBufferToSpill( int subpartitionId, List buffers) { - bufferToSpill.computeIfAbsent(subpartitionId, ArrayList::new).addAll(buffers); + bufferToSpill + .computeIfAbsent(subpartitionId, k -> new ArrayList<>()) + .addAll(buffers); return this; } public Builder addBufferToSpill( int subpartitionId, Deque buffers) { - bufferToSpill.computeIfAbsent(subpartitionId, ArrayList::new).addAll(buffers); + bufferToSpill + .computeIfAbsent(subpartitionId, k -> new ArrayList<>()) + .addAll(buffers); return this; } public Builder addBufferToRelease(BufferIndexAndChannel buffer) { - bufferToRelease.computeIfAbsent(buffer.getChannel(), ArrayList::new).add(buffer); + bufferToRelease + .computeIfAbsent(buffer.getChannel(), k -> new ArrayList<>()) + .add(buffer); return this; } public Builder addBufferToRelease( int subpartitionId, List buffers) { - bufferToRelease.computeIfAbsent(subpartitionId, ArrayList::new).addAll(buffers); + bufferToRelease + .computeIfAbsent(subpartitionId, k -> new ArrayList<>()) + .addAll(buffers); return this; } public Builder addBufferToRelease( int subpartitionId, Deque buffers) { - bufferToRelease.computeIfAbsent(subpartitionId, ArrayList::new).addAll(buffers); + bufferToRelease + .computeIfAbsent(subpartitionId, k -> new ArrayList<>()) + .addAll(buffers); return this; } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/HsSpillingStrategyUtils.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/HsSpillingStrategyUtils.java index c166f4a1c4b46..5e3786daf0c9e 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/HsSpillingStrategyUtils.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/HsSpillingStrategyUtils.java @@ -73,7 +73,7 @@ public class HsSpillingStrategyUtils { BufferConsumptionPriorityIterator bufferConsumptionPriorityIterator = heap.poll(); BufferIndexAndChannel bufferIndexAndChannel = bufferConsumptionPriorityIterator.next(); subpartitionToHighPriorityBuffers - .computeIfAbsent(bufferIndexAndChannel.getChannel(), ArrayList::new) + .computeIfAbsent(bufferIndexAndChannel.getChannel(), k -> new ArrayList<>()) .add(bufferIndexAndChannel); // if this iterator has next, re-added it. if (bufferConsumptionPriorityIterator.hasNext()) { diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/tiered/file/ProducerMergedPartitionFileIndex.java b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/tiered/file/ProducerMergedPartitionFileIndex.java index d6a08da5fb772..3046683f47535 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/tiered/file/ProducerMergedPartitionFileIndex.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/tiered/file/ProducerMergedPartitionFileIndex.java @@ -168,7 +168,7 @@ private static void addRegionToMap( checkArgument(firstBufferInRegion.getBufferIndex() <= lastBufferInRegion.getBufferIndex()); subpartitionRegionMap - .computeIfAbsent(firstBufferInRegion.getSubpartitionId(), ArrayList::new) + .computeIfAbsent(firstBufferInRegion.getSubpartitionId(), k -> new ArrayList<>()) .add( new FixedSizeRegion( firstBufferInRegion.getBufferIndex(), diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/metrics/groups/InternalSinkWriterMetricGroup.java b/flink-runtime/src/main/java/org/apache/flink/runtime/metrics/groups/InternalSinkWriterMetricGroup.java index 81aa8d78ce3e3..27d0c72ed5c7b 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/metrics/groups/InternalSinkWriterMetricGroup.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/metrics/groups/InternalSinkWriterMetricGroup.java @@ -26,7 +26,6 @@ import org.apache.flink.metrics.groups.OperatorIOMetricGroup; import org.apache.flink.metrics.groups.OperatorMetricGroup; import org.apache.flink.metrics.groups.SinkWriterMetricGroup; -import org.apache.flink.metrics.groups.UnregisteredMetricsGroup; import org.apache.flink.runtime.metrics.MetricNames; /** Special {@link org.apache.flink.metrics.MetricGroup} representing an Operator. */ @@ -40,7 +39,8 @@ public class InternalSinkWriterMetricGroup extends ProxyMetricGroup private final Counter numBytesWritten; private final OperatorIOMetricGroup operatorIOMetricGroup; - private InternalSinkWriterMetricGroup( + @VisibleForTesting + InternalSinkWriterMetricGroup( MetricGroup parentMetricGroup, OperatorIOMetricGroup operatorIOMetricGroup) { super(parentMetricGroup); numRecordsOutErrors = parentMetricGroup.counter(MetricNames.NUM_RECORDS_OUT_ERRORS); @@ -61,18 +61,6 @@ public static InternalSinkWriterMetricGroup wrap(OperatorMetricGroup operatorMet operatorMetricGroup, operatorMetricGroup.getIOMetricGroup()); } - @VisibleForTesting - public static InternalSinkWriterMetricGroup mock(MetricGroup metricGroup) { - return new InternalSinkWriterMetricGroup( - metricGroup, UnregisteredMetricsGroup.createOperatorIOMetricGroup()); - } - - @VisibleForTesting - public static InternalSinkWriterMetricGroup mock( - MetricGroup metricGroup, OperatorIOMetricGroup operatorIOMetricGroup) { - return new InternalSinkWriterMetricGroup(metricGroup, operatorIOMetricGroup); - } - @Override public OperatorIOMetricGroup getIOMetricGroup() { return operatorIOMetricGroup; diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/metrics/util/MetricUtils.java b/flink-runtime/src/main/java/org/apache/flink/runtime/metrics/util/MetricUtils.java index 520a54ec6035b..0074dfbe07e58 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/metrics/util/MetricUtils.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/metrics/util/MetricUtils.java @@ -24,6 +24,7 @@ import org.apache.flink.configuration.Configuration; import org.apache.flink.configuration.MetricOptions; import org.apache.flink.metrics.Gauge; +import org.apache.flink.metrics.MeterView; import org.apache.flink.metrics.MetricGroup; import org.apache.flink.runtime.clusterframework.types.AllocationID; import org.apache.flink.runtime.clusterframework.types.ResourceID; @@ -129,7 +130,8 @@ public static void instantiateStatusMetrics(MetricGroup metricGroup) { MetricGroup jvm = metricGroup.addGroup("JVM"); instantiateClassLoaderMetrics(jvm.addGroup("ClassLoader")); - instantiateGarbageCollectorMetrics(jvm.addGroup("GarbageCollector")); + instantiateGarbageCollectorMetrics( + jvm.addGroup("GarbageCollector"), ManagementFactory.getGarbageCollectorMXBeans()); instantiateMemoryMetrics(jvm.addGroup(METRIC_GROUP_MEMORY)); instantiateThreadMetrics(jvm.addGroup("Threads")); instantiateCPUMetrics(jvm.addGroup("CPU")); @@ -222,16 +224,32 @@ private static void instantiateClassLoaderMetrics(MetricGroup metrics) { metrics.>gauge("ClassesUnloaded", mxBean::getUnloadedClassCount); } - private static void instantiateGarbageCollectorMetrics(MetricGroup metrics) { - List garbageCollectors = - ManagementFactory.getGarbageCollectorMXBeans(); - + @VisibleForTesting + static void instantiateGarbageCollectorMetrics( + MetricGroup metrics, List garbageCollectors) { for (final GarbageCollectorMXBean garbageCollector : garbageCollectors) { MetricGroup gcGroup = metrics.addGroup(garbageCollector.getName()); - gcGroup.>gauge("Count", garbageCollector::getCollectionCount); - gcGroup.>gauge("Time", garbageCollector::getCollectionTime); + gcGroup.gauge("Count", garbageCollector::getCollectionCount); + Gauge timeGauge = gcGroup.gauge("Time", garbageCollector::getCollectionTime); + gcGroup.meter("TimeMsPerSecond", new MeterView(timeGauge)); } + Gauge totalGcTime = + () -> + garbageCollectors.stream() + .mapToLong(GarbageCollectorMXBean::getCollectionTime) + .sum(); + + Gauge totalGcCount = + () -> + garbageCollectors.stream() + .mapToLong(GarbageCollectorMXBean::getCollectionCount) + .sum(); + + MetricGroup allGroup = metrics.addGroup("All"); + allGroup.gauge("Count", totalGcCount); + Gauge totalTime = allGroup.gauge("Time", totalGcTime); + allGroup.meter("TimeMsPerSecond", new MeterView(totalTime)); } private static void instantiateMemoryMetrics(MetricGroup metrics) { diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/resourcemanager/ResourceManagerRuntimeServices.java b/flink-runtime/src/main/java/org/apache/flink/runtime/resourcemanager/ResourceManagerRuntimeServices.java index 1a18364c2077a..1368748fb9c6f 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/resourcemanager/ResourceManagerRuntimeServices.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/resourcemanager/ResourceManagerRuntimeServices.java @@ -90,7 +90,7 @@ private static SlotManager createSlotManager( SlotManagerUtils.generateTaskManagerTotalResourceProfile( slotManagerConfiguration.getDefaultWorkerResourceSpec()), slotManagerConfiguration.getNumSlotsPerWorker(), - slotManagerConfiguration.isEvenlySpreadOutSlots(), + slotManagerConfiguration.getTaskManagerLoadBalanceMode(), slotManagerConfiguration.getTaskManagerTimeout(), slotManagerConfiguration.getRedundantTaskManagerNum(), slotManagerConfiguration.getMinTotalCpu(), diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/resourcemanager/slotmanager/DefaultResourceAllocationStrategy.java b/flink-runtime/src/main/java/org/apache/flink/runtime/resourcemanager/slotmanager/DefaultResourceAllocationStrategy.java index 9f12c46972ce5..62f31480b4abc 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/resourcemanager/slotmanager/DefaultResourceAllocationStrategy.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/resourcemanager/slotmanager/DefaultResourceAllocationStrategy.java @@ -40,6 +40,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.apache.flink.configuration.TaskManagerOptions.TaskManagerLoadBalanceMode; import static org.apache.flink.runtime.resourcemanager.slotmanager.SlotManagerUtils.getEffectiveResourceProfile; /** @@ -84,7 +85,7 @@ public class DefaultResourceAllocationStrategy implements ResourceAllocationStra public DefaultResourceAllocationStrategy( ResourceProfile totalResourceProfile, int numSlotsPerWorker, - boolean evenlySpreadOutSlots, + TaskManagerLoadBalanceMode taskManagerLoadBalanceMode, Time taskManagerTimeout, int redundantTaskManagerNum, CPUResource minTotalCPU, @@ -95,7 +96,7 @@ public DefaultResourceAllocationStrategy( SlotManagerUtils.generateDefaultSlotResourceProfile( totalResourceProfile, numSlotsPerWorker); this.availableResourceMatchingStrategy = - evenlySpreadOutSlots + taskManagerLoadBalanceMode == TaskManagerLoadBalanceMode.SLOTS ? LeastUtilizationResourceMatchingStrategy.INSTANCE : AnyMatchingResourceMatchingStrategy.INSTANCE; this.taskManagerTimeout = taskManagerTimeout; diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/resourcemanager/slotmanager/SlotManagerConfiguration.java b/flink-runtime/src/main/java/org/apache/flink/runtime/resourcemanager/slotmanager/SlotManagerConfiguration.java index 330a65b783470..a41b0c16ef528 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/resourcemanager/slotmanager/SlotManagerConfiguration.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/resourcemanager/slotmanager/SlotManagerConfiguration.java @@ -21,7 +21,6 @@ import org.apache.flink.api.common.resources.CPUResource; import org.apache.flink.api.common.time.Time; import org.apache.flink.configuration.AkkaOptions; -import org.apache.flink.configuration.ClusterOptions; import org.apache.flink.configuration.Configuration; import org.apache.flink.configuration.MemorySize; import org.apache.flink.configuration.ResourceManagerOptions; @@ -33,6 +32,8 @@ import java.math.RoundingMode; import java.time.Duration; +import static org.apache.flink.configuration.TaskManagerOptions.TaskManagerLoadBalanceMode; + /** Configuration for the {@link SlotManager}. */ public class SlotManagerConfiguration { private final Time taskManagerRequestTimeout; @@ -41,7 +42,7 @@ public class SlotManagerConfiguration { private final Duration declareNeededResourceDelay; private final boolean waitResultConsumedBeforeRelease; private final SlotMatchingStrategy slotMatchingStrategy; - private final boolean evenlySpreadOutSlots; + private final TaskManagerLoadBalanceMode taskManagerLoadBalanceMode; private final WorkerResourceSpec defaultWorkerResourceSpec; private final int numSlotsPerWorker; private final int minSlotNum; @@ -59,7 +60,7 @@ public SlotManagerConfiguration( Duration declareNeededResourceDelay, boolean waitResultConsumedBeforeRelease, SlotMatchingStrategy slotMatchingStrategy, - boolean evenlySpreadOutSlots, + TaskManagerLoadBalanceMode taskManagerLoadBalanceMode, WorkerResourceSpec defaultWorkerResourceSpec, int numSlotsPerWorker, int minSlotNum, @@ -76,7 +77,7 @@ public SlotManagerConfiguration( this.declareNeededResourceDelay = Preconditions.checkNotNull(declareNeededResourceDelay); this.waitResultConsumedBeforeRelease = waitResultConsumedBeforeRelease; this.slotMatchingStrategy = Preconditions.checkNotNull(slotMatchingStrategy); - this.evenlySpreadOutSlots = evenlySpreadOutSlots; + this.taskManagerLoadBalanceMode = taskManagerLoadBalanceMode; this.defaultWorkerResourceSpec = Preconditions.checkNotNull(defaultWorkerResourceSpec); Preconditions.checkState(numSlotsPerWorker > 0); this.numSlotsPerWorker = numSlotsPerWorker; @@ -199,8 +200,8 @@ public SlotMatchingStrategy getSlotMatchingStrategy() { return slotMatchingStrategy; } - public boolean isEvenlySpreadOutSlots() { - return evenlySpreadOutSlots; + public TaskManagerLoadBalanceMode getTaskManagerLoadBalanceMode() { + return taskManagerLoadBalanceMode; } public WorkerResourceSpec getDefaultWorkerResourceSpec() { @@ -260,10 +261,10 @@ public static SlotManagerConfiguration fromConfiguration( configuration.getBoolean( ResourceManagerOptions.TASK_MANAGER_RELEASE_WHEN_RESULT_CONSUMED); - boolean evenlySpreadOutSlots = - configuration.getBoolean(ClusterOptions.EVENLY_SPREAD_OUT_SLOTS_STRATEGY); + TaskManagerLoadBalanceMode taskManagerLoadBalanceMode = + TaskManagerLoadBalanceMode.loadFromConfiguration(configuration); final SlotMatchingStrategy slotMatchingStrategy = - evenlySpreadOutSlots + taskManagerLoadBalanceMode == TaskManagerLoadBalanceMode.SLOTS ? LeastUtilizationSlotMatchingStrategy.INSTANCE : AnyMatchingSlotMatchingStrategy.INSTANCE; @@ -282,7 +283,7 @@ public static SlotManagerConfiguration fromConfiguration( declareNeededResourceDelay, waitResultConsumedBeforeRelease, slotMatchingStrategy, - evenlySpreadOutSlots, + taskManagerLoadBalanceMode, defaultWorkerResourceSpec, numSlotsPerWorker, minSlotNum, diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/JobExceptionsHandler.java b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/JobExceptionsHandler.java index 5ece82a267173..55c7875e85cf4 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/JobExceptionsHandler.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/JobExceptionsHandler.java @@ -242,6 +242,7 @@ private static JobExceptionsInfoWithHistory.RootExceptionInfo createRootExceptio historyEntry.getFailureLabels(), historyEntry.getFailingTaskName(), toString(historyEntry.getTaskManagerLocation()), + toString(historyEntry.getTaskManagerLocation()), toTaskManagerId(historyEntry.getTaskManagerLocation()), concurrentExceptions); } @@ -257,6 +258,7 @@ private static JobExceptionsInfoWithHistory.ExceptionInfo createExceptionInfo( exceptionHistoryEntry.getFailureLabels(), exceptionHistoryEntry.getFailingTaskName(), toString(exceptionHistoryEntry.getTaskManagerLocation()), + toString(exceptionHistoryEntry.getTaskManagerLocation()), toTaskManagerId(exceptionHistoryEntry.getTaskManagerLocation())); } @@ -270,9 +272,7 @@ private static void assertLocalExceptionInfo(ExceptionHistoryEntry exceptionHist static String toString(@Nullable TaskManagerLocation location) { // '(unassigned)' being the default value is added to support backward-compatibility for the // deprecated fields - return location != null - ? taskManagerLocationToString(location.getFQDNHostname(), location.dataPort()) - : "(unassigned)"; + return location != null ? location.getEndpoint() : "(unassigned)"; } @VisibleForTesting @@ -285,9 +285,7 @@ static String toTaskManagerId(@Nullable TaskManagerLocation location) { @VisibleForTesting @Nullable static String toString(@Nullable ExceptionHistoryEntry.ArchivedTaskManagerLocation location) { - return location != null - ? taskManagerLocationToString(location.getFQDNHostname(), location.getPort()) - : null; + return location != null ? location.getEndpoint() : null; } @VisibleForTesting @@ -295,8 +293,4 @@ static String toTaskManagerId( @Nullable ExceptionHistoryEntry.ArchivedTaskManagerLocation location) { return location != null ? String.format("%s", location.getResourceID()) : null; } - - private static String taskManagerLocationToString(String fqdnHostname, int port) { - return String.format("%s:%d", fqdnHostname, port); - } } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/JobVertexTaskManagersHandler.java b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/JobVertexTaskManagersHandler.java index 0ed6565ac2452..948bdcb1de8f5 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/JobVertexTaskManagersHandler.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/JobVertexTaskManagersHandler.java @@ -132,8 +132,7 @@ private static JobVertexTaskManagersInfo createJobVertexTaskManagersInfo( JobID jobID, @Nullable MetricFetcher metricFetcher) { // Build a map that groups task executions by TaskManager - Map taskManagerId2Host = new HashMap<>(); - Map> taskManagerExecutions = new HashMap<>(); + Map> taskManagerExecutions = new HashMap<>(); Set representativeExecutions = new HashSet<>(); for (AccessExecutionVertex vertex : jobVertex.getTaskVertices()) { AccessExecution representativeAttempt = vertex.getCurrentExecutionAttempt(); @@ -141,16 +140,9 @@ private static JobVertexTaskManagersInfo createJobVertexTaskManagersInfo( for (AccessExecution execution : vertex.getCurrentExecutions()) { TaskManagerLocation location = execution.getAssignedResourceLocation(); - String taskManagerHost = - location == null - ? "(unassigned)" - : location.getHostname() + ':' + location.dataPort(); - String taskmanagerId = - location == null ? "(unassigned)" : location.getResourceID().toString(); - taskManagerId2Host.put(taskmanagerId, taskManagerHost); List executions = taskManagerExecutions.computeIfAbsent( - taskmanagerId, ignored -> new ArrayList<>()); + location, ignored -> new ArrayList<>()); executions.add(execution); } } @@ -158,9 +150,17 @@ private static JobVertexTaskManagersInfo createJobVertexTaskManagersInfo( final long now = System.currentTimeMillis(); List taskManagersInfoList = new ArrayList<>(4); - for (Map.Entry> entry : taskManagerExecutions.entrySet()) { - String taskmanagerId = entry.getKey(); - String host = taskManagerId2Host.get(taskmanagerId); + for (Map.Entry> entry : + taskManagerExecutions.entrySet()) { + TaskManagerLocation location = entry.getKey(); + // Port information is included in the host field for backward-compatibility + String host = + location == null + ? "(unassigned)" + : location.getHostname() + ':' + location.dataPort(); + String endpoint = location == null ? "(unassigned)" : location.getEndpoint(); + String taskmanagerId = + location == null ? "(unassigned)" : location.getResourceID().toString(); List executions = entry.getValue(); List ioMetricsInfos = new ArrayList<>(); @@ -266,6 +266,7 @@ private static JobVertexTaskManagersInfo createJobVertexTaskManagersInfo( taskManagersInfoList.add( new JobVertexTaskManagersInfo.TaskManagersInfo( host, + endpoint, jobVertexState, startTime, endTime, diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/SubtasksAllAccumulatorsHandler.java b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/SubtasksAllAccumulatorsHandler.java index a47ad6a46be44..4db45a2f3e418 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/SubtasksAllAccumulatorsHandler.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/SubtasksAllAccumulatorsHandler.java @@ -78,7 +78,8 @@ protected SubtasksAllAccumulatorsInfo handleRequest( for (AccessExecutionVertex vertex : jobVertex.getTaskVertices()) { for (AccessExecution execution : vertex.getCurrentExecutions()) { TaskManagerLocation location = execution.getAssignedResourceLocation(); - String locationString = location == null ? "(unassigned)" : location.getHostname(); + String host = location == null ? "(unassigned)" : location.getHostname(); + String endpoint = location == null ? "(unassigned)" : location.getEndpoint(); StringifiedAccumulatorResult[] accs = execution.getUserAccumulatorsStringified(); List userAccumulators = new ArrayList<>(accs.length); @@ -91,7 +92,8 @@ protected SubtasksAllAccumulatorsInfo handleRequest( new SubtasksAllAccumulatorsInfo.SubtaskAccumulatorsInfo( execution.getParallelSubtaskIndex(), execution.getAttemptNumber(), - locationString, + host, + endpoint, userAccumulators)); } } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/SubtasksTimesHandler.java b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/SubtasksTimesHandler.java index f097bbfbb4530..34e3fed0673ac 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/SubtasksTimesHandler.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/job/SubtasksTimesHandler.java @@ -113,7 +113,8 @@ private static SubtasksTimesInfo createSubtaskTimesInfo(AccessExecutionJobVertex long duration = start >= 0 ? end - start : -1L; TaskManagerLocation location = vertex.getCurrentAssignedResourceLocation(); - String locationString = location == null ? "(unassigned)" : location.getHostname(); + String host = location == null ? "(unassigned)" : location.getHostname(); + String endpoint = location == null ? "(unassigned)" : location.getEndpoint(); Map timestampMap = CollectionUtil.newHashMapWithExpectedSize(ExecutionState.values().length); @@ -123,7 +124,7 @@ private static SubtasksTimesInfo createSubtaskTimesInfo(AccessExecutionJobVertex subtasks.add( new SubtasksTimesInfo.SubtaskTimeInfo( - num++, locationString, duration, timestampMap)); + num++, host, endpoint, duration, timestampMap)); } return new SubtasksTimesInfo(id, name, now, subtasks); } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/JobExceptionsInfo.java b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/JobExceptionsInfo.java index 2bd3fd46bfc39..0acc64d4b47dc 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/JobExceptionsInfo.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/JobExceptionsInfo.java @@ -144,7 +144,8 @@ public boolean isTruncated() { public static final class ExecutionExceptionInfo { public static final String FIELD_NAME_EXCEPTION = "exception"; public static final String FIELD_NAME_TASK = "task"; - public static final String FIELD_NAME_LOCATION = "location"; + @Deprecated public static final String FIELD_NAME_LOCATION = "location"; + public static final String FIELD_NAME_ENDPOINT = "endpoint"; public static final String FIELD_NAME_TIMESTAMP = "timestamp"; public static final String FIELD_NAME_TASK_MANAGER_ID = "taskManagerId"; @@ -157,22 +158,36 @@ public static final class ExecutionExceptionInfo { @JsonProperty(FIELD_NAME_LOCATION) private final String location; + @JsonProperty(FIELD_NAME_ENDPOINT) + private final String endpoint; + @JsonProperty(FIELD_NAME_TIMESTAMP) private final long timestamp; @JsonProperty(FIELD_NAME_TASK_MANAGER_ID) private final String taskManagerId; + public ExecutionExceptionInfo( + String exception, + String task, + String endpoint, + long timestamp, + String taskManagerId) { + this(exception, task, endpoint, endpoint, timestamp, taskManagerId); + } + @JsonCreator public ExecutionExceptionInfo( @JsonProperty(FIELD_NAME_EXCEPTION) String exception, @JsonProperty(FIELD_NAME_TASK) String task, @JsonProperty(FIELD_NAME_LOCATION) String location, + @JsonProperty(FIELD_NAME_ENDPOINT) String endpoint, @JsonProperty(FIELD_NAME_TIMESTAMP) long timestamp, @JsonProperty(FIELD_NAME_TASK_MANAGER_ID) String taskManagerId) { this.exception = Preconditions.checkNotNull(exception); this.task = Preconditions.checkNotNull(task); this.location = Preconditions.checkNotNull(location); + this.endpoint = Preconditions.checkNotNull(endpoint); this.timestamp = timestamp; this.taskManagerId = taskManagerId; } @@ -191,12 +206,13 @@ public boolean equals(Object o) { && Objects.equals(exception, that.exception) && Objects.equals(task, that.task) && Objects.equals(location, that.location) + && Objects.equals(endpoint, that.endpoint) && Objects.equals(taskManagerId, that.taskManagerId); } @Override public int hashCode() { - return Objects.hash(timestamp, exception, task, location, taskManagerId); + return Objects.hash(timestamp, exception, task, location, endpoint, taskManagerId); } @Override @@ -205,6 +221,7 @@ public String toString() { .add("exception='" + exception + "'") .add("task='" + task + "'") .add("location='" + location + "'") + .add("endpoint='" + endpoint + "'") .add("timestamp=" + timestamp) .add("taskManagerId=" + taskManagerId) .toString(); diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/JobExceptionsInfoWithHistory.java b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/JobExceptionsInfoWithHistory.java index dd6b264386e9b..cb15825cc6bd6 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/JobExceptionsInfoWithHistory.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/JobExceptionsInfoWithHistory.java @@ -176,7 +176,8 @@ public static class ExceptionInfo { public static final String FIELD_NAME_EXCEPTION_STACKTRACE = "stacktrace"; public static final String FIELD_NAME_EXCEPTION_TIMESTAMP = "timestamp"; public static final String FIELD_NAME_TASK_NAME = "taskName"; - public static final String FIELD_NAME_LOCATION = "location"; + @Deprecated public static final String FIELD_NAME_LOCATION = "location"; + public static final String FIELD_NAME_ENDPOINT = "endpoint"; public static final String FIELD_NAME_TASK_MANAGER_ID = "taskManagerId"; public static final String FIELD_NAME_FAILURE_LABELS = "failureLabels"; @@ -194,11 +195,18 @@ public static class ExceptionInfo { @Nullable private final String taskName; + /** @deprecated Use {@link ExceptionInfo#endpoint} instead. */ + @Deprecated @JsonInclude(NON_NULL) @JsonProperty(FIELD_NAME_LOCATION) @Nullable private final String location; + @JsonInclude(NON_NULL) + @JsonProperty(FIELD_NAME_ENDPOINT) + @Nullable + private final String endpoint; + @JsonInclude(NON_NULL) @JsonProperty(FIELD_NAME_TASK_MANAGER_ID) @Nullable @@ -208,7 +216,15 @@ public static class ExceptionInfo { private final Map failureLabels; public ExceptionInfo(String exceptionName, String stacktrace, long timestamp) { - this(exceptionName, stacktrace, timestamp, Collections.emptyMap(), null, null, null); + this( + exceptionName, + stacktrace, + timestamp, + Collections.emptyMap(), + null, + null, + null, + null); } @JsonCreator @@ -219,6 +235,7 @@ public ExceptionInfo( @JsonProperty(FIELD_NAME_FAILURE_LABELS) Map failureLabels, @JsonProperty(FIELD_NAME_TASK_NAME) @Nullable String taskName, @JsonProperty(FIELD_NAME_LOCATION) @Nullable String location, + @JsonProperty(FIELD_NAME_ENDPOINT) @Nullable String endpoint, @JsonProperty(FIELD_NAME_TASK_MANAGER_ID) @Nullable String taskManagerId) { this.exceptionName = checkNotNull(exceptionName); this.stacktrace = checkNotNull(stacktrace); @@ -226,6 +243,7 @@ public ExceptionInfo( this.failureLabels = checkNotNull(failureLabels); this.taskName = taskName; this.location = location; + this.endpoint = endpoint; this.taskManagerId = taskManagerId; } @@ -250,12 +268,19 @@ public String getTaskName() { return taskName; } + @Deprecated @JsonIgnore @Nullable public String getLocation() { return location; } + @JsonIgnore + @Nullable + public String getEndpoint() { + return endpoint; + } + @JsonIgnore @Nullable public String getTaskManagerId() { @@ -283,13 +308,20 @@ public boolean equals(Object o) { && Objects.equals(timestamp, that.timestamp) && Objects.equals(failureLabels, that.failureLabels) && Objects.equals(taskName, that.taskName) - && Objects.equals(location, that.location); + && Objects.equals(location, that.location) + && Objects.equals(location, that.endpoint); } @Override public int hashCode() { return Objects.hash( - exceptionName, stacktrace, timestamp, failureLabels, taskName, location); + exceptionName, + stacktrace, + timestamp, + failureLabels, + taskName, + location, + endpoint); } @Override @@ -300,7 +332,7 @@ public String toString() { .add("timestamp=" + timestamp) .add("failureLabels=" + failureLabels) .add("taskName='" + taskName + "'") - .add("location='" + location + "'") + .add("endpoint='" + endpoint + "'") .toString(); } } @@ -330,6 +362,7 @@ public RootExceptionInfo( null, null, null, + null, concurrentExceptions); } @@ -341,6 +374,7 @@ public RootExceptionInfo( @JsonProperty(FIELD_NAME_FAILURE_LABELS) Map failureLabels, @JsonProperty(FIELD_NAME_TASK_NAME) @Nullable String taskName, @JsonProperty(FIELD_NAME_LOCATION) @Nullable String location, + @JsonProperty(FIELD_NAME_ENDPOINT) @Nullable String endpoint, @JsonProperty(FIELD_NAME_TASK_MANAGER_ID) @Nullable String taskManagerId, @JsonProperty(FIELD_NAME_CONCURRENT_EXCEPTIONS) Collection concurrentExceptions) { @@ -351,6 +385,7 @@ public RootExceptionInfo( failureLabels, taskName, location, + endpoint, taskManagerId); this.concurrentExceptions = concurrentExceptions; } @@ -386,7 +421,7 @@ public String toString() { .add("stacktrace='" + getStacktrace() + "'") .add("timestamp=" + getTimestamp()) .add("taskName='" + getTaskName() + "'") - .add("location='" + getLocation() + "'") + .add("endpoint='" + getEndpoint() + "'") .add("concurrentExceptions=" + getConcurrentExceptions()) .toString(); } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/JobVertexTaskManagersInfo.java b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/JobVertexTaskManagersInfo.java index de6a388292779..c0b2e34dfe2b7 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/JobVertexTaskManagersInfo.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/JobVertexTaskManagersInfo.java @@ -100,7 +100,8 @@ public int hashCode() { /** Detailed information about task managers. */ @Schema(name = "JobVertexTaskManagerInfo") public static class TaskManagersInfo { - public static final String TASK_MANAGERS_FIELD_HOST = "host"; + @Deprecated public static final String TASK_MANAGERS_FIELD_HOST = "host"; + public static final String TASK_MANAGERS_FIELD_ENDPOINT = "endpoint"; public static final String TASK_MANAGERS_FIELD_STATUS = "status"; public static final String TASK_MANAGERS_FIELD_START_TIME = "start-time"; public static final String TASK_MANAGERS_FIELD_END_TIME = "end-time"; @@ -113,6 +114,9 @@ public static class TaskManagersInfo { @JsonProperty(TASK_MANAGERS_FIELD_HOST) private final String host; + @JsonProperty(TASK_MANAGERS_FIELD_ENDPOINT) + private final String endpoint; + @JsonProperty(TASK_MANAGERS_FIELD_STATUS) private final ExecutionState status; @@ -140,6 +144,7 @@ public static class TaskManagersInfo { @JsonCreator public TaskManagersInfo( @JsonProperty(TASK_MANAGERS_FIELD_HOST) String host, + @JsonProperty(TASK_MANAGERS_FIELD_ENDPOINT) String endpoint, @JsonProperty(TASK_MANAGERS_FIELD_STATUS) ExecutionState status, @JsonProperty(TASK_MANAGERS_FIELD_START_TIME) long startTime, @JsonProperty(TASK_MANAGERS_FIELD_END_TIME) long endTime, @@ -151,6 +156,7 @@ public TaskManagersInfo( @JsonProperty(TASK_MANAGERS_FIELD_AGGREGATED) AggregatedTaskDetailsInfo aggregated) { this.host = checkNotNull(host); + this.endpoint = checkNotNull(endpoint); this.status = checkNotNull(status); this.startTime = startTime; this.endTime = endTime; @@ -171,6 +177,7 @@ public boolean equals(Object o) { } TaskManagersInfo that = (TaskManagersInfo) o; return Objects.equals(host, that.host) + && Objects.equals(endpoint, that.endpoint) && Objects.equals(status, that.status) && startTime == that.startTime && endTime == that.endTime @@ -185,6 +192,7 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash( host, + endpoint, status, startTime, endTime, diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/MessageHeaders.java b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/MessageHeaders.java index 63c540834934f..122429e3c0d49 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/MessageHeaders.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/MessageHeaders.java @@ -78,12 +78,15 @@ default Collection> getResponseTypeParameters() { * @return short description */ default String operationId() { + final String className = getClass().getSimpleName(); + if (getHttpMethod() != HttpMethodWrapper.GET) { throw new UnsupportedOperationException( - "The default implementation is only supported for GET calls. Please override 'operationId()'."); + "The default implementation is only supported for GET calls. Please override 'operationId()' in '" + + className + + "'."); } - final String className = getClass().getSimpleName(); final int headersSuffixStart = className.lastIndexOf("Headers"); if (headersSuffixStart == -1) { throw new IllegalStateException( diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/SubtasksTimesInfo.java b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/SubtasksTimesInfo.java index 7da481f2ad9f8..67cf50778e8cb 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/SubtasksTimesInfo.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/SubtasksTimesInfo.java @@ -92,7 +92,8 @@ public int hashCode() { public static final class SubtaskTimeInfo { public static final String FIELD_NAME_SUBTASK = "subtask"; - public static final String FIELD_NAME_HOST = "host"; + @Deprecated public static final String FIELD_NAME_HOST = "host"; + public static final String FIELD_NAME_ENDPOINT = "endpoint"; public static final String FIELD_NAME_DURATION = "duration"; public static final String FIELD_NAME_TIMESTAMPS = "timestamps"; @@ -102,6 +103,9 @@ public static final class SubtaskTimeInfo { @JsonProperty(FIELD_NAME_HOST) private final String host; + @JsonProperty(FIELD_NAME_ENDPOINT) + private final String endpoint; + @JsonProperty(FIELD_NAME_DURATION) private final long duration; @@ -111,10 +115,12 @@ public static final class SubtaskTimeInfo { public SubtaskTimeInfo( @JsonProperty(FIELD_NAME_SUBTASK) int subtask, @JsonProperty(FIELD_NAME_HOST) String host, + @JsonProperty(FIELD_NAME_ENDPOINT) String endpoint, @JsonProperty(FIELD_NAME_DURATION) long duration, @JsonProperty(FIELD_NAME_TIMESTAMPS) Map timestamps) { this.subtask = subtask; this.host = checkNotNull(host); + this.endpoint = checkNotNull(endpoint); this.duration = duration; this.timestamps = checkNotNull(timestamps); } @@ -132,13 +138,14 @@ public boolean equals(Object o) { SubtaskTimeInfo that = (SubtaskTimeInfo) o; return subtask == that.subtask && Objects.equals(host, that.host) + && Objects.equals(endpoint, that.endpoint) && duration == that.duration && Objects.equals(timestamps, that.timestamps); } @Override public int hashCode() { - return Objects.hash(subtask, host, duration, timestamps); + return Objects.hash(subtask, host, endpoint, duration, timestamps); } } } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/checkpoints/CheckpointStatistics.java b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/checkpoints/CheckpointStatistics.java index fd8ed6f087be5..b38f57c987896 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/checkpoints/CheckpointStatistics.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/checkpoints/CheckpointStatistics.java @@ -41,6 +41,9 @@ import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.annotation.JsonDeserialize; import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.v3.oas.annotations.media.DiscriminatorMapping; +import io.swagger.v3.oas.annotations.media.Schema; + import javax.annotation.Nullable; import java.util.Collection; @@ -64,6 +67,19 @@ value = CheckpointStatistics.PendingCheckpointStatistics.class, name = "in_progress") }) +@Schema( + discriminatorProperty = "className", + discriminatorMapping = { + @DiscriminatorMapping( + value = "completed", + schema = CheckpointStatistics.CompletedCheckpointStatistics.class), + @DiscriminatorMapping( + value = "failed", + schema = CheckpointStatistics.FailedCheckpointStatistics.class), + @DiscriminatorMapping( + value = "in_progress", + schema = CheckpointStatistics.PendingCheckpointStatistics.class), + }) public class CheckpointStatistics implements ResponseBody { public static final String FIELD_NAME_ID = "id"; diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/checkpoints/SubtaskCheckpointStatistics.java b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/checkpoints/SubtaskCheckpointStatistics.java index 642509a965c7e..613916eb3d762 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/checkpoints/SubtaskCheckpointStatistics.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/checkpoints/SubtaskCheckpointStatistics.java @@ -23,6 +23,9 @@ import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.annotation.JsonSubTypes; import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.swagger.v3.oas.annotations.media.DiscriminatorMapping; +import io.swagger.v3.oas.annotations.media.Schema; + import java.util.Objects; /** Checkpoint statistics for a subtask. */ @@ -38,6 +41,17 @@ value = SubtaskCheckpointStatistics.PendingSubtaskCheckpointStatistics.class, name = "pending") }) +@Schema( + discriminatorProperty = "className", + discriminatorMapping = { + @DiscriminatorMapping( + value = "completed", + schema = + SubtaskCheckpointStatistics.CompletedSubtaskCheckpointStatistics.class), + @DiscriminatorMapping( + value = "pending", + schema = SubtaskCheckpointStatistics.PendingSubtaskCheckpointStatistics.class), + }) public class SubtaskCheckpointStatistics { public static final String FIELD_NAME_INDEX = "index"; diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/job/SubtaskExecutionAttemptDetailsInfo.java b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/job/SubtaskExecutionAttemptDetailsInfo.java index 1a8463ab5597c..df0fed46d0b94 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/job/SubtaskExecutionAttemptDetailsInfo.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/job/SubtaskExecutionAttemptDetailsInfo.java @@ -54,7 +54,9 @@ public class SubtaskExecutionAttemptDetailsInfo implements ResponseBody { public static final String FIELD_NAME_ATTEMPT = "attempt"; - public static final String FIELD_NAME_HOST = "host"; + @Deprecated public static final String FIELD_NAME_HOST = "host"; + + public static final String FIELD_NAME_ENDPOINT = "endpoint"; public static final String FIELD_NAME_START_TIME = "start-time"; @@ -84,6 +86,9 @@ public class SubtaskExecutionAttemptDetailsInfo implements ResponseBody { @JsonProperty(FIELD_NAME_HOST) private final String host; + @JsonProperty(FIELD_NAME_ENDPOINT) + private final String endpoint; + @JsonProperty(FIELD_NAME_START_TIME) private final long startTime; @@ -118,6 +123,7 @@ public SubtaskExecutionAttemptDetailsInfo( @JsonProperty(FIELD_NAME_STATUS) ExecutionState status, @JsonProperty(FIELD_NAME_ATTEMPT) int attempt, @JsonProperty(FIELD_NAME_HOST) String host, + @JsonProperty(FIELD_NAME_ENDPOINT) String endpoint, @JsonProperty(FIELD_NAME_START_TIME) long startTime, @JsonProperty(FIELD_NAME_END_TIME) long endTime, @JsonProperty(FIELD_NAME_DURATION) long duration, @@ -131,6 +137,7 @@ public SubtaskExecutionAttemptDetailsInfo( this.status = Preconditions.checkNotNull(status); this.attempt = attempt; this.host = Preconditions.checkNotNull(host); + this.endpoint = Preconditions.checkNotNull(endpoint); this.startTime = startTime; this.startTimeCompatible = startTime; this.endTime = endTime; @@ -153,10 +160,15 @@ public int getAttempt() { return attempt; } + @Deprecated public String getHost() { return host; } + public String getEndpoint() { + return endpoint; + } + public long getStartTime() { return startTime; } @@ -203,7 +215,8 @@ public static SubtaskExecutionAttemptDetailsInfo create( final long now = System.currentTimeMillis(); final TaskManagerLocation location = execution.getAssignedResourceLocation(); - final String locationString = location == null ? "(unassigned)" : location.getHostname(); + final String host = location == null ? "(unassigned)" : location.getHostname(); + final String endpoint = location == null ? "(unassigned)" : location.getEndpoint(); String taskmanagerId = location == null ? "(unassigned)" : location.getResourceID().toString(); @@ -235,7 +248,8 @@ public static SubtaskExecutionAttemptDetailsInfo create( execution.getParallelSubtaskIndex(), status, execution.getAttemptNumber(), - locationString, + host, + endpoint, startTime, endTime, duration, @@ -260,6 +274,7 @@ public boolean equals(Object o) { && status == that.status && attempt == that.attempt && Objects.equals(host, that.host) + && Objects.equals(endpoint, that.endpoint) && startTime == that.startTime && startTimeCompatible == that.startTimeCompatible && endTime == that.endTime @@ -277,6 +292,7 @@ public int hashCode() { status, attempt, host, + endpoint, startTime, startTimeCompatible, endTime, diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/job/SubtasksAllAccumulatorsInfo.java b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/job/SubtasksAllAccumulatorsInfo.java index e245a19388b53..8035ff2a35512 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/job/SubtasksAllAccumulatorsInfo.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/messages/job/SubtasksAllAccumulatorsInfo.java @@ -90,7 +90,8 @@ public int hashCode() { public static class SubtaskAccumulatorsInfo { public static final String FIELD_NAME_SUBTASK_INDEX = "subtask"; public static final String FIELD_NAME_ATTEMPT_NUM = "attempt"; - public static final String FIELD_NAME_HOST = "host"; + @Deprecated public static final String FIELD_NAME_HOST = "host"; + public static final String FIELD_NAME_ENDPOINT = "endpoint"; public static final String FIELD_NAME_USER_ACCUMULATORS = "user-accumulators"; @JsonProperty(FIELD_NAME_SUBTASK_INDEX) @@ -102,6 +103,9 @@ public static class SubtaskAccumulatorsInfo { @JsonProperty(FIELD_NAME_HOST) private final String host; + @JsonProperty(FIELD_NAME_ENDPOINT) + private final String endpoint; + @JsonProperty(FIELD_NAME_USER_ACCUMULATORS) private final Collection userAccumulators; @@ -110,12 +114,14 @@ public SubtaskAccumulatorsInfo( @JsonProperty(FIELD_NAME_SUBTASK_INDEX) int subtaskIndex, @JsonProperty(FIELD_NAME_ATTEMPT_NUM) int attemptNum, @JsonProperty(FIELD_NAME_HOST) String host, + @JsonProperty(FIELD_NAME_ENDPOINT) String endpoint, @JsonProperty(FIELD_NAME_USER_ACCUMULATORS) Collection userAccumulators) { this.subtaskIndex = subtaskIndex; this.attemptNum = attemptNum; this.host = Preconditions.checkNotNull(host); + this.endpoint = Preconditions.checkNotNull(endpoint); this.userAccumulators = Preconditions.checkNotNull(userAccumulators); } @@ -131,12 +137,13 @@ public boolean equals(Object o) { return subtaskIndex == that.subtaskIndex && attemptNum == that.attemptNum && Objects.equals(host, that.host) + && Objects.equals(endpoint, that.endpoint) && Objects.equals(userAccumulators, that.userAccumulators); } @Override public int hashCode() { - return Objects.hash(subtaskIndex, attemptNum, host, userAccumulators); + return Objects.hash(subtaskIndex, attemptNum, host, endpoint, userAccumulators); } } } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/DefaultSchedulerFactory.java b/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/DefaultSchedulerFactory.java index 25c623e18461e..a3b043eb987e5 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/DefaultSchedulerFactory.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/DefaultSchedulerFactory.java @@ -21,6 +21,7 @@ import org.apache.flink.api.common.JobStatus; import org.apache.flink.api.common.time.Time; +import org.apache.flink.configuration.CheckpointingOptions; import org.apache.flink.configuration.Configuration; import org.apache.flink.configuration.JobManagerOptions; import org.apache.flink.core.failure.FailureEnricher; @@ -122,6 +123,11 @@ public SchedulerNG createInstance( shuffleMaster, partitionTracker); + final CheckpointsCleaner checkpointsCleaner = + new CheckpointsCleaner( + jobMasterConfiguration.getBoolean( + CheckpointingOptions.CLEANER_PARALLEL_MODE)); + return new DefaultScheduler( log, jobGraph, @@ -130,7 +136,7 @@ public SchedulerNG createInstance( schedulerComponents.getStartUpAction(), new ScheduledExecutorServiceAdapter(futureExecutor), userCodeLoader, - new CheckpointsCleaner(), + checkpointsCleaner, checkpointRecoveryFactory, jobManagerJobMetricGroup, schedulerComponents.getSchedulingStrategyFactory(), diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/AdaptiveScheduler.java b/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/AdaptiveScheduler.java index 4ee22c9584840..67615ea79cc29 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/AdaptiveScheduler.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/AdaptiveScheduler.java @@ -108,6 +108,7 @@ import org.apache.flink.runtime.scheduler.adaptive.allocator.SlotAllocator; import org.apache.flink.runtime.scheduler.adaptive.allocator.VertexParallelism; import org.apache.flink.runtime.scheduler.adaptive.scalingpolicy.EnforceMinimalIncreaseRescalingController; +import org.apache.flink.runtime.scheduler.adaptive.scalingpolicy.EnforceParallelismChangeRescalingController; import org.apache.flink.runtime.scheduler.adaptive.scalingpolicy.RescalingController; import org.apache.flink.runtime.scheduler.exceptionhistory.ExceptionHistoryEntry; import org.apache.flink.runtime.scheduler.exceptionhistory.RootExceptionHistoryEntry; @@ -204,6 +205,8 @@ public class AdaptiveScheduler private final RescalingController rescalingController; + private final RescalingController forceRescalingController; + private final Duration initialResourceAllocationTimeout; private final Duration resourceStabilizationTimeout; @@ -294,6 +297,8 @@ public AdaptiveScheduler( this.rescalingController = new EnforceMinimalIncreaseRescalingController(configuration); + this.forceRescalingController = new EnforceParallelismChangeRescalingController(); + this.initialResourceAllocationTimeout = initialResourceAllocationTimeout; this.resourceStabilizationTimeout = resourceStabilizationTimeout; @@ -1162,16 +1167,24 @@ private ExecutionGraph createExecutionGraphAndRestoreState( LOG); } + /** + * In regular mode, rescale the job if added resource meets {@link + * JobManagerOptions#MIN_PARALLELISM_INCREASE}. In force mode rescale if the parallelism has + * changed. + */ @Override - public boolean shouldRescale(ExecutionGraph executionGraph) { + public boolean shouldRescale(ExecutionGraph executionGraph, boolean forceRescale) { final Optional maybeNewParallelism = slotAllocator.determineParallelism( jobInformation, declarativeSlotPool.getAllSlotsInformation()); return maybeNewParallelism .filter( - vertexParallelism -> - rescalingController.shouldRescale( - getCurrentParallelism(executionGraph), vertexParallelism)) + vertexParallelism -> { + RescalingController rescalingControllerToUse = + forceRescale ? forceRescalingController : rescalingController; + return rescalingControllerToUse.shouldRescale( + getCurrentParallelism(executionGraph), vertexParallelism); + }) .isPresent(); } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/Executing.java b/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/Executing.java index 9cdb9afc7f286..10f872bbbd578 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/Executing.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/Executing.java @@ -19,6 +19,8 @@ package org.apache.flink.runtime.scheduler.adaptive; import org.apache.flink.api.common.JobStatus; +import org.apache.flink.configuration.Configuration; +import org.apache.flink.configuration.JobManagerOptions; import org.apache.flink.core.execution.SavepointFormatType; import org.apache.flink.runtime.JobException; import org.apache.flink.runtime.checkpoint.CheckpointScheduling; @@ -39,6 +41,7 @@ import javax.annotation.Nullable; import java.time.Duration; +import java.time.Instant; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledFuture; @@ -47,6 +50,11 @@ class Executing extends StateWithExecutionGraph implements ResourceListener { private final Context context; + private final Instant lastRescale; + // only one schedule at the time + private boolean rescaleScheduled = false; + private final Duration scalingIntervalMin; + @Nullable private final Duration scalingIntervalMax; Executing( ExecutionGraph executionGraph, @@ -55,7 +63,10 @@ class Executing extends StateWithExecutionGraph implements ResourceListener { Logger logger, Context context, ClassLoader userCodeClassLoader, - List failureCollection) { + List failureCollection, + Duration scalingIntervalMin, + @Nullable Duration scalingIntervalMax, + Instant lastRescale) { super( context, executionGraph, @@ -67,11 +78,29 @@ class Executing extends StateWithExecutionGraph implements ResourceListener { this.context = context; Preconditions.checkState( executionGraph.getState() == JobStatus.RUNNING, "Assuming running execution graph"); + this.scalingIntervalMin = scalingIntervalMin; + this.scalingIntervalMax = scalingIntervalMax; + // Executing is recreated with each restart (when we rescale) + // we consider the first execution of the pipeline as a rescale event + this.lastRescale = lastRescale; + Preconditions.checkState( + !scalingIntervalMin.isNegative(), + "%s must be positive integer or 0", + JobManagerOptions.SCHEDULER_SCALING_INTERVAL_MIN.key()); + if (scalingIntervalMax != null) { + Preconditions.checkState( + scalingIntervalMax.compareTo(scalingIntervalMin) > 0, + "%s(%d) must be greater than %s(%d)", + JobManagerOptions.SCHEDULER_SCALING_INTERVAL_MAX.key(), + scalingIntervalMax, + JobManagerOptions.SCHEDULER_SCALING_INTERVAL_MIN.key(), + scalingIntervalMin); + } deploy(); // check if new resources have come available in the meantime - context.runIfState(this, this::maybeRescale, Duration.ZERO); + rescaleWhenCooldownPeriodIsOver(); } @Override @@ -124,23 +153,74 @@ private void handleDeploymentFailure(ExecutionVertex executionVertex, JobExcepti @Override public void onNewResourcesAvailable() { - maybeRescale(); + rescaleWhenCooldownPeriodIsOver(); } @Override public void onNewResourceRequirements() { - maybeRescale(); + rescaleWhenCooldownPeriodIsOver(); } + /** Force rescaling as long as the target parallelism is different from the current one. */ + private void forceRescale() { + if (context.shouldRescale(getExecutionGraph(), true)) { + getLogger() + .info( + "Added resources are still there after {} time({}), force a rescale.", + JobManagerOptions.SCHEDULER_SCALING_INTERVAL_MAX.key(), + scalingIntervalMax); + context.goToRestarting( + getExecutionGraph(), + getExecutionGraphHandler(), + getOperatorCoordinatorHandler(), + Duration.ofMillis(0L), + getFailures()); + } + } + + /** + * Rescale the job if {@link Context#shouldRescale} is true. Otherwise, force a rescale using + * {@link Executing#forceRescale()} after {@link + * JobManagerOptions#SCHEDULER_SCALING_INTERVAL_MAX}. + */ private void maybeRescale() { - if (context.shouldRescale(getExecutionGraph())) { - getLogger().info("Can change the parallelism of job. Restarting job."); + rescaleScheduled = false; + if (context.shouldRescale(getExecutionGraph(), false)) { + getLogger().info("Can change the parallelism of the job. Restarting the job."); context.goToRestarting( getExecutionGraph(), getExecutionGraphHandler(), getOperatorCoordinatorHandler(), Duration.ofMillis(0L), getFailures()); + } else if (scalingIntervalMax != null) { + getLogger() + .info( + "The longer the pipeline runs, the more the (small) resource gain is worth the restarting time. " + + "Last resource added does not meet {}, force a rescale after {} time({}) if the resource is still there.", + JobManagerOptions.MIN_PARALLELISM_INCREASE, + JobManagerOptions.SCHEDULER_SCALING_INTERVAL_MAX.key(), + scalingIntervalMax); + if (timeSinceLastRescale().compareTo(scalingIntervalMax) > 0) { + forceRescale(); + } else { + // schedule a force rescale in JobManagerOptions.SCHEDULER_SCALING_INTERVAL_MAX time + context.runIfState(this, this::forceRescale, scalingIntervalMax); + } + } + } + + private Duration timeSinceLastRescale() { + return Duration.between(lastRescale, Instant.now()); + } + + private void rescaleWhenCooldownPeriodIsOver() { + if (timeSinceLastRescale().compareTo(scalingIntervalMin) > 0) { + maybeRescale(); + } else if (!rescaleScheduled) { + rescaleScheduled = true; + // schedule maybeRescale resetting the cooldown period + context.runIfState(this, this::maybeRescale, scalingIntervalMin); } } @@ -196,9 +276,10 @@ interface Context * Asks if we should rescale the currently executing job. * * @param executionGraph executionGraph for making the scaling decision. + * @param forceRescale should we force rescaling * @return true, if we should rescale */ - boolean shouldRescale(ExecutionGraph executionGraph); + boolean shouldRescale(ExecutionGraph executionGraph, boolean forceRescale); /** * Runs the given action after a delay if the state at this time equals the expected state. @@ -244,6 +325,7 @@ public Class getStateClass() { } public Executing getState() { + final Configuration jobConfiguration = executionGraph.getJobConfiguration(); return new Executing( executionGraph, executionGraphHandler, @@ -251,7 +333,10 @@ public Executing getState() { log, context, userCodeClassLoader, - failureCollection); + failureCollection, + jobConfiguration.get(JobManagerOptions.SCHEDULER_SCALING_INTERVAL_MIN), + jobConfiguration.get(JobManagerOptions.SCHEDULER_SCALING_INTERVAL_MAX), + Instant.now()); } } } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/scalingpolicy/EnforceMinimalIncreaseRescalingController.java b/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/scalingpolicy/EnforceMinimalIncreaseRescalingController.java index b02907acb7fd6..edf82d9a42fbd 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/scalingpolicy/EnforceMinimalIncreaseRescalingController.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/scalingpolicy/EnforceMinimalIncreaseRescalingController.java @@ -24,8 +24,8 @@ import static org.apache.flink.configuration.JobManagerOptions.MIN_PARALLELISM_INCREASE; /** - * Simple scaling policy for a reactive mode. The user can configure a minimum cumulative - * parallelism increase to allow a scale up. + * Simple scaling policy. The user can configure a minimum cumulative parallelism increase to allow + * a scale up. */ public class EnforceMinimalIncreaseRescalingController implements RescalingController { diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/scalingpolicy/EnforceParallelismChangeRescalingController.java b/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/scalingpolicy/EnforceParallelismChangeRescalingController.java new file mode 100644 index 0000000000000..94c11cf0250b3 --- /dev/null +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/adaptive/scalingpolicy/EnforceParallelismChangeRescalingController.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.runtime.scheduler.adaptive.scalingpolicy; + +import org.apache.flink.runtime.jobgraph.JobVertexID; +import org.apache.flink.runtime.scheduler.adaptive.allocator.VertexParallelism; + +/** + * Simple scaling policy. It just checks that the new parallelism is different (either increase or + * decrease) from the current parallelism. + */ +public class EnforceParallelismChangeRescalingController implements RescalingController { + @Override + public boolean shouldRescale( + VertexParallelism currentParallelism, VertexParallelism newParallelism) { + for (JobVertexID vertex : currentParallelism.getVertices()) { + int parallelismChange = + newParallelism.getParallelism(vertex) + - currentParallelism.getParallelism(vertex); + if (parallelismChange != 0) { + return true; + } + } + return false; + } +} diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/exceptionhistory/ExceptionHistoryEntry.java b/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/exceptionhistory/ExceptionHistoryEntry.java index b0ab00e26ac29..6435ff81762e6 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/exceptionhistory/ExceptionHistoryEntry.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/scheduler/exceptionhistory/ExceptionHistoryEntry.java @@ -237,6 +237,10 @@ public String getFQDNHostname() { return fqdnHostname; } + public String getEndpoint() { + return String.format("%s:%d", fqdnHostname, port); + } + @Override public String toString() { return new StringJoiner( diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/shuffle/ShuffleEnvironmentContext.java b/flink-runtime/src/main/java/org/apache/flink/runtime/shuffle/ShuffleEnvironmentContext.java index c50fcb0c0f89b..6024f60afdb66 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/shuffle/ShuffleEnvironmentContext.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/shuffle/ShuffleEnvironmentContext.java @@ -23,6 +23,7 @@ import org.apache.flink.metrics.MetricGroup; import org.apache.flink.runtime.clusterframework.types.ResourceID; import org.apache.flink.runtime.io.network.TaskEventPublisher; +import org.apache.flink.util.concurrent.ScheduledExecutor; import java.net.InetAddress; import java.util.concurrent.Executor; @@ -42,6 +43,7 @@ public class ShuffleEnvironmentContext { private final String[] tmpDirPaths; private final Executor ioExecutor; + private final ScheduledExecutor scheduledExecutor; public ShuffleEnvironmentContext( Configuration configuration, @@ -53,7 +55,8 @@ public ShuffleEnvironmentContext( String[] tmpDirPaths, TaskEventPublisher eventPublisher, MetricGroup parentMetricGroup, - Executor ioExecutor) { + Executor ioExecutor, + ScheduledExecutor scheduledExecutor) { this.configuration = checkNotNull(configuration); this.taskExecutorResourceId = checkNotNull(taskExecutorResourceId); this.networkMemorySize = networkMemorySize; @@ -62,6 +65,7 @@ public ShuffleEnvironmentContext( this.eventPublisher = checkNotNull(eventPublisher); this.parentMetricGroup = checkNotNull(parentMetricGroup); this.ioExecutor = ioExecutor; + this.scheduledExecutor = scheduledExecutor; this.numberOfSlots = numberOfSlots; this.tmpDirPaths = checkNotNull(tmpDirPaths); } @@ -98,6 +102,10 @@ public Executor getIoExecutor() { return ioExecutor; } + public ScheduledExecutor getScheduledExecutor() { + return scheduledExecutor; + } + public int getNumberOfSlots() { return numberOfSlots; } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/state/AbstractIncrementalStateHandle.java b/flink-runtime/src/main/java/org/apache/flink/runtime/state/AbstractIncrementalStateHandle.java new file mode 100644 index 0000000000000..85f12329f5715 --- /dev/null +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/state/AbstractIncrementalStateHandle.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.runtime.state; + +import javax.annotation.Nonnull; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +/** Abstract superclass for all {@link IncrementalKeyedStateHandle}. */ +public abstract class AbstractIncrementalStateHandle implements IncrementalKeyedStateHandle { + private static final long serialVersionUID = 1L; + + /** The checkpoint Id. */ + protected final long checkpointId; + + /** + * UUID to identify the backend which created this state handle. This is in creating the key for + * the {@link SharedStateRegistry}. + */ + protected final UUID backendIdentifier; + + /** The key-group range covered by this state handle. */ + protected final KeyGroupRange keyGroupRange; + + /** Shared state in the incremental checkpoint. */ + protected final List sharedState; + + /** Primary meta data state of the incremental checkpoint. */ + protected final StreamStateHandle metaStateHandle; + + /** Unique id for this state handle. */ + protected final StateHandleID stateHandleId; + + public AbstractIncrementalStateHandle( + UUID backendIdentifier, + KeyGroupRange keyGroupRange, + long checkpointId, + List sharedState, + StreamStateHandle metaStateHandle, + StateHandleID stateHandleId) { + this.checkpointId = checkpointId; + this.keyGroupRange = keyGroupRange; + this.backendIdentifier = backendIdentifier; + this.sharedState = sharedState; + this.metaStateHandle = metaStateHandle; + this.stateHandleId = stateHandleId; + } + + @Override + public long getCheckpointId() { + return checkpointId; + } + + @Nonnull + @Override + public UUID getBackendIdentifier() { + return backendIdentifier; + } + + @Override + public KeyGroupRange getKeyGroupRange() { + return keyGroupRange; + } + + @Nonnull + @Override + public List getSharedStateHandles() { + return sharedState; + } + + @Nonnull + @Override + public StreamStateHandle getMetaDataStateHandle() { + return metaStateHandle; + } + + @Override + public StateHandleID getStateHandleId() { + return stateHandleId; + } + + @Override + public KeyedStateHandle getIntersection(KeyGroupRange keyGroupRange) { + return KeyGroupRange.EMPTY_KEY_GROUP_RANGE.equals( + getKeyGroupRange().getIntersection(keyGroupRange)) + ? null + : this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AbstractIncrementalStateHandle that = (AbstractIncrementalStateHandle) o; + return Objects.equals(stateHandleId, that.stateHandleId); + } + + @Override + public int hashCode() { + return stateHandleId.hashCode(); + } + + @Override + public String toString() { + return "AbstractIncrementalStateHandle{" + + "checkpointId=" + + checkpointId + + ", backendIdentifier=" + + backendIdentifier + + ", keyGroupRange=" + + keyGroupRange + + ", sharedState=" + + sharedState + + ", metaStateHandle=" + + metaStateHandle + + ", stateHandleId=" + + stateHandleId + + '}'; + } +} diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/state/DirectoryKeyedStateHandle.java b/flink-runtime/src/main/java/org/apache/flink/runtime/state/DirectoryKeyedStateHandle.java deleted file mode 100644 index 3f922a2e9680c..0000000000000 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/state/DirectoryKeyedStateHandle.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.flink.runtime.state; - -import javax.annotation.Nonnull; - -/** - * This class is a keyed state handle based on a directory. It combines a {@link - * DirectoryStateHandle} and a {@link KeyGroupRange}. - */ -public class DirectoryKeyedStateHandle implements KeyedStateHandle { - - private static final long serialVersionUID = 1L; - - /** The directory state handle. */ - @Nonnull private final DirectoryStateHandle directoryStateHandle; - - /** The key-group range. */ - @Nonnull private final KeyGroupRange keyGroupRange; - - private final StateHandleID stateHandleId; - - public DirectoryKeyedStateHandle( - @Nonnull DirectoryStateHandle directoryStateHandle, - @Nonnull KeyGroupRange keyGroupRange) { - - this.directoryStateHandle = directoryStateHandle; - this.keyGroupRange = keyGroupRange; - this.stateHandleId = StateHandleID.randomStateHandleId(); - } - - @Nonnull - public DirectoryStateHandle getDirectoryStateHandle() { - return directoryStateHandle; - } - - @Nonnull - @Override - public KeyGroupRange getKeyGroupRange() { - return keyGroupRange; - } - - @Override - public void discardState() throws Exception { - directoryStateHandle.discardState(); - } - - @Override - public long getStateSize() { - return directoryStateHandle.getStateSize(); - } - - @Override - public long getCheckpointedSize() { - return getStateSize(); - } - - @Override - public KeyedStateHandle getIntersection(KeyGroupRange otherKeyGroupRange) { - return this.keyGroupRange.getIntersection(otherKeyGroupRange).getNumberOfKeyGroups() > 0 - ? this - : null; - } - - @Override - public StateHandleID getStateHandleId() { - return stateHandleId; - } - - @Override - public void registerSharedStates(SharedStateRegistry stateRegistry, long checkpointID) { - // Nothing to do, this is for local use only. - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - DirectoryKeyedStateHandle that = (DirectoryKeyedStateHandle) o; - - if (!getDirectoryStateHandle().equals(that.getDirectoryStateHandle())) { - return false; - } - return getKeyGroupRange().equals(that.getKeyGroupRange()); - } - - @Override - public int hashCode() { - int result = getDirectoryStateHandle().hashCode(); - result = 31 * result + getKeyGroupRange().hashCode(); - return result; - } - - @Override - public String toString() { - return "DirectoryKeyedStateHandle{" - + "directoryStateHandle=" - + directoryStateHandle - + ", keyGroupRange=" - + keyGroupRange - + '}'; - } -} diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/state/IncrementalKeyedStateHandle.java b/flink-runtime/src/main/java/org/apache/flink/runtime/state/IncrementalKeyedStateHandle.java index 9ce3cbd332fcb..f162efa936bac 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/state/IncrementalKeyedStateHandle.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/state/IncrementalKeyedStateHandle.java @@ -42,6 +42,9 @@ public interface IncrementalKeyedStateHandle @Nonnull List getSharedStateHandles(); + @Nonnull + StreamStateHandle getMetaDataStateHandle(); + /** A Holder of StreamStateHandle and the corresponding localPath. */ final class HandleAndLocalPath implements Serializable { diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/state/IncrementalLocalKeyedStateHandle.java b/flink-runtime/src/main/java/org/apache/flink/runtime/state/IncrementalLocalKeyedStateHandle.java index d782cf886bd5a..ac457f00622b6 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/state/IncrementalLocalKeyedStateHandle.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/state/IncrementalLocalKeyedStateHandle.java @@ -32,22 +32,11 @@ * DirectoryStateHandle} that represents the directory of the native RocksDB snapshot, the key * groups, and a stream state handle for Flink's state meta data file. */ -public class IncrementalLocalKeyedStateHandle extends DirectoryKeyedStateHandle - implements IncrementalKeyedStateHandle { +public class IncrementalLocalKeyedStateHandle extends AbstractIncrementalStateHandle { private static final long serialVersionUID = 1L; - /** Id of the checkpoint that created this state handle. */ - @Nonnegative private final long checkpointId; - - /** UUID to identify the backend which created this state handle. */ - @Nonnull private final UUID backendIdentifier; - - /** Handle to Flink's state meta data. */ - @Nonnull private final StreamStateHandle metaDataState; - - /** All shared state handles and the corresponding localPath used by the checkpoint. */ - @Nonnull private final List sharedState; + private final DirectoryStateHandle directoryStateHandle; public IncrementalLocalKeyedStateHandle( @Nonnull UUID backendIdentifier, @@ -57,21 +46,14 @@ public IncrementalLocalKeyedStateHandle( @Nonnull StreamStateHandle metaDataState, @Nonnull List sharedState) { - super(directoryStateHandle, keyGroupRange); - this.backendIdentifier = backendIdentifier; - this.checkpointId = checkpointId; - this.metaDataState = metaDataState; - this.sharedState = new ArrayList<>(sharedState); - } - - @Nonnull - public StreamStateHandle getMetaDataState() { - return metaDataState; - } - - @Override - public long getCheckpointId() { - return checkpointId; + super( + backendIdentifier, + keyGroupRange, + checkpointId, + new ArrayList<>(sharedState), + metaDataState, + StateHandleID.randomStateHandleId()); + this.directoryStateHandle = directoryStateHandle; } @Override @@ -81,52 +63,23 @@ public CheckpointBoundKeyedStateHandle rebound(long checkpointId) { checkpointId, getDirectoryStateHandle(), getKeyGroupRange(), - getMetaDataState(), + getMetaDataStateHandle(), getSharedStateHandles()); } - @Override - @Nonnull - public UUID getBackendIdentifier() { - return backendIdentifier; - } - - @Override - @Nonnull - public List getSharedStateHandles() { - return sharedState; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - if (!super.equals(o)) { - return false; - } - - IncrementalLocalKeyedStateHandle that = (IncrementalLocalKeyedStateHandle) o; - - return getMetaDataState().equals(that.getMetaDataState()); - } - @Override public void discardState() throws Exception { Exception collectedEx = null; try { - super.discardState(); + directoryStateHandle.discardState(); } catch (Exception e) { collectedEx = e; } try { - metaDataState.discardState(); + metaStateHandle.discardState(); } catch (Exception e) { collectedEx = ExceptionUtils.firstOrSuppressed(e, collectedEx); } @@ -138,22 +91,30 @@ public void discardState() throws Exception { @Override public long getStateSize() { - return super.getStateSize() + metaDataState.getStateSize(); - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + getMetaDataState().hashCode(); - return result; + return directoryStateHandle.getStateSize() + metaStateHandle.getStateSize(); } @Override public String toString() { return "IncrementalLocalKeyedStateHandle{" - + "metaDataState=" - + metaDataState + + "directoryStateHandle=" + + directoryStateHandle + "} " + super.toString(); } + + @Override + public void registerSharedStates(SharedStateRegistry stateRegistry, long checkpointID) { + // Nothing to do, this is for local use only. + } + + @Override + public long getCheckpointedSize() { + return directoryStateHandle.getStateSize(); + } + + @Nonnull + public DirectoryStateHandle getDirectoryStateHandle() { + return directoryStateHandle; + } } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/state/IncrementalRemoteKeyedStateHandle.java b/flink-runtime/src/main/java/org/apache/flink/runtime/state/IncrementalRemoteKeyedStateHandle.java index fd524d74f2adc..41b9a0466cc2b 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/state/IncrementalRemoteKeyedStateHandle.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/state/IncrementalRemoteKeyedStateHandle.java @@ -56,7 +56,7 @@ * should not be called from production code. This means this class is also not suited to serve as a * key, e.g. in hash maps. */ -public class IncrementalRemoteKeyedStateHandle implements IncrementalKeyedStateHandle { +public class IncrementalRemoteKeyedStateHandle extends AbstractIncrementalStateHandle { public static final long UNKNOWN_CHECKPOINTED_SIZE = -1L; @@ -65,31 +65,11 @@ public class IncrementalRemoteKeyedStateHandle implements IncrementalKeyedStateH private static final long serialVersionUID = -8328808513197388231L; - /** - * UUID to identify the backend which created this state handle. This is in creating the key for - * the {@link SharedStateRegistry}. - */ - private final UUID backendIdentifier; - - /** The key-group range covered by this state handle. */ - private final KeyGroupRange keyGroupRange; - - /** The checkpoint Id. */ - private final long checkpointId; - - /** Shared state in the incremental checkpoint. */ - private final List sharedState; - /** Private state in the incremental checkpoint. */ private final List privateState; - /** Primary meta data state of the incremental checkpoint. */ - private final StreamStateHandle metaStateHandle; - private final long persistedSizeOfThisCheckpoint; - private final StateHandleID stateHandleId; - /** * Once the shared states are registered, it is the {@link SharedStateRegistry}'s responsibility * to cleanup those shared states. But in the cases where the state handle is discarded before @@ -147,18 +127,19 @@ protected IncrementalRemoteKeyedStateHandle( StreamStateHandle metaStateHandle, long persistedSizeOfThisCheckpoint, StateHandleID stateHandleId) { - this.backendIdentifier = Preconditions.checkNotNull(backendIdentifier); - this.keyGroupRange = Preconditions.checkNotNull(keyGroupRange); - this.checkpointId = checkpointId; - this.sharedState = Preconditions.checkNotNull(sharedState); + super( + Preconditions.checkNotNull(backendIdentifier), + Preconditions.checkNotNull(keyGroupRange), + checkpointId, + sharedState, + metaStateHandle, + stateHandleId); this.privateState = Preconditions.checkNotNull(privateState); - this.metaStateHandle = Preconditions.checkNotNull(metaStateHandle); this.sharedStateRegistry = null; this.persistedSizeOfThisCheckpoint = persistedSizeOfThisCheckpoint == UNKNOWN_CHECKPOINTED_SIZE ? getStateSize() : persistedSizeOfThisCheckpoint; - this.stateHandleId = stateHandleId; } public static IncrementalRemoteKeyedStateHandle restore( @@ -181,16 +162,6 @@ public static IncrementalRemoteKeyedStateHandle restore( stateHandleId); } - @Override - public KeyGroupRange getKeyGroupRange() { - return keyGroupRange; - } - - @Override - public long getCheckpointId() { - return checkpointId; - } - @Override public CheckpointBoundKeyedStateHandle rebound(long checkpointId) { return new IncrementalRemoteKeyedStateHandle( @@ -212,15 +183,6 @@ public List getPrivateState() { return privateState; } - public StreamStateHandle getMetaStateHandle() { - return metaStateHandle; - } - - @Nonnull - public UUID getBackendIdentifier() { - return backendIdentifier; - } - @Nonnull @Override public List getSharedStateHandles() { @@ -231,19 +193,6 @@ public SharedStateRegistry getSharedStateRegistry() { return sharedStateRegistry; } - @Override - public KeyedStateHandle getIntersection(KeyGroupRange keyGroupRange) { - return KeyGroupRange.EMPTY_KEY_GROUP_RANGE.equals( - this.keyGroupRange.getIntersection(keyGroupRange)) - ? null - : this; - } - - @Override - public StateHandleID getStateHandleId() { - return stateHandleId; - } - @Override public void discardState() throws Exception { @@ -357,75 +306,16 @@ IncrementalRemoteKeyedStateHandle copy() { stateHandleId); } - /** - * This method is should only be called in tests! This should never serve as key in a hash map. - */ - @VisibleForTesting - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - IncrementalRemoteKeyedStateHandle that = (IncrementalRemoteKeyedStateHandle) o; - - if (!getStateHandleId().equals(that.getStateHandleId())) { - return false; - } - if (getCheckpointId() != that.getCheckpointId()) { - return false; - } - if (!getBackendIdentifier().equals(that.getBackendIdentifier())) { - return false; - } - if (!getKeyGroupRange().equals(that.getKeyGroupRange())) { - return false; - } - if (!getSharedState().equals(that.getSharedState())) { - return false; - } - if (!getPrivateState().equals(that.getPrivateState())) { - return false; - } - return getMetaStateHandle().equals(that.getMetaStateHandle()); - } - - /** This method should only be called in tests! This should never serve as key in a hash map. */ - @VisibleForTesting - @Override - public int hashCode() { - int result = getBackendIdentifier().hashCode(); - result = 31 * result + getKeyGroupRange().hashCode(); - result = 31 * result + (int) (getCheckpointId() ^ (getCheckpointId() >>> 32)); - result = 31 * result + getSharedState().hashCode(); - result = 31 * result + getPrivateState().hashCode(); - result = 31 * result + getMetaStateHandle().hashCode(); - result = 31 * result + getStateHandleId().hashCode(); - return result; - } - @Override public String toString() { return "IncrementalRemoteKeyedStateHandle{" - + "backendIdentifier=" - + backendIdentifier - + ", stateHandleId=" - + stateHandleId - + ", keyGroupRange=" - + keyGroupRange - + ", checkpointId=" - + checkpointId - + ", sharedState=" - + sharedState - + ", privateState=" + + "privateState=" + privateState - + ", metaStateHandle=" - + metaStateHandle + + ", persistedSizeOfThisCheckpoint=" + + persistedSizeOfThisCheckpoint + ", registered=" + (sharedStateRegistry != null) - + '}'; + + "} " + + super.toString(); } } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/state/ListDelimitedSerializer.java b/flink-runtime/src/main/java/org/apache/flink/runtime/state/ListDelimitedSerializer.java index ddfb9163cb085..0574c6f491ea1 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/state/ListDelimitedSerializer.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/state/ListDelimitedSerializer.java @@ -21,7 +21,6 @@ import org.apache.flink.api.common.typeutils.TypeSerializer; import org.apache.flink.core.memory.DataInputDeserializer; import org.apache.flink.core.memory.DataOutputSerializer; -import org.apache.flink.util.FlinkRuntimeException; import org.apache.flink.util.Preconditions; import java.io.IOException; @@ -39,7 +38,8 @@ public final class ListDelimitedSerializer { private final DataInputDeserializer dataInputView = new DataInputDeserializer(); private final DataOutputSerializer dataOutputView = new DataOutputSerializer(128); - public List deserializeList(byte[] valueBytes, TypeSerializer elementSerializer) { + public List deserializeList(byte[] valueBytes, TypeSerializer elementSerializer) + throws IOException { if (valueBytes == null) { return null; } @@ -76,17 +76,13 @@ public byte[] serializeList(List valueList, TypeSerializer elementSeri /** Deserializes a single element from a serialized list. */ public static T deserializeNextElement( - DataInputDeserializer in, TypeSerializer elementSerializer) { - try { + DataInputDeserializer in, TypeSerializer elementSerializer) throws IOException { + if (in.available() > 0) { + T element = elementSerializer.deserialize(in); if (in.available() > 0) { - T element = elementSerializer.deserialize(in); - if (in.available() > 0) { - in.readByte(); - } - return element; + in.readByte(); } - } catch (IOException e) { - throw new FlinkRuntimeException("Unexpected list element deserialization failure", e); + return element; } return null; } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/state/PartitionableListState.java b/flink-runtime/src/main/java/org/apache/flink/runtime/state/PartitionableListState.java index 7cb363b91c332..35d6c78e7eff5 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/state/PartitionableListState.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/state/PartitionableListState.java @@ -29,6 +29,8 @@ import java.util.ArrayList; import java.util.List; +import static org.apache.flink.util.Preconditions.checkNotNull; + /** * Implementation of operator list state. * @@ -129,8 +131,12 @@ public void update(List values) { @Override public void addAll(List values) { - if (values != null && !values.isEmpty()) { - internalList.addAll(values); + Preconditions.checkNotNull(values, "List of values to add cannot be null."); + if (!values.isEmpty()) { + for (S value : values) { + checkNotNull(value, "Any value to add to a list cannot be null."); + add(value); + } } } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/state/changelog/ChangelogStateBackendHandle.java b/flink-runtime/src/main/java/org/apache/flink/runtime/state/changelog/ChangelogStateBackendHandle.java index 2d786c44d0d97..6c5c7feae06c9 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/state/changelog/ChangelogStateBackendHandle.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/state/changelog/ChangelogStateBackendHandle.java @@ -174,7 +174,7 @@ private static KeyedStateHandle castToAbsolutePath( StreamStateHandle castMetaStateHandle = restoreFileStateHandle( - incrementalRemoteKeyedStateHandle.getMetaStateHandle()); + incrementalRemoteKeyedStateHandle.getMetaDataStateHandle()); List castSharedStates = incrementalRemoteKeyedStateHandle.getSharedState().stream() .map( diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/state/heap/HeapAggregatingState.java b/flink-runtime/src/main/java/org/apache/flink/runtime/state/heap/HeapAggregatingState.java index fbf8914f87d6a..59a86de87f992 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/state/heap/HeapAggregatingState.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/state/heap/HeapAggregatingState.java @@ -28,8 +28,6 @@ import org.apache.flink.runtime.state.internal.InternalAggregatingState; import org.apache.flink.util.Preconditions; -import java.io.IOException; - /** * Heap-backed partitioned {@link AggregatingState} that is snapshotted into files. * @@ -93,20 +91,14 @@ public OUT get() { } @Override - public void add(IN value) throws IOException { + public void add(IN value) throws Exception { final N namespace = currentNamespace; if (value == null) { clear(); return; } - - try { - stateTable.transform(namespace, value, aggregateTransformation); - } catch (Exception e) { - throw new IOException( - "Exception while applying AggregateFunction in aggregating state", e); - } + stateTable.transform(namespace, value, aggregateTransformation); } // ------------------------------------------------------------------------ diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/state/heap/HeapListState.java b/flink-runtime/src/main/java/org/apache/flink/runtime/state/heap/HeapListState.java index 0f32d0bbc270a..919700f539d6a 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/state/heap/HeapListState.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/state/heap/HeapListState.java @@ -30,6 +30,7 @@ import org.apache.flink.util.Preconditions; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -106,7 +107,7 @@ public byte[] getSerializedValue( final TypeSerializer safeKeySerializer, final TypeSerializer safeNamespaceSerializer, final TypeSerializer> safeValueSerializer) - throws Exception { + throws IOException { Preconditions.checkNotNull(serializedKeyAndNamespace); Preconditions.checkNotNull(safeKeySerializer); diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/state/heap/HeapReducingState.java b/flink-runtime/src/main/java/org/apache/flink/runtime/state/heap/HeapReducingState.java index 18739252ac516..b7926af7d6fc7 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/state/heap/HeapReducingState.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/state/heap/HeapReducingState.java @@ -28,8 +28,6 @@ import org.apache.flink.runtime.state.internal.InternalReducingState; import org.apache.flink.util.Preconditions; -import java.io.IOException; - /** * Heap-backed partitioned {@link ReducingState} that is snapshotted into files. * @@ -89,18 +87,14 @@ public V get() { } @Override - public void add(V value) throws IOException { + public void add(V value) throws Exception { if (value == null) { clear(); return; } - try { - stateTable.transform(currentNamespace, value, reduceTransformation); - } catch (Exception e) { - throw new IOException("Exception while applying ReduceFunction in reducing state", e); - } + stateTable.transform(currentNamespace, value, reduceTransformation); } // ------------------------------------------------------------------------ diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/state/internal/InternalListState.java b/flink-runtime/src/main/java/org/apache/flink/runtime/state/internal/InternalListState.java index bfb75a00bb2f9..48096efa24eab 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/state/internal/InternalListState.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/state/internal/InternalListState.java @@ -39,10 +39,13 @@ public interface InternalListState * given list of values. The next time {@link #get()} is called (for the same state partition) * the returned state will represent the updated list. * - *

If `null` or an empty list is passed in, the state value will be null + *

If an empty list is passed in, the state value will be null + * + *

Null value passed in or any null value in list is not allowed. * * @param values The new values for the state. - * @throws Exception The method may forward exception thrown internally (by I/O or functions). + * @throws Exception The method may forward exception thrown internally (by I/O or functions, or + * sanity check for null value). */ void update(List values) throws Exception; @@ -51,10 +54,13 @@ public interface InternalListState * existing list of values. The next time {@link #get()} is called (for the same state * partition) the returned state will represent the updated list. * - *

If `null` or an empty list is passed in, the state value remains unchanged + *

If an empty list is passed in, the state value remains unchanged + * + *

Null value passed in or any null value in list is not allowed. * * @param values The new values to be added to the state. - * @throws Exception The method may forward exception thrown internally (by I/O or functions). + * @throws Exception The method may forward exception thrown internally (by I/O or functions, or + * sanity check for null value). */ void addAll(List values) throws Exception; } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/DefaultShuffleDescriptorsCache.java b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/DefaultShuffleDescriptorsCache.java deleted file mode 100644 index 99a97f2370b27..0000000000000 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/DefaultShuffleDescriptorsCache.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.flink.runtime.taskexecutor; - -import org.apache.flink.annotation.VisibleForTesting; -import org.apache.flink.api.common.JobID; -import org.apache.flink.runtime.blob.PermanentBlobKey; -import org.apache.flink.runtime.deployment.TaskDeploymentDescriptorFactory.ShuffleDescriptorGroup; - -import org.apache.flink.shaded.guava31.com.google.common.base.Ticker; -import org.apache.flink.shaded.guava31.com.google.common.cache.Cache; -import org.apache.flink.shaded.guava31.com.google.common.cache.CacheBuilder; -import org.apache.flink.shaded.guava31.com.google.common.cache.RemovalNotification; - -import java.time.Duration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import static org.apache.flink.util.Preconditions.checkNotNull; - -/** Default implement of {@link ShuffleDescriptorsCache}. Entries will be expired after timeout. */ -public class DefaultShuffleDescriptorsCache implements ShuffleDescriptorsCache { - private final Cache shuffleDescriptorsCache; - private final Map> cachedBlobKeysPerJob; - - private DefaultShuffleDescriptorsCache( - Duration expireTimeout, int cacheSizeLimit, Ticker ticker) { - this.cachedBlobKeysPerJob = new HashMap<>(); - this.shuffleDescriptorsCache = - CacheBuilder.newBuilder() - .concurrencyLevel(1) - .maximumSize(cacheSizeLimit) - .expireAfterAccess(expireTimeout) - .ticker(ticker) - .removalListener(this::onCacheRemoval) - .build(); - } - - @Override - public void clear() { - cachedBlobKeysPerJob.clear(); - shuffleDescriptorsCache.cleanUp(); - } - - @Override - public ShuffleDescriptorGroup get(PermanentBlobKey blobKey) { - ShuffleDescriptorCacheEntry entry = shuffleDescriptorsCache.getIfPresent(blobKey); - return entry == null ? null : entry.getShuffleDescriptorGroup(); - } - - @Override - public void put( - JobID jobId, PermanentBlobKey blobKey, ShuffleDescriptorGroup shuffleDescriptorGroup) { - shuffleDescriptorsCache.put( - blobKey, new ShuffleDescriptorCacheEntry(shuffleDescriptorGroup, jobId)); - cachedBlobKeysPerJob.computeIfAbsent(jobId, ignore -> new HashSet<>()).add(blobKey); - } - - @Override - public void clearCacheForJob(JobID jobId) { - Set removed = cachedBlobKeysPerJob.remove(jobId); - if (removed != null) { - shuffleDescriptorsCache.invalidateAll(removed); - } - } - - /** - * Removal listener that remove the index of serializedShuffleDescriptorsPerJob . - * - * @param removalNotification of removed element. - */ - private void onCacheRemoval( - RemovalNotification - removalNotification) { - PermanentBlobKey blobKey = removalNotification.getKey(); - ShuffleDescriptorCacheEntry entry = removalNotification.getValue(); - if (blobKey != null && entry != null) { - cachedBlobKeysPerJob.computeIfPresent( - entry.getJobId(), - (jobID, permanentBlobKeys) -> { - permanentBlobKeys.remove(blobKey); - if (permanentBlobKeys.isEmpty()) { - return null; - } else { - return permanentBlobKeys; - } - }); - } - } - - private static class ShuffleDescriptorCacheEntry { - private final ShuffleDescriptorGroup shuffleDescriptorGroup; - private final JobID jobId; - - public ShuffleDescriptorCacheEntry( - ShuffleDescriptorGroup shuffleDescriptorGroup, JobID jobId) { - this.shuffleDescriptorGroup = checkNotNull(shuffleDescriptorGroup); - this.jobId = checkNotNull(jobId); - } - - public ShuffleDescriptorGroup getShuffleDescriptorGroup() { - return shuffleDescriptorGroup; - } - - public JobID getJobId() { - return jobId; - } - } - - /** The Factory of {@link DefaultShuffleDescriptorsCache}. */ - public static class Factory { - private static final Duration DEFAULT_CACHE_EXPIRE_TIMEOUT = Duration.ofSeconds(300); - private static final int DEFAULT_CACHE_SIZE_LIMIT = 100; - private static final Ticker DEFAULT_TICKER = Ticker.systemTicker(); - - private final Duration cacheExpireTimeout; - private final int cacheSizeLimit; - private final Ticker ticker; - - public Factory() { - this(DEFAULT_CACHE_EXPIRE_TIMEOUT, DEFAULT_CACHE_SIZE_LIMIT, DEFAULT_TICKER); - } - - @VisibleForTesting - public Factory(Duration cacheExpireTimeout, int cacheSizeLimit, Ticker ticker) { - this.cacheExpireTimeout = cacheExpireTimeout; - this.cacheSizeLimit = cacheSizeLimit; - this.ticker = ticker; - } - - public DefaultShuffleDescriptorsCache create() { - return new DefaultShuffleDescriptorsCache(cacheExpireTimeout, cacheSizeLimit, ticker); - } - } -} diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/ShuffleDescriptorsCache.java b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/ShuffleDescriptorsCache.java deleted file mode 100644 index a86e6a67722da..0000000000000 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/ShuffleDescriptorsCache.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.flink.runtime.taskexecutor; - -import org.apache.flink.api.common.JobID; -import org.apache.flink.runtime.blob.PermanentBlobKey; -import org.apache.flink.runtime.deployment.TaskDeploymentDescriptorFactory.ShuffleDescriptorGroup; - -/** Cache of shuffle descriptors in TaskExecutor. */ -public interface ShuffleDescriptorsCache { - - /** clear all cache. */ - void clear(); - - /** - * Get shuffle descriptor group in cache. - * - * @param blobKey identify the shuffle descriptor group - * @return shuffle descriptor group in cache if exists, otherwise null - */ - ShuffleDescriptorGroup get(PermanentBlobKey blobKey); - - /** - * Put shuffle descriptor group to cache. - * - * @param jobId of job - * @param blobKey identify the shuffle descriptor group - * @param shuffleDescriptorGroup shuffle descriptor group to cache - */ - void put(JobID jobId, PermanentBlobKey blobKey, ShuffleDescriptorGroup shuffleDescriptorGroup); - - /** - * Clear all cache for the Job. - * - * @param jobId of job - */ - void clearCacheForJob(JobID jobId); -} diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskExecutor.java b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskExecutor.java index d42a56585980a..c3a705e0e0e68 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskExecutor.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskExecutor.java @@ -25,6 +25,7 @@ import org.apache.flink.management.jmx.JMXService; import org.apache.flink.runtime.accumulators.AccumulatorSnapshot; import org.apache.flink.runtime.blob.JobPermanentBlobService; +import org.apache.flink.runtime.blob.PermanentBlobKey; import org.apache.flink.runtime.blob.TaskExecutorBlobService; import org.apache.flink.runtime.blob.TransientBlobKey; import org.apache.flink.runtime.blob.TransientBlobService; @@ -39,6 +40,7 @@ import org.apache.flink.runtime.clusterframework.types.SlotID; import org.apache.flink.runtime.deployment.ResultPartitionDeploymentDescriptor; import org.apache.flink.runtime.deployment.TaskDeploymentDescriptor; +import org.apache.flink.runtime.deployment.TaskDeploymentDescriptorFactory.ShuffleDescriptorGroup; import org.apache.flink.runtime.entrypoint.ClusterInformation; import org.apache.flink.runtime.execution.ExecutionState; import org.apache.flink.runtime.execution.librarycache.LibraryCacheManager; @@ -131,6 +133,7 @@ import org.apache.flink.runtime.taskmanager.TaskExecutionState; import org.apache.flink.runtime.taskmanager.TaskManagerActions; import org.apache.flink.runtime.taskmanager.UnresolvedTaskManagerLocation; +import org.apache.flink.runtime.util.GroupCache; import org.apache.flink.runtime.webmonitor.threadinfo.ThreadInfoSamplesRequest; import org.apache.flink.types.SerializableOptional; import org.apache.flink.util.CollectionUtil; @@ -297,7 +300,10 @@ public class TaskExecutor extends RpcEndpoint implements TaskExecutorGateway { private final ThreadInfoSampleService threadInfoSampleService; - private final ShuffleDescriptorsCache shuffleDescriptorsCache; + private final GroupCache jobInformationCache; + private final GroupCache taskInformationCache; + private final GroupCache + shuffleDescriptorsCache; public TaskExecutor( RpcService rpcService, @@ -374,6 +380,8 @@ public TaskExecutor( taskExecutorServices.getSlotAllocationSnapshotPersistenceService(); this.sharedResources = taskExecutorServices.getSharedResources(); + this.jobInformationCache = taskExecutorServices.getJobInformationCache(); + this.taskInformationCache = taskExecutorServices.getTaskInformationCache(); this.shuffleDescriptorsCache = taskExecutorServices.getShuffleDescriptorCache(); } @@ -504,6 +512,8 @@ public CompletableFuture onStop() { changelogStoragesManager.shutdown(); channelStateExecutorFactoryManager.shutdown(); + jobInformationCache.clear(); + taskInformationCache.clear(); shuffleDescriptorsCache.clear(); Preconditions.checkState(jobTable.isEmpty()); @@ -668,7 +678,10 @@ public CompletableFuture submitTask( // re-integrate offloaded data and deserialize shuffle descriptors try { tdd.loadBigData( - taskExecutorBlobService.getPermanentBlobService(), shuffleDescriptorsCache); + taskExecutorBlobService.getPermanentBlobService(), + jobInformationCache, + taskInformationCache, + shuffleDescriptorsCache); } catch (IOException | ClassNotFoundException e) { throw new TaskSubmissionException( "Could not re-integrate offloaded TaskDeploymentDescriptor data.", e); @@ -678,12 +691,8 @@ public CompletableFuture submitTask( final JobInformation jobInformation; final TaskInformation taskInformation; try { - jobInformation = - tdd.getSerializedJobInformation() - .deserializeValue(getClass().getClassLoader()); - taskInformation = - tdd.getSerializedTaskInformation() - .deserializeValue(getClass().getClassLoader()); + jobInformation = tdd.getJobInformation(); + taskInformation = tdd.getTaskInformation(); } catch (IOException | ClassNotFoundException e) { throw new TaskSubmissionException( "Could not deserialize the job or task information.", e); @@ -1470,7 +1479,7 @@ private void establishResourceManagerConnection( getResourceID(), taskExecutorRegistrationId, taskSlotTable.createSlotReport(getResourceID()), - taskManagerConfiguration.getRpcTimeout()); + Time.fromDuration(taskManagerConfiguration.getRpcTimeout())); slotReportResponseFuture.whenCompleteAsync( (acknowledge, throwable) -> { @@ -1610,7 +1619,7 @@ private void internalOfferSlotsToJobManager(JobTable.Connection jobManagerConnec jobMasterGateway.offerSlots( getResourceID(), reservedSlots, - taskManagerConfiguration.getRpcTimeout()); + Time.fromDuration(taskManagerConfiguration.getRpcTimeout())); acceptedSlotsFuture.whenCompleteAsync( handleAcceptedSlotOffers( @@ -1903,7 +1912,9 @@ private void releaseJobResources(JobID jobId, Exception cause) { changelogStoragesManager.releaseResourcesForJob(jobId); currentSlotOfferPerJob.remove(jobId); channelStateExecutorFactoryManager.releaseResourcesForJob(jobId); - shuffleDescriptorsCache.clearCacheForJob(jobId); + jobInformationCache.clearCacheForGroup(jobId); + taskInformationCache.clearCacheForGroup(jobId); + shuffleDescriptorsCache.clearCacheForGroup(jobId); fileMergingManager.releaseMergingSnapshotManagerForJob(jobId); } diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskManagerConfiguration.java b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskManagerConfiguration.java index 2ec461b40cd23..ca7aaabe813c6 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskManagerConfiguration.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskManagerConfiguration.java @@ -18,7 +18,6 @@ package org.apache.flink.runtime.taskexecutor; -import org.apache.flink.api.common.time.Time; import org.apache.flink.configuration.AkkaOptions; import org.apache.flink.configuration.ConfigConstants; import org.apache.flink.configuration.Configuration; @@ -51,9 +50,9 @@ public class TaskManagerConfiguration implements TaskManagerRuntimeInfo { private final String[] tmpDirectories; - private final Time rpcTimeout; + private final Duration rpcTimeout; - private final Time slotTimeout; + private final Duration slotTimeout; // null indicates an infinite duration @Nullable private final Duration maxRegistrationDuration; @@ -79,8 +78,8 @@ public TaskManagerConfiguration( ResourceProfile defaultSlotResourceProfile, ResourceProfile totalResourceProfile, String[] tmpDirectories, - Time rpcTimeout, - Time slotTimeout, + Duration rpcTimeout, + Duration slotTimeout, @Nullable Duration maxRegistrationDuration, Configuration configuration, boolean exitJvmOnOutOfMemory, @@ -121,11 +120,11 @@ public ResourceProfile getTotalResourceProfile() { return totalResourceProfile; } - public Time getRpcTimeout() { + public Duration getRpcTimeout() { return rpcTimeout; } - public Time getSlotTimeout() { + public Duration getSlotTimeout() { return slotTimeout; } @@ -195,13 +194,11 @@ public static TaskManagerConfiguration fromConfiguration( final String[] tmpDirPaths = ConfigurationUtils.parseTempDirectories(configuration); - final Time rpcTimeout = - Time.fromDuration(configuration.get(AkkaOptions.ASK_TIMEOUT_DURATION)); + final Duration rpcTimeout = configuration.get(AkkaOptions.ASK_TIMEOUT_DURATION); LOG.debug("Messages have a max timeout of " + rpcTimeout); - final Time slotTimeout = - Time.milliseconds(configuration.get(TaskManagerOptions.SLOT_TIMEOUT).toMillis()); + final Duration slotTimeout = configuration.get(TaskManagerOptions.SLOT_TIMEOUT); Duration finiteRegistrationDuration; try { diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskManagerRunner.java b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskManagerRunner.java index 258c5ca9be2e0..f1ec64ce45194 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskManagerRunner.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskManagerRunner.java @@ -638,6 +638,7 @@ public static TaskExecutor startTaskManager( taskExecutorBlobService.getPermanentBlobService(), taskManagerMetricGroup.f1, ioExecutor, + rpcService.getScheduledExecutor(), fatalErrorHandler, workingDirectory); diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskManagerServices.java b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskManagerServices.java index da8a063618944..586d8291a0f51 100755 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskManagerServices.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/TaskManagerServices.java @@ -18,14 +18,19 @@ package org.apache.flink.runtime.taskexecutor; +import org.apache.flink.api.common.JobID; import org.apache.flink.configuration.CoreOptions; import org.apache.flink.metrics.MetricGroup; +import org.apache.flink.runtime.blob.PermanentBlobKey; import org.apache.flink.runtime.blob.PermanentBlobService; import org.apache.flink.runtime.broadcast.BroadcastVariableManager; import org.apache.flink.runtime.clusterframework.types.AllocationID; +import org.apache.flink.runtime.deployment.TaskDeploymentDescriptorFactory.ShuffleDescriptorGroup; import org.apache.flink.runtime.entrypoint.WorkingDirectory; import org.apache.flink.runtime.execution.librarycache.BlobLibraryCacheManager; import org.apache.flink.runtime.execution.librarycache.LibraryCacheManager; +import org.apache.flink.runtime.executiongraph.JobInformation; +import org.apache.flink.runtime.executiongraph.TaskInformation; import org.apache.flink.runtime.io.disk.iomanager.IOManager; import org.apache.flink.runtime.io.disk.iomanager.IOManagerAsync; import org.apache.flink.runtime.io.network.TaskEventDispatcher; @@ -48,9 +53,12 @@ import org.apache.flink.runtime.taskexecutor.slot.TimerService; import org.apache.flink.runtime.taskmanager.Task; import org.apache.flink.runtime.taskmanager.UnresolvedTaskManagerLocation; +import org.apache.flink.runtime.util.DefaultGroupCache; +import org.apache.flink.runtime.util.GroupCache; import org.apache.flink.util.ExceptionUtils; import org.apache.flink.util.FlinkException; import org.apache.flink.util.Preconditions; +import org.apache.flink.util.concurrent.ScheduledExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -89,7 +97,10 @@ public class TaskManagerServices { private final LibraryCacheManager libraryCacheManager; private final SlotAllocationSnapshotPersistenceService slotAllocationSnapshotPersistenceService; private final SharedResources sharedResources; - private final ShuffleDescriptorsCache shuffleDescriptorsCache; + private final GroupCache jobInformationCache; + private final GroupCache taskInformationCache; + private final GroupCache + shuffleDescriptorsCache; TaskManagerServices( UnresolvedTaskManagerLocation unresolvedTaskManagerLocation, @@ -110,7 +121,9 @@ public class TaskManagerServices { LibraryCacheManager libraryCacheManager, SlotAllocationSnapshotPersistenceService slotAllocationSnapshotPersistenceService, SharedResources sharedResources, - ShuffleDescriptorsCache shuffleDescriptorsCache) { + GroupCache jobInformationCache, + GroupCache taskInformationCache, + GroupCache shuffleDescriptorsCache) { this.unresolvedTaskManagerLocation = Preconditions.checkNotNull(unresolvedTaskManagerLocation); @@ -132,6 +145,8 @@ public class TaskManagerServices { this.libraryCacheManager = Preconditions.checkNotNull(libraryCacheManager); this.slotAllocationSnapshotPersistenceService = slotAllocationSnapshotPersistenceService; this.sharedResources = Preconditions.checkNotNull(sharedResources); + this.jobInformationCache = jobInformationCache; + this.taskInformationCache = taskInformationCache; this.shuffleDescriptorsCache = Preconditions.checkNotNull(shuffleDescriptorsCache); } @@ -207,7 +222,15 @@ public SharedResources getSharedResources() { return sharedResources; } - public ShuffleDescriptorsCache getShuffleDescriptorCache() { + public GroupCache getJobInformationCache() { + return jobInformationCache; + } + + public GroupCache getTaskInformationCache() { + return taskInformationCache; + } + + public GroupCache getShuffleDescriptorCache() { return shuffleDescriptorsCache; } @@ -293,6 +316,7 @@ public void shutDown() throws FlinkException { * @param permanentBlobService permanentBlobService used by the services * @param taskManagerMetricGroup metric group of the task manager * @param ioExecutor executor for async IO operations + * @param scheduledExecutor scheduled executor in rpc service * @param fatalErrorHandler to handle class loading OOMs * @param workingDirectory the working directory of the process * @return task manager components @@ -303,6 +327,7 @@ public static TaskManagerServices fromConfiguration( PermanentBlobService permanentBlobService, MetricGroup taskManagerMetricGroup, ExecutorService ioExecutor, + ScheduledExecutor scheduledExecutor, FatalErrorHandler fatalErrorHandler, WorkingDirectory workingDirectory) throws Exception { @@ -321,7 +346,8 @@ public static TaskManagerServices fromConfiguration( taskManagerServicesConfiguration, taskEventDispatcher, taskManagerMetricGroup, - ioExecutor); + ioExecutor, + scheduledExecutor); final int listeningDataPort = shuffleEnvironment.start(); LOG.info( @@ -405,8 +431,14 @@ public static TaskManagerServices fromConfiguration( NoOpSlotAllocationSnapshotPersistenceService.INSTANCE; } - final ShuffleDescriptorsCache shuffleDescriptorsCache = - new DefaultShuffleDescriptorsCache.Factory().create(); + final GroupCache jobInformationCache = + new DefaultGroupCache.Factory().create(); + final GroupCache taskInformationCache = + new DefaultGroupCache.Factory().create(); + + final GroupCache shuffleDescriptorsCache = + new DefaultGroupCache.Factory() + .create(); return new TaskManagerServices( unresolvedTaskManagerLocation, @@ -427,6 +459,8 @@ public static TaskManagerServices fromConfiguration( libraryCacheManager, slotAllocationSnapshotPersistenceService, new SharedResources(), + jobInformationCache, + taskInformationCache, shuffleDescriptorsCache); } @@ -454,7 +488,8 @@ private static TaskSlotTable createTaskSlotTable( TaskManagerServicesConfiguration taskManagerServicesConfiguration, TaskEventDispatcher taskEventDispatcher, MetricGroup taskManagerMetricGroup, - Executor ioExecutor) + Executor ioExecutor, + ScheduledExecutor scheduledExecutor) throws FlinkException { final ShuffleEnvironmentContext shuffleEnvironmentContext = @@ -468,7 +503,8 @@ private static TaskSlotTable createTaskSlotTable( taskManagerServicesConfiguration.getTmpDirPaths(), taskEventDispatcher, taskManagerMetricGroup, - ioExecutor); + ioExecutor, + scheduledExecutor); return ShuffleServiceLoader.loadShuffleServiceFactory( taskManagerServicesConfiguration.getConfiguration()) diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/rpc/RpcInputSplitProvider.java b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/rpc/RpcInputSplitProvider.java index 31b4ae67ec8aa..0afdf05f0ebb8 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/rpc/RpcInputSplitProvider.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/rpc/RpcInputSplitProvider.java @@ -18,7 +18,6 @@ package org.apache.flink.runtime.taskexecutor.rpc; -import org.apache.flink.api.common.time.Time; import org.apache.flink.core.io.InputSplit; import org.apache.flink.runtime.executiongraph.ExecutionAttemptID; import org.apache.flink.runtime.jobgraph.JobVertexID; @@ -29,19 +28,21 @@ import org.apache.flink.util.InstantiationUtil; import org.apache.flink.util.Preconditions; +import java.time.Duration; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; public class RpcInputSplitProvider implements InputSplitProvider { private final JobMasterGateway jobMasterGateway; private final JobVertexID jobVertexID; private final ExecutionAttemptID executionAttemptID; - private final Time timeout; + private final Duration timeout; public RpcInputSplitProvider( JobMasterGateway jobMasterGateway, JobVertexID jobVertexID, ExecutionAttemptID executionAttemptID, - Time timeout) { + Duration timeout) { this.jobMasterGateway = Preconditions.checkNotNull(jobMasterGateway); this.jobVertexID = Preconditions.checkNotNull(jobVertexID); this.executionAttemptID = Preconditions.checkNotNull(executionAttemptID); @@ -58,7 +59,7 @@ public InputSplit getNextInputSplit(ClassLoader userCodeClassLoader) try { SerializedInputSplit serializedInputSplit = - futureInputSplit.get(timeout.getSize(), timeout.getUnit()); + futureInputSplit.get(timeout.toMillis(), TimeUnit.MILLISECONDS); if (serializedInputSplit.isEmpty()) { return null; diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/slot/TaskSlotTable.java b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/slot/TaskSlotTable.java index 045ffebc7a592..9594c5f009236 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/slot/TaskSlotTable.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/slot/TaskSlotTable.java @@ -20,7 +20,6 @@ import org.apache.flink.annotation.VisibleForTesting; import org.apache.flink.api.common.JobID; -import org.apache.flink.api.common.time.Time; import org.apache.flink.runtime.clusterframework.types.AllocationID; import org.apache.flink.runtime.clusterframework.types.ResourceID; import org.apache.flink.runtime.clusterframework.types.ResourceProfile; @@ -32,6 +31,7 @@ import javax.annotation.Nullable; +import java.time.Duration; import java.util.Iterator; import java.util.Set; import java.util.UUID; @@ -96,7 +96,7 @@ public interface TaskSlotTable * @return True if the task slot could be allocated; otherwise false */ @VisibleForTesting - boolean allocateSlot(int index, JobID jobId, AllocationID allocationId, Time slotTimeout); + boolean allocateSlot(int index, JobID jobId, AllocationID allocationId, Duration slotTimeout); /** * Allocate the slot with the given index for the given job and allocation id. If negative index @@ -116,7 +116,7 @@ boolean allocateSlot( JobID jobId, AllocationID allocationId, ResourceProfile resourceProfile, - Time slotTimeout); + Duration slotTimeout); /** * Marks the slot under the given allocation id as active. If the slot could not be found, then @@ -137,7 +137,7 @@ boolean allocateSlot( * @throws SlotNotFoundException if the slot could not be found for the given allocation id * @return True if the slot could be marked inactive */ - boolean markSlotInactive(AllocationID allocationId, Time slotTimeout) + boolean markSlotInactive(AllocationID allocationId, Duration slotTimeout) throws SlotNotFoundException; /** diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/slot/TaskSlotTableImpl.java b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/slot/TaskSlotTableImpl.java index d8ee8e0546339..1099edad8a6ef 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/slot/TaskSlotTableImpl.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/taskexecutor/slot/TaskSlotTableImpl.java @@ -20,7 +20,6 @@ import org.apache.flink.annotation.VisibleForTesting; import org.apache.flink.api.common.JobID; -import org.apache.flink.api.common.time.Time; import org.apache.flink.runtime.clusterframework.types.AllocationID; import org.apache.flink.runtime.clusterframework.types.ResourceBudgetManager; import org.apache.flink.runtime.clusterframework.types.ResourceID; @@ -42,6 +41,7 @@ import javax.annotation.Nullable; +import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -54,6 +54,7 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** Default implementation of {@link TaskSlotTable}. */ @@ -274,7 +275,7 @@ public SlotReport createSlotReport(ResourceID resourceId) { @VisibleForTesting @Override public boolean allocateSlot( - int index, JobID jobId, AllocationID allocationId, Time slotTimeout) { + int index, JobID jobId, AllocationID allocationId, Duration slotTimeout) { return allocateSlot(index, jobId, allocationId, defaultSlotResourceProfile, slotTimeout); } @@ -284,7 +285,7 @@ public boolean allocateSlot( JobID jobId, AllocationID allocationId, ResourceProfile resourceProfile, - Time slotTimeout) { + Duration slotTimeout) { checkRunning(); Preconditions.checkArgument(requestedIndex < numberSlots); @@ -334,7 +335,7 @@ public boolean allocateSlot( allocatedSlots.put(allocationId, taskSlot); // register a timeout for this slot since it's in state allocated - timerService.registerTimeout(allocationId, slotTimeout.getSize(), slotTimeout.getUnit()); + timerService.registerTimeout(allocationId, slotTimeout.toMillis(), TimeUnit.MILLISECONDS); // add this slot to the set of job slots Set slots = slotsPerJob.get(jobId); @@ -398,7 +399,7 @@ private boolean markExistingSlotActive(TaskSlot taskSlot) { } @Override - public boolean markSlotInactive(AllocationID allocationId, Time slotTimeout) + public boolean markSlotInactive(AllocationID allocationId, Duration slotTimeout) throws SlotNotFoundException { checkStarted(); @@ -408,7 +409,7 @@ public boolean markSlotInactive(AllocationID allocationId, Time slotTimeout) if (taskSlot.markInactive()) { // register a timeout to free the slot timerService.registerTimeout( - allocationId, slotTimeout.getSize(), slotTimeout.getUnit()); + allocationId, slotTimeout.toMillis(), TimeUnit.MILLISECONDS); return true; } else { diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/taskmanager/NettyShuffleEnvironmentConfiguration.java b/flink-runtime/src/main/java/org/apache/flink/runtime/taskmanager/NettyShuffleEnvironmentConfiguration.java index ecd9c04471ae6..54a75156ecb4d 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/taskmanager/NettyShuffleEnvironmentConfiguration.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/taskmanager/NettyShuffleEnvironmentConfiguration.java @@ -65,6 +65,8 @@ public class NettyShuffleEnvironmentConfiguration { private final int partitionRequestMaxBackoff; + private final int partitionRequestListenerTimeout; + /** * Number of network buffers to use for each outgoing/incoming channel (subpartition/input * channel). @@ -122,6 +124,7 @@ public NettyShuffleEnvironmentConfiguration( int networkBufferSize, int partitionRequestInitialBackoff, int partitionRequestMaxBackoff, + int partitionRequestListenerTimeout, int networkBuffersPerChannel, int floatingNetworkBuffersPerGate, Optional maxRequiredBuffersPerGate, @@ -148,6 +151,7 @@ public NettyShuffleEnvironmentConfiguration( this.networkBufferSize = networkBufferSize; this.partitionRequestInitialBackoff = partitionRequestInitialBackoff; this.partitionRequestMaxBackoff = partitionRequestMaxBackoff; + this.partitionRequestListenerTimeout = partitionRequestListenerTimeout; this.networkBuffersPerChannel = networkBuffersPerChannel; this.floatingNetworkBuffersPerGate = floatingNetworkBuffersPerGate; this.maxRequiredBuffersPerGate = maxRequiredBuffersPerGate; @@ -190,6 +194,10 @@ public int partitionRequestMaxBackoff() { return partitionRequestMaxBackoff; } + public int getPartitionRequestListenerTimeout() { + return partitionRequestListenerTimeout; + } + public int networkBuffersPerChannel() { return networkBuffersPerChannel; } @@ -317,6 +325,13 @@ public static NettyShuffleEnvironmentConfiguration fromConfiguration( int maxRequestBackoff = configuration.getInteger( NettyShuffleEnvironmentOptions.NETWORK_REQUEST_BACKOFF_MAX); + int listenerTimeout = + (int) + configuration + .get( + NettyShuffleEnvironmentOptions + .NETWORK_PARTITION_REQUEST_TIMEOUT) + .toMillis(); int buffersPerChannel = configuration.getInteger( @@ -419,6 +434,7 @@ public static NettyShuffleEnvironmentConfiguration fromConfiguration( pageSize, initialRequestBackoff, maxRequestBackoff, + listenerTimeout, buffersPerChannel, extraBuffersPerGate, maxRequiredBuffersPerGate, @@ -561,6 +577,7 @@ public int hashCode() { result = 31 * result + networkBufferSize; result = 31 * result + partitionRequestInitialBackoff; result = 31 * result + partitionRequestMaxBackoff; + result = 31 * result + partitionRequestListenerTimeout; result = 31 * result + networkBuffersPerChannel; result = 31 * result + floatingNetworkBuffersPerGate; result = 31 * result + requestSegmentsTimeout.hashCode(); @@ -604,6 +621,7 @@ public boolean equals(Object obj) { && Arrays.equals(this.tempDirs, that.tempDirs) && this.batchShuffleCompressionEnabled == that.batchShuffleCompressionEnabled && this.maxBuffersPerChannel == that.maxBuffersPerChannel + && this.partitionRequestListenerTimeout == that.partitionRequestListenerTimeout && Objects.equals(this.compressionCodec, that.compressionCodec) && this.maxNumberOfConnections == that.maxNumberOfConnections && this.connectionReuseEnabled == that.connectionReuseEnabled @@ -638,6 +656,8 @@ public String toString() { + compressionCodec + ", maxBuffersPerChannel=" + maxBuffersPerChannel + + ", partitionRequestListenerTimeout" + + partitionRequestListenerTimeout + ", batchShuffleReadMemoryBytes=" + batchShuffleReadMemoryBytes + ", sortShuffleMinBuffers=" diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/taskmanager/TaskManagerLocation.java b/flink-runtime/src/main/java/org/apache/flink/runtime/taskmanager/TaskManagerLocation.java index 3690324854846..3e5f87883415b 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/taskmanager/TaskManagerLocation.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/taskmanager/TaskManagerLocation.java @@ -224,6 +224,15 @@ public String getNodeId() { return nodeId; } + /** + * Gets the endpoint of the TaskManager in the format of "$HOST:$PORT". + * + * @return The endpoint of the TaskManager. + */ + public String getEndpoint() { + return String.format("%s:%d", getFQDNHostname(), dataPort); + } + // -------------------------------------------------------------------------------------------- // Utilities // -------------------------------------------------------------------------------------------- diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/util/DefaultGroupCache.java b/flink-runtime/src/main/java/org/apache/flink/runtime/util/DefaultGroupCache.java new file mode 100644 index 0000000000000..e424e0521b69e --- /dev/null +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/util/DefaultGroupCache.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.runtime.util; + +import org.apache.flink.annotation.VisibleForTesting; + +import org.apache.flink.shaded.guava31.com.google.common.base.Ticker; +import org.apache.flink.shaded.guava31.com.google.common.cache.Cache; +import org.apache.flink.shaded.guava31.com.google.common.cache.CacheBuilder; +import org.apache.flink.shaded.guava31.com.google.common.cache.RemovalNotification; + +import javax.annotation.concurrent.NotThreadSafe; + +import java.time.Duration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** Default implement of {@link GroupCache}. Entries will be expired after timeout. */ +@NotThreadSafe +public class DefaultGroupCache implements GroupCache { + private final Cache, V> cache; + private final Map>> cachedBlobKeysPerJob; + + private DefaultGroupCache(Duration expireTimeout, int cacheSizeLimit, Ticker ticker) { + this.cachedBlobKeysPerJob = new HashMap<>(); + this.cache = + CacheBuilder.newBuilder() + .concurrencyLevel(1) + .maximumSize(cacheSizeLimit) + .expireAfterAccess(expireTimeout) + .ticker(ticker) + .removalListener(this::onCacheRemoval) + .build(); + } + + @Override + public void clear() { + cachedBlobKeysPerJob.clear(); + cache.cleanUp(); + } + + @Override + public V get(G group, K key) { + return cache.getIfPresent(new CacheKey<>(group, key)); + } + + @Override + public void put(G group, K key, V value) { + CacheKey cacheKey = new CacheKey<>(group, key); + cache.put(cacheKey, value); + cachedBlobKeysPerJob.computeIfAbsent(group, ignore -> new HashSet<>()).add(cacheKey); + } + + @Override + public void clearCacheForGroup(G group) { + Set> removed = cachedBlobKeysPerJob.remove(group); + if (removed != null) { + cache.invalidateAll(removed); + } + } + + /** + * Removal listener that remove the cache key of this group . + * + * @param removalNotification of removed element. + */ + private void onCacheRemoval(RemovalNotification, V> removalNotification) { + CacheKey cacheKey = removalNotification.getKey(); + V value = removalNotification.getValue(); + if (cacheKey != null && value != null) { + cachedBlobKeysPerJob.computeIfPresent( + cacheKey.getGroup(), + (group, keys) -> { + keys.remove(cacheKey); + if (keys.isEmpty()) { + return null; + } else { + return keys; + } + }); + } + } + + private static class CacheKey { + private final G group; + private final K key; + + public CacheKey(G group, K key) { + this.group = group; + this.key = key; + } + + public G getGroup() { + return group; + } + + public K getKey() { + return key; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CacheKey cacheKey = (CacheKey) o; + return Objects.equals(group, cacheKey.group) && Objects.equals(key, cacheKey.key); + } + + @Override + public int hashCode() { + return Objects.hash(group, key); + } + } + + /** The Factory of {@link DefaultGroupCache}. */ + public static class Factory { + private static final Duration DEFAULT_CACHE_EXPIRE_TIMEOUT = Duration.ofSeconds(300); + private static final int DEFAULT_CACHE_SIZE_LIMIT = 100; + private static final Ticker DEFAULT_TICKER = Ticker.systemTicker(); + + private final Duration cacheExpireTimeout; + private final int cacheSizeLimit; + private final Ticker ticker; + + public Factory() { + this(DEFAULT_CACHE_EXPIRE_TIMEOUT, DEFAULT_CACHE_SIZE_LIMIT, DEFAULT_TICKER); + } + + @VisibleForTesting + public Factory(Duration cacheExpireTimeout, int cacheSizeLimit, Ticker ticker) { + this.cacheExpireTimeout = cacheExpireTimeout; + this.cacheSizeLimit = cacheSizeLimit; + this.ticker = ticker; + } + + public DefaultGroupCache create() { + return new DefaultGroupCache<>(cacheExpireTimeout, cacheSizeLimit, ticker); + } + } +} diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/util/GroupCache.java b/flink-runtime/src/main/java/org/apache/flink/runtime/util/GroupCache.java new file mode 100644 index 0000000000000..25757837878be --- /dev/null +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/util/GroupCache.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.runtime.util; + +import javax.annotation.Nullable; + +/** + * This {@link GroupCache} can cache group, key and value. The group and key are cache key, each key + * belongs to a certain group. All corresponding keys and values will be cleared if a group is + * cleared. + * + * @param The group. + * @param The key. + * @param The value. + */ +public interface GroupCache { + + /** clear all cache. */ + void clear(); + + /** + * Get value in cache. + * + * @return value in cache if exists, otherwise null + */ + @Nullable + V get(G group, K key); + + /** Put group, key and value to cache. */ + void put(G group, K key, V value); + + /** Clear all caches of the corresponding group. */ + void clearCacheForGroup(G group); +} diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/util/SlotSelectionStrategyUtils.java b/flink-runtime/src/main/java/org/apache/flink/runtime/util/SlotSelectionStrategyUtils.java index 3eb7a9f20f3ad..e4693fa353b1c 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/util/SlotSelectionStrategyUtils.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/util/SlotSelectionStrategyUtils.java @@ -20,8 +20,8 @@ package org.apache.flink.runtime.util; import org.apache.flink.configuration.CheckpointingOptions; -import org.apache.flink.configuration.ClusterOptions; import org.apache.flink.configuration.Configuration; +import org.apache.flink.configuration.TaskManagerOptions; import org.apache.flink.runtime.jobgraph.JobType; import org.apache.flink.runtime.jobmaster.slotpool.LocationPreferenceSlotSelectionStrategy; import org.apache.flink.runtime.jobmaster.slotpool.PreviousAllocationSlotSelectionStrategy; @@ -30,6 +30,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.flink.configuration.TaskManagerOptions.TaskManagerLoadBalanceMode; + /** Utility class for selecting {@link SlotSelectionStrategy}. */ public class SlotSelectionStrategyUtils { @@ -37,13 +39,13 @@ public class SlotSelectionStrategyUtils { public static SlotSelectionStrategy selectSlotSelectionStrategy( final JobType jobType, final Configuration configuration) { - final boolean evenlySpreadOutSlots = - configuration.getBoolean(ClusterOptions.EVENLY_SPREAD_OUT_SLOTS_STRATEGY); + TaskManagerLoadBalanceMode taskManagerLoadBalanceMode = + TaskManagerOptions.TaskManagerLoadBalanceMode.loadFromConfiguration(configuration); final SlotSelectionStrategy locationPreferenceSlotSelectionStrategy; locationPreferenceSlotSelectionStrategy = - evenlySpreadOutSlots + taskManagerLoadBalanceMode == TaskManagerLoadBalanceMode.SLOTS ? LocationPreferenceSlotSelectionStrategy.createEvenlySpreadOut() : LocationPreferenceSlotSelectionStrategy.createDefault(); diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/threadinfo/VertexFlameGraphFactory.java b/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/threadinfo/VertexFlameGraphFactory.java index 266f379c6995a..87d725c209f37 100644 --- a/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/threadinfo/VertexFlameGraphFactory.java +++ b/flink-runtime/src/main/java/org/apache/flink/runtime/webmonitor/threadinfo/VertexFlameGraphFactory.java @@ -105,6 +105,9 @@ private static VertexFlameGraph createFlameGraphFromSample( private static final Pattern LAMBDA_CLASS_NAME = Pattern.compile("(\\$Lambda\\$)\\d+/(0x)?\\p{XDigit}+$"); + private static final Pattern JDK21_LAMBDA_CLASS_NAME = + Pattern.compile("(\\$\\$Lambda)/(0x)?\\p{XDigit}+$"); + // Drops stack trace elements with class names matching the above regular expression. // These elements are useless, because they don't provide any additional information // except the fact that a lambda is used (they don't have source information, for example), @@ -115,9 +118,12 @@ private static VertexFlameGraph createFlameGraphFromSample( // lambdas, so we have to clean them up explicitly. private static StackTraceElement[] cleanLambdaNames(StackTraceElement[] stackTrace) { StackTraceElement[] result = new StackTraceElement[stackTrace.length]; + final String javaVersion = System.getProperty("java.version"); + final Pattern lambdaClassName = + javaVersion.compareTo("21") >= 0 ? JDK21_LAMBDA_CLASS_NAME : LAMBDA_CLASS_NAME; for (int i = 0; i < stackTrace.length; i++) { StackTraceElement element = stackTrace[i]; - Matcher matcher = LAMBDA_CLASS_NAME.matcher(element.getClassName()); + Matcher matcher = lambdaClassName.matcher(element.getClassName()); if (matcher.find()) { // org.apache.flink.streaming.runtime.io.RecordProcessorUtils$$Lambda$773/0x00000001007f84a0 // --> diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CheckpointCoordinatorFailureTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CheckpointCoordinatorFailureTest.java index 51ad43605c781..8873b938f1a25 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CheckpointCoordinatorFailureTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CheckpointCoordinatorFailureTest.java @@ -170,7 +170,6 @@ void testFailingCompletedCheckpointStoreAdd() throws Exception { assertThat(pendingCheckpoint.isDisposed()).isTrue(); // make sure that the subtask state has been discarded after we could not complete it. - verify(operatorSubtaskState).discardState(); verify(operatorSubtaskState.getManagedOperatorState().iterator().next()).discardState(); verify(operatorSubtaskState.getRawOperatorState().iterator().next()).discardState(); verify(operatorSubtaskState.getManagedKeyedState().iterator().next()).discardState(); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CheckpointCoordinatorTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CheckpointCoordinatorTest.java index cb0e17bea488a..1174d9ee18bb2 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CheckpointCoordinatorTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CheckpointCoordinatorTest.java @@ -22,6 +22,7 @@ import org.apache.flink.api.common.JobStatus; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.core.execution.SavepointFormatType; +import org.apache.flink.core.fs.FSDataInputStream; import org.apache.flink.core.fs.FileSystem; import org.apache.flink.core.fs.Path; import org.apache.flink.core.io.SimpleVersionedSerializer; @@ -60,6 +61,7 @@ import org.apache.flink.runtime.state.KeyedStateHandle; import org.apache.flink.runtime.state.OperatorStateHandle; import org.apache.flink.runtime.state.OperatorStreamStateHandle; +import org.apache.flink.runtime.state.PhysicalStateHandleID; import org.apache.flink.runtime.state.PlaceholderStreamStateHandle; import org.apache.flink.runtime.state.SharedStateRegistry; import org.apache.flink.runtime.state.SharedStateRegistryImpl; @@ -1520,9 +1522,12 @@ void testSuccessfulCheckpointSubsumesUnsuccessful() throws Exception { TaskStateSnapshot taskOperatorSubtaskStates12 = spy(new TaskStateSnapshot()); TaskStateSnapshot taskOperatorSubtaskStates13 = spy(new TaskStateSnapshot()); - OperatorSubtaskState subtaskState11 = mock(OperatorSubtaskState.class); - OperatorSubtaskState subtaskState12 = mock(OperatorSubtaskState.class); - OperatorSubtaskState subtaskState13 = mock(OperatorSubtaskState.class); + OperatorSubtaskStateMock subtaskState11mock = new OperatorSubtaskStateMock(); + OperatorSubtaskStateMock subtaskState12mock = new OperatorSubtaskStateMock(); + OperatorSubtaskStateMock subtaskState13mock = new OperatorSubtaskStateMock(); + OperatorSubtaskState subtaskState11 = subtaskState11mock.getSubtaskState(); + OperatorSubtaskState subtaskState12 = subtaskState12mock.getSubtaskState(); + OperatorSubtaskState subtaskState13 = subtaskState13mock.getSubtaskState(); taskOperatorSubtaskStates11.putSubtaskStateByOperatorID(opID1, subtaskState11); taskOperatorSubtaskStates12.putSubtaskStateByOperatorID(opID2, subtaskState12); taskOperatorSubtaskStates13.putSubtaskStateByOperatorID(opID3, subtaskState13); @@ -1561,9 +1566,12 @@ void testSuccessfulCheckpointSubsumesUnsuccessful() throws Exception { TaskStateSnapshot taskOperatorSubtaskStates22 = spy(new TaskStateSnapshot()); TaskStateSnapshot taskOperatorSubtaskStates23 = spy(new TaskStateSnapshot()); - OperatorSubtaskState subtaskState21 = mock(OperatorSubtaskState.class); - OperatorSubtaskState subtaskState22 = mock(OperatorSubtaskState.class); - OperatorSubtaskState subtaskState23 = mock(OperatorSubtaskState.class); + OperatorSubtaskStateMock subtaskState21mock = new OperatorSubtaskStateMock(); + OperatorSubtaskStateMock subtaskState22mock = new OperatorSubtaskStateMock(); + OperatorSubtaskStateMock subtaskState23mock = new OperatorSubtaskStateMock(); + OperatorSubtaskState subtaskState21 = subtaskState21mock.getSubtaskState(); + OperatorSubtaskState subtaskState22 = subtaskState22mock.getSubtaskState(); + OperatorSubtaskState subtaskState23 = subtaskState23mock.getSubtaskState(); taskOperatorSubtaskStates21.putSubtaskStateByOperatorID(opID1, subtaskState21); taskOperatorSubtaskStates22.putSubtaskStateByOperatorID(opID2, subtaskState22); @@ -1625,13 +1633,13 @@ void testSuccessfulCheckpointSubsumesUnsuccessful() throws Exception { assertThat(checkpointCoordinator.getNumberOfRetainedSuccessfulCheckpoints()).isOne(); // validate that all received subtask states in the first checkpoint have been discarded - verify(subtaskState11, times(1)).discardState(); - verify(subtaskState12, times(1)).discardState(); + subtaskState11mock.verifyDiscard(); + subtaskState12mock.verifyDiscard(); // validate that all subtask states in the second checkpoint are not discarded - verify(subtaskState21, never()).discardState(); - verify(subtaskState22, never()).discardState(); - verify(subtaskState23, never()).discardState(); + subtaskState21mock.verifyNotDiscard(); + subtaskState22mock.verifyNotDiscard(); + subtaskState23mock.verifyNotDiscard(); // validate the committed checkpoints List scs = checkpointCoordinator.getSuccessfulCheckpoints(); @@ -1656,15 +1664,15 @@ void testSuccessfulCheckpointSubsumesUnsuccessful() throws Exception { new CheckpointMetrics(), taskOperatorSubtaskStates13), TASK_MANAGER_LOCATION_INFO); - verify(subtaskState13, times(1)).discardState(); + subtaskState13mock.verifyDiscard(); checkpointCoordinator.shutdown(); completedCheckpointStore.shutdown(JobStatus.FINISHED, new CheckpointsCleaner()); // validate that the states in the second checkpoint have been discarded - verify(subtaskState21, times(1)).discardState(); - verify(subtaskState22, times(1)).discardState(); - verify(subtaskState23, times(1)).discardState(); + subtaskState21mock.verifyDiscard(); + subtaskState22mock.verifyDiscard(); + subtaskState23mock.verifyDiscard(); } @Test @@ -1708,7 +1716,8 @@ void testCheckpointTimeoutIsolated() throws Exception { OperatorID opID1 = vertex1.getJobVertex().getOperatorIDs().get(0).getGeneratedOperatorID(); TaskStateSnapshot taskOperatorSubtaskStates1 = spy(new TaskStateSnapshot()); - OperatorSubtaskState subtaskState1 = mock(OperatorSubtaskState.class); + OperatorSubtaskStateMock operatorSubtaskStateMock = new OperatorSubtaskStateMock(); + OperatorSubtaskState subtaskState1 = operatorSubtaskStateMock.getSubtaskState(); taskOperatorSubtaskStates1.putSubtaskStateByOperatorID(opID1, subtaskState1); checkpointCoordinator.receiveAcknowledgeMessage( @@ -1727,9 +1736,7 @@ void testCheckpointTimeoutIsolated() throws Exception { .isTrue(); assertThat(checkpointCoordinator.getNumberOfPendingCheckpoints()).isZero(); assertThat(checkpointCoordinator.getNumberOfRetainedSuccessfulCheckpoints()).isZero(); - - // validate that the received states have been discarded - verify(subtaskState1, times(1)).discardState(); + operatorSubtaskStateMock.verifyDiscard(); // no confirm message must have been sent for (ExecutionVertex vertex : Arrays.asList(vertex1, vertex2)) { @@ -1852,7 +1859,8 @@ void testStateCleanupForLateOrUnknownMessages() throws Exception { vertex1.getJobVertex().getOperatorIDs().get(0).getGeneratedOperatorID(); TaskStateSnapshot taskOperatorSubtaskStatesTrigger = spy(new TaskStateSnapshot()); - OperatorSubtaskState subtaskStateTrigger = mock(OperatorSubtaskState.class); + OperatorSubtaskStateMock subtaskStateMock = new OperatorSubtaskStateMock(); + OperatorSubtaskState subtaskStateTrigger = subtaskStateMock.getSubtaskState(); taskOperatorSubtaskStatesTrigger.putSubtaskStateByOperatorID( opIDtrigger, subtaskStateTrigger); @@ -1867,7 +1875,7 @@ void testStateCleanupForLateOrUnknownMessages() throws Exception { TASK_MANAGER_LOCATION_INFO); // verify that the subtask state has not been discarded - verify(subtaskStateTrigger, never()).discardState(); + subtaskStateMock.verifyNotDiscard(); TaskStateSnapshot unknownSubtaskState = mock(TaskStateSnapshot.class); @@ -1914,7 +1922,7 @@ void testStateCleanupForLateOrUnknownMessages() throws Exception { verify(triggerSubtaskState, never()).discardState(); // let the checkpoint fail at the first ack vertex - reset(subtaskStateTrigger); + subtaskStateMock.reset(); checkpointCoordinator.receiveDeclineMessage( new DeclineCheckpoint( graph.getJobID(), @@ -1926,7 +1934,7 @@ void testStateCleanupForLateOrUnknownMessages() throws Exception { assertThat(pendingCheckpoint.isDisposed()).isTrue(); // check that we've cleaned up the already acknowledged state - verify(subtaskStateTrigger, times(1)).discardState(); + subtaskStateMock.verifyDiscard(); TaskStateSnapshot ackSubtaskState = mock(TaskStateSnapshot.class); @@ -2979,7 +2987,7 @@ void testSharedStateRegistrationOnRestore() throws Exception { handleAndLocalPath.getHandle(), TernaryBoolean.FALSE); } - verify(incrementalKeyedStateHandle.getMetaStateHandle(), never()) + verify(incrementalKeyedStateHandle.getMetaDataStateHandle(), never()) .discardState(); } @@ -4303,4 +4311,88 @@ private void ackCheckpoint( .build()))), "test"); } + + static class OperatorSubtaskStateMock { + OperatorSubtaskState subtaskState; + TestingOperatorStateHandle managedOpHandle; + TestingOperatorStateHandle rawOpHandle; + + OperatorSubtaskStateMock() { + this.managedOpHandle = new TestingOperatorStateHandle(); + this.rawOpHandle = new TestingOperatorStateHandle(); + this.subtaskState = + OperatorSubtaskState.builder() + .setManagedOperatorState(managedOpHandle) + .setRawOperatorState(rawOpHandle) + .build(); + } + + public OperatorSubtaskState getSubtaskState() { + return this.subtaskState; + } + + public void reset() { + managedOpHandle.reset(); + rawOpHandle.reset(); + } + + public void verifyDiscard() { + assert (managedOpHandle.isDiscarded() && rawOpHandle.discarded); + } + + public void verifyNotDiscard() { + assert (!managedOpHandle.isDiscarded() && !rawOpHandle.isDiscarded()); + } + + private static class TestingOperatorStateHandle implements OperatorStateHandle { + + private static final long serialVersionUID = 983594934287613083L; + + boolean discarded; + + @Override + public Map getStateNameToPartitionOffsets() { + return Collections.emptyMap(); + } + + @Override + public FSDataInputStream openInputStream() throws IOException { + throw new IOException("Cannot open input streams in testing implementation."); + } + + @Override + public PhysicalStateHandleID getStreamStateHandleID() { + throw new RuntimeException("Cannot return ID in testing implementation."); + } + + @Override + public Optional asBytesIfInMemory() { + return Optional.empty(); + } + + @Override + public StreamStateHandle getDelegateStateHandle() { + return null; + } + + @Override + public void discardState() throws Exception { + assertThat(discarded).isFalse(); + discarded = true; + } + + @Override + public long getStateSize() { + return 0L; + } + + public void reset() { + discarded = false; + } + + public boolean isDiscarded() { + return discarded; + } + } + } } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CheckpointCoordinatorTestingUtils.java b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CheckpointCoordinatorTestingUtils.java index ad07d34211772..03d8d5ced590b 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CheckpointCoordinatorTestingUtils.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CheckpointCoordinatorTestingUtils.java @@ -47,12 +47,15 @@ import org.apache.flink.runtime.messages.Acknowledge; import org.apache.flink.runtime.state.ChainedStateHandle; import org.apache.flink.runtime.state.CheckpointStorage; +import org.apache.flink.runtime.state.InputChannelStateHandle; import org.apache.flink.runtime.state.KeyGroupRange; import org.apache.flink.runtime.state.KeyGroupRangeOffsets; import org.apache.flink.runtime.state.KeyGroupsStateHandle; import org.apache.flink.runtime.state.KeyedStateHandle; import org.apache.flink.runtime.state.OperatorStateHandle; import org.apache.flink.runtime.state.OperatorStreamStateHandle; +import org.apache.flink.runtime.state.ResultSubpartitionStateHandle; +import org.apache.flink.runtime.state.StateObject; import org.apache.flink.runtime.state.filesystem.FileStateHandle; import org.apache.flink.runtime.state.memory.ByteStreamStateHandle; import org.apache.flink.runtime.state.memory.MemoryStateBackend; @@ -74,6 +77,7 @@ import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -88,6 +92,8 @@ import java.util.function.BiConsumer; import java.util.function.BiFunction; +import static org.apache.flink.runtime.checkpoint.StateHandleDummyUtil.createNewInputChannelStateHandle; +import static org.apache.flink.runtime.checkpoint.StateHandleDummyUtil.createNewResultSubpartitionStateHandle; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.spy; @@ -425,6 +431,55 @@ public static KeyGroupsStateHandle generateKeyGroupState( return new KeyGroupsStateHandle(keyGroupRangeOffsets, allSerializedStatesHandle); } + public static Tuple2, OperatorSubtaskState> + generateSampleOperatorSubtaskState() throws IOException { + JobVertexID jobVertexID = new JobVertexID(); + int index = 0; + Random random = new Random(); + OperatorStateHandle managedOpHandle = + generatePartitionableStateHandle(jobVertexID, index, 2, 8, false); + OperatorStateHandle rawOpHandle = + generatePartitionableStateHandle(jobVertexID, index, 2, 8, true); + KeyedStateHandle managedKeyedHandle = + generateKeyGroupState(jobVertexID, new KeyGroupRange(0, random.nextInt(12)), false); + KeyedStateHandle rawKeyedHandle = + generateKeyGroupState(jobVertexID, new KeyGroupRange(0, random.nextInt(10)), true); + InputChannelStateHandle inputChannelStateHandle = + createNewInputChannelStateHandle(3, random); + ResultSubpartitionStateHandle resultSubpartitionStateHandle = + createNewResultSubpartitionStateHandle(3, random); + OperatorSubtaskState operatorSubtaskState = + OperatorSubtaskState.builder() + .setManagedOperatorState(managedOpHandle) + .setRawOperatorState(rawOpHandle) + .setManagedKeyedState(managedKeyedHandle) + .setRawKeyedState(rawKeyedHandle) + .setInputChannelState( + StateObjectCollection.singleton(inputChannelStateHandle)) + .setResultSubpartitionState( + StateObjectCollection.singleton(resultSubpartitionStateHandle)) + .setInputRescalingDescriptor( + InflightDataRescalingDescriptorUtil.rescalingDescriptor( + new int[1], + new RescaleMappings[0], + Collections.singleton(1))) + .setOutputRescalingDescriptor( + InflightDataRescalingDescriptorUtil.rescalingDescriptor( + new int[1], + new RescaleMappings[0], + Collections.singleton(2))) + .build(); + return new Tuple2<>( + Arrays.asList( + managedOpHandle, + rawOpHandle, + managedKeyedHandle, + rawKeyedHandle, + inputChannelStateHandle, + resultSubpartitionStateHandle), + operatorSubtaskState); + } + public static TaskStateSnapshot createSnapshotWithUnionListState( File stateFile, OperatorID operatorId, boolean isTaskFinished) throws IOException { TaskStateSnapshot taskStateSnapshot = new TaskStateSnapshot(1, isTaskFinished); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CompletedCheckpointStoreTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CompletedCheckpointStoreTest.java index 06e6ff4146e75..376f251a3d796 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CompletedCheckpointStoreTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/CompletedCheckpointStoreTest.java @@ -32,6 +32,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; @@ -272,7 +273,7 @@ private void verifyCheckpoint(CompletedCheckpoint expected, CompletedCheckpoint /** * A test {@link CompletedCheckpoint}. We want to verify that the correct class loader is used * when discarding. Spying on a regular {@link CompletedCheckpoint} instance with Mockito - * doesn't work, because it it breaks serializability. + * doesn't work, because it breaks serializability. */ protected static class TestCompletedCheckpoint extends CompletedCheckpoint { @@ -327,8 +328,12 @@ public boolean awaitDiscard(long timeout) throws InterruptedException { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } TestCompletedCheckpoint that = (TestCompletedCheckpoint) o; @@ -346,6 +351,15 @@ public class TestCompletedCheckpointDiscardObject extends CompletedCheckpointDis @Override public void discard() throws Exception { super.discard(); + updateDiscards(); + } + + @Override + public CompletableFuture discardAsync(Executor executor) { + return super.discardAsync(executor).thenRun(this::updateDiscards); + } + + private void updateDiscards() { if (!isDiscarded) { isDiscarded = true; diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/FullyFinishedOperatorStateTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/FullyFinishedOperatorStateTest.java index 68144ef630fb7..3dd2ef9e43614 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/FullyFinishedOperatorStateTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/FullyFinishedOperatorStateTest.java @@ -18,11 +18,20 @@ package org.apache.flink.runtime.checkpoint; +import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.runtime.jobgraph.OperatorID; +import org.apache.flink.runtime.state.InputChannelStateHandle; +import org.apache.flink.runtime.state.ResultSubpartitionStateHandle; +import org.apache.flink.runtime.state.StateObject; import org.apache.flink.runtime.state.memory.ByteStreamStateHandle; import org.junit.jupiter.api.Test; +import java.io.IOException; +import java.util.HashSet; +import java.util.List; + +import static org.apache.flink.runtime.checkpoint.CheckpointCoordinatorTestingUtils.generateSampleOperatorSubtaskState; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -49,4 +58,30 @@ void testFullyFinishedOperatorState() { .as("Should not be able to put new subtask states for a fully finished state") .isInstanceOf(UnsupportedOperationException.class); } + + @Test + void testGetDiscardables() throws IOException { + Tuple2, OperatorSubtaskState> opSubtaskStates1 = + generateSampleOperatorSubtaskState(); + Tuple2, OperatorSubtaskState> opSubtaskStates2 = + generateSampleOperatorSubtaskState(); + + OperatorState operatorState = new OperatorState(new OperatorID(), 2, 256); + operatorState.putState(0, opSubtaskStates1.f1); + operatorState.putState(1, opSubtaskStates2.f1); + ByteStreamStateHandle coordinatorState = + new ByteStreamStateHandle("test", new byte[] {1, 2, 3, 4}); + operatorState.setCoordinatorState(coordinatorState); + HashSet discardables = new HashSet<>(); + discardables.addAll(opSubtaskStates1.f0.subList(0, 4)); + discardables.add(((InputChannelStateHandle) opSubtaskStates1.f0.get(4)).getDelegate()); + discardables.add( + ((ResultSubpartitionStateHandle) opSubtaskStates1.f0.get(5)).getDelegate()); + discardables.addAll(opSubtaskStates2.f0.subList(0, 4)); + discardables.add(((InputChannelStateHandle) opSubtaskStates2.f0.get(4)).getDelegate()); + discardables.add( + ((ResultSubpartitionStateHandle) opSubtaskStates2.f0.get(5)).getDelegate()); + discardables.add(coordinatorState); + assertThat(new HashSet<>(operatorState.getDiscardables())).isEqualTo(discardables); + } } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/OperatorSubtaskStateTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/OperatorSubtaskStateTest.java index 51074a49e7c03..bf746a3497251 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/OperatorSubtaskStateTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/OperatorSubtaskStateTest.java @@ -17,28 +17,26 @@ package org.apache.flink.runtime.checkpoint; +import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.runtime.checkpoint.channel.InputChannelInfo; import org.apache.flink.runtime.checkpoint.channel.ResultSubpartitionInfo; -import org.apache.flink.runtime.jobgraph.JobVertexID; import org.apache.flink.runtime.state.InputChannelStateHandle; -import org.apache.flink.runtime.state.KeyGroupRange; import org.apache.flink.runtime.state.ResultSubpartitionStateHandle; +import org.apache.flink.runtime.state.StateObject; import org.apache.flink.runtime.state.StreamStateHandle; import org.apache.flink.runtime.state.memory.ByteStreamStateHandle; import org.junit.jupiter.api.Test; import java.io.IOException; -import java.util.Collections; -import java.util.Random; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.apache.commons.lang3.builder.EqualsBuilder.reflectionEquals; -import static org.apache.flink.runtime.checkpoint.CheckpointCoordinatorTestingUtils.generateKeyGroupState; -import static org.apache.flink.runtime.checkpoint.CheckpointCoordinatorTestingUtils.generatePartitionableStateHandle; -import static org.apache.flink.runtime.checkpoint.StateHandleDummyUtil.createNewInputChannelStateHandle; -import static org.apache.flink.runtime.checkpoint.StateHandleDummyUtil.createNewResultSubpartitionStateHandle; +import static org.apache.flink.runtime.checkpoint.CheckpointCoordinatorTestingUtils.generateSampleOperatorSubtaskState; import static org.assertj.core.api.Assertions.assertThat; /** {@link OperatorSubtaskState} test. */ @@ -65,37 +63,7 @@ void testDiscardDuplicatedDelegatesOnce() { @Test void testToBuilderCorrectness() throws IOException { // given: Initialized operator subtask state. - JobVertexID jobVertexID = new JobVertexID(); - int index = 0; - Random random = new Random(); - - OperatorSubtaskState operatorSubtaskState = - OperatorSubtaskState.builder() - .setManagedOperatorState( - generatePartitionableStateHandle(jobVertexID, index, 2, 8, false)) - .setRawOperatorState( - generatePartitionableStateHandle(jobVertexID, index, 2, 8, true)) - .setManagedKeyedState( - generateKeyGroupState(jobVertexID, new KeyGroupRange(0, 11), false)) - .setRawKeyedState( - generateKeyGroupState(jobVertexID, new KeyGroupRange(0, 9), true)) - .setInputChannelState( - StateObjectCollection.singleton( - createNewInputChannelStateHandle(3, random))) - .setResultSubpartitionState( - StateObjectCollection.singleton( - createNewResultSubpartitionStateHandle(3, random))) - .setInputRescalingDescriptor( - InflightDataRescalingDescriptorUtil.rescalingDescriptor( - new int[1], - new RescaleMappings[0], - Collections.singleton(1))) - .setOutputRescalingDescriptor( - InflightDataRescalingDescriptorUtil.rescalingDescriptor( - new int[1], - new RescaleMappings[0], - Collections.singleton(2))) - .build(); + OperatorSubtaskState operatorSubtaskState = generateSampleOperatorSubtaskState().f1; // when: Copy the operator subtask state. OperatorSubtaskState operatorSubtaskStateCopy = operatorSubtaskState.toBuilder().build(); @@ -104,6 +72,24 @@ void testToBuilderCorrectness() throws IOException { assertThat(reflectionEquals(operatorSubtaskState, operatorSubtaskStateCopy)).isTrue(); } + @Test + void testGetDiscardables() throws IOException { + Tuple2, OperatorSubtaskState> opStates = + generateSampleOperatorSubtaskState(); + List states = opStates.f0; + OperatorSubtaskState operatorSubtaskState = opStates.f1; + List discardables = + Arrays.asList( + states.get(0), + states.get(1), + states.get(2), + states.get(3), + ((InputChannelStateHandle) states.get(4)).getDelegate(), + ((ResultSubpartitionStateHandle) states.get(5)).getDelegate()); + assertThat(new HashSet<>(operatorSubtaskState.getDiscardables())) + .isEqualTo(new HashSet<>(discardables)); + } + private ResultSubpartitionStateHandle buildSubpartitionHandle( StreamStateHandle delegate, int subPartitionIdx1) { return new ResultSubpartitionStateHandle( diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/PendingCheckpointTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/PendingCheckpointTest.java index 9d3e14bccc3c5..e530edef9b15d 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/PendingCheckpointTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/PendingCheckpointTest.java @@ -24,6 +24,7 @@ import org.apache.flink.core.fs.local.LocalFileSystem; import org.apache.flink.core.io.SimpleVersionedSerializer; import org.apache.flink.runtime.OperatorIDPair; +import org.apache.flink.runtime.checkpoint.CheckpointCoordinatorTest.OperatorSubtaskStateMock; import org.apache.flink.runtime.checkpoint.CheckpointCoordinatorTestingUtils.StringSerializer; import org.apache.flink.runtime.checkpoint.PendingCheckpoint.TaskAcknowledgeResult; import org.apache.flink.runtime.checkpoint.hooks.MasterHooks; @@ -35,7 +36,6 @@ import org.apache.flink.runtime.operators.coordination.OperatorInfo; import org.apache.flink.runtime.operators.coordination.TestingOperatorInfo; import org.apache.flink.runtime.state.CheckpointStorageLocationReference; -import org.apache.flink.runtime.state.SharedStateRegistry; import org.apache.flink.runtime.state.TestingStreamStateHandle; import org.apache.flink.runtime.state.filesystem.FsCheckpointStorageLocation; import org.apache.flink.runtime.state.memory.ByteStreamStateHandle; @@ -44,7 +44,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import org.mockito.Mockito; import javax.annotation.Nullable; @@ -66,11 +65,7 @@ import static org.apache.flink.util.Preconditions.checkNotNull; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -230,8 +225,10 @@ void testAbortDiscardsState() throws Exception { false, CheckpointType.CHECKPOINT, false, false, false, false, false, false); QueueExecutor executor = new QueueExecutor(); - OperatorState state = mock(OperatorState.class); - doNothing().when(state).registerSharedStates(any(SharedStateRegistry.class), eq(0L)); + OperatorState state = new OperatorState(new OperatorID(), 1, 256); + OperatorSubtaskStateMock subtaskStateMock = new OperatorSubtaskStateMock(); + OperatorSubtaskState subtaskState = subtaskStateMock.getSubtaskState(); + state.putState(0, subtaskState); // Abort declined PendingCheckpoint pending = createPendingCheckpoint(props, executor); @@ -240,10 +237,10 @@ void testAbortDiscardsState() throws Exception { abort(pending, CheckpointFailureReason.CHECKPOINT_DECLINED); // execute asynchronous discard operation executor.runQueuedCommands(); - verify(state, times(1)).discardState(); + subtaskStateMock.verifyDiscard(); // Abort error - Mockito.reset(state); + subtaskStateMock.reset(); pending = createPendingCheckpoint(props, executor); setTaskState(pending, state); @@ -251,10 +248,10 @@ void testAbortDiscardsState() throws Exception { abort(pending, CheckpointFailureReason.CHECKPOINT_DECLINED); // execute asynchronous discard operation executor.runQueuedCommands(); - verify(state, times(1)).discardState(); + subtaskStateMock.verifyDiscard(); // Abort expired - Mockito.reset(state); + subtaskStateMock.reset(); pending = createPendingCheckpoint(props, executor); setTaskState(pending, state); @@ -262,10 +259,10 @@ void testAbortDiscardsState() throws Exception { abort(pending, CheckpointFailureReason.CHECKPOINT_EXPIRED); // execute asynchronous discard operation executor.runQueuedCommands(); - verify(state, times(1)).discardState(); + subtaskStateMock.verifyDiscard(); // Abort subsumed - Mockito.reset(state); + subtaskStateMock.reset(); pending = createPendingCheckpoint(props, executor); setTaskState(pending, state); @@ -273,7 +270,7 @@ void testAbortDiscardsState() throws Exception { abort(pending, CheckpointFailureReason.CHECKPOINT_SUBSUMED); // execute asynchronous discard operation executor.runQueuedCommands(); - verify(state, times(1)).discardState(); + subtaskStateMock.verifyDiscard(); } /** diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/PrioritizedOperatorSubtaskStateTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/PrioritizedOperatorSubtaskStateTest.java index 11f41bc8baac4..fa892cea9daae 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/PrioritizedOperatorSubtaskStateTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/PrioritizedOperatorSubtaskStateTest.java @@ -27,15 +27,19 @@ import org.apache.flink.runtime.state.StateObject; import org.apache.flink.util.Preconditions; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Random; import java.util.function.Function; +import java.util.function.IntFunction; import java.util.stream.Collectors; import static org.apache.flink.runtime.checkpoint.StateHandleDummyUtil.createNewInputChannelStateHandle; @@ -51,6 +55,107 @@ class PrioritizedOperatorSubtaskStateTest { private static final Random RANDOM = new Random(0x42); + @Test + void testTryCreateMixedLocalAndRemoteAlternative() { + testTryCreateMixedLocalAndRemoteAlternative( + StateHandleDummyUtil::createKeyedStateHandleFromSeed, + KeyedStateHandle::getKeyGroupRange); + } + + void testTryCreateMixedLocalAndRemoteAlternative( + IntFunction stateHandleFactory, Function idExtractor) { + + SH remote0 = stateHandleFactory.apply(0); + SH remote1 = stateHandleFactory.apply(1); + SH remote2 = stateHandleFactory.apply(2); + SH remote3 = stateHandleFactory.apply(3); + + List jmState = Arrays.asList(remote0, remote1, remote2, remote3); + + SH local0 = stateHandleFactory.apply(0); + SH local3a = stateHandleFactory.apply(3); + + List alternativeA = Arrays.asList(local0, local3a); + + SH local1 = stateHandleFactory.apply(1); + SH local3b = stateHandleFactory.apply(3); + SH local5 = stateHandleFactory.apply(5); + + List alternativeB = Arrays.asList(local1, local3b, local5); + + List> alternatives = + Arrays.asList( + new StateObjectCollection<>(alternativeA), + new StateObjectCollection<>(Collections.emptyList()), + new StateObjectCollection<>(alternativeB)); + + StateObjectCollection result = + PrioritizedOperatorSubtaskState.Builder.tryComputeMixedLocalAndRemoteAlternative( + new StateObjectCollection<>(jmState), alternatives, idExtractor) + .get(); + + assertThat(result).hasSameElementsAs(Arrays.asList(local0, local1, remote2, local3a)); + } + + @Test + void testTryCreateMixedLocalAndRemoteAlternativeEmptyAlternative() { + testTryCreateMixedLocalAndRemoteAlternativeEmptyAlternative( + StateHandleDummyUtil::createKeyedStateHandleFromSeed, + KeyedStateHandle::getKeyGroupRange); + } + + void testTryCreateMixedLocalAndRemoteAlternativeEmptyAlternative( + IntFunction stateHandleFactory, Function idExtractor) { + List jmState = + Arrays.asList( + stateHandleFactory.apply(0), + stateHandleFactory.apply(1), + stateHandleFactory.apply(2), + stateHandleFactory.apply(3)); + + Assertions.assertFalse( + PrioritizedOperatorSubtaskState.Builder.tryComputeMixedLocalAndRemoteAlternative( + new StateObjectCollection<>(jmState), + Collections.emptyList(), + idExtractor) + .isPresent()); + + Assertions.assertFalse( + PrioritizedOperatorSubtaskState.Builder.tryComputeMixedLocalAndRemoteAlternative( + new StateObjectCollection<>(jmState), + Collections.singletonList(new StateObjectCollection<>()), + idExtractor) + .isPresent()); + } + + @Test + void testTryCreateMixedLocalAndRemoteAlternativeEmptyJMState() { + testTryCreateMixedLocalAndRemoteAlternativeEmptyJMState( + StateHandleDummyUtil::createKeyedStateHandleFromSeed, + KeyedStateHandle::getKeyGroupRange); + } + + void testTryCreateMixedLocalAndRemoteAlternativeEmptyJMState( + IntFunction stateHandleFactory, Function idExtractor) { + List alternativeA = + Arrays.asList(stateHandleFactory.apply(0), stateHandleFactory.apply(3)); + + Assertions.assertFalse( + PrioritizedOperatorSubtaskState.Builder.tryComputeMixedLocalAndRemoteAlternative( + new StateObjectCollection<>(Collections.emptyList()), + Collections.singletonList( + new StateObjectCollection<>(alternativeA)), + idExtractor) + .isPresent()); + + Assertions.assertFalse( + PrioritizedOperatorSubtaskState.Builder.tryComputeMixedLocalAndRemoteAlternative( + new StateObjectCollection<>(Collections.emptyList()), + Collections.emptyList(), + KeyedStateHandle::getKeyGroupRange) + .isPresent()); + } + /** * This tests attempts to test (almost) the full space of significantly different options for * verifying and prioritizing {@link OperatorSubtaskState} options for local recovery over @@ -106,16 +211,17 @@ void testPrioritization() { : onlyPrimary)) .isTrue(); - assertThat( - checkResultAsExpected( - OperatorSubtaskState::getManagedKeyedState, - PrioritizedOperatorSubtaskState - ::getPrioritizedManagedKeyedState, - prioritizedOperatorSubtaskState, - primaryAndFallback.getManagedKeyedState().size() == 1 - ? validAlternatives - : onlyPrimary)) - .isTrue(); + StateObjectCollection expManagedKeyed = + computeExpectedMixedState( + orderedAlternativesList, + primaryAndFallback, + OperatorSubtaskState::getManagedKeyedState, + KeyedStateHandle::getKeyGroupRange); + + assertResultAsExpected( + expManagedKeyed, + primaryAndFallback.getManagedKeyedState(), + prioritizedOperatorSubtaskState.getPrioritizedManagedKeyedState()); assertThat( checkResultAsExpected( @@ -128,16 +234,17 @@ void testPrioritization() { : onlyPrimary)) .isTrue(); - assertThat( - checkResultAsExpected( - OperatorSubtaskState::getRawKeyedState, - PrioritizedOperatorSubtaskState - ::getPrioritizedRawKeyedState, - prioritizedOperatorSubtaskState, - primaryAndFallback.getRawKeyedState().size() == 1 - ? validAlternatives - : onlyPrimary)) - .isTrue(); + StateObjectCollection expRawKeyed = + computeExpectedMixedState( + orderedAlternativesList, + primaryAndFallback, + OperatorSubtaskState::getRawKeyedState, + KeyedStateHandle::getKeyGroupRange); + + assertResultAsExpected( + expRawKeyed, + primaryAndFallback.getRawKeyedState(), + prioritizedOperatorSubtaskState.getPrioritizedRawKeyedState()); } } } @@ -390,4 +497,41 @@ private static T deepCopy(T stateObject) { throw new IllegalStateException(); } } + + private StateObjectCollection computeExpectedMixedState( + List orderedAlternativesList, + OperatorSubtaskState primaryAndFallback, + Function> stateExtractor, + Function idExtractor) { + + List reverseAlternatives = new ArrayList<>(orderedAlternativesList); + Collections.reverse(reverseAlternatives); + + Map map = + stateExtractor.apply(primaryAndFallback).stream() + .collect(Collectors.toMap(idExtractor, Function.identity())); + + reverseAlternatives.stream() + .flatMap(x -> stateExtractor.apply(x).stream()) + .forEach(x -> map.replace(idExtractor.apply(x), x)); + + return new StateObjectCollection<>(map.values()); + } + + static void assertResultAsExpected( + StateObjectCollection expected, + StateObjectCollection primary, + List> actual) { + Assertions.assertTrue(!actual.isEmpty() && actual.size() <= 2); + Assertions.assertTrue(isSameContentUnordered(expected, actual.get(0))); + if (actual.size() == 1) { + Assertions.assertTrue(isSameContentUnordered(primary, actual.get(0))); + } else { + Assertions.assertTrue(isSameContentUnordered(primary, actual.get(1))); + } + } + + static boolean isSameContentUnordered(Collection a, Collection b) { + return a.size() == b.size() && a.containsAll(b); + } } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/StateHandleDummyUtil.java b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/StateHandleDummyUtil.java index f7e4a7ef2dc15..52a8bf032b622 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/StateHandleDummyUtil.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/checkpoint/StateHandleDummyUtil.java @@ -55,7 +55,8 @@ public static OperatorStateHandle createNewOperatorStateHandle( OperatorStateHandle.StateMetaInfo metaInfo = new OperatorStateHandle.StateMetaInfo( offsets, OperatorStateHandle.Mode.SPLIT_DISTRIBUTE); - operatorStateMetaData.put(String.valueOf(UUID.randomUUID()), metaInfo); + operatorStateMetaData.put( + String.valueOf(new UUID(random.nextLong(), random.nextLong())), metaInfo); } return new OperatorStreamStateHandle( operatorStateMetaData, createStreamStateHandle(numNamedStates, random)); @@ -65,7 +66,8 @@ private static ByteStreamStateHandle createStreamStateHandle( int numNamedStates, Random random) { byte[] streamData = new byte[numNamedStates * 4]; random.nextBytes(streamData); - return new ByteStreamStateHandle(String.valueOf(UUID.randomUUID()), streamData); + return new ByteStreamStateHandle( + String.valueOf(new UUID(random.nextLong(), random.nextLong())), streamData); } /** Creates a new test {@link KeyedStateHandle} for the given key-group. */ @@ -149,11 +151,11 @@ public static InputChannelStateHandle createNewInputChannelStateHandle( } public static ResultSubpartitionStateHandle createNewResultSubpartitionStateHandle( - int i, Random random) { + int numNamedStates, Random random) { return new ResultSubpartitionStateHandle( new ResultSubpartitionInfo(random.nextInt(), random.nextInt()), - createStreamStateHandle(i, random), - genOffsets(i, random)); + createStreamStateHandle(numNamedStates, random), + genOffsets(numNamedStates, random)); } private static ArrayList genOffsets(int size, Random random) { @@ -164,6 +166,23 @@ private static ArrayList genOffsets(int size, Random random) { return offsets; } + public static KeyedStateHandle createKeyedStateHandleFromSeed(int seed) { + return createNewKeyedStateHandle(KeyGroupRange.of(seed * 4, seed * 4 + 3)); + } + + public static OperatorStateHandle createOperatorStateHandleFromSeed(int seed) { + return createNewOperatorStateHandle(1 + (seed % 3), new Random(seed)); + } + + public static InputChannelStateHandle createInputChannelStateHandleFromSeed(int seed) { + return createNewInputChannelStateHandle(1 + (seed % 3), new Random(seed)); + } + + public static ResultSubpartitionStateHandle createResultSubpartitionStateHandleFromSeed( + int seed) { + return createNewResultSubpartitionStateHandle(1 + (seed % 3), new Random(seed)); + } + /** KeyedStateHandle that only holds a key-group information. */ private static class DummyKeyedStateHandle implements KeyedStateHandle { diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/deployment/TaskDeploymentDescriptorTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/deployment/TaskDeploymentDescriptorTest.java index 6191b19767d20..ac003665584fe 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/deployment/TaskDeploymentDescriptorTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/deployment/TaskDeploymentDescriptorTest.java @@ -20,9 +20,13 @@ import org.apache.flink.api.common.ExecutionConfig; import org.apache.flink.api.common.JobID; +import org.apache.flink.configuration.BlobServerOptions; import org.apache.flink.configuration.Configuration; import org.apache.flink.core.testutils.CommonTestUtils; +import org.apache.flink.runtime.blob.BlobServer; +import org.apache.flink.runtime.blob.BlobWriter; import org.apache.flink.runtime.blob.PermanentBlobKey; +import org.apache.flink.runtime.blob.VoidBlobStore; import org.apache.flink.runtime.checkpoint.JobManagerTaskRestore; import org.apache.flink.runtime.checkpoint.TaskStateSnapshot; import org.apache.flink.runtime.clusterframework.types.AllocationID; @@ -32,14 +36,21 @@ import org.apache.flink.runtime.jobgraph.JobVertexID; import org.apache.flink.runtime.jobgraph.tasks.AbstractInvokable; import org.apache.flink.runtime.operators.BatchTask; +import org.apache.flink.runtime.util.DefaultGroupCache; +import org.apache.flink.runtime.util.GroupCache; +import org.apache.flink.runtime.util.NoOpGroupCache; +import org.apache.flink.testutils.junit.utils.TempDirUtils; +import org.apache.flink.types.Either; import org.apache.flink.util.SerializedValue; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import javax.annotation.Nonnull; import java.io.IOException; import java.net.URL; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -50,6 +61,8 @@ /** Tests for the {@link TaskDeploymentDescriptor}. */ class TaskDeploymentDescriptorTest { + @TempDir Path temporaryFolder; + private static final JobID jobID = new JobID(); private static final JobVertexID vertexID = new JobVertexID(); private static final ExecutionAttemptID execId = createExecutionAttemptId(vertexID); @@ -73,24 +86,28 @@ class TaskDeploymentDescriptorTest { private final SerializedValue executionConfig = new SerializedValue<>(new ExecutionConfig()); + + private final JobInformation jobInformation = + new JobInformation( + jobID, + jobName, + executionConfig, + jobConfiguration, + requiredJars, + requiredClasspaths); private final SerializedValue serializedJobInformation = - new SerializedValue<>( - new JobInformation( - jobID, - jobName, - executionConfig, - jobConfiguration, - requiredJars, - requiredClasspaths)); + new SerializedValue<>(jobInformation); + + private final TaskInformation taskInformation = + new TaskInformation( + vertexID, + taskName, + currentNumberOfSubtasks, + numberOfKeyGroups, + invokableClass.getName(), + taskConfiguration); private final SerializedValue serializedJobVertexInformation = - new SerializedValue<>( - new TaskInformation( - vertexID, - taskName, - currentNumberOfSubtasks, - numberOfKeyGroups, - invokableClass.getName(), - taskConfiguration)); + new SerializedValue<>(taskInformation); TaskDeploymentDescriptorTest() throws IOException {} @@ -104,19 +121,15 @@ void testSerialization() throws Exception { final TaskDeploymentDescriptor copy = CommonTestUtils.createCopySerializable(orig); - assertThat(orig.getSerializedJobInformation()) - .isNotSameAs(copy.getSerializedJobInformation()); - assertThat(orig.getSerializedTaskInformation()) - .isNotSameAs(copy.getSerializedTaskInformation()); + assertThat(orig.getJobInformation()).isNotSameAs(copy.getJobInformation()); + assertThat(orig.getTaskInformation()).isNotSameAs(copy.getTaskInformation()); assertThat(orig.getExecutionAttemptId()).isNotSameAs(copy.getExecutionAttemptId()); assertThat(orig.getTaskRestore()).isNotSameAs(copy.getTaskRestore()); assertThat(orig.getProducedPartitions()).isNotSameAs(copy.getProducedPartitions()); assertThat(orig.getInputGates()).isNotSameAs(copy.getInputGates()); - assertThat(orig.getSerializedJobInformation()) - .isEqualTo(copy.getSerializedJobInformation()); - assertThat(orig.getSerializedTaskInformation()) - .isEqualTo(copy.getSerializedTaskInformation()); + assertThat(orig.getJobInformation()).isEqualTo(copy.getJobInformation()); + assertThat(orig.getTaskInformation()).isEqualTo(copy.getTaskInformation()); assertThat(orig.getExecutionAttemptId()).isEqualTo(copy.getExecutionAttemptId()); assertThat(orig.getAllocationId()).isEqualTo(copy.getAllocationId()); assertThat(orig.getSubtaskIndex()).isEqualTo(copy.getSubtaskIndex()); @@ -130,20 +143,88 @@ void testSerialization() throws Exception { } @Test - void testOffLoadedAndNonOffLoadedPayload() { + void testOffLoadedAndNonOffLoadedPayload() throws IOException, ClassNotFoundException { final TaskDeploymentDescriptor taskDeploymentDescriptor = createTaskDeploymentDescriptor( new TaskDeploymentDescriptor.NonOffloaded<>(serializedJobInformation), new TaskDeploymentDescriptor.Offloaded<>(new PermanentBlobKey())); - SerializedValue actualSerializedJobInformation = - taskDeploymentDescriptor.getSerializedJobInformation(); - assertThat(actualSerializedJobInformation).isSameAs(serializedJobInformation); + JobInformation actualJobInformation = taskDeploymentDescriptor.getJobInformation(); + assertThat(actualJobInformation).isEqualTo(jobInformation); - assertThatThrownBy(taskDeploymentDescriptor::getSerializedTaskInformation) + assertThatThrownBy(taskDeploymentDescriptor::getTaskInformation) .isInstanceOf(IllegalStateException.class); } + @Test + void testTaskInformationCache() throws IOException, ClassNotFoundException { + try (BlobServer blobServer = setupBlobServer()) { + // Serialize taskInformation to blobServer and get the permanentBlobKey + Either, PermanentBlobKey> taskInformationOrBlobKey = + BlobWriter.serializeAndTryOffload(taskInformation, jobID, blobServer); + assertThat(taskInformationOrBlobKey.isRight()).isTrue(); + PermanentBlobKey permanentBlobKey = taskInformationOrBlobKey.right(); + + GroupCache taskInformationCache = + new DefaultGroupCache.Factory() + .create(); + // Test for tdd1 + final TaskDeploymentDescriptor tdd1 = + createTaskDeploymentDescriptor( + new TaskDeploymentDescriptor.NonOffloaded<>(serializedJobInformation), + new TaskDeploymentDescriptor.Offloaded<>(permanentBlobKey)); + assertThat(taskInformationCache.get(jobID, permanentBlobKey)).isNull(); + tdd1.loadBigData( + blobServer, + new NoOpGroupCache<>(), + taskInformationCache, + new NoOpGroupCache<>()); + TaskInformation taskInformation1 = tdd1.getTaskInformation(); + assertThat(taskInformation1).isEqualTo(taskInformation); + // The TaskInformation is cached in taskInformationCache, and it's equals to + // taskInformation1. + assertThat(taskInformationCache.get(jobID, permanentBlobKey)) + .isNotNull() + .isEqualTo(taskInformation1); + + // Test for tdd2 + final TaskDeploymentDescriptor tdd2 = + createTaskDeploymentDescriptor( + new TaskDeploymentDescriptor.NonOffloaded<>(serializedJobInformation), + new TaskDeploymentDescriptor.Offloaded<>(permanentBlobKey)); + tdd2.loadBigData( + blobServer, + new NoOpGroupCache<>(), + taskInformationCache, + new NoOpGroupCache<>()); + TaskInformation taskInformation2 = tdd2.getTaskInformation(); + // The TaskInformation2 is equals to taskInformation1 and original taskInformation, but + // they are not same. + assertThat(taskInformation2) + .isNotNull() + .isEqualTo(taskInformation1) + .isNotSameAs(taskInformation1) + .isEqualTo(taskInformation); + // Configuration may be changed by subtask, so the configuration must be not same. + assertThat(taskInformation2.getTaskConfiguration()) + .isNotNull() + .isEqualTo(taskInformation1.getTaskConfiguration()) + .isNotSameAs(taskInformation1.getTaskConfiguration()); + } + } + + private BlobServer setupBlobServer() throws IOException { + + Configuration config = new Configuration(); + // always offload the serialized job and task information + config.setInteger(BlobServerOptions.OFFLOAD_MINSIZE, 0); + BlobServer blobServer = + new BlobServer( + config, TempDirUtils.newFolder(temporaryFolder), new VoidBlobStore()); + blobServer.start(); + return blobServer; + } + @Nonnull private TaskDeploymentDescriptor createTaskDeploymentDescriptor( TaskDeploymentDescriptor.MaybeOffloaded jobInformation, diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/DispatcherResourceCleanupTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/DispatcherResourceCleanupTest.java index 4954b1ba2c3eb..122441459d8e2 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/DispatcherResourceCleanupTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/DispatcherResourceCleanupTest.java @@ -575,9 +575,7 @@ public void testFatalErrorIfJobCannotBeMarkedDirtyInJobResultStore() throws Exce final CompletableFuture errorFuture = this.testingFatalErrorHandlerResource.getFatalErrorHandler().getErrorFuture(); - assertThat( - errorFuture.get(100, TimeUnit.MILLISECONDS), - IsInstanceOf.instanceOf(FlinkException.class)); + assertThat(errorFuture.get(), IsInstanceOf.instanceOf(FlinkException.class)); testingFatalErrorHandlerResource.getFatalErrorHandler().clearError(); } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/JobMasterTester.java b/flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/JobMasterTester.java index a5508f9698ec7..08783509a9fe1 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/JobMasterTester.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/dispatcher/JobMasterTester.java @@ -204,9 +204,7 @@ private CompletableFuture getTaskInformation( "Task descriptor for %s not found.", executionAttemptId))); try { - return descriptor - .getSerializedTaskInformation() - .deserializeValue(Thread.currentThread().getContextClassLoader()); + return descriptor.getTaskInformation(); } catch (Exception e) { throw new IllegalStateException( String.format( diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/DefaultExecutionGraphDeploymentTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/DefaultExecutionGraphDeploymentTest.java index fc2df07230376..d354b49fedde9 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/DefaultExecutionGraphDeploymentTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/DefaultExecutionGraphDeploymentTest.java @@ -58,7 +58,6 @@ import org.apache.flink.runtime.scheduler.TestingPhysicalSlotProvider; import org.apache.flink.runtime.scheduler.strategy.ConsumedPartitionGroup; import org.apache.flink.runtime.shuffle.ShuffleDescriptor; -import org.apache.flink.runtime.taskexecutor.NoOpShuffleDescriptorsCache; import org.apache.flink.runtime.taskexecutor.TestingTaskExecutorGateway; import org.apache.flink.runtime.taskexecutor.TestingTaskExecutorGatewayBuilder; import org.apache.flink.runtime.taskmanager.LocalTaskManagerLocation; @@ -66,6 +65,7 @@ import org.apache.flink.runtime.taskmanager.TaskManagerLocation; import org.apache.flink.runtime.testtasks.NoOpInvokable; import org.apache.flink.runtime.testutils.DirectScheduledExecutorService; +import org.apache.flink.runtime.util.NoOpGroupCache; import org.apache.flink.testutils.TestingUtils; import org.apache.flink.testutils.executor.TestExecutorExtension; import org.apache.flink.util.function.FunctionUtils; @@ -180,7 +180,10 @@ void testBuildDeploymentDescriptor() throws Exception { FunctionUtils.uncheckedConsumer( taskDeploymentDescriptor -> { taskDeploymentDescriptor.loadBigData( - blobCache, NoOpShuffleDescriptorsCache.INSTANCE); + blobCache, + new NoOpGroupCache<>(), + new NoOpGroupCache<>(), + new NoOpGroupCache<>()); tdd.complete(taskDeploymentDescriptor); })); @@ -203,10 +206,8 @@ void testBuildDeploymentDescriptor() throws Exception { TaskDeploymentDescriptor descr = tdd.get(); assertThat(descr).isNotNull(); - JobInformation jobInformation = - descr.getSerializedJobInformation().deserializeValue(getClass().getClassLoader()); - TaskInformation taskInformation = - descr.getSerializedTaskInformation().deserializeValue(getClass().getClassLoader()); + JobInformation jobInformation = descr.getJobInformation(); + TaskInformation taskInformation = descr.getTaskInformation(); assertThat(descr.getJobId()).isEqualTo(jobId); assertThat(jobInformation.getJobId()).isEqualTo(jobId); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/DefaultExecutionGraphDeploymentWithSmallBlobCacheSizeLimitTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/DefaultExecutionGraphDeploymentWithSmallBlobCacheSizeLimitTest.java index 6fdc9de9adeb5..fae6a754646a4 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/DefaultExecutionGraphDeploymentWithSmallBlobCacheSizeLimitTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/executiongraph/DefaultExecutionGraphDeploymentWithSmallBlobCacheSizeLimitTest.java @@ -42,8 +42,8 @@ import org.apache.flink.runtime.jobmaster.TestingLogicalSlotBuilder; import org.apache.flink.runtime.operators.BatchTask; import org.apache.flink.runtime.scheduler.strategy.ConsumedPartitionGroup; -import org.apache.flink.runtime.taskexecutor.NoOpShuffleDescriptorsCache; import org.apache.flink.runtime.testutils.DirectScheduledExecutorService; +import org.apache.flink.runtime.util.NoOpGroupCache; import org.apache.flink.testutils.junit.utils.TempDirUtils; import org.apache.flink.util.function.FunctionUtils; @@ -121,7 +121,10 @@ void testDeployMultipleTasksWithSmallBlobCacheSizeLimit() throws Exception { FunctionUtils.uncheckedConsumer( taskDeploymentDescriptor -> { taskDeploymentDescriptor.loadBigData( - blobCache, NoOpShuffleDescriptorsCache.INSTANCE); + blobCache, + new NoOpGroupCache<>(), + new NoOpGroupCache<>(), + new NoOpGroupCache<>()); tdds.offer(taskDeploymentDescriptor); })); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/BatchShuffleReadBufferPoolTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/BatchShuffleReadBufferPoolTest.java index 116fb5473bc4b..5e64751855a02 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/BatchShuffleReadBufferPoolTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/BatchShuffleReadBufferPoolTest.java @@ -18,71 +18,70 @@ package org.apache.flink.runtime.io.disk; import org.apache.flink.core.memory.MemorySegment; +import org.apache.flink.core.testutils.CheckedThread; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.Timeout; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** Tests for {@link BatchShuffleReadBufferPool}. */ -public class BatchShuffleReadBufferPoolTest { +@Timeout(value = 60) +class BatchShuffleReadBufferPoolTest { - @Rule public Timeout timeout = new Timeout(60, TimeUnit.SECONDS); - - @Test(expected = IllegalArgumentException.class) - public void testIllegalTotalBytes() { - createBufferPool(0, 1024); + @Test + void testIllegalTotalBytes() { + assertThatThrownBy(() -> createBufferPool(0, 1024)) + .isInstanceOf(IllegalArgumentException.class); } - @Test(expected = IllegalArgumentException.class) - public void testIllegalBufferSize() { - createBufferPool(32 * 1024 * 1024, 0); + @Test + void testIllegalBufferSize() { + assertThatThrownBy(() -> createBufferPool(32 * 1024 * 1024, 0)) + .isInstanceOf(IllegalArgumentException.class); } @Test - public void testLargeTotalBytes() { + void testLargeTotalBytes() { BatchShuffleReadBufferPool bufferPool = createBufferPool(Long.MAX_VALUE, 1024); - assertEquals(Integer.MAX_VALUE, bufferPool.getNumTotalBuffers()); + assertThat(bufferPool.getNumTotalBuffers()).isEqualTo(Integer.MAX_VALUE); bufferPool.destroy(); } - @Test(expected = IllegalArgumentException.class) - public void testTotalBytesSmallerThanBufferSize() { - createBufferPool(4096, 32 * 1024); + @Test + void testTotalBytesSmallerThanBufferSize() { + assertThatThrownBy(() -> createBufferPool(4096, 32 * 1024)) + .isInstanceOf(IllegalArgumentException.class); } @Test - public void testBufferCalculation() { + void testBufferCalculation() { long totalBytes = 32 * 1024 * 1024; for (int bufferSize = 4 * 1024; bufferSize <= totalBytes; bufferSize += 1024) { BatchShuffleReadBufferPool bufferPool = createBufferPool(totalBytes, bufferSize); - assertEquals(totalBytes, bufferPool.getTotalBytes()); - assertEquals(totalBytes / bufferSize, bufferPool.getNumTotalBuffers()); - assertTrue(bufferPool.getNumBuffersPerRequest() <= bufferPool.getNumTotalBuffers()); - assertTrue(bufferPool.getNumBuffersPerRequest() > 0); + assertThat(bufferPool.getTotalBytes()).isEqualTo(totalBytes); + assertThat(bufferPool.getNumTotalBuffers()).isEqualTo(totalBytes / bufferSize); + assertThat(bufferPool.getNumBuffersPerRequest()) + .isLessThanOrEqualTo(bufferPool.getNumTotalBuffers()); + assertThat(bufferPool.getNumBuffersPerRequest()).isGreaterThan(0); } } @Test - public void testRequestBuffers() throws Exception { + void testRequestBuffers() throws Exception { BatchShuffleReadBufferPool bufferPool = createBufferPool(); List buffers = new ArrayList<>(); try { buffers.addAll(bufferPool.requestBuffers()); - assertEquals(bufferPool.getNumBuffersPerRequest(), buffers.size()); + assertThat(buffers).hasSize(bufferPool.getNumBuffersPerRequest()); } finally { bufferPool.recycle(buffers); bufferPool.destroy(); @@ -90,46 +89,45 @@ public void testRequestBuffers() throws Exception { } @Test - public void testRecycle() throws Exception { + void testRecycle() throws Exception { BatchShuffleReadBufferPool bufferPool = createBufferPool(); List buffers = bufferPool.requestBuffers(); bufferPool.recycle(buffers); - assertEquals(bufferPool.getNumTotalBuffers(), bufferPool.getAvailableBuffers()); + assertThat(bufferPool.getAvailableBuffers()).isEqualTo(bufferPool.getNumTotalBuffers()); } @Test - public void testBufferOperationTimestampUpdated() throws Exception { + void testBufferOperationTimestampUpdated() throws Exception { BatchShuffleReadBufferPool bufferPool = new BatchShuffleReadBufferPool(1024, 1024); long oldTimestamp = bufferPool.getLastBufferOperationTimestamp(); Thread.sleep(100); List buffers = bufferPool.requestBuffers(); - assertEquals(1, buffers.size()); + assertThat(buffers).hasSize(1); // The timestamp is updated when requesting buffers successfully - assertTrue(bufferPool.getLastBufferOperationTimestamp() > oldTimestamp); + assertThat(bufferPool.getLastBufferOperationTimestamp()).isGreaterThan(oldTimestamp); oldTimestamp = bufferPool.getLastBufferOperationTimestamp(); Thread.sleep(100); bufferPool.recycle(buffers); // The timestamp is updated when recycling buffers - assertTrue(bufferPool.getLastBufferOperationTimestamp() > oldTimestamp); + assertThat(bufferPool.getLastBufferOperationTimestamp()).isGreaterThan(oldTimestamp); buffers = bufferPool.requestBuffers(); oldTimestamp = bufferPool.getLastBufferOperationTimestamp(); Thread.sleep(100); - assertEquals(0, bufferPool.requestBuffers().size()); + assertThat(bufferPool.requestBuffers()).isEmpty(); // The timestamp is not updated when requesting buffers is failed - assertEquals(oldTimestamp, bufferPool.getLastBufferOperationTimestamp()); + assertThat(bufferPool.getLastBufferOperationTimestamp()).isEqualTo(oldTimestamp); bufferPool.recycle(buffers); bufferPool.destroy(); } @Test - public void testBufferFulfilledByRecycledBuffers() throws Exception { + void testBufferFulfilledByRecycledBuffers() throws Exception { int numRequestThreads = 2; - AtomicReference exception = new AtomicReference<>(); BatchShuffleReadBufferPool bufferPool = createBufferPool(); Map> buffers = new ConcurrentHashMap<>(); @@ -139,24 +137,22 @@ public void testBufferFulfilledByRecycledBuffers() throws Exception { owners[i] = new Object(); buffers.put(owners[i], bufferPool.requestBuffers()); } - assertEquals(0, bufferPool.getAvailableBuffers()); + assertThat(bufferPool.getAvailableBuffers()).isZero(); - Thread[] requestThreads = new Thread[numRequestThreads]; + CheckedThread[] requestThreads = new CheckedThread[numRequestThreads]; for (int i = 0; i < numRequestThreads; ++i) { requestThreads[i] = - new Thread( - () -> { - try { - Object owner = new Object(); - List allocated = null; - while (allocated == null || allocated.isEmpty()) { - allocated = bufferPool.requestBuffers(); - } - buffers.put(owner, allocated); - } catch (Throwable throwable) { - exception.set(throwable); - } - }); + new CheckedThread() { + @Override + public void go() throws Exception { + Object owner = new Object(); + List allocated = null; + while (allocated == null || allocated.isEmpty()) { + allocated = bufferPool.requestBuffers(); + } + buffers.put(owner, allocated); + } + }; requestThreads[i].start(); } @@ -168,125 +164,115 @@ public void testBufferFulfilledByRecycledBuffers() throws Exception { // bulk recycle bufferPool.recycle(buffers.remove(owners[1])); - for (Thread requestThread : requestThreads) { - requestThread.join(); + for (CheckedThread requestThread : requestThreads) { + requestThread.sync(); } - assertNull(exception.get()); - assertEquals(0, bufferPool.getAvailableBuffers()); - assertEquals(8, buffers.size()); + assertThat(bufferPool.getAvailableBuffers()).isZero(); + assertThat(buffers).hasSize(8); } finally { for (Object owner : buffers.keySet()) { bufferPool.recycle(buffers.remove(owner)); } - assertEquals(bufferPool.getNumTotalBuffers(), bufferPool.getAvailableBuffers()); + assertThat(bufferPool.getAvailableBuffers()).isEqualTo(bufferPool.getNumTotalBuffers()); bufferPool.destroy(); } } @Test - public void testMultipleThreadRequestAndRecycle() throws Exception { + void testMultipleThreadRequestAndRecycle() throws Exception { int numRequestThreads = 10; - AtomicReference exception = new AtomicReference<>(); BatchShuffleReadBufferPool bufferPool = createBufferPool(); try { - Thread[] requestThreads = new Thread[numRequestThreads]; + CheckedThread[] requestThreads = new CheckedThread[numRequestThreads]; for (int i = 0; i < numRequestThreads; ++i) { requestThreads[i] = - new Thread( - () -> { - try { - for (int j = 0; j < 100; ++j) { - List buffers = - bufferPool.requestBuffers(); - Thread.sleep(10); - if (j % 2 == 0) { - bufferPool.recycle(buffers); - } else { - for (MemorySegment segment : buffers) { - bufferPool.recycle(segment); - } - } + new CheckedThread() { + @Override + public void go() throws Exception { + for (int j = 0; j < 100; ++j) { + List buffers = bufferPool.requestBuffers(); + Thread.sleep(10); + if (j % 2 == 0) { + bufferPool.recycle(buffers); + } else { + for (MemorySegment segment : buffers) { + bufferPool.recycle(segment); } - } catch (Throwable throwable) { - exception.set(throwable); } - }); + } + } + }; requestThreads[i].start(); } - for (Thread requestThread : requestThreads) { - requestThread.join(); + for (CheckedThread requestThread : requestThreads) { + requestThread.sync(); } - assertNull(exception.get()); - assertEquals(bufferPool.getNumTotalBuffers(), bufferPool.getAvailableBuffers()); + assertThat(bufferPool.getAvailableBuffers()).isEqualTo(bufferPool.getNumTotalBuffers()); } finally { bufferPool.destroy(); } } @Test - public void testDestroy() throws Exception { + void testDestroy() throws Exception { BatchShuffleReadBufferPool bufferPool = createBufferPool(); List buffers = bufferPool.requestBuffers(); bufferPool.recycle(buffers); - assertFalse(bufferPool.isDestroyed()); - assertEquals(bufferPool.getNumTotalBuffers(), bufferPool.getAvailableBuffers()); + assertThat(bufferPool.isDestroyed()).isFalse(); + assertThat(bufferPool.getAvailableBuffers()).isEqualTo(bufferPool.getNumTotalBuffers()); buffers = bufferPool.requestBuffers(); - assertEquals( - bufferPool.getNumTotalBuffers() - buffers.size(), bufferPool.getAvailableBuffers()); + assertThat(bufferPool.getAvailableBuffers()) + .isEqualTo(bufferPool.getNumTotalBuffers() - buffers.size()); bufferPool.destroy(); - assertTrue(bufferPool.isDestroyed()); - assertEquals(0, bufferPool.getAvailableBuffers()); + assertThat(bufferPool.isDestroyed()).isTrue(); + assertThat(bufferPool.getAvailableBuffers()).isZero(); } - @Test(expected = IllegalStateException.class) - public void testRequestBuffersAfterDestroyed() throws Exception { + @Test + void testRequestBuffersAfterDestroyed() throws Exception { BatchShuffleReadBufferPool bufferPool = createBufferPool(); bufferPool.requestBuffers(); bufferPool.destroy(); - bufferPool.requestBuffers(); + assertThatThrownBy(bufferPool::requestBuffers).isInstanceOf(IllegalStateException.class); } @Test - public void testRecycleAfterDestroyed() throws Exception { + void testRecycleAfterDestroyed() throws Exception { BatchShuffleReadBufferPool bufferPool = createBufferPool(); List buffers = bufferPool.requestBuffers(); bufferPool.destroy(); bufferPool.recycle(buffers); - assertEquals(0, bufferPool.getAvailableBuffers()); + assertThat(bufferPool.getAvailableBuffers()).isZero(); } @Test - public void testDestroyWhileBlockingRequest() throws Exception { - AtomicReference exception = new AtomicReference<>(); + void testDestroyWhileBlockingRequest() throws Exception { BatchShuffleReadBufferPool bufferPool = createBufferPool(); - Thread requestThread = - new Thread( - () -> { - try { - while (true) { - bufferPool.requestBuffers(); - } - } catch (Throwable throwable) { - exception.set(throwable); - } - }); + CheckedThread requestThread = + new CheckedThread() { + @Override + public void go() throws Exception { + while (true) { + bufferPool.requestBuffers(); + } + } + }; requestThread.start(); Thread.sleep(1000); bufferPool.destroy(); - requestThread.join(); - assertTrue(exception.get() instanceof IllegalStateException); + assertThatThrownBy(requestThread::sync).isInstanceOf(IllegalStateException.class); } private BatchShuffleReadBufferPool createBufferPool(long totalBytes, int bufferSize) { diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/ChannelViewsTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/ChannelViewsTest.java index 40257f22463b3..b76c5eb41a76b 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/ChannelViewsTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/ChannelViewsTest.java @@ -36,16 +36,18 @@ import org.apache.flink.runtime.operators.testutils.TestData.TupleGenerator.KeyMode; import org.apache.flink.runtime.operators.testutils.TestData.TupleGenerator.ValueMode; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.EOFException; import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + /** */ -public class ChannelViewsTest { +class ChannelViewsTest { private static final long SEED = 649180756312423613L; private static final int KEY_MAX = Integer.MAX_VALUE; @@ -72,8 +74,8 @@ public class ChannelViewsTest { // -------------------------------------------------------------------------------------------- - @Before - public void beforeTest() { + @BeforeEach + void beforeTest() { this.memoryManager = MemoryManagerBuilder.newBuilder() .setMemorySize(MEMORY_SIZE) @@ -82,14 +84,15 @@ public void beforeTest() { this.ioManager = new IOManagerAsync(); } - @After - public void afterTest() throws Exception { + @AfterEach + void afterTest() throws Exception { this.ioManager.close(); if (memoryManager != null) { - Assert.assertTrue( - "Memory leak: not all segments have been returned to the memory manager.", - this.memoryManager.verifyEmpty()); + assertThat(this.memoryManager.verifyEmpty()) + .withFailMessage( + "Memory leak: not all segments have been returned to the memory manager.") + .isTrue(); this.memoryManager.shutdown(); this.memoryManager = null; } @@ -98,7 +101,7 @@ public void afterTest() throws Exception { // -------------------------------------------------------------------------------------------- @Test - public void testWriteReadSmallRecords() throws Exception { + void testWriteReadSmallRecords() throws Exception { final TestData.TupleGenerator generator = new TestData.TupleGenerator( SEED, KEY_MAX, VALUE_SHORT_LENGTH, KeyMode.RANDOM, ValueMode.RANDOM_LENGTH); @@ -134,16 +137,7 @@ public void testWriteReadSmallRecords() throws Exception { for (int i = 0; i < NUM_PAIRS_SHORT; i++) { generator.next(rec); serializer.deserialize(readRec, inView); - - int k1 = rec.f0; - String v1 = rec.f1; - - int k2 = readRec.f0; - String v2 = readRec.f1; - - Assert.assertTrue( - "The re-generated and the read record do not match.", - k1 == k2 && v1.equals(v2)); + assertReadRecordMatchRegenerated(readRec, rec); } this.memoryManager.release(inView.close()); @@ -151,7 +145,7 @@ public void testWriteReadSmallRecords() throws Exception { } @Test - public void testWriteAndReadLongRecords() throws Exception { + void testWriteAndReadLongRecords() throws Exception { final TestData.TupleGenerator generator = new TestData.TupleGenerator( SEED, KEY_MAX, VALUE_LONG_LENGTH, KeyMode.RANDOM, ValueMode.RANDOM_LENGTH); @@ -188,13 +182,7 @@ public void testWriteAndReadLongRecords() throws Exception { for (int i = 0; i < NUM_PAIRS_LONG; i++) { generator.next(rec); serializer.deserialize(readRec, inView); - final int k1 = rec.f0; - final String v1 = rec.f1; - final int k2 = readRec.f0; - final String v2 = readRec.f1; - Assert.assertTrue( - "The re-generated and the read record do not match.", - k1 == k2 && v1.equals(v2)); + assertReadRecordMatchRegenerated(readRec, rec); } this.memoryManager.release(inView.close()); @@ -202,7 +190,7 @@ public void testWriteAndReadLongRecords() throws Exception { } @Test - public void testReadTooMany() throws Exception { + void testReadTooMany() throws Exception { final TestData.TupleGenerator generator = new TestData.TupleGenerator( SEED, KEY_MAX, VALUE_SHORT_LENGTH, KeyMode.RANDOM, ValueMode.RANDOM_LENGTH); @@ -235,33 +223,24 @@ public void testReadTooMany() throws Exception { generator.reset(); // read and re-generate all records and compare them - try { - final Tuple2 readRec = new Tuple2<>(); - for (int i = 0; i < NUM_PAIRS_SHORT + 1; i++) { - generator.next(rec); - serializer.deserialize(readRec, inView); - final int k1 = rec.f0; - final String v1 = rec.f1; - final int k2 = readRec.f0; - final String v2 = readRec.f1; - Assert.assertTrue( - "The re-generated and the read record do not match.", - k1 == k2 && v1.equals(v2)); - } - Assert.fail("Expected an EOFException which did not occur."); - } catch (EOFException eofex) { - // expected - } catch (Throwable t) { - // unexpected - Assert.fail("Unexpected Exception: " + t.getMessage()); + final Tuple2 readRec = new Tuple2<>(); + for (int i = 0; i < NUM_PAIRS_SHORT; i++) { + generator.next(rec); + serializer.deserialize(readRec, inView); + assertReadRecordMatchRegenerated(readRec, rec); } + generator.next(rec); + assertThatThrownBy(() -> serializer.deserialize(readRec, inView)) + .withFailMessage("Expected an EOFException which did not occur.") + .isInstanceOf(EOFException.class); + this.memoryManager.release(inView.close()); reader.deleteChannel(); } @Test - public void testReadWithoutKnownBlockCount() throws Exception { + void testReadWithoutKnownBlockCount() throws Exception { final TestData.TupleGenerator generator = new TestData.TupleGenerator( SEED, KEY_MAX, VALUE_SHORT_LENGTH, KeyMode.RANDOM, ValueMode.RANDOM_LENGTH); @@ -297,16 +276,7 @@ public void testReadWithoutKnownBlockCount() throws Exception { for (int i = 0; i < NUM_PAIRS_SHORT; i++) { generator.next(rec); serializer.deserialize(readRec, inView); - - int k1 = rec.f0; - String v1 = rec.f1; - - int k2 = readRec.f0; - String v2 = readRec.f1; - - Assert.assertTrue( - "The re-generated and the read record do not match.", - k1 == k2 && v1.equals(v2)); + assertReadRecordMatchRegenerated(readRec, rec); } this.memoryManager.release(inView.close()); @@ -314,7 +284,7 @@ public void testReadWithoutKnownBlockCount() throws Exception { } @Test - public void testWriteReadOneBufferOnly() throws Exception { + void testWriteReadOneBufferOnly() throws Exception { final TestData.TupleGenerator generator = new TestData.TupleGenerator( SEED, KEY_MAX, VALUE_SHORT_LENGTH, KeyMode.RANDOM, ValueMode.RANDOM_LENGTH); @@ -350,16 +320,7 @@ public void testWriteReadOneBufferOnly() throws Exception { for (int i = 0; i < NUM_PAIRS_SHORT; i++) { generator.next(rec); serializer.deserialize(readRec, inView); - - int k1 = rec.f0; - String v1 = rec.f1; - - int k2 = readRec.f0; - String v2 = readRec.f1; - - Assert.assertTrue( - "The re-generated and the read record do not match.", - k1 == k2 && v1.equals(v2)); + assertReadRecordMatchRegenerated(readRec, rec); } this.memoryManager.release(inView.close()); @@ -367,7 +328,7 @@ public void testWriteReadOneBufferOnly() throws Exception { } @Test - public void testWriteReadNotAll() throws Exception { + void testWriteReadNotAll() throws Exception { final TestData.TupleGenerator generator = new TestData.TupleGenerator( SEED, KEY_MAX, VALUE_SHORT_LENGTH, KeyMode.RANDOM, ValueMode.RANDOM_LENGTH); @@ -404,19 +365,26 @@ public void testWriteReadNotAll() throws Exception { for (int i = 0; i < NUM_PAIRS_SHORT / 2; i++) { generator.next(rec); serializer.deserialize(readRec, inView); - - int k1 = rec.f0; - String v1 = rec.f1; - - int k2 = readRec.f0; - String v2 = readRec.f1; - - Assert.assertTrue( - "The re-generated and the read record do not match.", - k1 == k2 && v1.equals(v2)); + assertReadRecordMatchRegenerated(readRec, rec); } this.memoryManager.release(inView.close()); reader.deleteChannel(); } + + private static void assertReadRecordMatchRegenerated( + Tuple2 readRec, Tuple2 rec) { + int k1 = rec.f0; + String v1 = rec.f1; + + int k2 = readRec.f0; + String v2 = readRec.f1; + + assertThat(k2) + .withFailMessage("The re-generated and the read record do not match.") + .isEqualTo(k1); + assertThat(v2) + .withFailMessage("The re-generated and the read record do not match.") + .isEqualTo(v1); + } } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/FileChannelManagerImplTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/FileChannelManagerImplTest.java index dc96ea654e2dc..17ab746d41a30 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/FileChannelManagerImplTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/FileChannelManagerImplTest.java @@ -21,26 +21,25 @@ import org.apache.flink.api.common.time.Deadline; import org.apache.flink.runtime.io.disk.iomanager.FileIOChannel; import org.apache.flink.runtime.testutils.TestJvmProcess; +import org.apache.flink.testutils.junit.utils.TempDirUtils; import org.apache.flink.util.OperatingSystem; import org.apache.flink.util.ShutdownHookUtil; -import org.apache.flink.util.TestLogger; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; +import java.nio.file.Path; import java.time.Duration; import java.util.concurrent.atomic.AtomicInteger; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assume.assumeTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; /** Tests the logic of {@link FileChannelManagerImpl}. */ -public class FileChannelManagerImplTest extends TestLogger { +class FileChannelManagerImplTest { private static final Logger LOG = LoggerFactory.getLogger(FileChannelManagerImplTest.class); private static final String DIR_NAME_PREFIX = "manager-test"; @@ -54,63 +53,68 @@ public class FileChannelManagerImplTest extends TestLogger { private static final Duration TEST_TIMEOUT = Duration.ofSeconds(10); - @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); + @TempDir private Path temporaryFolder; @Test - public void testFairness() throws Exception { - String directory1 = temporaryFolder.newFolder().getAbsoluteFile().getAbsolutePath(); - String directory2 = temporaryFolder.newFolder().getAbsoluteFile().getAbsolutePath(); - FileChannelManager fileChannelManager = - new FileChannelManagerImpl(new String[] {directory1, directory2}, "test"); - - int numChannelIDs = 100000; - AtomicInteger counter1 = new AtomicInteger(); - AtomicInteger counter2 = new AtomicInteger(); - - int numThreads = 10; - Thread[] threads = new Thread[numThreads]; - for (int i = 0; i < numThreads; ++i) { - threads[i] = - new Thread( - () -> { - for (int j = 0; j < numChannelIDs; ++j) { - FileIOChannel.ID channelID = fileChannelManager.createChannel(); - if (channelID.getPath().startsWith(directory1)) { - counter1.incrementAndGet(); - } else { - counter2.incrementAndGet(); + void testFairness() throws Exception { + String directory1 = + TempDirUtils.newFolder(temporaryFolder).getAbsoluteFile().getAbsolutePath(); + String directory2 = + TempDirUtils.newFolder(temporaryFolder).getAbsoluteFile().getAbsolutePath(); + + try (FileChannelManager fileChannelManager = + new FileChannelManagerImpl(new String[] {directory1, directory2}, "test")) { + int numChannelIDs = 100000; + AtomicInteger counter1 = new AtomicInteger(); + AtomicInteger counter2 = new AtomicInteger(); + + int numThreads = 10; + Thread[] threads = new Thread[numThreads]; + for (int i = 0; i < numThreads; ++i) { + threads[i] = + new Thread( + () -> { + for (int j = 0; j < numChannelIDs; ++j) { + FileIOChannel.ID channelID = + fileChannelManager.createChannel(); + if (channelID.getPath().startsWith(directory1)) { + counter1.incrementAndGet(); + } else { + counter2.incrementAndGet(); + } } - } - }); - threads[i].start(); - } + }); + threads[i].start(); + } - for (int i = 0; i < numThreads; ++i) { - threads[i].join(); - } + for (int i = 0; i < numThreads; ++i) { + threads[i].join(); + } - assertEquals(counter1.get(), counter2.get()); + assertThat(counter2).hasValue(counter1.get()); + } } @Test - public void testDirectoriesCleanupOnKillWithoutCallerHook() throws Exception { + void testDirectoriesCleanupOnKillWithoutCallerHook() throws Exception { testDirectoriesCleanupOnKill(false); } @Test - public void testDirectoriesCleanupOnKillWithCallerHook() throws Exception { + void testDirectoriesCleanupOnKillWithCallerHook() throws Exception { testDirectoriesCleanupOnKill(true); } private void testDirectoriesCleanupOnKill(boolean callerHasHook) throws Exception { - assumeTrue( - OperatingSystem.isLinux() - || OperatingSystem.isFreeBSD() - || OperatingSystem.isSolaris() - || OperatingSystem.isMac()); - - File fileChannelDir = temporaryFolder.newFolder(); - File signalDir = temporaryFolder.newFolder(); + assumeThat( + OperatingSystem.isLinux() + || OperatingSystem.isFreeBSD() + || OperatingSystem.isSolaris() + || OperatingSystem.isMac()) + .isTrue(); + + File fileChannelDir = TempDirUtils.newFolder(temporaryFolder); + File signalDir = TempDirUtils.newFolder(temporaryFolder); File signalFile = new File(signalDir.getAbsolutePath(), SIGNAL_FILE_FOR_KILLING); FileChannelManagerTestProcess fileChannelManagerTestProcess = @@ -130,24 +134,27 @@ private void testDirectoriesCleanupOnKill(boolean callerHasHook) throws Exceptio Runtime.getRuntime() .exec("kill " + fileChannelManagerTestProcess.getProcessId()); kill.waitFor(); - assertEquals("Failed to send SIG_TERM to process", 0, kill.exitValue()); + assertThat(kill.exitValue()) + .withFailMessage("Failed to send SIG_TERM to process") + .isZero(); Deadline deadline = Deadline.now().plus(TEST_TIMEOUT); while (fileChannelManagerTestProcess.isAlive() && deadline.hasTimeLeft()) { Thread.sleep(100); } - assertFalse( - "The file channel manager test process does not terminate in time, its output is: \n" - + fileChannelManagerTestProcess.getProcessOutput(), - fileChannelManagerTestProcess.isAlive()); + assertThat(fileChannelManagerTestProcess.isAlive()) + .withFailMessage( + "The file channel manager test process does not terminate in time, its output is: \n%s", + fileChannelManagerTestProcess.getProcessOutput()) + .isFalse(); // Checks if the directories are cleared. - assertFalse( - "The file channel manager test process does not remove the tmp shuffle directories after termination, " - + "its output is \n" - + fileChannelManagerTestProcess.getProcessOutput(), - fileOrDirExists(fileChannelDir, DIR_NAME_PREFIX)); + assertThat(fileOrDirExists(fileChannelDir, DIR_NAME_PREFIX)) + .withFailMessage( + "The file channel manager test process does not remove the tmp shuffle directories after termination, its output is \n%s", + fileChannelManagerTestProcess.getProcessOutput()) + .isFalse(); } finally { fileChannelManagerTestProcess.destroy(); } @@ -189,7 +196,7 @@ public String getEntryPointClassName() { } /** The entry point class to test the file channel manager cleanup with shutdown hook. */ - public static class FileChannelManagerCleanupRunner { + private static class FileChannelManagerCleanupRunner { public static void main(String[] args) throws Exception { boolean callerHasHook = Boolean.parseBoolean(args[0]); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/FileChannelStreamsITCase.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/FileChannelStreamsITCase.java index d6c24918f6bea..1290220ed3e32 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/FileChannelStreamsITCase.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/FileChannelStreamsITCase.java @@ -31,18 +31,18 @@ import org.apache.flink.runtime.operators.testutils.PairGenerator.KeyMode; import org.apache.flink.runtime.operators.testutils.PairGenerator.Pair; import org.apache.flink.runtime.operators.testutils.PairGenerator.ValueMode; -import org.apache.flink.util.TestLogger; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.EOFException; import java.util.List; -import static org.junit.Assert.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -public class FileChannelStreamsITCase extends TestLogger { +class FileChannelStreamsITCase { private static final long SEED = 649180756312423613L; @@ -66,8 +66,8 @@ public class FileChannelStreamsITCase extends TestLogger { // -------------------------------------------------------------------------------------------- - @Before - public void beforeTest() { + @BeforeEach + void beforeTest() { memManager = MemoryManagerBuilder.newBuilder() .setMemorySize(NUM_MEMORY_SEGMENTS * MEMORY_PAGE_SIZE) @@ -76,295 +76,261 @@ public void beforeTest() { ioManager = new IOManagerAsync(); } - @After - public void afterTest() throws Exception { + @AfterEach + void afterTest() throws Exception { ioManager.close(); - assertTrue("The memory has not been properly released", memManager.verifyEmpty()); + assertThat(memManager.verifyEmpty()) + .withFailMessage("The memory has not been properly released") + .isTrue(); } // -------------------------------------------------------------------------------------------- @Test - public void testWriteReadSmallRecords() { - try { - List memory = - memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); - - final PairGenerator generator = - new PairGenerator( - SEED, - KEY_MAX, - VALUE_SHORT_LENGTH, - KeyMode.RANDOM, - ValueMode.RANDOM_LENGTH); - final FileIOChannel.ID channel = ioManager.createChannel(); - - // create the writer output view - final BlockChannelWriter writer = - ioManager.createBlockChannelWriter(channel); - final FileChannelOutputView outView = - new FileChannelOutputView(writer, memManager, memory, MEMORY_PAGE_SIZE); - - // write a number of pairs - Pair pair = new Pair(); - for (int i = 0; i < NUM_PAIRS_SHORT; i++) { - generator.next(pair); - pair.write(outView); - } - outView.close(); - - // create the reader input view - List readMemory = - memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); - - final BlockChannelReader reader = - ioManager.createBlockChannelReader(channel); - final FileChannelInputView inView = - new FileChannelInputView( - reader, memManager, readMemory, outView.getBytesInLatestSegment()); - generator.reset(); - - // read and re-generate all records and compare them - Pair readPair = new Pair(); - for (int i = 0; i < NUM_PAIRS_SHORT; i++) { - generator.next(pair); - readPair.read(inView); - assertEquals("The re-generated and the read record do not match.", pair, readPair); - } - - inView.close(); - reader.deleteChannel(); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); + void testWriteReadSmallRecords() throws Exception { + List memory = + memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); + + final PairGenerator generator = + new PairGenerator( + SEED, KEY_MAX, VALUE_SHORT_LENGTH, KeyMode.RANDOM, ValueMode.RANDOM_LENGTH); + final FileIOChannel.ID channel = ioManager.createChannel(); + + // create the writer output view + final BlockChannelWriter writer = + ioManager.createBlockChannelWriter(channel); + final FileChannelOutputView outView = + new FileChannelOutputView(writer, memManager, memory, MEMORY_PAGE_SIZE); + + // write a number of pairs + Pair pair = new Pair(); + for (int i = 0; i < NUM_PAIRS_SHORT; i++) { + generator.next(pair); + pair.write(outView); } + outView.close(); + + // create the reader input view + List readMemory = + memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); + + final BlockChannelReader reader = + ioManager.createBlockChannelReader(channel); + final FileChannelInputView inView = + new FileChannelInputView( + reader, memManager, readMemory, outView.getBytesInLatestSegment()); + generator.reset(); + + // read and re-generate all records and compare them + Pair readPair = new Pair(); + for (int i = 0; i < NUM_PAIRS_SHORT; i++) { + generator.next(pair); + readPair.read(inView); + assertThat(readPair) + .withFailMessage("The re-generated and the read record do not match.") + .isEqualTo(pair); + } + + inView.close(); + reader.deleteChannel(); } @Test - public void testWriteAndReadLongRecords() { - try { - final List memory = - memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); - - final PairGenerator generator = - new PairGenerator( - SEED, - KEY_MAX, - VALUE_LONG_LENGTH, - KeyMode.RANDOM, - ValueMode.RANDOM_LENGTH); - final FileIOChannel.ID channel = this.ioManager.createChannel(); - - // create the writer output view - final BlockChannelWriter writer = - this.ioManager.createBlockChannelWriter(channel); - final FileChannelOutputView outView = - new FileChannelOutputView(writer, memManager, memory, MEMORY_PAGE_SIZE); - - // write a number of pairs - Pair pair = new Pair(); - for (int i = 0; i < NUM_PAIRS_LONG; i++) { - generator.next(pair); - pair.write(outView); - } - outView.close(); - - // create the reader input view - List readMemory = - memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); - - final BlockChannelReader reader = - ioManager.createBlockChannelReader(channel); - final FileChannelInputView inView = - new FileChannelInputView( - reader, memManager, readMemory, outView.getBytesInLatestSegment()); - generator.reset(); - - // read and re-generate all records and compare them - Pair readPair = new Pair(); - for (int i = 0; i < NUM_PAIRS_LONG; i++) { - generator.next(pair); - readPair.read(inView); - assertEquals("The re-generated and the read record do not match.", pair, readPair); - } - - inView.close(); - reader.deleteChannel(); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); + void testWriteAndReadLongRecords() throws Exception { + final List memory = + memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); + + final PairGenerator generator = + new PairGenerator( + SEED, KEY_MAX, VALUE_LONG_LENGTH, KeyMode.RANDOM, ValueMode.RANDOM_LENGTH); + final FileIOChannel.ID channel = this.ioManager.createChannel(); + + // create the writer output view + final BlockChannelWriter writer = + this.ioManager.createBlockChannelWriter(channel); + final FileChannelOutputView outView = + new FileChannelOutputView(writer, memManager, memory, MEMORY_PAGE_SIZE); + + // write a number of pairs + Pair pair = new Pair(); + for (int i = 0; i < NUM_PAIRS_LONG; i++) { + generator.next(pair); + pair.write(outView); + } + outView.close(); + + // create the reader input view + List readMemory = + memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); + + final BlockChannelReader reader = + ioManager.createBlockChannelReader(channel); + final FileChannelInputView inView = + new FileChannelInputView( + reader, memManager, readMemory, outView.getBytesInLatestSegment()); + generator.reset(); + + // read and re-generate all records and compare them + Pair readPair = new Pair(); + for (int i = 0; i < NUM_PAIRS_LONG; i++) { + generator.next(pair); + readPair.read(inView); + assertThat(readPair) + .withFailMessage("The re-generated and the read record do not match.") + .isEqualTo(pair); } + + inView.close(); + reader.deleteChannel(); } @Test - public void testReadTooMany() { - try { - final List memory = - memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); - - final PairGenerator generator = - new PairGenerator( - SEED, - KEY_MAX, - VALUE_SHORT_LENGTH, - KeyMode.RANDOM, - ValueMode.RANDOM_LENGTH); - final FileIOChannel.ID channel = this.ioManager.createChannel(); - - // create the writer output view - final BlockChannelWriter writer = - this.ioManager.createBlockChannelWriter(channel); - final FileChannelOutputView outView = - new FileChannelOutputView(writer, memManager, memory, MEMORY_PAGE_SIZE); - - // write a number of pairs - Pair pair = new Pair(); - for (int i = 0; i < NUM_PAIRS_SHORT; i++) { - generator.next(pair); - pair.write(outView); - } - outView.close(); - - // create the reader input view - List readMemory = - memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); - - final BlockChannelReader reader = - ioManager.createBlockChannelReader(channel); - final FileChannelInputView inView = - new FileChannelInputView( - reader, memManager, readMemory, outView.getBytesInLatestSegment()); - generator.reset(); - - // read and re-generate all records and compare them - try { - Pair readPair = new Pair(); - for (int i = 0; i < NUM_PAIRS_SHORT + 1; i++) { - generator.next(pair); - readPair.read(inView); - assertEquals( - "The re-generated and the read record do not match.", pair, readPair); - } - fail("Expected an EOFException which did not occur."); - } catch (EOFException eofex) { - // expected - } - - inView.close(); - reader.deleteChannel(); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); + void testReadTooMany() throws Exception { + final List memory = + memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); + + final PairGenerator generator = + new PairGenerator( + SEED, KEY_MAX, VALUE_SHORT_LENGTH, KeyMode.RANDOM, ValueMode.RANDOM_LENGTH); + final FileIOChannel.ID channel = this.ioManager.createChannel(); + + // create the writer output view + final BlockChannelWriter writer = + this.ioManager.createBlockChannelWriter(channel); + final FileChannelOutputView outView = + new FileChannelOutputView(writer, memManager, memory, MEMORY_PAGE_SIZE); + + // write a number of pairs + Pair pair = new Pair(); + for (int i = 0; i < NUM_PAIRS_SHORT; i++) { + generator.next(pair); + pair.write(outView); + } + outView.close(); + + // create the reader input view + List readMemory = + memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); + + final BlockChannelReader reader = + ioManager.createBlockChannelReader(channel); + final FileChannelInputView inView = + new FileChannelInputView( + reader, memManager, readMemory, outView.getBytesInLatestSegment()); + generator.reset(); + + // read and re-generate all records and compare them + Pair readPair = new Pair(); + for (int i = 0; i < NUM_PAIRS_SHORT; i++) { + generator.next(pair); + readPair.read(inView); + assertThat(readPair) + .withFailMessage("The re-generated and the read record do not match.") + .isEqualTo(pair); } + + generator.next(pair); + assertThatThrownBy(() -> readPair.read(inView)) + .withFailMessage("Read too much, expected EOFException.") + .isInstanceOf(EOFException.class); + + inView.close(); + reader.deleteChannel(); } @Test - public void testWriteReadOneBufferOnly() { - try { - final List memory = memManager.allocatePages(new DummyInvokable(), 1); - - final PairGenerator generator = - new PairGenerator( - SEED, - KEY_MAX, - VALUE_SHORT_LENGTH, - KeyMode.RANDOM, - ValueMode.RANDOM_LENGTH); - final FileIOChannel.ID channel = this.ioManager.createChannel(); - - // create the writer output view - final BlockChannelWriter writer = - this.ioManager.createBlockChannelWriter(channel); - final FileChannelOutputView outView = - new FileChannelOutputView(writer, memManager, memory, MEMORY_PAGE_SIZE); - - // write a number of pairs - Pair pair = new Pair(); - for (int i = 0; i < NUM_PAIRS_SHORT; i++) { - generator.next(pair); - pair.write(outView); - } - outView.close(); - - // create the reader input view - List readMemory = memManager.allocatePages(new DummyInvokable(), 1); - - final BlockChannelReader reader = - ioManager.createBlockChannelReader(channel); - final FileChannelInputView inView = - new FileChannelInputView( - reader, memManager, readMemory, outView.getBytesInLatestSegment()); - generator.reset(); - - // read and re-generate all records and compare them - Pair readPair = new Pair(); - for (int i = 0; i < NUM_PAIRS_SHORT; i++) { - generator.next(pair); - readPair.read(inView); - assertEquals("The re-generated and the read record do not match.", pair, readPair); - } - - inView.close(); - reader.deleteChannel(); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); + void testWriteReadOneBufferOnly() throws Exception { + final List memory = memManager.allocatePages(new DummyInvokable(), 1); + + final PairGenerator generator = + new PairGenerator( + SEED, KEY_MAX, VALUE_SHORT_LENGTH, KeyMode.RANDOM, ValueMode.RANDOM_LENGTH); + final FileIOChannel.ID channel = this.ioManager.createChannel(); + + // create the writer output view + final BlockChannelWriter writer = + this.ioManager.createBlockChannelWriter(channel); + final FileChannelOutputView outView = + new FileChannelOutputView(writer, memManager, memory, MEMORY_PAGE_SIZE); + + // write a number of pairs + Pair pair = new Pair(); + for (int i = 0; i < NUM_PAIRS_SHORT; i++) { + generator.next(pair); + pair.write(outView); + } + outView.close(); + + // create the reader input view + List readMemory = memManager.allocatePages(new DummyInvokable(), 1); + + final BlockChannelReader reader = + ioManager.createBlockChannelReader(channel); + final FileChannelInputView inView = + new FileChannelInputView( + reader, memManager, readMemory, outView.getBytesInLatestSegment()); + generator.reset(); + + // read and re-generate all records and compare them + Pair readPair = new Pair(); + for (int i = 0; i < NUM_PAIRS_SHORT; i++) { + generator.next(pair); + readPair.read(inView); + assertThat(readPair) + .withFailMessage("The re-generated and the read record do not match.") + .isEqualTo(pair); } + + inView.close(); + reader.deleteChannel(); } @Test - public void testWriteReadNotAll() { - try { - final List memory = - memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); - - final PairGenerator generator = - new PairGenerator( - SEED, - KEY_MAX, - VALUE_SHORT_LENGTH, - KeyMode.RANDOM, - ValueMode.RANDOM_LENGTH); - final FileIOChannel.ID channel = this.ioManager.createChannel(); - - // create the writer output view - final BlockChannelWriter writer = - this.ioManager.createBlockChannelWriter(channel); - final FileChannelOutputView outView = - new FileChannelOutputView(writer, memManager, memory, MEMORY_PAGE_SIZE); - - // write a number of pairs - Pair pair = new Pair(); - for (int i = 0; i < NUM_PAIRS_SHORT; i++) { - generator.next(pair); - pair.write(outView); - } - outView.close(); - - // create the reader input view - List readMemory = - memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); - - final BlockChannelReader reader = - ioManager.createBlockChannelReader(channel); - final FileChannelInputView inView = - new FileChannelInputView( - reader, memManager, readMemory, outView.getBytesInLatestSegment()); - generator.reset(); - - // read and re-generate all records and compare them - Pair readPair = new Pair(); - for (int i = 0; i < NUM_PAIRS_SHORT / 2; i++) { - generator.next(pair); - readPair.read(inView); - assertEquals("The re-generated and the read record do not match.", pair, readPair); - } - - inView.close(); - reader.deleteChannel(); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); + void testWriteReadNotAll() throws Exception { + final List memory = + memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); + + final PairGenerator generator = + new PairGenerator( + SEED, KEY_MAX, VALUE_SHORT_LENGTH, KeyMode.RANDOM, ValueMode.RANDOM_LENGTH); + final FileIOChannel.ID channel = this.ioManager.createChannel(); + + // create the writer output view + final BlockChannelWriter writer = + this.ioManager.createBlockChannelWriter(channel); + final FileChannelOutputView outView = + new FileChannelOutputView(writer, memManager, memory, MEMORY_PAGE_SIZE); + + // write a number of pairs + Pair pair = new Pair(); + for (int i = 0; i < NUM_PAIRS_SHORT; i++) { + generator.next(pair); + pair.write(outView); } + outView.close(); + + // create the reader input view + List readMemory = + memManager.allocatePages(new DummyInvokable(), NUM_MEMORY_SEGMENTS); + + final BlockChannelReader reader = + ioManager.createBlockChannelReader(channel); + final FileChannelInputView inView = + new FileChannelInputView( + reader, memManager, readMemory, outView.getBytesInLatestSegment()); + generator.reset(); + + // read and re-generate all records and compare them + Pair readPair = new Pair(); + for (int i = 0; i < NUM_PAIRS_SHORT / 2; i++) { + generator.next(pair); + readPair.read(inView); + assertThat(readPair) + .withFailMessage("The re-generated and the read record do not match.") + .isEqualTo(pair); + } + + inView.close(); + reader.deleteChannel(); } } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/FileChannelStreamsTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/FileChannelStreamsTest.java index 1a0838345e51d..79621341e90e1 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/FileChannelStreamsTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/FileChannelStreamsTest.java @@ -29,19 +29,19 @@ import org.apache.flink.runtime.operators.testutils.DummyInvokable; import org.apache.flink.types.StringValue; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; import java.io.FileWriter; import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.*; +import static org.assertj.core.api.Assertions.assertThat; -public class FileChannelStreamsTest { +class FileChannelStreamsTest { @Test - public void testCloseAndDeleteOutputView() { + void testCloseAndDeleteOutputView() throws Exception { try (IOManager ioManager = new IOManagerAsync()) { MemoryManager memMan = MemoryManagerBuilder.newBuilder().build(); List memory = new ArrayList(); @@ -56,22 +56,19 @@ public void testCloseAndDeleteOutputView() { // close for the first time, make sure all memory returns out.close(); - assertTrue(memMan.verifyEmpty()); + assertThat(memMan.verifyEmpty()).isTrue(); // close again, should not cause an exception out.close(); // delete, make sure file is removed out.closeAndDelete(); - assertFalse(new File(channel.getPath()).exists()); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); + assertThat(new File(channel.getPath())).doesNotExist(); } } @Test - public void testCloseAndDeleteInputView() { + void testCloseAndDeleteInputView() throws Exception { try (IOManager ioManager = new IOManagerAsync()) { MemoryManager memMan = MemoryManagerBuilder.newBuilder().build(); List memory = new ArrayList(); @@ -92,17 +89,14 @@ public void testCloseAndDeleteInputView() { // close for the first time, make sure all memory returns in.close(); - assertTrue(memMan.verifyEmpty()); + assertThat(memMan.verifyEmpty()).isTrue(); // close again, should not cause an exception in.close(); // delete, make sure file is removed in.closeAndDelete(); - assertFalse(new File(channel.getPath()).exists()); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); + assertThat(new File(channel.getPath())).doesNotExist(); } } } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/SeekableFileChannelInputViewTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/SeekableFileChannelInputViewTest.java index 36b5912c5e46a..ebbf2850329c5 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/SeekableFileChannelInputViewTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/SeekableFileChannelInputViewTest.java @@ -27,18 +27,19 @@ import org.apache.flink.runtime.memory.MemoryManagerBuilder; import org.apache.flink.runtime.operators.testutils.DummyInvokable; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.EOFException; import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -public class SeekableFileChannelInputViewTest { +class SeekableFileChannelInputViewTest { @Test - public void testSeek() { + void testSeek() throws Exception { final int PAGE_SIZE = 16 * 1024; final int NUM_RECORDS = 120000; // integers across 7.x pages (7 pages = 114.688 bytes, 8 pages = 131.072 bytes) @@ -64,7 +65,7 @@ public void testSeek() { } // close for the first time, make sure all memory returns out.close(); - assertTrue(memMan.verifyEmpty()); + assertThat(memMan.verifyEmpty()).isTrue(); memMan.allocatePages(new DummyInvokable(), memory, 4); SeekableFileChannelInputView in = @@ -73,97 +74,76 @@ public void testSeek() { // read first, complete for (int i = 0; i < NUM_RECORDS; i += 4) { - assertEquals(i, in.readInt()); - } - try { - in.readInt(); - fail("should throw EOF exception"); - } catch (EOFException ignored) { + assertThat(in.readInt()).isEqualTo(i); } + assertThatThrownBy(in::readInt) + .withFailMessage("should throw EOF exception") + .isInstanceOf(EOFException.class); // seek to the middle of the 3rd page int i = 2 * PAGE_SIZE + PAGE_SIZE / 4; in.seek(i); for (; i < NUM_RECORDS; i += 4) { - assertEquals(i, in.readInt()); - } - try { - in.readInt(); - fail("should throw EOF exception"); - } catch (EOFException ignored) { + assertThat(in.readInt()).isEqualTo(i); } + assertThatThrownBy(in::readInt) + .withFailMessage("should throw EOF exception") + .isInstanceOf(EOFException.class); // seek to the end i = 120000 - 4; in.seek(i); for (; i < NUM_RECORDS; i += 4) { - assertEquals(i, in.readInt()); - } - try { - in.readInt(); - fail("should throw EOF exception"); - } catch (EOFException ignored) { + assertThat(in.readInt()).isEqualTo(i); } + assertThatThrownBy(in::readInt) + .withFailMessage("should throw EOF exception") + .isInstanceOf(EOFException.class); // seek to the beginning i = 0; in.seek(i); for (; i < NUM_RECORDS; i += 4) { - assertEquals(i, in.readInt()); - } - try { - in.readInt(); - fail("should throw EOF exception"); - } catch (EOFException ignored) { + assertThat(in.readInt()).isEqualTo(i); } + assertThatThrownBy(in::readInt) + .withFailMessage("should throw EOF exception") + .isInstanceOf(EOFException.class); // seek to after a page i = PAGE_SIZE; in.seek(i); for (; i < NUM_RECORDS; i += 4) { - assertEquals(i, in.readInt()); - } - try { - in.readInt(); - fail("should throw EOF exception"); - } catch (EOFException ignored) { + assertThat(in.readInt()).isEqualTo(i); } + assertThatThrownBy(in::readInt) + .withFailMessage("should throw EOF exception") + .isInstanceOf(EOFException.class); // seek to after a page i = 3 * PAGE_SIZE; in.seek(i); for (; i < NUM_RECORDS; i += 4) { - assertEquals(i, in.readInt()); - } - try { - in.readInt(); - fail("should throw EOF exception"); - } catch (EOFException ignored) { + assertThat(in.readInt()).isEqualTo(i); } + assertThatThrownBy(in::readInt) + .withFailMessage("should throw EOF exception") + .isInstanceOf(EOFException.class); // seek to the end i = NUM_RECORDS; in.seek(i); - try { - in.readInt(); - fail("should throw EOF exception"); - } catch (EOFException ignored) { - } + assertThatThrownBy(in::readInt) + .withFailMessage("should throw EOF exception") + .isInstanceOf(EOFException.class); // seek out of bounds - try { - in.seek(-10); - fail("should throw an exception"); - } catch (IllegalArgumentException ignored) { - } - try { - in.seek(NUM_RECORDS + 1); - fail("should throw an exception"); - } catch (IllegalArgumentException ignored) { - } - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); + assertThatThrownBy(() -> in.seek(-10)) + .withFailMessage("should throw an exception") + .isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> in.seek(NUM_RECORDS + 1)) + .withFailMessage("should throw an exception") + .isInstanceOf(IllegalArgumentException.class); } } } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/SpillingBufferTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/SpillingBufferTest.java index 9db101888982e..e600da1c864ea 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/SpillingBufferTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/SpillingBufferTest.java @@ -33,15 +33,17 @@ import org.apache.flink.runtime.operators.testutils.TestData.TupleGenerator.KeyMode; import org.apache.flink.runtime.operators.testutils.TestData.TupleGenerator.ValueMode; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.EOFException; import java.util.ArrayList; -public class SpillingBufferTest { +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class SpillingBufferTest { private static final long SEED = 649180756312423613L; @@ -65,20 +67,21 @@ public class SpillingBufferTest { // -------------------------------------------------------------------------------------------- - @Before - public void beforeTest() { + @BeforeEach + void beforeTest() { memoryManager = MemoryManagerBuilder.newBuilder().setMemorySize(MEMORY_SIZE).build(); ioManager = new IOManagerAsync(); } - @After - public void afterTest() throws Exception { + @AfterEach + void afterTest() throws Exception { ioManager.close(); if (memoryManager != null) { - Assert.assertTrue( - "Memory leak: not all segments have been returned to the memory manager.", - memoryManager.verifyEmpty()); + assertThat(memoryManager.verifyEmpty()) + .withFailMessage( + "Memory leak: not all segments have been returned to the memory manager.") + .isTrue(); memoryManager.shutdown(); memoryManager = null; } @@ -87,7 +90,7 @@ public void afterTest() throws Exception { // -------------------------------------------------------------------------------------------- @Test - public void testWriteReadInMemory() throws Exception { + void testWriteReadInMemory() throws Exception { final TestData.TupleGenerator generator = new TestData.TupleGenerator( SEED, KEY_MAX, VALUE_LENGTH, KeyMode.RANDOM, ValueMode.RANDOM_LENGTH); @@ -126,9 +129,9 @@ public void testWriteReadInMemory() throws Exception { int k2 = readRec.f0; String v2 = readRec.f1; - Assert.assertTrue( - "The re-generated and the notifyNonEmpty record do not match.", - k1 == k2 && v1.equals(v2)); + assertThat(k1 == k2 && v1.equals(v2)) + .withFailMessage("The re-generated and the notifyNonEmpty record do not match.") + .isTrue(); } // re-notifyNonEmpty the data @@ -146,9 +149,9 @@ public void testWriteReadInMemory() throws Exception { int k2 = readRec.f0; String v2 = readRec.f1; - Assert.assertTrue( - "The re-generated and the notifyNonEmpty record do not match.", - k1 == k2 && v1.equals(v2)); + assertThat(k1 == k2 && v1.equals(v2)) + .withFailMessage("The re-generated and the notifyNonEmpty record do not match.") + .isTrue(); } this.memoryManager.release(outView.close()); @@ -156,7 +159,7 @@ public void testWriteReadInMemory() throws Exception { } @Test - public void testWriteReadTooMuchInMemory() throws Exception { + void testWriteReadTooMuchInMemory() throws Exception { final TestData.TupleGenerator generator = new TestData.TupleGenerator( SEED, KEY_MAX, VALUE_LENGTH, KeyMode.RANDOM, ValueMode.RANDOM_LENGTH); @@ -185,34 +188,34 @@ public void testWriteReadTooMuchInMemory() throws Exception { // notifyNonEmpty and re-generate all records and compare them final Tuple2 readRec = new Tuple2<>(); - try { - for (int i = 0; i < NUM_PAIRS_INMEM + 1; i++) { - generator.next(rec); - serializer.deserialize(readRec, inView); - - int k1 = rec.f0; - String v1 = rec.f1; - - int k2 = readRec.f0; - String v2 = readRec.f1; - - Assert.assertTrue( - "The re-generated and the notifyNonEmpty record do not match.", - k1 == k2 && v1.equals(v2)); - } - Assert.fail("Read too much, expected EOFException."); - } catch (EOFException eofex) { - // expected + for (int i = 0; i < NUM_PAIRS_INMEM; i++) { + generator.next(rec); + serializer.deserialize(readRec, inView); + + int k1 = rec.f0; + String v1 = rec.f1; + + int k2 = readRec.f0; + String v2 = readRec.f1; + + assertThat(k1 == k2 && v1.equals(v2)) + .withFailMessage("The re-generated and the notifyNonEmpty record do not match.") + .isTrue(); } + generator.next(rec); + assertThatThrownBy(() -> serializer.deserialize(readRec, inView)) + .withFailMessage("Read too much, expected EOFException.") + .isInstanceOf(EOFException.class); + // re-notifyNonEmpty the data - inView = outView.flip(); + DataInputView nextInView = outView.flip(); generator.reset(); // notifyNonEmpty and re-generate all records and compare them for (int i = 0; i < NUM_PAIRS_INMEM; i++) { generator.next(rec); - serializer.deserialize(readRec, inView); + serializer.deserialize(readRec, nextInView); int k1 = rec.f0; String v1 = rec.f1; @@ -220,9 +223,9 @@ public void testWriteReadTooMuchInMemory() throws Exception { int k2 = readRec.f0; String v2 = readRec.f1; - Assert.assertTrue( - "The re-generated and the notifyNonEmpty record do not match.", - k1 == k2 && v1.equals(v2)); + assertThat(k1 == k2 && v1.equals(v2)) + .withFailMessage("The re-generated and the notifyNonEmpty record do not match.") + .isTrue(); } this.memoryManager.release(outView.close()); @@ -232,7 +235,7 @@ public void testWriteReadTooMuchInMemory() throws Exception { // -------------------------------------------------------------------------------------------- @Test - public void testWriteReadExternal() throws Exception { + void testWriteReadExternal() throws Exception { final TestData.TupleGenerator generator = new TestData.TupleGenerator( SEED, KEY_MAX, VALUE_LENGTH, KeyMode.RANDOM, ValueMode.RANDOM_LENGTH); @@ -271,9 +274,9 @@ public void testWriteReadExternal() throws Exception { int k2 = readRec.f0; String v2 = readRec.f1; - Assert.assertTrue( - "The re-generated and the notifyNonEmpty record do not match.", - k1 == k2 && v1.equals(v2)); + assertThat(k1 == k2 && v1.equals(v2)) + .withFailMessage("The re-generated and the notifyNonEmpty record do not match.") + .isTrue(); } // re-notifyNonEmpty the data @@ -291,9 +294,9 @@ public void testWriteReadExternal() throws Exception { int k2 = readRec.f0; String v2 = readRec.f1; - Assert.assertTrue( - "The re-generated and the notifyNonEmpty record do not match.", - k1 == k2 && v1.equals(v2)); + assertThat(k1 == k2 && v1.equals(v2)) + .withFailMessage("The re-generated and the notifyNonEmpty record do not match.") + .isTrue(); } this.memoryManager.release(outView.close()); @@ -301,7 +304,7 @@ public void testWriteReadExternal() throws Exception { } @Test - public void testWriteReadTooMuchExternal() throws Exception { + void testWriteReadTooMuchExternal() throws Exception { final TestData.TupleGenerator generator = new TestData.TupleGenerator( SEED, KEY_MAX, VALUE_LENGTH, KeyMode.RANDOM, ValueMode.RANDOM_LENGTH); @@ -330,34 +333,34 @@ public void testWriteReadTooMuchExternal() throws Exception { // notifyNonEmpty and re-generate all records and compare them final Tuple2 readRec = new Tuple2<>(); - try { - for (int i = 0; i < NUM_PAIRS_EXTERNAL + 1; i++) { - generator.next(rec); - serializer.deserialize(readRec, inView); - - int k1 = rec.f0; - String v1 = rec.f1; - - int k2 = readRec.f0; - String v2 = readRec.f1; - - Assert.assertTrue( - "The re-generated and the notifyNonEmpty record do not match.", - k1 == k2 && v1.equals(v2)); - } - Assert.fail("Read too much, expected EOFException."); - } catch (EOFException eofex) { - // expected + for (int i = 0; i < NUM_PAIRS_EXTERNAL; i++) { + generator.next(rec); + serializer.deserialize(readRec, inView); + + int k1 = rec.f0; + String v1 = rec.f1; + + int k2 = readRec.f0; + String v2 = readRec.f1; + + assertThat(k1 == k2 && v1.equals(v2)) + .withFailMessage("The re-generated and the notifyNonEmpty record do not match.") + .isTrue(); } + generator.next(rec); + assertThatThrownBy(() -> serializer.deserialize(readRec, inView)) + .withFailMessage("Read too much, expected EOFException.") + .isInstanceOf(EOFException.class); + // re-notifyNonEmpty the data - inView = outView.flip(); + DataInputView nextInView = outView.flip(); generator.reset(); // notifyNonEmpty and re-generate all records and compare them for (int i = 0; i < NUM_PAIRS_EXTERNAL; i++) { generator.next(rec); - serializer.deserialize(readRec, inView); + serializer.deserialize(readRec, nextInView); int k1 = rec.f0; String v1 = rec.f1; @@ -365,9 +368,9 @@ public void testWriteReadTooMuchExternal() throws Exception { int k2 = readRec.f0; String v2 = readRec.f1; - Assert.assertTrue( - "The re-generated and the notifyNonEmpty record do not match.", - k1 == k2 && v1.equals(v2)); + assertThat(k1 == k2 && v1.equals(v2)) + .withFailMessage("The re-generated and the notifyNonEmpty record do not match.") + .isTrue(); } this.memoryManager.release(outView.close()); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/AsynchronousBufferFileWriterTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/AsynchronousBufferFileWriterTest.java index 17d191f406801..1bb9e4336a73d 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/AsynchronousBufferFileWriterTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/AsynchronousBufferFileWriterTest.java @@ -19,34 +19,29 @@ package org.apache.flink.runtime.io.disk.iomanager; import org.apache.flink.core.memory.MemorySegmentFactory; +import org.apache.flink.core.testutils.CheckedThread; import org.apache.flink.runtime.io.network.buffer.Buffer; import org.apache.flink.runtime.io.network.buffer.FreeingBufferRecycler; import org.apache.flink.runtime.io.network.buffer.NetworkBuffer; import org.apache.flink.runtime.io.network.util.TestNotificationListener; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicReference; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.fail; import static org.mockito.Mockito.mock; /** Tests for {@link AsynchronousBufferFileWriter}. */ -public class AsynchronousBufferFileWriterTest { - @Rule public ExpectedException exception = ExpectedException.none(); +class AsynchronousBufferFileWriterTest { private static final IOManager ioManager = new IOManagerAsync(); @@ -54,139 +49,118 @@ public class AsynchronousBufferFileWriterTest { private AsynchronousBufferFileWriter writer; - @AfterClass - public static void shutdown() throws Exception { + @AfterAll + static void shutdown() throws Exception { ioManager.close(); } - @Before - public void setUp() throws IOException { + @BeforeEach + void setUp() throws IOException { writer = new AsynchronousBufferFileWriter( ioManager.createChannel(), new RequestQueue()); } @Test - public void testAddAndHandleRequest() throws Exception { + void testAddAndHandleRequest() throws Exception { addRequest(); - assertEquals( - "Didn't increment number of outstanding requests.", - 1, - writer.getNumberOfOutstandingRequests()); + assertThat(writer.getNumberOfOutstandingRequests()) + .withFailMessage("Didn't increment number of outstanding requests.") + .isOne(); handleRequest(); - assertEquals( - "Didn't decrement number of outstanding requests.", - 0, - writer.getNumberOfOutstandingRequests()); + assertThat(writer.getNumberOfOutstandingRequests()) + .withFailMessage("Didn't decrement number of outstanding requests.") + .isZero(); } @Test - public void testAddWithFailingWriter() throws Exception { + void testAddWithFailingWriter() throws Exception { AsynchronousBufferFileWriter writer = new AsynchronousBufferFileWriter(ioManager.createChannel(), new RequestQueue<>()); writer.close(); - exception.expect(IOException.class); - Buffer buffer = new NetworkBuffer( MemorySegmentFactory.allocateUnpooledSegment(4096), FreeingBufferRecycler.INSTANCE); - try { - writer.writeBlock(buffer); - } finally { - if (!buffer.isRecycled()) { - buffer.recycleBuffer(); - Assert.fail("buffer not recycled"); - } - assertEquals( - "Shouln't increment number of outstanding requests.", - 0, - writer.getNumberOfOutstandingRequests()); + + assertThatThrownBy(() -> writer.writeBlock(buffer)).isInstanceOf(IOException.class); + + if (!buffer.isRecycled()) { + buffer.recycleBuffer(); + fail("buffer not recycled"); } + assertThat(writer.getNumberOfOutstandingRequests()) + .withFailMessage("Shouldn't increment number of outstanding requests.") + .isZero(); } @Test - public void testSubscribe() throws Exception { + void testSubscribe() throws Exception { final TestNotificationListener listener = new TestNotificationListener(); // Unsuccessful subscription, because no outstanding requests - assertFalse( - "Allowed to subscribe w/o any outstanding requests.", - writer.registerAllRequestsProcessedListener(listener)); + assertThat(writer.registerAllRequestsProcessedListener(listener)) + .withFailMessage("Allowed to subscribe w/o any outstanding requests.") + .isFalse(); // Successful subscription addRequest(); - assertTrue( - "Didn't allow to subscribe.", - writer.registerAllRequestsProcessedListener(listener)); + assertThat(writer.registerAllRequestsProcessedListener(listener)) + .withFailMessage("Didn't allow to subscribe.") + .isTrue(); // Test notification handleRequest(); - assertEquals("Listener was not notified.", 1, listener.getNumberOfNotifications()); + assertThat(listener.getNumberOfNotifications()) + .withFailMessage("Listener was not notified.") + .isOne(); } @Test - public void testSubscribeAndClose() throws IOException, InterruptedException { + void testSubscribeAndClose() throws Exception { final TestNotificationListener listener = new TestNotificationListener(); - final AtomicReference error = new AtomicReference(); - - final CountDownLatch sync = new CountDownLatch(1); - addRequest(); addRequest(); writer.registerAllRequestsProcessedListener(listener); - final Thread asyncCloseThread = - new Thread( - new Runnable() { - @Override - public void run() { - try { - writer.close(); - } catch (Throwable t) { - error.set(t); - } finally { - sync.countDown(); - } - } - }); + final CheckedThread asyncCloseThread = + new CheckedThread() { + @Override + public void go() throws Exception { + writer.close(); + } + }; asyncCloseThread.start(); handleRequest(); handleRequest(); - sync.await(); + asyncCloseThread.sync(); - assertEquals("Listener was not notified.", 1, listener.getNumberOfNotifications()); + assertThat(listener.getNumberOfNotifications()) + .withFailMessage("Listener was not notified.") + .isOne(); } @Test - public void testConcurrentSubscribeAndHandleRequest() throws Exception { + void testConcurrentSubscribeAndHandleRequest() throws Exception { final ExecutorService executor = Executors.newFixedThreadPool(2); final TestNotificationListener listener = new TestNotificationListener(); final Callable subscriber = - new Callable() { - @Override - public Boolean call() throws Exception { - return writer.registerAllRequestsProcessedListener(listener); - } - }; + () -> writer.registerAllRequestsProcessedListener(listener); final Callable requestHandler = - new Callable() { - @Override - public Void call() throws Exception { - handleRequest(); - return null; - } + () -> { + handleRequest(); + return null; }; try { @@ -201,22 +175,13 @@ public Void call() throws Exception { handleRequestFuture.get(); - try { - if (subscribeFuture.get()) { - assertEquals( - "Race: Successfully subscribed, but was never notified.", - 1, - listener.getNumberOfNotifications()); - } else { - assertEquals( - "Race: Never subscribed successfully, but was notified.", - 0, - listener.getNumberOfNotifications()); - } - } catch (Throwable t) { - System.out.println(i); - Assert.fail(t.getMessage()); - } + boolean subscribed = subscribeFuture.get(); + assertThat(listener.getNumberOfNotifications()) + .withFailMessage( + subscribed + ? "Race: Successfully subscribed, but was never notified." + : "Race: Never subscribed successfully, but was notified.") + .isEqualTo(subscribed ? 1 : 0); } } finally { executor.shutdownNow(); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/AsynchronousFileIOChannelTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/AsynchronousFileIOChannelTest.java index af85fc4073cec..a461a358d34c4 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/AsynchronousFileIOChannelTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/AsynchronousFileIOChannelTest.java @@ -23,8 +23,10 @@ import org.apache.flink.runtime.io.network.buffer.Buffer; import org.apache.flink.runtime.io.network.buffer.BufferBuilderTestUtils; import org.apache.flink.runtime.io.network.util.TestNotificationListener; +import org.apache.flink.testutils.executor.TestExecutorExtension; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,27 +43,28 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -public class AsynchronousFileIOChannelTest { +class AsynchronousFileIOChannelTest { private static final Logger LOG = LoggerFactory.getLogger(AsynchronousFileIOChannelTest.class); + @RegisterExtension + private static final TestExecutorExtension EXECUTOR_EXTENSION = + new TestExecutorExtension<>(Executors::newCachedThreadPool); + @Test - public void testAllRequestsProcessedListenerNotification() throws Exception { + void testAllRequestsProcessedListenerNotification() throws Exception { // -- Config ---------------------------------------------------------- final int numberOfRuns = 10; final int numberOfRequests = 100; // -- Setup ----------------------------------------------------------- - final ExecutorService executor = Executors.newFixedThreadPool(3); - final Random random = new Random(); - final RequestQueue requestQueue = new RequestQueue(); + final RequestQueue requestQueue = new RequestQueue<>(); final RequestDoneCallback ioChannelCallback = new NoOpCallback<>(); @@ -85,81 +88,72 @@ public void testAllRequestsProcessedListenerNotification() throws Exception { // Add requests task Callable addRequestsTask = - new Callable() { - @Override - public Void call() throws Exception { - for (int i = 0; i < numberOfRuns; i++) { - LOG.debug("Starting run {}.", i + 1); - - for (int j = 0; j < numberOfRequests; j++) { - ioChannel.addRequest(request); - } - - LOG.debug( - "Added all ({}) requests of run {}.", - numberOfRequests, - i + 1); + () -> { + for (int i = 0; i < numberOfRuns; i++) { + LOG.debug("Starting run {}.", i + 1); - int sleep = random.nextInt(10); - LOG.debug("Sleeping for {} ms before next run.", sleep); - - Thread.sleep(sleep); + for (int j = 0; j < numberOfRequests; j++) { + ioChannel.addRequest(request); } - LOG.debug("Done. Closing channel."); - ioChannel.close(); + LOG.debug( + "Added all ({}) requests of run {}.", + numberOfRequests, + i + 1); - sync.countDown(); + int sleep = random.nextInt(10); + LOG.debug("Sleeping for {} ms before next run.", sleep); - return null; + Thread.sleep(sleep); } + + LOG.debug("Done. Closing channel."); + ioChannel.close(); + + sync.countDown(); + + return null; }; // Process requests task Callable processRequestsTask = - new Callable() { - @Override - public Void call() throws Exception { - int total = numberOfRequests * numberOfRuns; - for (int i = 0; i < total; i++) { - requestQueue.take(); - - ioChannel.handleProcessedBuffer(buffer, null); - } + () -> { + int total = numberOfRequests * numberOfRuns; + for (int i = 0; i < total; i++) { + requestQueue.take(); - LOG.debug("Processed all ({}) requests.", numberOfRequests); + ioChannel.handleProcessedBuffer(buffer, null); + } - sync.countDown(); + LOG.debug("Processed all ({}) requests.", numberOfRequests); - return null; - } + sync.countDown(); + + return null; }; // Listener Callable registerListenerTask = - new Callable() { - @Override - public Void call() throws Exception { - while (true) { - int current = listener.getNumberOfNotifications(); - - if (ioChannel.registerAllRequestsProcessedListener(listener)) { - listener.waitForNotification(current); - } else if (ioChannel.isClosed()) { - break; - } + () -> { + while (true) { + int current = listener.getNumberOfNotifications(); + + if (ioChannel.registerAllRequestsProcessedListener(listener)) { + listener.waitForNotification(current); + } else if (ioChannel.isClosed()) { + break; } + } - LOG.debug("Stopping listener. Channel closed."); + LOG.debug("Stopping listener. Channel closed."); - sync.countDown(); + sync.countDown(); - return null; - } + return null; }; // Run tasks in random order - final List> tasks = new LinkedList>(); + final List> tasks = new LinkedList<>(); tasks.add(addRequestsTask); tasks.add(processRequestsTask); tasks.add(registerListenerTask); @@ -167,33 +161,29 @@ public Void call() throws Exception { Collections.shuffle(tasks); for (Callable task : tasks) { - executor.submit(task); + EXECUTOR_EXTENSION.getExecutor().submit(task); } - if (!sync.await(2, TimeUnit.MINUTES)) { - fail( - "Test failed due to a timeout. This indicates a deadlock due to the way" - + "that listeners are registered/notified in the asynchronous file I/O" - + "channel."); - } + assertThat(sync.await(2, TimeUnit.MINUTES)) + .withFailMessage( + "Test failed due to a timeout. This indicates a deadlock due to the way" + + "that listeners are registered/notified in the asynchronous file I/O" + + "channel.") + .isTrue(); listener.reset(); } - } finally { - executor.shutdown(); } } @Test - public void testClosedButAddRequestAndRegisterListenerRace() throws Exception { + void testClosedButAddRequestAndRegisterListenerRace() throws Exception { // -- Config ---------------------------------------------------------- final int numberOfRuns = 1024; // -- Setup ----------------------------------------------------------- - final ExecutorService executor = Executors.newFixedThreadPool(2); - - final RequestQueue requestQueue = new RequestQueue(); + final RequestQueue requestQueue = new RequestQueue<>(); @SuppressWarnings("unchecked") final RequestDoneCallback ioChannelCallback = new NoOpCallback<>(); @@ -216,61 +206,53 @@ public void testClosedButAddRequestAndRegisterListenerRace() throws Exception { // Add request task Callable addRequestTask = - new Callable() { - @Override - public Void call() throws Exception { - try { - ioChannel.addRequest(request); - } catch (Throwable expected) { - } finally { - sync.countDown(); - } - - return null; + () -> { + try { + ioChannel.addRequest(request); + } catch (Throwable expected) { + } finally { + sync.countDown(); } + + return null; }; // Listener Callable registerListenerTask = - new Callable() { - @Override - public Void call() throws Exception { - try { - while (true) { - int current = listener.getNumberOfNotifications(); - - if (ioChannel.registerAllRequestsProcessedListener( - listener)) { - listener.waitForNotification(current); - } else if (ioChannel.isClosed()) { - break; - } + () -> { + try { + while (true) { + int current = listener.getNumberOfNotifications(); + + if (ioChannel.registerAllRequestsProcessedListener(listener)) { + listener.waitForNotification(current); + } else if (ioChannel.isClosed()) { + break; } - } finally { - sync.countDown(); } - - return null; + } finally { + sync.countDown(); } + + return null; }; + ExecutorService executor = EXECUTOR_EXTENSION.getExecutor(); executor.submit(addRequestTask); executor.submit(registerListenerTask); - if (!sync.await(2, TimeUnit.MINUTES)) { - fail( - "Test failed due to a timeout. This indicates a deadlock due to the way" - + "that listeners are registered/notified in the asynchronous file I/O" - + "channel."); - } + assertThat(sync.await(2, TimeUnit.MINUTES)) + .withFailMessage( + "Test failed due to a timeout. This indicates a deadlock due to the way" + + "that listeners are registered/notified in the asynchronous file I/O" + + "channel.") + .isTrue(); } - } finally { - executor.shutdown(); } } @Test - public void testClosingWaits() { + void testClosingWaits() throws Exception { try (final IOManagerAsync ioMan = new IOManagerAsync()) { final int NUM_BLOCKS = 100; @@ -308,19 +290,16 @@ public void requestFailed(MemorySegment buffer, IOException e) { writer.close(); - assertEquals(NUM_BLOCKS, callbackCounter.get()); - assertFalse(exceptionOccurred.get()); + assertThat(callbackCounter).hasValue(NUM_BLOCKS); + assertThat(exceptionOccurred).isFalse(); } finally { writer.closeAndDelete(); } - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); } } @Test - public void testExceptionForwardsToClose() throws Exception { + void testExceptionForwardsToClose() throws Exception { try (IOManagerAsync ioMan = new IOManagerAsync()) { testExceptionForwardsToClose(ioMan, 100, 1); testExceptionForwardsToClose(ioMan, 100, 50); @@ -329,51 +308,42 @@ public void testExceptionForwardsToClose() throws Exception { } private void testExceptionForwardsToClose( - IOManagerAsync ioMan, final int numBlocks, final int failingBlock) { - try { - MemorySegment seg = MemorySegmentFactory.allocateUnpooledSegment(32 * 1024); - FileIOChannel.ID channelId = ioMan.createChannel(); + IOManagerAsync ioMan, final int numBlocks, final int failingBlock) throws IOException { + MemorySegment seg = MemorySegmentFactory.allocateUnpooledSegment(32 * 1024); + FileIOChannel.ID channelId = ioMan.createChannel(); - BlockChannelWriterWithCallback writer = - new AsynchronousBlockWriterWithCallback( - channelId, - ioMan.getWriteRequestQueue(channelId), - new NoOpCallback<>()) { + BlockChannelWriterWithCallback writer = + new AsynchronousBlockWriterWithCallback( + channelId, ioMan.getWriteRequestQueue(channelId), new NoOpCallback<>()) { - private int numBlocks; + private int numBlocks; - @Override - public void writeBlock(MemorySegment segment) throws IOException { - numBlocks++; - - if (numBlocks == failingBlock) { - this.requestsNotReturned.incrementAndGet(); - this.requestQueue.add(new FailingWriteRequest(this, segment)); - } else { - super.writeBlock(segment); - } - } - }; + @Override + public void writeBlock(MemorySegment segment) throws IOException { + numBlocks++; - try { - for (int i = 0; i < numBlocks; i++) { - writer.writeBlock(seg); - } + if (numBlocks == failingBlock) { + this.requestsNotReturned.incrementAndGet(); + this.requestQueue.add(new FailingWriteRequest(this, segment)); + } else { + super.writeBlock(segment); + } + } + }; + + assertThatThrownBy( + () -> { + try { + for (int i = 0; i < numBlocks; i++) { + writer.writeBlock(seg); + } - writer.close(); - fail("did not forward exception"); - } catch (IOException e) { - // expected - } finally { - try { - writer.closeAndDelete(); - } catch (Throwable ignored) { - } - } - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); - } + writer.close(); + } finally { + writer.closeAndDelete(); + } + }) + .isInstanceOf(IOException.class); } private static class NoOpCallback implements RequestDoneCallback { diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/BufferFileWriterFileSegmentReaderTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/BufferFileWriterFileSegmentReaderTest.java index 064700c9aa596..bec845a8cea5c 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/BufferFileWriterFileSegmentReaderTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/BufferFileWriterFileSegmentReaderTest.java @@ -26,10 +26,10 @@ import org.apache.flink.runtime.util.event.NotificationListener; import org.apache.flink.util.IOUtils; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.ByteBuffer; @@ -39,12 +39,10 @@ import static org.apache.flink.runtime.io.disk.iomanager.BufferFileWriterReaderTest.fillBufferWithAscendingNumbers; import static org.apache.flink.runtime.io.disk.iomanager.BufferFileWriterReaderTest.verifyBufferFilledWithAscendingNumbers; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; -public class BufferFileWriterFileSegmentReaderTest { +class BufferFileWriterFileSegmentReaderTest { private static final int BUFFER_SIZE = 32 * 1024; @@ -60,13 +58,13 @@ public class BufferFileWriterFileSegmentReaderTest { private LinkedBlockingQueue returnedFileSegments = new LinkedBlockingQueue<>(); - @AfterClass - public static void shutdown() throws Exception { + @AfterAll + static void shutdown() throws Exception { ioManager.close(); } - @Before - public void setUpWriterAndReader() { + @BeforeEach + void setUpWriterAndReader() { final FileIOChannel.ID channel = ioManager.createChannel(); try { @@ -82,18 +80,18 @@ public void setUpWriterAndReader() { } } - @After - public void tearDownWriterAndReader() { + @AfterEach + void tearDownWriterAndReader() { if (writer != null) { if (!writer.isClosed()) { - IOUtils.closeQuietly(() -> writer.close()); + IOUtils.closeQuietly(writer::close); } writer.deleteChannel(); } if (reader != null) { if (!reader.isClosed()) { - IOUtils.closeQuietly(() -> reader.close()); + IOUtils.closeQuietly(reader::close); } reader.deleteChannel(); } @@ -102,7 +100,7 @@ public void tearDownWriterAndReader() { } @Test - public void testWriteRead() throws IOException, InterruptedException { + void testWriteRead() throws IOException, InterruptedException { int numBuffers = 1024; int currentNumber = 0; @@ -124,28 +122,24 @@ public void testWriteRead() throws IOException, InterruptedException { // Read buffers back in... for (int i = 0; i < numBuffers; i++) { - assertFalse(reader.hasReachedEndOfFile()); + assertThat(reader.hasReachedEndOfFile()).isFalse(); reader.read(); } // Wait for all requests to be finished final CountDownLatch sync = new CountDownLatch(1); - final NotificationListener listener = - new NotificationListener() { - @Override - public void onNotification() { - sync.countDown(); - } - }; + final NotificationListener listener = sync::countDown; if (reader.registerAllRequestsProcessedListener(listener)) { sync.await(); } - assertTrue(reader.hasReachedEndOfFile()); + assertThat(reader.hasReachedEndOfFile()).isTrue(); // Verify that the content is the same - assertEquals("Read less buffers than written.", numBuffers, returnedFileSegments.size()); + assertThat(returnedFileSegments) + .withFailMessage("Read less buffers than written.") + .hasSize(numBuffers); currentNumber = 0; FileSegment fileSegment; diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/BufferFileWriterReaderTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/BufferFileWriterReaderTest.java index e97109b4c755c..89b9658ef670d 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/BufferFileWriterReaderTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/BufferFileWriterReaderTest.java @@ -25,22 +25,20 @@ import org.apache.flink.runtime.io.network.buffer.FreeingBufferRecycler; import org.apache.flink.runtime.io.network.buffer.NetworkBuffer; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Random; import java.util.concurrent.LinkedBlockingQueue; import static org.apache.flink.util.Preconditions.checkArgument; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; -public class BufferFileWriterReaderTest { +class BufferFileWriterReaderTest { private static final int BUFFER_SIZE = 32 * 1024; @@ -56,13 +54,13 @@ public class BufferFileWriterReaderTest { private LinkedBlockingQueue returnedBuffers = new LinkedBlockingQueue<>(); - @AfterClass - public static void shutdown() throws Exception { + @AfterAll + static void shutdown() throws Exception { ioManager.close(); } - @Before - public void setUpWriterAndReader() { + @BeforeEach + void setUpWriterAndReader() { final FileIOChannel.ID channel = ioManager.createChannel(); try { @@ -83,8 +81,8 @@ public void setUpWriterAndReader() { } } - @After - public void tearDownWriterAndReader() { + @AfterEach + void tearDownWriterAndReader() { if (writer != null) { writer.deleteChannel(); } @@ -97,7 +95,7 @@ public void tearDownWriterAndReader() { } @Test - public void testWriteRead() throws IOException { + void testWriteRead() throws IOException { int numBuffers = 1024; int currentNumber = 0; @@ -119,16 +117,18 @@ public void testWriteRead() throws IOException { // Read buffers back in... for (int i = 0; i < numBuffers; i++) { - assertFalse(reader.hasReachedEndOfFile()); + assertThat(reader.hasReachedEndOfFile()).isFalse(); reader.readInto(createBuffer()); } reader.close(); - assertTrue(reader.hasReachedEndOfFile()); + assertThat(reader.hasReachedEndOfFile()).isTrue(); // Verify that the content is the same - assertEquals("Read less buffers than written.", numBuffers, returnedBuffers.size()); + assertThat(returnedBuffers) + .withFailMessage("Read less buffers than written.") + .hasSize(numBuffers); currentNumber = 0; Buffer buffer; @@ -139,7 +139,7 @@ public void testWriteRead() throws IOException { } @Test - public void testWriteSkipRead() throws IOException { + void testWriteSkipRead() throws IOException { int numBuffers = 1024; int currentNumber = 0; @@ -165,16 +165,18 @@ public void testWriteSkipRead() throws IOException { // Read buffers back in... for (int i = 0; i < numBuffers; i++) { - assertFalse(reader.hasReachedEndOfFile()); + assertThat(reader.hasReachedEndOfFile()).isFalse(); reader.readInto(createBuffer()); } reader.close(); - assertTrue(reader.hasReachedEndOfFile()); + assertThat(reader.hasReachedEndOfFile()).isTrue(); // Verify that the content is the same - assertEquals("Read less buffers than written.", numBuffers, returnedBuffers.size()); + assertThat(returnedBuffers) + .withFailMessage("Read less buffers than written.") + .hasSize(numBuffers); // Start number after skipped buffers... currentNumber = (BUFFER_SIZE / 4) * toSkip; diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/IOManagerAsyncTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/IOManagerAsyncTest.java index c274a607c2779..c077da5439355 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/IOManagerAsyncTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/IOManagerAsyncTest.java @@ -21,19 +21,18 @@ import org.apache.flink.core.memory.MemorySegment; import org.apache.flink.core.memory.MemorySegmentFactory; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; -public class IOManagerAsyncTest { +class IOManagerAsyncTest { private IOManagerAsync ioManager; @@ -41,13 +40,13 @@ public class IOManagerAsyncTest { // Setup & Shutdown // ------------------------------------------------------------------------ - @Before - public void beforeTest() { + @BeforeEach + void beforeTest() { ioManager = new IOManagerAsync(); } - @After - public void afterTest() throws Exception { + @AfterEach + void afterTest() throws Exception { this.ioManager.close(); } @@ -56,305 +55,273 @@ public void afterTest() throws Exception { // ------------------------------------------------------------------------ @Test - public void channelReadWriteOneSegment() { + void channelReadWriteOneSegment() throws Exception { final int NUM_IOS = 1111; - try { - final FileIOChannel.ID channelID = this.ioManager.createChannel(); - final BlockChannelWriter writer = - this.ioManager.createBlockChannelWriter(channelID); + final FileIOChannel.ID channelID = this.ioManager.createChannel(); + final BlockChannelWriter writer = + this.ioManager.createBlockChannelWriter(channelID); - MemorySegment memSeg = MemorySegmentFactory.allocateUnpooledSegment(32 * 1024); + MemorySegment memSeg = MemorySegmentFactory.allocateUnpooledSegment(32 * 1024); - for (int i = 0; i < NUM_IOS; i++) { - for (int pos = 0; pos < memSeg.size(); pos += 4) { - memSeg.putInt(pos, i); - } - - writer.writeBlock(memSeg); - memSeg = writer.getNextReturnedBlock(); + for (int i = 0; i < NUM_IOS; i++) { + for (int pos = 0; pos < memSeg.size(); pos += 4) { + memSeg.putInt(pos, i); } - writer.close(); + writer.writeBlock(memSeg); + memSeg = writer.getNextReturnedBlock(); + } - final BlockChannelReader reader = - this.ioManager.createBlockChannelReader(channelID); - for (int i = 0; i < NUM_IOS; i++) { - reader.readBlock(memSeg); - memSeg = reader.getNextReturnedBlock(); + writer.close(); - for (int pos = 0; pos < memSeg.size(); pos += 4) { - if (memSeg.getInt(pos) != i) { - fail("Read memory segment contains invalid data."); - } - } - } + final BlockChannelReader reader = + this.ioManager.createBlockChannelReader(channelID); + for (int i = 0; i < NUM_IOS; i++) { + reader.readBlock(memSeg); + memSeg = reader.getNextReturnedBlock(); - reader.closeAndDelete(); - } catch (Exception ex) { - ex.printStackTrace(); - fail("Test encountered an exception: " + ex.getMessage()); + for (int pos = 0; pos < memSeg.size(); pos += 4) { + assertThat(memSeg.getInt(pos)) + .withFailMessage("Read memory segment contains invalid data.") + .isEqualTo(i); + } } + + reader.closeAndDelete(); } @Test - public void channelReadWriteMultipleSegments() { + void channelReadWriteMultipleSegments() throws Exception { final int NUM_IOS = 1111; final int NUM_SEGS = 16; - try { - final List memSegs = new ArrayList(); - for (int i = 0; i < NUM_SEGS; i++) { - memSegs.add(MemorySegmentFactory.allocateUnpooledSegment(32 * 1024)); - } - - final FileIOChannel.ID channelID = this.ioManager.createChannel(); - final BlockChannelWriter writer = - this.ioManager.createBlockChannelWriter(channelID); + final List memSegs = new ArrayList(); + for (int i = 0; i < NUM_SEGS; i++) { + memSegs.add(MemorySegmentFactory.allocateUnpooledSegment(32 * 1024)); + } - for (int i = 0; i < NUM_IOS; i++) { - final MemorySegment memSeg = - memSegs.isEmpty() - ? writer.getNextReturnedBlock() - : memSegs.remove(memSegs.size() - 1); + final FileIOChannel.ID channelID = this.ioManager.createChannel(); + final BlockChannelWriter writer = + this.ioManager.createBlockChannelWriter(channelID); - for (int pos = 0; pos < memSeg.size(); pos += 4) { - memSeg.putInt(pos, i); - } + for (int i = 0; i < NUM_IOS; i++) { + final MemorySegment memSeg = + memSegs.isEmpty() + ? writer.getNextReturnedBlock() + : memSegs.remove(memSegs.size() - 1); - writer.writeBlock(memSeg); + for (int pos = 0; pos < memSeg.size(); pos += 4) { + memSeg.putInt(pos, i); } - writer.close(); - // get back the memory - while (memSegs.size() < NUM_SEGS) { - memSegs.add(writer.getNextReturnedBlock()); - } + writer.writeBlock(memSeg); + } + writer.close(); - final BlockChannelReader reader = - this.ioManager.createBlockChannelReader(channelID); - while (!memSegs.isEmpty()) { - reader.readBlock(memSegs.remove(0)); - } + // get back the memory + while (memSegs.size() < NUM_SEGS) { + memSegs.add(writer.getNextReturnedBlock()); + } - for (int i = 0; i < NUM_IOS; i++) { - final MemorySegment memSeg = reader.getNextReturnedBlock(); + final BlockChannelReader reader = + this.ioManager.createBlockChannelReader(channelID); + while (!memSegs.isEmpty()) { + reader.readBlock(memSegs.remove(0)); + } - for (int pos = 0; pos < memSeg.size(); pos += 4) { - if (memSeg.getInt(pos) != i) { - fail("Read memory segment contains invalid data."); - } - } - reader.readBlock(memSeg); + for (int i = 0; i < NUM_IOS; i++) { + final MemorySegment memSeg = reader.getNextReturnedBlock(); + + for (int pos = 0; pos < memSeg.size(); pos += 4) { + assertThat(memSeg.getInt(pos)) + .withFailMessage("Read memory segment contains invalid data.") + .isEqualTo(i); } + reader.readBlock(memSeg); + } - reader.closeAndDelete(); + reader.closeAndDelete(); - // get back the memory - while (memSegs.size() < NUM_SEGS) { - memSegs.add(reader.getNextReturnedBlock()); - } - } catch (Exception ex) { - ex.printStackTrace(); - fail("TEst encountered an exception: " + ex.getMessage()); + // get back the memory + while (memSegs.size() < NUM_SEGS) { + memSegs.add(reader.getNextReturnedBlock()); } } @Test - public void testExceptionPropagationReader() { - try { - // use atomic boolean as a boolean reference - final AtomicBoolean handlerCalled = new AtomicBoolean(); - final AtomicBoolean exceptionForwarded = new AtomicBoolean(); - - ReadRequest req = - new ReadRequest() { - - @Override - public void requestDone(IOException ioex) { - if (ioex instanceof TestIOException) { - exceptionForwarded.set(true); - } - - synchronized (handlerCalled) { - handlerCalled.set(true); - handlerCalled.notifyAll(); - } + void testExceptionPropagationReader() throws Exception { + // use atomic boolean as a boolean reference + final AtomicBoolean handlerCalled = new AtomicBoolean(); + final AtomicBoolean exceptionForwarded = new AtomicBoolean(); + + ReadRequest req = + new ReadRequest() { + + @Override + public void requestDone(IOException ioex) { + if (ioex instanceof TestIOException) { + exceptionForwarded.set(true); } - @Override - public void read() throws IOException { - throw new TestIOException(); + synchronized (handlerCalled) { + handlerCalled.set(true); + handlerCalled.notifyAll(); } - }; + } + + @Override + public void read() throws IOException { + throw new TestIOException(); + } + }; - // test the read queue - RequestQueue rq = ioManager.getReadRequestQueue(ioManager.createChannel()); - rq.add(req); + // test the read queue + RequestQueue rq = ioManager.getReadRequestQueue(ioManager.createChannel()); + rq.add(req); - // wait until the asynchronous request has been handled - synchronized (handlerCalled) { - while (!handlerCalled.get()) { - handlerCalled.wait(); - } + // wait until the asynchronous request has been handled + synchronized (handlerCalled) { + while (!handlerCalled.get()) { + handlerCalled.wait(); } - - assertTrue(exceptionForwarded.get()); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); } + + assertThat(exceptionForwarded).isTrue(); } @Test - public void testExceptionPropagationWriter() { - try { - // use atomic boolean as a boolean reference - final AtomicBoolean handlerCalled = new AtomicBoolean(); - final AtomicBoolean exceptionForwarded = new AtomicBoolean(); - - WriteRequest req = - new WriteRequest() { - - @Override - public void requestDone(IOException ioex) { - if (ioex instanceof TestIOException) { - exceptionForwarded.set(true); - } - - synchronized (handlerCalled) { - handlerCalled.set(true); - handlerCalled.notifyAll(); - } + void testExceptionPropagationWriter() throws Exception { + // use atomic boolean as a boolean reference + final AtomicBoolean handlerCalled = new AtomicBoolean(); + final AtomicBoolean exceptionForwarded = new AtomicBoolean(); + + WriteRequest req = + new WriteRequest() { + + @Override + public void requestDone(IOException ioex) { + if (ioex instanceof TestIOException) { + exceptionForwarded.set(true); } - @Override - public void write() throws IOException { - throw new TestIOException(); + synchronized (handlerCalled) { + handlerCalled.set(true); + handlerCalled.notifyAll(); } - }; - - // test the read queue - RequestQueue rq = - ioManager.getWriteRequestQueue(ioManager.createChannel()); - rq.add(req); - - // wait until the asynchronous request has been handled - synchronized (handlerCalled) { - while (!handlerCalled.get()) { - handlerCalled.wait(); - } - } + } - assertTrue(exceptionForwarded.get()); - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); + @Override + public void write() throws IOException { + throw new TestIOException(); + } + }; + + // test the read queue + RequestQueue rq = ioManager.getWriteRequestQueue(ioManager.createChannel()); + rq.add(req); + + // wait until the asynchronous request has been handled + synchronized (handlerCalled) { + while (!handlerCalled.get()) { + handlerCalled.wait(); + } } + + assertThat(exceptionForwarded).isTrue(); } @Test - public void testExceptionInCallbackRead() { - try { - final AtomicBoolean handlerCalled = new AtomicBoolean(); - - ReadRequest regularRequest = - new ReadRequest() { - - @Override - public void requestDone(IOException ioex) { - synchronized (handlerCalled) { - handlerCalled.set(true); - handlerCalled.notifyAll(); - } + void testExceptionInCallbackRead() throws Exception { + final AtomicBoolean handlerCalled = new AtomicBoolean(); + + ReadRequest regularRequest = + new ReadRequest() { + + @Override + public void requestDone(IOException ioex) { + synchronized (handlerCalled) { + handlerCalled.set(true); + handlerCalled.notifyAll(); } + } - @Override - public void read() {} - }; + @Override + public void read() {} + }; - ReadRequest exceptionThrower = - new ReadRequest() { + ReadRequest exceptionThrower = + new ReadRequest() { - @Override - public void requestDone(IOException ioex) { - throw new RuntimeException(); - } + @Override + public void requestDone(IOException ioex) { + throw new RuntimeException(); + } - @Override - public void read() {} - }; + @Override + public void read() {} + }; - RequestQueue rq = ioManager.getReadRequestQueue(ioManager.createChannel()); + RequestQueue rq = ioManager.getReadRequestQueue(ioManager.createChannel()); - // queue first an exception thrower, then a regular request. - // we check that the regular request gets successfully handled - rq.add(exceptionThrower); - rq.add(regularRequest); + // queue first an exception thrower, then a regular request. + // we check that the regular request gets successfully handled + rq.add(exceptionThrower); + rq.add(regularRequest); - synchronized (handlerCalled) { - while (!handlerCalled.get()) { - handlerCalled.wait(); - } + synchronized (handlerCalled) { + while (!handlerCalled.get()) { + handlerCalled.wait(); } - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); } } @Test - public void testExceptionInCallbackWrite() { - try { - final AtomicBoolean handlerCalled = new AtomicBoolean(); - - WriteRequest regularRequest = - new WriteRequest() { - - @Override - public void requestDone(IOException ioex) { - synchronized (handlerCalled) { - handlerCalled.set(true); - handlerCalled.notifyAll(); - } + void testExceptionInCallbackWrite() throws Exception { + final AtomicBoolean handlerCalled = new AtomicBoolean(); + + WriteRequest regularRequest = + new WriteRequest() { + + @Override + public void requestDone(IOException ioex) { + synchronized (handlerCalled) { + handlerCalled.set(true); + handlerCalled.notifyAll(); } + } - @Override - public void write() {} - }; + @Override + public void write() {} + }; - WriteRequest exceptionThrower = - new WriteRequest() { + WriteRequest exceptionThrower = + new WriteRequest() { - @Override - public void requestDone(IOException ioex) { - throw new RuntimeException(); - } + @Override + public void requestDone(IOException ioex) { + throw new RuntimeException(); + } - @Override - public void write() {} - }; + @Override + public void write() {} + }; - RequestQueue rq = - ioManager.getWriteRequestQueue(ioManager.createChannel()); + RequestQueue rq = ioManager.getWriteRequestQueue(ioManager.createChannel()); - // queue first an exception thrower, then a regular request. - // we check that the regular request gets successfully handled - rq.add(exceptionThrower); - rq.add(regularRequest); + // queue first an exception thrower, then a regular request. + // we check that the regular request gets successfully handled + rq.add(exceptionThrower); + rq.add(regularRequest); - synchronized (handlerCalled) { - while (!handlerCalled.get()) { - handlerCalled.wait(); - } + synchronized (handlerCalled) { + while (!handlerCalled.get()) { + handlerCalled.wait(); } - } catch (Exception e) { - e.printStackTrace(); - fail(e.getMessage()); } } - final class TestIOException extends IOException { + private static final class TestIOException extends IOException { private static final long serialVersionUID = -814705441998024472L; } } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/IOManagerITCase.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/IOManagerITCase.java index 17ea323fa7ea2..ee7cff8f5ead8 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/IOManagerITCase.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/IOManagerITCase.java @@ -26,12 +26,10 @@ import org.apache.flink.runtime.memory.MemoryManager; import org.apache.flink.runtime.memory.MemoryManagerBuilder; import org.apache.flink.runtime.operators.testutils.DummyInvokable; -import org.apache.flink.util.TestLogger; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.EOFException; import java.io.File; @@ -39,8 +37,10 @@ import java.util.List; import java.util.Random; +import static org.assertj.core.api.Assertions.assertThat; + /** Integration test case for the I/O manager. */ -public class IOManagerITCase extends TestLogger { +class IOManagerITCase { private static final long SEED = 649180756312423613L; @@ -56,19 +56,19 @@ public class IOManagerITCase extends TestLogger { private MemoryManager memoryManager; - @Before - public void beforeTest() { + @BeforeEach + void beforeTest() { memoryManager = MemoryManagerBuilder.newBuilder().setMemorySize(MEMORY_SIZE).build(); ioManager = new IOManagerAsync(); } - @After - public void afterTest() throws Exception { + @AfterEach + void afterTest() throws Exception { ioManager.close(); - Assert.assertTrue( - "Not all memory was returned to the memory manager in the test.", - memoryManager.verifyEmpty()); + assertThat(memoryManager.verifyEmpty()) + .withFailMessage("Not all memory was returned to the memory manager in the test.") + .isTrue(); memoryManager.shutdown(); memoryManager = null; } @@ -82,7 +82,7 @@ public void afterTest() throws Exception { */ @Test @SuppressWarnings("unchecked") - public void parallelChannelsTest() throws Exception { + void parallelChannelsTest() throws Exception { final Random rnd = new Random(SEED); final AbstractInvokable memOwner = new DummyInvokable(); @@ -141,27 +141,22 @@ public void parallelChannelsTest() throws Exception { try { while (true) { val.read(in); - int intValue = 0; - try { - intValue = Integer.parseInt(val.value); - } catch (NumberFormatException nfex) { - Assert.fail( - "Invalid value read from reader. Valid decimal number expected."); - } - Assert.assertEquals( - "Written and read values do not match during sequential read.", - nextVal, - intValue); + int intValue = Integer.parseInt(val.value); + + assertThat(intValue) + .withFailMessage( + "Written and read values do not match during sequential read.") + .isEqualTo(nextVal); nextVal++; } } catch (EOFException eofex) { // expected } - Assert.assertEquals( - "NUmber of written numbers differs from number of read numbers.", - writingCounters[i], - nextVal); + assertThat(nextVal) + .withFailMessage( + "NUmber of written numbers differs from number of read numbers.") + .isEqualTo(writingCounters[i]); this.memoryManager.release(in.close()); } @@ -185,19 +180,11 @@ public void parallelChannelsTest() throws Exception { if (ins[channel] != null) { try { val.read(ins[channel]); - int intValue; - try { - intValue = Integer.parseInt(val.value); - } catch (NumberFormatException nfex) { - Assert.fail( - "Invalid value read from reader. Valid decimal number expected."); - return; - } - - Assert.assertEquals( - "Written and read values do not match.", - readingCounters[channel]++, - intValue); + int intValue = Integer.parseInt(val.value); + + assertThat(intValue) + .withFailMessage("Written and read values do not match.") + .isEqualTo(readingCounters[channel]++); break; } catch (EOFException eofex) { @@ -222,7 +209,7 @@ public void parallelChannelsTest() throws Exception { // check that files are deleted for (int i = 0; i < NUM_CHANNELS; i++) { File f = new File(ids[i].getPath()); - Assert.assertFalse("Channel file has not been deleted.", f.exists()); + assertThat(f).withFailMessage("Channel file has not been deleted.").doesNotExist(); } } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/IOManagerTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/IOManagerTest.java index ce8a50cb30305..67a65d1f24ae0 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/IOManagerTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/disk/iomanager/IOManagerTest.java @@ -22,27 +22,20 @@ import org.apache.flink.runtime.io.disk.iomanager.FileIOChannel.ID; import org.apache.flink.runtime.io.network.buffer.Buffer; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import java.io.File; -import java.io.IOException; +import java.nio.file.Files; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; -public class IOManagerTest { - - @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); +class IOManagerTest { @Test - public void channelEnumerator() throws Exception { - File tempPath = temporaryFolder.newFolder(); - + void channelEnumerator(@TempDir File tempPath) throws Exception { String[] tempDirs = new String[] { new File(tempPath, "a").getAbsolutePath(), @@ -59,24 +52,29 @@ public void channelEnumerator() throws Exception { for (int i = 0; i < 3 * tempDirs.length; i++) { FileIOChannel.ID id = enumerator.next(); - File path = id.getPathFile(); + File pathFile = id.getPathFile(); + Files.createFile(pathFile.toPath()); - assertTrue("Channel IDs must name an absolute path.", path.isAbsolute()); - assertFalse("Channel IDs must name a file, not a directory.", path.isDirectory()); + assertThat(pathFile) + .withFailMessage("Channel IDs must name an absolute path.") + .isAbsolute(); + assertThat(pathFile) + .withFailMessage("Channel IDs must name a file, not a directory.") + .isFile(); - assertTrue( - "Path is not in the temp directory.", - tempPath.equals(path.getParentFile().getParentFile().getParentFile())); + assertThat(pathFile.getParentFile().getParentFile().getParentFile()) + .withFailMessage("Path is not in the temp directory.") + .isEqualTo(tempPath); for (int k = 0; k < tempDirs.length; k++) { - if (path.getParentFile().getParent().equals(tempDirs[k])) { + if (pathFile.getParentFile().getParent().equals(tempDirs[k])) { counters[k]++; } } } for (int k = 0; k < tempDirs.length; k++) { - assertEquals(3, counters[k]); + assertThat(counters[k]).isEqualTo(3); } } } @@ -108,19 +106,19 @@ public BlockChannelReader createBlockChannelReader( } @Override - public BufferFileWriter createBufferFileWriter(ID channelID) throws IOException { + public BufferFileWriter createBufferFileWriter(ID channelID) { throw new UnsupportedOperationException(); } @Override public BufferFileReader createBufferFileReader( - ID channelID, RequestDoneCallback callback) throws IOException { + ID channelID, RequestDoneCallback callback) { throw new UnsupportedOperationException(); } @Override public BufferFileSegmentReader createBufferFileSegmentReader( - ID channelID, RequestDoneCallback callback) throws IOException { + ID channelID, RequestDoneCallback callback) { throw new UnsupportedOperationException(); } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/NettyShuffleEnvironmentBuilder.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/NettyShuffleEnvironmentBuilder.java index 23e01a929b573..1a8f21d7c131c 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/NettyShuffleEnvironmentBuilder.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/NettyShuffleEnvironmentBuilder.java @@ -19,6 +19,7 @@ package org.apache.flink.runtime.io.network; import org.apache.flink.configuration.Configuration; +import org.apache.flink.configuration.NettyShuffleEnvironmentOptions; import org.apache.flink.metrics.MetricGroup; import org.apache.flink.runtime.clusterframework.types.ResourceID; import org.apache.flink.runtime.io.network.netty.NettyConfig; @@ -55,6 +56,12 @@ public class NettyShuffleEnvironmentBuilder { private int partitionRequestMaxBackoff; + private int partitionRequestTimeout = + (int) + NettyShuffleEnvironmentOptions.NETWORK_PARTITION_REQUEST_TIMEOUT + .defaultValue() + .toMillis(); + private int networkBuffersPerChannel = 2; private int floatingNetworkBuffersPerGate = 8; @@ -125,6 +132,11 @@ public NettyShuffleEnvironmentBuilder setPartitionRequestMaxBackoff( return this; } + public NettyShuffleEnvironmentBuilder setPartitionRequestTimeout(int partitionRequestTimeout) { + this.partitionRequestTimeout = partitionRequestTimeout; + return this; + } + public NettyShuffleEnvironmentBuilder setNetworkBuffersPerChannel( int networkBuffersPerChannel) { this.networkBuffersPerChannel = networkBuffersPerChannel; @@ -246,6 +258,7 @@ public NettyShuffleEnvironment build() { bufferSize, partitionRequestInitialBackoff, partitionRequestMaxBackoff, + partitionRequestTimeout, networkBuffersPerChannel, floatingNetworkBuffersPerGate, maxRequiredBuffersPerGate, diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/CancelPartitionRequestTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/CancelPartitionRequestTest.java index 7bc7e9914caa2..7bfa0d28f8501 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/CancelPartitionRequestTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/CancelPartitionRequestTest.java @@ -23,6 +23,7 @@ import org.apache.flink.runtime.io.network.buffer.BufferProvider; import org.apache.flink.runtime.io.network.netty.NettyTestUtil.NettyServerAndClient; import org.apache.flink.runtime.io.network.partition.BufferAvailabilityListener; +import org.apache.flink.runtime.io.network.partition.PartitionRequestListener; import org.apache.flink.runtime.io.network.partition.ResultPartitionID; import org.apache.flink.runtime.io.network.partition.ResultPartitionManager; import org.apache.flink.runtime.io.network.partition.ResultSubpartition.BufferAndBacklog; @@ -34,12 +35,12 @@ import org.apache.flink.shaded.netty4.io.netty.channel.Channel; import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import javax.annotation.Nullable; import java.io.IOException; +import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -83,20 +84,14 @@ public void testCancelPartitionRequest() throws Exception { spy(new InfiniteSubpartitionView(outboundBuffers, sync)); // Return infinite subpartition - when(partitions.createSubpartitionView( - eq(pid), eq(0), any(BufferAvailabilityListener.class))) + when(partitions.createSubpartitionViewOrRegisterListener( + eq(pid), + eq(0), + any(BufferAvailabilityListener.class), + any(PartitionRequestListener.class))) .thenAnswer( - new Answer() { - @Override - public ResultSubpartitionView answer( - InvocationOnMock invocationOnMock) throws Throwable { - BufferAvailabilityListener listener = - (BufferAvailabilityListener) - invocationOnMock.getArguments()[2]; - listener.notifyDataAvailable(); - return view; - } - }); + (Answer>) + invocationOnMock -> Optional.of(view)); NettyProtocol protocol = new NettyProtocol(partitions, mock(TaskEventDispatcher.class)); @@ -140,20 +135,20 @@ public void testDuplicateCancel() throws Exception { spy(new InfiniteSubpartitionView(outboundBuffers, sync)); // Return infinite subpartition - when(partitions.createSubpartitionView( - eq(pid), eq(0), any(BufferAvailabilityListener.class))) + when(partitions.createSubpartitionViewOrRegisterListener( + eq(pid), + eq(0), + any(BufferAvailabilityListener.class), + any(PartitionRequestListener.class))) .thenAnswer( - new Answer() { - @Override - public ResultSubpartitionView answer( - InvocationOnMock invocationOnMock) throws Throwable { - BufferAvailabilityListener listener = - (BufferAvailabilityListener) - invocationOnMock.getArguments()[2]; - listener.notifyDataAvailable(); - return view; - } - }); + (Answer>) + invocationOnMock -> { + BufferAvailabilityListener listener = + (BufferAvailabilityListener) + invocationOnMock.getArguments()[2]; + listener.notifyDataAvailable(); + return Optional.of(view); + }); NettyProtocol protocol = new NettyProtocol(partitions, mock(TaskEventDispatcher.class)); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/CreditBasedPartitionRequestClientHandlerTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/CreditBasedPartitionRequestClientHandlerTest.java index 6b39979fac828..f46c25ff5a2d7 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/CreditBasedPartitionRequestClientHandlerTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/CreditBasedPartitionRequestClientHandlerTest.java @@ -822,6 +822,7 @@ private static class TestRemoteInputChannelForError extends RemoteInputChannel { new TestingConnectionManager(), 0, 100, + 100, 2, new SimpleCounter(), new SimpleCounter(), diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/CreditBasedSequenceNumberingViewReaderTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/CreditBasedSequenceNumberingViewReaderTest.java index 33b903ff68899..e8a6c70704443 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/CreditBasedSequenceNumberingViewReaderTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/CreditBasedSequenceNumberingViewReaderTest.java @@ -19,7 +19,7 @@ package org.apache.flink.runtime.io.network.netty; import org.apache.flink.runtime.io.network.partition.NoOpResultSubpartitionView; -import org.apache.flink.runtime.io.network.partition.ResultPartitionID; +import org.apache.flink.runtime.io.network.partition.TestingResultPartition; import org.apache.flink.runtime.io.network.partition.consumer.InputChannelID; import org.apache.flink.shaded.netty4.io.netty.channel.embedded.EmbeddedChannel; @@ -83,9 +83,11 @@ private CreditBasedSequenceNumberingViewReader createNetworkSequenceViewReader( CreditBasedSequenceNumberingViewReader reader = new CreditBasedSequenceNumberingViewReader( new InputChannelID(), initialCredit, queue); - reader.requestSubpartitionView( - (ignored1, ignored2, ignored3) -> new NoOpResultSubpartitionView(), - new ResultPartitionID(), + reader.notifySubpartitionCreated( + TestingResultPartition.newBuilder() + .setCreateSubpartitionViewFunction( + (index, listener) -> new NoOpResultSubpartitionView()) + .build(), 0); return reader; } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/NettyPartitionRequestClientTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/NettyPartitionRequestClientTest.java index ad34bdf363e85..ccfef5f2442df 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/NettyPartitionRequestClientTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/NettyPartitionRequestClientTest.java @@ -107,7 +107,7 @@ public void testRetriggerPartitionRequest() throws Exception { InputChannelBuilder.newBuilder() .setConnectionManager( mockConnectionManagerWithPartitionRequestClient(client)) - .setInitialBackoff(1) + .setPartitionRequestListenerTimeout(1) .setMaxBackoff(2) .buildRemoteChannel(inputGate); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/PartitionRequestQueueTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/PartitionRequestQueueTest.java index 34b949742db62..1a16710a8119f 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/PartitionRequestQueueTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/PartitionRequestQueueTest.java @@ -28,16 +28,17 @@ import org.apache.flink.runtime.io.network.partition.BufferAvailabilityListener; import org.apache.flink.runtime.io.network.partition.NoOpBufferAvailablityListener; import org.apache.flink.runtime.io.network.partition.NoOpResultSubpartitionView; +import org.apache.flink.runtime.io.network.partition.PartitionNotFoundException; import org.apache.flink.runtime.io.network.partition.PipelinedSubpartition; import org.apache.flink.runtime.io.network.partition.PipelinedSubpartitionTest; import org.apache.flink.runtime.io.network.partition.PipelinedSubpartitionView; import org.apache.flink.runtime.io.network.partition.ResultPartition; import org.apache.flink.runtime.io.network.partition.ResultPartitionID; import org.apache.flink.runtime.io.network.partition.ResultPartitionManager; -import org.apache.flink.runtime.io.network.partition.ResultPartitionProvider; import org.apache.flink.runtime.io.network.partition.ResultPartitionType; import org.apache.flink.runtime.io.network.partition.ResultSubpartition.BufferAndBacklog; import org.apache.flink.runtime.io.network.partition.ResultSubpartitionView; +import org.apache.flink.runtime.io.network.partition.TestingResultPartition; import org.apache.flink.runtime.io.network.partition.consumer.InputChannelID; import org.apache.flink.runtime.io.network.util.TestBufferFactory; @@ -90,6 +91,44 @@ public static void shutdown() throws Exception { fileChannelManager.close(); } + /** Test that PartitionNotFound message will be sent to downstream in notifying timeout. */ + @Test + public void testNotifyReaderPartitionTimeout() throws Exception { + PartitionRequestQueue queue = new PartitionRequestQueue(); + EmbeddedChannel channel = new EmbeddedChannel(queue); + ResultPartitionManager resultPartitionManager = new ResultPartitionManager(); + ResultPartitionID resultPartitionId = new ResultPartitionID(); + CreditBasedSequenceNumberingViewReader reader = + new CreditBasedSequenceNumberingViewReader(new InputChannelID(0, 0), 10, queue); + reader.requestSubpartitionViewOrRegisterListener( + resultPartitionManager, resultPartitionId, 0); + + assertEquals( + resultPartitionManager + .getListenerManagers() + .get(resultPartitionId) + .getPartitionRequestListeners() + .size(), + 1); + + reader.notifyPartitionRequestTimeout( + resultPartitionManager + .getListenerManagers() + .get(resultPartitionId) + .getPartitionRequestListeners() + .iterator() + .next()); + + channel.runPendingTasks(); + + Object read = channel.readOutbound(); + assertNotNull(read); + assertThat(read, instanceOf(NettyMessage.ErrorResponse.class)); + assertThat( + ((NettyMessage.ErrorResponse) read).cause, + instanceOf(PartitionNotFoundException.class)); + } + /** * In case of enqueuing an empty reader and a reader that actually has some buffers when channel * is not writable, on channelWritability change event should result in reading all of the @@ -106,10 +145,12 @@ public void testNotifyReaderNonEmptyOnEmptyReaders() throws Exception { CreditBasedSequenceNumberingViewReader reader2 = new CreditBasedSequenceNumberingViewReader(new InputChannelID(1, 1), 10, queue); - reader1.requestSubpartitionView( - (partitionId, index, availabilityListener) -> - new EmptyAlwaysAvailableResultSubpartitionView(), - new ResultPartitionID(), + reader1.notifySubpartitionCreated( + TestingResultPartition.newBuilder() + .setCreateSubpartitionViewFunction( + (index, listener) -> + new EmptyAlwaysAvailableResultSubpartitionView()) + .build(), 0); reader1.notifyDataAvailable(); assertTrue(reader1.getAvailabilityAndBacklog().isAvailable()); @@ -122,10 +163,12 @@ public void testNotifyReaderNonEmptyOnEmptyReaders() throws Exception { channel.runPendingTasks(); reader2.notifyDataAvailable(); - reader2.requestSubpartitionView( - (partitionId, index, availabilityListener) -> - new DefaultBufferResultSubpartitionView(buffersToWrite), - new ResultPartitionID(), + reader2.notifySubpartitionCreated( + TestingResultPartition.newBuilder() + .setCreateSubpartitionViewFunction( + (index, listener) -> + new DefaultBufferResultSubpartitionView(buffersToWrite)) + .build(), 0); assertTrue(reader2.getAvailabilityAndBacklog().isAvailable()); assertFalse(reader2.isRegisteredAsAvailable()); @@ -152,8 +195,10 @@ public void testReadOnlyBufferWriting() throws Exception { private void testBufferWriting(ResultSubpartitionView view) throws IOException { // setup - ResultPartitionProvider partitionProvider = - (partitionId, index, availabilityListener) -> view; + ResultPartition partition = + TestingResultPartition.newBuilder() + .setCreateSubpartitionViewFunction((index, listener) -> view) + .build(); final InputChannelID receiverId = new InputChannelID(); final PartitionRequestQueue queue = new PartitionRequestQueue(); @@ -161,7 +206,7 @@ private void testBufferWriting(ResultSubpartitionView view) throws IOException { new CreditBasedSequenceNumberingViewReader(receiverId, Integer.MAX_VALUE, queue); final EmbeddedChannel channel = new EmbeddedChannel(queue); - reader.requestSubpartitionView(partitionProvider, new ResultPartitionID(), 0); + reader.notifySubpartitionCreated(partition, 0); // notify about buffer availability and encode one buffer reader.notifyDataAvailable(); @@ -246,8 +291,10 @@ public void testEnqueueReaderByNotifyingEventBuffer() throws Exception { // setup final ResultSubpartitionView view = new NextIsEventResultSubpartitionView(); - ResultPartitionProvider partitionProvider = - (partitionId, index, availabilityListener) -> view; + ResultPartition partition = + TestingResultPartition.newBuilder() + .setCreateSubpartitionViewFunction((index, listener) -> view) + .build(); final InputChannelID receiverId = new InputChannelID(); final PartitionRequestQueue queue = new PartitionRequestQueue(); @@ -255,7 +302,7 @@ public void testEnqueueReaderByNotifyingEventBuffer() throws Exception { new CreditBasedSequenceNumberingViewReader(receiverId, 0, queue); final EmbeddedChannel channel = new EmbeddedChannel(queue); - reader.requestSubpartitionView(partitionProvider, new ResultPartitionID(), 0); + reader.notifySubpartitionCreated(partition, 0); // block the channel so that we see an intermediate state in the test ByteBuf channelBlockingBuffer = blockChannel(channel); @@ -297,8 +344,10 @@ public void testEnqueueReaderByNotifyingBufferAndCredit() throws Exception { // setup final ResultSubpartitionView view = new DefaultBufferResultSubpartitionView(10); - ResultPartitionProvider partitionProvider = - (partitionId, index, availabilityListener) -> view; + ResultPartition partition = + TestingResultPartition.newBuilder() + .setCreateSubpartitionViewFunction((index, listener) -> view) + .build(); final InputChannelID receiverId = new InputChannelID(); final PartitionRequestQueue queue = new PartitionRequestQueue(); @@ -307,7 +356,7 @@ public void testEnqueueReaderByNotifyingBufferAndCredit() throws Exception { final EmbeddedChannel channel = new EmbeddedChannel(queue); reader.addCredit(-2); - reader.requestSubpartitionView(partitionProvider, new ResultPartitionID(), 0); + reader.notifySubpartitionCreated(partition, 0); queue.notifyReaderCreated(reader); // block the channel so that we see an intermediate state in the test @@ -375,8 +424,10 @@ public void testEnqueueReaderByResumingConsumption() throws Exception { BufferAvailabilityListener bufferAvailabilityListener = new NoOpBufferAvailablityListener(); PipelinedSubpartitionView view = subpartition.createReadView(bufferAvailabilityListener); - ResultPartitionProvider partitionProvider = - (partitionId, index, availabilityListener) -> view; + ResultPartition partition = + TestingResultPartition.newBuilder() + .setCreateSubpartitionViewFunction((index, listener) -> view) + .build(); InputChannelID receiverId = new InputChannelID(); PartitionRequestQueue queue = new PartitionRequestQueue(); @@ -384,7 +435,7 @@ public void testEnqueueReaderByResumingConsumption() throws Exception { new CreditBasedSequenceNumberingViewReader(receiverId, 2, queue); EmbeddedChannel channel = new EmbeddedChannel(queue); - reader.requestSubpartitionView(partitionProvider, new ResultPartitionID(), 0); + reader.notifySubpartitionCreated(partition, 0); queue.notifyReaderCreated(reader); assertTrue(reader.getAvailabilityAndBacklog().isAvailable()); @@ -413,8 +464,10 @@ public void testAnnounceBacklog() throws Exception { PipelinedSubpartitionView view = subpartition.createReadView(new NoOpBufferAvailablityListener()); - ResultPartitionProvider partitionProvider = - (partitionId, index, availabilityListener) -> view; + ResultPartition partition = + TestingResultPartition.newBuilder() + .setCreateSubpartitionViewFunction((index, listener) -> view) + .build(); PartitionRequestQueue queue = new PartitionRequestQueue(); InputChannelID receiverId = new InputChannelID(); @@ -422,7 +475,7 @@ public void testAnnounceBacklog() throws Exception { new CreditBasedSequenceNumberingViewReader(receiverId, 0, queue); EmbeddedChannel channel = new EmbeddedChannel(queue); - reader.requestSubpartitionView(partitionProvider, new ResultPartitionID(), 0); + reader.notifySubpartitionCreated(partition, 0); queue.notifyReaderCreated(reader); reader.notifyDataAvailable(); @@ -459,7 +512,7 @@ private void testCancelPartitionRequest(boolean isAvailableView) throws Exceptio new CreditBasedSequenceNumberingViewReader(receiverId, 2, queue); final EmbeddedChannel channel = new EmbeddedChannel(queue); - reader.requestSubpartitionView(partitionManager, partition.getPartitionId(), 0); + reader.notifySubpartitionCreated(partition, 0); // add this reader into allReaders queue queue.notifyReaderCreated(reader); @@ -494,8 +547,10 @@ public void testNotifyNewBufferSize() throws Exception { BufferAvailabilityListener bufferAvailabilityListener = new NoOpBufferAvailablityListener(); ResultSubpartitionView view = parent.createSubpartitionView(0, bufferAvailabilityListener); - ResultPartitionProvider partitionProvider = - (partitionId, index, availabilityListener) -> view; + ResultPartition partition = + TestingResultPartition.newBuilder() + .setCreateSubpartitionViewFunction((index, listener) -> view) + .build(); InputChannelID receiverId = new InputChannelID(); PartitionRequestQueue queue = new PartitionRequestQueue(); @@ -503,7 +558,7 @@ public void testNotifyNewBufferSize() throws Exception { new CreditBasedSequenceNumberingViewReader(receiverId, 2, queue); EmbeddedChannel channel = new EmbeddedChannel(queue); - reader.requestSubpartitionView(partitionProvider, new ResultPartitionID(), 0); + reader.notifySubpartitionCreated(partition, 0); queue.notifyReaderCreated(reader); // when: New buffer size received. diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/PartitionRequestRegistrationTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/PartitionRequestRegistrationTest.java new file mode 100644 index 0000000000000..3f98c8701b9b0 --- /dev/null +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/PartitionRequestRegistrationTest.java @@ -0,0 +1,267 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.runtime.io.network.netty; + +import org.apache.flink.metrics.SimpleCounter; +import org.apache.flink.runtime.checkpoint.channel.ChannelStateWriter; +import org.apache.flink.runtime.event.TaskEvent; +import org.apache.flink.runtime.io.network.NetworkClientHandler; +import org.apache.flink.runtime.io.network.TaskEventPublisher; +import org.apache.flink.runtime.io.network.TestingConnectionManager; +import org.apache.flink.runtime.io.network.partition.BufferAvailabilityListener; +import org.apache.flink.runtime.io.network.partition.PartitionRequestListener; +import org.apache.flink.runtime.io.network.partition.ResultPartition; +import org.apache.flink.runtime.io.network.partition.ResultPartitionID; +import org.apache.flink.runtime.io.network.partition.ResultPartitionManager; +import org.apache.flink.runtime.io.network.partition.ResultPartitionProvider; +import org.apache.flink.runtime.io.network.partition.ResultSubpartitionView; +import org.apache.flink.runtime.io.network.partition.TestingResultPartition; +import org.apache.flink.runtime.io.network.partition.consumer.InputChannelBuilder; +import org.apache.flink.runtime.io.network.partition.consumer.InputChannelID; +import org.apache.flink.runtime.io.network.partition.consumer.RemoteInputChannel; +import org.apache.flink.runtime.io.network.partition.consumer.SingleInputGateBuilder; +import org.apache.flink.runtime.io.network.util.TestPooledBufferProvider; +import org.apache.flink.testutils.TestingUtils; + +import org.apache.flink.shaded.netty4.io.netty.channel.Channel; + +import org.junit.jupiter.api.Test; + +import java.util.Optional; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static org.apache.flink.runtime.io.network.netty.NettyTestUtil.connect; +import static org.apache.flink.runtime.io.network.netty.NettyTestUtil.initServerAndClient; +import static org.apache.flink.runtime.io.network.netty.NettyTestUtil.shutdown; +import static org.assertj.core.api.Assertions.fail; + +/** + * Tests {@link NettyMessage.PartitionRequest} before and after {@link ResultPartitionManager} + * registers given {@link ResultPartition}. + */ +class PartitionRequestRegistrationTest { + + /** + * Verifies that result partition manager registers partition before receive partition request. + */ + @Test + void testRegisterResultPartitionBeforeRequest() throws Exception { + final TestPooledBufferProvider outboundBuffers = new TestPooledBufferProvider(16); + final CountDownLatch sync = new CountDownLatch(1); + final ResultSubpartitionView view = + new CancelPartitionRequestTest.InfiniteSubpartitionView(outboundBuffers, sync); + + ResultPartitionManager partitionManager = new ResultPartitionManager(); + ResultPartition resultPartition = + TestingResultPartition.newBuilder() + .setResultPartitionManager(partitionManager) + .setCreateSubpartitionViewFunction((index, listener) -> view) + .build(); + + // Register result partition before request + partitionManager.registerResultPartition(resultPartition); + + NettyTestUtil.NettyServerAndClient serverAndClient = null; + try { + + NettyProtocol protocol = + new NettyProtocol(partitionManager, new NoOpTaskEventPublisher()); + + serverAndClient = initServerAndClient(protocol); + + Channel ch = connect(serverAndClient); + + // Request for non-existing input channel => results in cancel request + ch.writeAndFlush( + new NettyMessage.PartitionRequest( + resultPartition.getPartitionId(), + 0, + new InputChannelID(), + Integer.MAX_VALUE)) + .await(); + + // Wait for the notification + if (!sync.await(TestingUtils.TESTING_DURATION.toMillis(), TimeUnit.MILLISECONDS)) { + fail( + "Timed out after waiting for " + + TestingUtils.TESTING_DURATION.toMillis() + + " ms to be notified about cancelled partition."); + } + } finally { + shutdown(serverAndClient); + } + } + + /** + * Verifies that result partition manager registers partition after receive partition request. + */ + @Test + void testRegisterResultPartitionAfterRequest() throws Exception { + final TestPooledBufferProvider outboundBuffers = new TestPooledBufferProvider(16); + final CountDownLatch sync = new CountDownLatch(1); + final ResultSubpartitionView view = + new CancelPartitionRequestTest.InfiniteSubpartitionView(outboundBuffers, sync); + + ResultPartitionManager partitionManager = new ResultPartitionManager(); + ResultPartition resultPartition = + TestingResultPartition.newBuilder() + .setResultPartitionManager(partitionManager) + .setCreateSubpartitionViewFunction((index, listener) -> view) + .build(); + + NettyTestUtil.NettyServerAndClient serverAndClient = null; + try { + + NettyProtocol protocol = + new NettyProtocol(partitionManager, new NoOpTaskEventPublisher()); + + serverAndClient = initServerAndClient(protocol); + + Channel ch = connect(serverAndClient); + + // Request for non-existing input channel => results in cancel request + ch.writeAndFlush( + new NettyMessage.PartitionRequest( + resultPartition.getPartitionId(), + 0, + new InputChannelID(), + Integer.MAX_VALUE)) + .await(); + + // Register result partition after partition request + partitionManager.registerResultPartition(resultPartition); + + // Wait for the notification + if (!sync.await(TestingUtils.TESTING_DURATION.toMillis(), TimeUnit.MILLISECONDS)) { + fail( + "Timed out after waiting for " + + TestingUtils.TESTING_DURATION.toMillis() + + " ms to be notified about cancelled partition."); + } + } finally { + shutdown(serverAndClient); + } + } + + /** Verifies that result partition manager notifier timeout. */ + @Test + void testPartitionRequestNotifierTimeout() throws Exception { + final ResultPartitionID pid = new ResultPartitionID(); + final CountDownLatch sync = new CountDownLatch(1); + + NettyTestUtil.NettyServerAndClient serverAndClient = null; + try { + ResultPartitionProvider partitions = + new ResultPartitionProvider() { + @Override + public ResultSubpartitionView createSubpartitionView( + ResultPartitionID partitionId, + int index, + BufferAvailabilityListener availabilityListener) { + return null; + } + + @Override + public Optional + createSubpartitionViewOrRegisterListener( + ResultPartitionID partitionId, + int index, + BufferAvailabilityListener availabilityListener, + PartitionRequestListener partitionRequestListener) { + partitionRequestListener.notifyPartitionCreatedTimeout(); + return Optional.empty(); + } + + @Override + public void releasePartitionRequestListener( + PartitionRequestListener listener) {} + }; + + NettyProtocol protocol = new NettyProtocol(partitions, new NoOpTaskEventPublisher()); + + serverAndClient = initServerAndClient(protocol); + + Channel ch = connect(serverAndClient); + + NetworkClientHandler clientHandler = ch.pipeline().get(NetworkClientHandler.class); + RemoteInputChannel remoteInputChannel = + new TestRemoteInputChannelForPartitionNotFound(sync); + clientHandler.addInputChannel(remoteInputChannel); + + // Request for non-existing input channel => results in cancel request + ch.writeAndFlush( + new NettyMessage.PartitionRequest( + pid, + 0, + remoteInputChannel.getInputChannelId(), + Integer.MAX_VALUE)) + .await(); + + // Wait for the notification + if (!sync.await(TestingUtils.TESTING_DURATION.toMillis(), TimeUnit.MILLISECONDS)) { + fail( + "Timed out after waiting for " + + TestingUtils.TESTING_DURATION.toMillis() + + " ms to be notified about cancelled partition."); + } + } finally { + shutdown(serverAndClient); + } + } + + /** + * The test remote input channel to count down the latch when it receives partition not found + * exception. + */ + private static class TestRemoteInputChannelForPartitionNotFound extends RemoteInputChannel { + private final CountDownLatch latch; + + TestRemoteInputChannelForPartitionNotFound(CountDownLatch latch) { + super( + new SingleInputGateBuilder().setNumberOfChannels(1).build(), + 0, + new ResultPartitionID(), + 0, + InputChannelBuilder.STUB_CONNECTION_ID, + new TestingConnectionManager(), + 0, + 100, + 10000, + 2, + new SimpleCounter(), + new SimpleCounter(), + ChannelStateWriter.NO_OP); + this.latch = latch; + } + + @Override + public void onFailedPartitionRequest() { + latch.countDown(); + } + } + + /** A testing implementation of {@link TaskEventPublisher} without operation. */ + private static class NoOpTaskEventPublisher implements TaskEventPublisher { + @Override + public boolean publish(ResultPartitionID partitionId, TaskEvent event) { + return true; + } + } +} diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/PartitionRequestServerHandlerTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/PartitionRequestServerHandlerTest.java index 67cf02554a759..d7941ef352df1 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/PartitionRequestServerHandlerTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/PartitionRequestServerHandlerTest.java @@ -21,15 +21,10 @@ import org.apache.flink.runtime.io.network.NetworkSequenceViewReader; import org.apache.flink.runtime.io.network.TaskEventDispatcher; import org.apache.flink.runtime.io.network.api.StopMode; -import org.apache.flink.runtime.io.network.netty.NettyMessage.ErrorResponse; -import org.apache.flink.runtime.io.network.netty.NettyMessage.PartitionRequest; import org.apache.flink.runtime.io.network.netty.NettyMessage.ResumeConsumption; -import org.apache.flink.runtime.io.network.partition.PartitionNotFoundException; import org.apache.flink.runtime.io.network.partition.PartitionTestUtils; import org.apache.flink.runtime.io.network.partition.ResultPartition; -import org.apache.flink.runtime.io.network.partition.ResultPartitionID; import org.apache.flink.runtime.io.network.partition.ResultPartitionManager; -import org.apache.flink.runtime.io.network.partition.ResultPartitionProvider; import org.apache.flink.runtime.io.network.partition.ResultPartitionType; import org.apache.flink.runtime.io.network.partition.consumer.InputChannelID; import org.apache.flink.util.TestLogger; @@ -41,46 +36,13 @@ import java.io.IOException; import java.util.concurrent.CompletableFuture; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** Tests for {@link PartitionRequestServerHandler}. */ public class PartitionRequestServerHandlerTest extends TestLogger { - /** - * Tests that {@link PartitionRequestServerHandler} responds {@link ErrorResponse} with wrapped - * {@link PartitionNotFoundException} after receiving invalid {@link PartitionRequest}. - */ - @Test - public void testResponsePartitionNotFoundException() { - final PartitionRequestServerHandler serverHandler = - new PartitionRequestServerHandler( - new ResultPartitionManager(), - new TaskEventDispatcher(), - new PartitionRequestQueue()); - final EmbeddedChannel channel = new EmbeddedChannel(serverHandler); - final ResultPartitionID partitionId = new ResultPartitionID(); - - // Write the message of partition request to server - channel.writeInbound(new PartitionRequest(partitionId, 0, new InputChannelID(), 2)); - channel.runPendingTasks(); - - // Read the response message after handling partition request - final Object msg = channel.readOutbound(); - assertThat(msg, instanceOf(ErrorResponse.class)); - - final ErrorResponse err = (ErrorResponse) msg; - assertThat(err.cause, instanceOf(PartitionNotFoundException.class)); - - final ResultPartitionID actualPartitionId = - ((PartitionNotFoundException) err.cause).getPartitionId(); - assertThat(partitionId, is(actualPartitionId)); - } - @Test public void testResumeConsumption() { final InputChannelID inputChannelID = new InputChannelID(); @@ -108,9 +70,6 @@ public void testAcknowledgeAllRecordsProcessed() throws IOException { ResultPartition resultPartition = PartitionTestUtils.createPartition(ResultPartitionType.PIPELINED_BOUNDED); - ResultPartitionProvider partitionProvider = - (partitionId, index, availabilityListener) -> - resultPartition.createSubpartitionView(index, availabilityListener); // Creates the netty network handler stack. PartitionRequestQueue partitionRequestQueue = new PartitionRequestQueue(); @@ -125,7 +84,7 @@ public void testAcknowledgeAllRecordsProcessed() throws IOException { NetworkSequenceViewReader viewReader = new CreditBasedSequenceNumberingViewReader( inputChannelID, 2, partitionRequestQueue); - viewReader.requestSubpartitionView(partitionProvider, resultPartition.getPartitionId(), 0); + viewReader.notifySubpartitionCreated(resultPartition, 0); partitionRequestQueue.notifyReaderCreated(viewReader); // Write the message to acknowledge all records are processed to server diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/ServerTransportErrorHandlingTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/ServerTransportErrorHandlingTest.java index c8faf422fc088..78bfa73e600da 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/ServerTransportErrorHandlingTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/netty/ServerTransportErrorHandlingTest.java @@ -19,8 +19,8 @@ package org.apache.flink.runtime.io.network.netty; import org.apache.flink.runtime.io.network.TaskEventDispatcher; -import org.apache.flink.runtime.io.network.netty.CancelPartitionRequestTest.InfiniteSubpartitionView; import org.apache.flink.runtime.io.network.partition.BufferAvailabilityListener; +import org.apache.flink.runtime.io.network.partition.PartitionRequestListener; import org.apache.flink.runtime.io.network.partition.ResultPartitionID; import org.apache.flink.runtime.io.network.partition.ResultPartitionManager; import org.apache.flink.runtime.io.network.partition.ResultSubpartitionView; @@ -36,6 +36,7 @@ import org.junit.Test; import org.mockito.stubbing.Answer; +import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -59,19 +60,18 @@ public void testRemoteClose() throws Exception { final ResultPartitionManager partitionManager = mock(ResultPartitionManager.class); - when(partitionManager.createSubpartitionView( + when(partitionManager.createSubpartitionViewOrRegisterListener( any(ResultPartitionID.class), anyInt(), - any(BufferAvailabilityListener.class))) + any(BufferAvailabilityListener.class), + any(PartitionRequestListener.class))) .thenAnswer( - (Answer) - invocationOnMock -> { - BufferAvailabilityListener listener = - (BufferAvailabilityListener) - invocationOnMock.getArguments()[2]; - listener.notifyDataAvailable(); - return new InfiniteSubpartitionView(outboundBuffers, sync); - }); + (Answer>) + invocationOnMock -> + Optional.of( + new CancelPartitionRequestTest + .InfiniteSubpartitionView( + outboundBuffers, sync))); NettyProtocol protocol = new NettyProtocol(partitionManager, mock(TaskEventDispatcher.class)) { diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/PartitionRequestListenerManagerTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/PartitionRequestListenerManagerTest.java new file mode 100644 index 0000000000000..40acdcb98be6f --- /dev/null +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/PartitionRequestListenerManagerTest.java @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.runtime.io.network.partition; + +import org.apache.flink.runtime.io.network.netty.NettyPartitionRequestListener; +import org.apache.flink.runtime.io.network.partition.consumer.InputChannelID; +import org.apache.flink.util.TestLogger; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** Test case for {@link PartitionRequestListenerManager}. */ +class PartitionRequestListenerManagerTest extends TestLogger { + /** Test add listener to {@link PartitionRequestListenerManager}. */ + @Test + void testAddListener() { + PartitionRequestListenerManager partitionRequestListenerManager = + new PartitionRequestListenerManager(); + assertThat(partitionRequestListenerManager.isEmpty()).isTrue(); + + List listenerList = new ArrayList<>(); + NettyPartitionRequestListener listener1 = + new NettyPartitionRequestListener( + TestingResultPartitionProvider.newBuilder().build(), + TestingSubpartitionCreatedViewReader.newBuilder() + .setReceiverId(new InputChannelID()) + .build(), + 0, + new ResultPartitionID()); + partitionRequestListenerManager.registerListener(listener1); + listenerList.add(listener1); + + NettyPartitionRequestListener listener2 = + new NettyPartitionRequestListener( + TestingResultPartitionProvider.newBuilder().build(), + TestingSubpartitionCreatedViewReader.newBuilder() + .setReceiverId(new InputChannelID()) + .build(), + 1, + new ResultPartitionID()); + partitionRequestListenerManager.registerListener(listener2); + listenerList.add(listener2); + + NettyPartitionRequestListener listener3 = + new NettyPartitionRequestListener( + TestingResultPartitionProvider.newBuilder().build(), + TestingSubpartitionCreatedViewReader.newBuilder() + .setReceiverId(new InputChannelID()) + .build(), + 2, + new ResultPartitionID()); + partitionRequestListenerManager.registerListener(listener3); + listenerList.add(listener3); + + assertThat(partitionRequestListenerManager.getPartitionRequestListeners()) + .hasSize(listenerList.size()); + assertThat(listenerList) + .containsAll(partitionRequestListenerManager.getPartitionRequestListeners()); + } + + /** + * Test remove listener from {@link PartitionRequestListenerManager} by {@link InputChannelID}. + */ + @Test + void testRemoveListener() { + PartitionRequestListenerManager partitionRequestListenerManager = + new PartitionRequestListenerManager(); + assertThat(partitionRequestListenerManager.isEmpty()).isTrue(); + + List listenerList = new ArrayList<>(); + NettyPartitionRequestListener listener1 = + new NettyPartitionRequestListener( + TestingResultPartitionProvider.newBuilder().build(), + TestingSubpartitionCreatedViewReader.newBuilder() + .setReceiverId(new InputChannelID()) + .build(), + 0, + new ResultPartitionID()); + partitionRequestListenerManager.registerListener(listener1); + + NettyPartitionRequestListener listener2 = + new NettyPartitionRequestListener( + TestingResultPartitionProvider.newBuilder().build(), + TestingSubpartitionCreatedViewReader.newBuilder() + .setReceiverId(new InputChannelID()) + .build(), + 1, + new ResultPartitionID()); + partitionRequestListenerManager.registerListener(listener2); + listenerList.add(listener2); + + NettyPartitionRequestListener listener3 = + new NettyPartitionRequestListener( + TestingResultPartitionProvider.newBuilder().build(), + TestingSubpartitionCreatedViewReader.newBuilder() + .setReceiverId(new InputChannelID()) + .build(), + 2, + new ResultPartitionID()); + partitionRequestListenerManager.registerListener(listener3); + listenerList.add(listener3); + + partitionRequestListenerManager.remove(listener1.getReceiverId()); + assertThat(partitionRequestListenerManager.getPartitionRequestListeners()) + .hasSize(listenerList.size()); + assertThat(listenerList) + .containsAll(partitionRequestListenerManager.getPartitionRequestListeners()); + } + + /** Test remove expire listeners from {@link PartitionRequestListenerManager}. */ + @Test + void testRemoveExpiration() { + PartitionRequestListenerManager partitionRequestListenerManager = + new PartitionRequestListenerManager(); + assertThat(partitionRequestListenerManager.isEmpty()).isTrue(); + + List listenerList = new ArrayList<>(); + List expireListenerList = new ArrayList<>(); + NettyPartitionRequestListener listener1 = + new NettyPartitionRequestListener( + TestingResultPartitionProvider.newBuilder().build(), + TestingSubpartitionCreatedViewReader.newBuilder() + .setReceiverId(new InputChannelID()) + .build(), + 0, + new ResultPartitionID(), + 0L); + partitionRequestListenerManager.registerListener(listener1); + expireListenerList.add(listener1); + + NettyPartitionRequestListener listener2 = + new NettyPartitionRequestListener( + TestingResultPartitionProvider.newBuilder().build(), + TestingSubpartitionCreatedViewReader.newBuilder() + .setReceiverId(new InputChannelID()) + .build(), + 1, + new ResultPartitionID(), + 0L); + partitionRequestListenerManager.registerListener(listener2); + expireListenerList.add(listener2); + + long currentTimestamp = System.currentTimeMillis(); + NettyPartitionRequestListener listener3 = + new NettyPartitionRequestListener( + TestingResultPartitionProvider.newBuilder().build(), + TestingSubpartitionCreatedViewReader.newBuilder() + .setReceiverId(new InputChannelID()) + .build(), + 2, + new ResultPartitionID(), + currentTimestamp); + partitionRequestListenerManager.registerListener(listener3); + listenerList.add(listener3); + + List removeExpireListenerList = new ArrayList<>(); + partitionRequestListenerManager.removeExpiration( + currentTimestamp, 1L, removeExpireListenerList); + + assertThat(partitionRequestListenerManager.getPartitionRequestListeners()) + .hasSize(listenerList.size()); + assertThat(listenerList) + .containsAll(partitionRequestListenerManager.getPartitionRequestListeners()); + + assertThat(removeExpireListenerList).hasSize(expireListenerList.size()); + assertThat(expireListenerList).containsAll(removeExpireListenerList); + } +} diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/PipelinedResultPartitionReleaseOnConsumptionTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/PipelinedResultPartitionReleaseOnConsumptionTest.java index ed169f9322a75..8c3c67815ac21 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/PipelinedResultPartitionReleaseOnConsumptionTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/PipelinedResultPartitionReleaseOnConsumptionTest.java @@ -32,7 +32,7 @@ public class PipelinedResultPartitionReleaseOnConsumptionTest extends TestLogger { @Test - public void testConsumptionBasedPartitionRelease() { + public void testConsumptionBasedPartitionRelease() throws IOException { final ResultPartitionManager manager = new ResultPartitionManager(); final ResultPartition partition = new ResultPartitionBuilder() @@ -70,7 +70,7 @@ public void testConsumptionBeforePartitionClose() throws IOException { } @Test - public void testMultipleReleaseCallsAreIdempotent() { + public void testMultipleReleaseCallsAreIdempotent() throws IOException { final ResultPartitionManager manager = new ResultPartitionManager(); final ResultPartition partition = new ResultPartitionBuilder() @@ -87,7 +87,7 @@ public void testMultipleReleaseCallsAreIdempotent() { } @Test - public void testReleaseAfterIdempotentCalls() { + public void testReleaseAfterIdempotentCalls() throws IOException { final ResultPartitionManager manager = new ResultPartitionManager(); final ResultPartition partition = new ResultPartitionBuilder() diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/ResultPartitionFactoryTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/ResultPartitionFactoryTest.java index 8e3b2263fbee6..d8b2a051f2dfe 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/ResultPartitionFactoryTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/ResultPartitionFactoryTest.java @@ -38,6 +38,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import java.io.IOException; import java.util.Optional; import java.util.concurrent.Executors; @@ -64,7 +65,7 @@ static void shutdown() throws Exception { } @Test - void testBoundedBlockingSubpartitionsCreated() { + void testBoundedBlockingSubpartitionsCreated() throws IOException { final BoundedBlockingResultPartition resultPartition = (BoundedBlockingResultPartition) createResultPartition(ResultPartitionType.BLOCKING); @@ -73,7 +74,7 @@ void testBoundedBlockingSubpartitionsCreated() { } @Test - void testPipelinedSubpartitionsCreated() { + void testPipelinedSubpartitionsCreated() throws IOException { final PipelinedResultPartition resultPartition = (PipelinedResultPartition) createResultPartition(ResultPartitionType.PIPELINED); assertThat(resultPartition.subpartitions) @@ -81,26 +82,26 @@ void testPipelinedSubpartitionsCreated() { } @Test - void testSortMergePartitionCreated() { + void testSortMergePartitionCreated() throws IOException { ResultPartition resultPartition = createResultPartition(ResultPartitionType.BLOCKING, 1); assertThat(resultPartition).isInstanceOf(SortMergeResultPartition.class); } @Test - void testHybridFullResultPartitionOfLegacyModeCreated() { + void testHybridFullResultPartitionOfLegacyModeCreated() throws IOException { ResultPartition resultPartition = createResultPartition(ResultPartitionType.HYBRID_FULL); assertThat(resultPartition).isInstanceOf(HsResultPartition.class); } @Test - void testHybridSelectiveResultPartitionOfLegacyModeCreated() { + void testHybridSelectiveResultPartitionOfLegacyModeCreated() throws IOException { ResultPartition resultPartition = createResultPartition(ResultPartitionType.HYBRID_SELECTIVE); assertThat(resultPartition).isInstanceOf(HsResultPartition.class); } @Test - void testHybridFullResultPartitionOfNewModeCreated() { + void testHybridFullResultPartitionOfNewModeCreated() throws IOException { ResultPartition resultPartition = createResultPartition( ResultPartitionType.HYBRID_FULL, createTieredResultPartitionFactory()); @@ -108,7 +109,7 @@ void testHybridFullResultPartitionOfNewModeCreated() { } @Test - void testHybridSelectiveResultPartitionOfNewModeCreated() { + void testHybridSelectiveResultPartitionOfNewModeCreated() throws IOException { ResultPartition resultPartition = createResultPartition( ResultPartitionType.HYBRID_SELECTIVE, createTieredResultPartitionFactory()); @@ -116,7 +117,7 @@ void testHybridSelectiveResultPartitionOfNewModeCreated() { } @Test - void testNoReleaseOnConsumptionForBoundedBlockingPartition() { + void testNoReleaseOnConsumptionForBoundedBlockingPartition() throws IOException { final ResultPartition resultPartition = createResultPartition(ResultPartitionType.BLOCKING); resultPartition.onConsumedSubpartition(0); @@ -125,7 +126,7 @@ void testNoReleaseOnConsumptionForBoundedBlockingPartition() { } @Test - void testNoReleaseOnConsumptionForSortMergePartition() { + void testNoReleaseOnConsumptionForSortMergePartition() throws IOException { final ResultPartition resultPartition = createResultPartition(ResultPartitionType.BLOCKING, 1); @@ -135,7 +136,7 @@ void testNoReleaseOnConsumptionForSortMergePartition() { } @Test - void testNoReleaseOnConsumptionForHybridFullPartitionOfLegacyMode() { + void testNoReleaseOnConsumptionForHybridFullPartitionOfLegacyMode() throws IOException { final ResultPartition resultPartition = createResultPartition(ResultPartitionType.HYBRID_FULL); @@ -145,7 +146,7 @@ void testNoReleaseOnConsumptionForHybridFullPartitionOfLegacyMode() { } @Test - void testNoReleaseOnConsumptionForHybridSelectivePartitionOfLegacyMode() { + void testNoReleaseOnConsumptionForHybridSelectivePartitionOfLegacyMode() throws IOException { final ResultPartition resultPartition = createResultPartition(ResultPartitionType.HYBRID_SELECTIVE); @@ -155,7 +156,7 @@ void testNoReleaseOnConsumptionForHybridSelectivePartitionOfLegacyMode() { } @Test - void testNoReleaseOnConsumptionForHybridFullPartitionOfNewMode() { + void testNoReleaseOnConsumptionForHybridFullPartitionOfNewMode() throws IOException { ResultPartition resultPartition = createResultPartition( ResultPartitionType.HYBRID_FULL, createTieredResultPartitionFactory()); @@ -166,7 +167,7 @@ void testNoReleaseOnConsumptionForHybridFullPartitionOfNewMode() { } @Test - void testNoReleaseOnConsumptionForHybridSelectivePartitionOfNewMode() { + void testNoReleaseOnConsumptionForHybridSelectivePartitionOfNewMode() throws IOException { ResultPartition resultPartition = createResultPartition( ResultPartitionType.HYBRID_SELECTIVE, createTieredResultPartitionFactory()); @@ -176,18 +177,19 @@ void testNoReleaseOnConsumptionForHybridSelectivePartitionOfNewMode() { assertThat(resultPartition.isReleased()).isFalse(); } - private static ResultPartition createResultPartition(ResultPartitionType partitionType) { + private static ResultPartition createResultPartition(ResultPartitionType partitionType) + throws IOException { return createResultPartition(partitionType, Integer.MAX_VALUE, false, Optional.empty()); } private static ResultPartition createResultPartition( - ResultPartitionType partitionType, - Optional tieredStorage) { + ResultPartitionType partitionType, Optional tieredStorage) + throws IOException { return createResultPartition(partitionType, Integer.MAX_VALUE, false, tieredStorage); } private static ResultPartition createResultPartition( - ResultPartitionType partitionType, int sortShuffleMinParallelism) { + ResultPartitionType partitionType, int sortShuffleMinParallelism) throws IOException { return createResultPartition( partitionType, sortShuffleMinParallelism, false, Optional.empty()); } @@ -196,7 +198,8 @@ private static ResultPartition createResultPartition( ResultPartitionType partitionType, int sortShuffleMinParallelism, boolean isBroadcast, - Optional tieredStorage) { + Optional tieredStorage) + throws IOException { final ResultPartitionManager manager = new ResultPartitionManager(); final ResultPartitionFactory factory = diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/ResultPartitionManagerTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/ResultPartitionManagerTest.java index b9e1dc4a6ae2b..04171937552ac 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/ResultPartitionManagerTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/ResultPartitionManagerTest.java @@ -18,15 +18,22 @@ package org.apache.flink.runtime.io.network.partition; +import org.apache.flink.runtime.io.network.netty.NettyPartitionRequestListener; +import org.apache.flink.runtime.io.network.partition.consumer.InputChannelID; import org.apache.flink.util.TestLogger; +import org.apache.flink.util.concurrent.ManuallyTriggeredScheduledExecutor; -import org.junit.Test; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import static org.apache.flink.runtime.io.network.partition.PartitionTestUtils.createPartition; import static org.apache.flink.runtime.io.network.partition.PartitionTestUtils.verifyCreateSubpartitionViewThrowsException; +import static org.assertj.core.api.Assertions.assertThat; /** Tests for {@link ResultPartitionManager}. */ -public class ResultPartitionManagerTest extends TestLogger { +class ResultPartitionManagerTest extends TestLogger { /** * Tests that {@link ResultPartitionManager#createSubpartitionView(ResultPartitionID, int, @@ -34,7 +41,7 @@ public class ResultPartitionManagerTest extends TestLogger { * was not registered before. */ @Test - public void testThrowPartitionNotFoundException() throws Exception { + void testThrowPartitionNotFoundException() { final ResultPartitionManager partitionManager = new ResultPartitionManager(); final ResultPartition partition = createPartition(); @@ -46,7 +53,7 @@ public void testThrowPartitionNotFoundException() throws Exception { * BufferAvailabilityListener)} successful if this partition was already registered before. */ @Test - public void testCreateViewForRegisteredPartition() throws Exception { + void testCreateViewForRegisteredPartition() throws Exception { final ResultPartitionManager partitionManager = new ResultPartitionManager(); final ResultPartition partition = createPartition(); @@ -55,13 +62,91 @@ public void testCreateViewForRegisteredPartition() throws Exception { partition.getPartitionId(), 0, new NoOpBufferAvailablityListener()); } + /** + * {@link ResultPartitionManager} creates subpartition view reader after the given partition is + * registered. + */ + @Test + void testCreateSubpartitionViewAfterRegisteredPartition() throws Exception { + final ResultPartitionManager partitionManager = new ResultPartitionManager(); + final ResultPartition partition = createPartition(); + + assertThat(partitionManager.getListenerManagers().isEmpty()).isTrue(); + + partitionManager.registerResultPartition(partition); + PartitionRequestListener partitionRequestListener = + TestingPartitionRequestListener.newBuilder().build(); + assertThat( + partitionManager.createSubpartitionViewOrRegisterListener( + partition.getPartitionId(), + 0, + new NoOpBufferAvailablityListener(), + partitionRequestListener)) + .isPresent(); + assertThat(partitionManager.getListenerManagers().isEmpty()).isTrue(); + } + + /** + * The {@link ResultPartitionManager} registers {@link PartitionRequestListener} before specify + * {@link ResultPartition} is registered. When the {@link ResultPartition} is registered, the + * {@link ResultPartitionManager} will find the listener and create partition view reader. an + */ + @Test + void testRegisterPartitionListenerBeforeRegisteredPartition() throws Exception { + final ResultPartitionManager partitionManager = new ResultPartitionManager(); + final ResultPartition partition = createPartition(); + + assertThat(partitionManager.getListenerManagers().isEmpty()).isTrue(); + + final CompletableFuture notifySubpartitionCreatedFuture = + new CompletableFuture<>(); + PartitionRequestListener partitionRequestListener = + TestingPartitionRequestListener.newBuilder() + .setResultPartitionId(partition.getPartitionId()) + .setNetworkSequenceViewReader( + TestingSubpartitionCreatedViewReader.newBuilder() + .setNotifySubpartitionCreatedConsumer( + tuple -> + notifySubpartitionCreatedFuture.complete( + tuple.f0)) + .build()) + .build(); + assertThat( + partitionManager.createSubpartitionViewOrRegisterListener( + partition.getPartitionId(), + 0, + new NoOpBufferAvailablityListener(), + partitionRequestListener)) + .isNotPresent(); + assertThat(partitionManager.getListenerManagers()).hasSize(1); + + // Check if the partition request listener is registered. + PartitionRequestListenerManager listenerManager = + partitionManager.getListenerManagers().get(partition.getPartitionId()); + assertThat(listenerManager).isNotNull(); + assertThat(listenerManager.isEmpty()).isFalse(); + assertThat(listenerManager.getPartitionRequestListeners()).hasSize(1); + PartitionRequestListener listener = + listenerManager.getPartitionRequestListeners().iterator().next(); + assertThat(listener.getResultPartitionId()).isEqualTo(partition.getPartitionId()); + assertThat(notifySubpartitionCreatedFuture).isNotDone(); + + partitionManager.registerResultPartition(partition); + + // Check if the listener is notified. + ResultPartition notifyPartition = + notifySubpartitionCreatedFuture.get(10, TimeUnit.MILLISECONDS); + assertThat(partition.getPartitionId()).isEqualTo(notifyPartition.getPartitionId()); + assertThat(partitionManager.getListenerManagers().isEmpty()).isTrue(); + } + /** * Tests {@link ResultPartitionManager#createSubpartitionView(ResultPartitionID, int, * BufferAvailabilityListener)} would throw a {@link PartitionNotFoundException} if this * partition was already released before. */ @Test - public void testCreateViewForReleasedPartition() throws Exception { + void testCreateViewForReleasedPartition() throws Exception { final ResultPartitionManager partitionManager = new ResultPartitionManager(); final ResultPartition partition = createPartition(); @@ -70,4 +155,55 @@ public void testCreateViewForReleasedPartition() throws Exception { verifyCreateSubpartitionViewThrowsException(partitionManager, partition.getPartitionId()); } + + /** Test notifier timeout in {@link ResultPartitionManager}. */ + @Test + void testCreateViewReaderForNotifierTimeout() throws Exception { + ManuallyTriggeredScheduledExecutor scheduledExecutor = + new ManuallyTriggeredScheduledExecutor(); + final ResultPartitionManager partitionManager = + new ResultPartitionManager(1000000, scheduledExecutor); + final ResultPartition partition1 = createPartition(); + final ResultPartition partition2 = createPartition(); + + CompletableFuture timeoutFuture1 = new CompletableFuture<>(); + CompletableFuture timeoutFuture2 = new CompletableFuture<>(); + partitionManager.createSubpartitionViewOrRegisterListener( + partition1.getPartitionId(), + 0, + new NoOpBufferAvailablityListener(), + new NettyPartitionRequestListener( + TestingResultPartitionProvider.newBuilder().build(), + TestingSubpartitionCreatedViewReader.newBuilder() + .setReceiverId(new InputChannelID()) + .setPartitionRequestListenerTimeoutConsumer( + timeoutFuture1::complete) + .build(), + 0, + partition1.getPartitionId(), + 0L)); + partitionManager.createSubpartitionViewOrRegisterListener( + partition2.getPartitionId(), + 0, + new NoOpBufferAvailablityListener(), + new NettyPartitionRequestListener( + TestingResultPartitionProvider.newBuilder().build(), + TestingSubpartitionCreatedViewReader.newBuilder() + .setReceiverId(new InputChannelID()) + .setPartitionRequestListenerTimeoutConsumer( + timeoutFuture2::complete) + .build(), + 0, + partition2.getPartitionId())); + scheduledExecutor.triggerScheduledTasks(); + + assertThat(timeoutFuture1.isDone()).isTrue(); + assertThat(partition1.getPartitionId()) + .isEqualTo(timeoutFuture1.get().getResultPartitionId()); + assertThat(timeoutFuture2.isDone()).isFalse(); + assertThat(partitionManager.getListenerManagers().get(partition1.getPartitionId())) + .isNull(); + assertThat(partitionManager.getListenerManagers().get(partition2.getPartitionId())) + .isNotNull(); + } } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/TestingPartitionRequestListener.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/TestingPartitionRequestListener.java new file mode 100644 index 0000000000000..e8341e09f7d3e --- /dev/null +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/TestingPartitionRequestListener.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.runtime.io.network.partition; + +import org.apache.flink.runtime.io.network.NetworkSequenceViewReader; +import org.apache.flink.runtime.io.network.partition.consumer.InputChannelID; + +import java.io.IOException; + +/** {@link PartitionRequestListener} implementation for testing purposes. */ +public class TestingPartitionRequestListener implements PartitionRequestListener { + private final long createTimestamp; + private final ResultPartitionID resultPartitionId; + private final InputChannelID inputChannelId; + private final NetworkSequenceViewReader reader; + + private TestingPartitionRequestListener( + ResultPartitionID resultPartitionId, + InputChannelID inputChannelId, + NetworkSequenceViewReader reader) { + this.createTimestamp = System.currentTimeMillis(); + this.resultPartitionId = resultPartitionId; + this.inputChannelId = inputChannelId; + this.reader = reader; + } + + @Override + public long getCreateTimestamp() { + return createTimestamp; + } + + @Override + public ResultPartitionID getResultPartitionId() { + return resultPartitionId; + } + + @Override + public NetworkSequenceViewReader getViewReader() { + return reader; + } + + @Override + public InputChannelID getReceiverId() { + return inputChannelId; + } + + @Override + public void notifyPartitionCreated(ResultPartition partition) throws IOException { + reader.notifySubpartitionCreated(partition, 0); + } + + @Override + public void notifyPartitionCreatedTimeout() {} + + @Override + public void releaseListener() {} + + public static TestingPartitionRequestListenerBuilder newBuilder() { + return new TestingPartitionRequestListenerBuilder(); + } + + /** Factory for {@link TestingPartitionRequestListener}. */ + public static class TestingPartitionRequestListenerBuilder { + private ResultPartitionID resultPartitionId = new ResultPartitionID(); + private InputChannelID inputChannelId = new InputChannelID(); + private NetworkSequenceViewReader reader = null; + + public TestingPartitionRequestListenerBuilder setResultPartitionId( + ResultPartitionID resultPartitionId) { + this.resultPartitionId = resultPartitionId; + return this; + } + + public TestingPartitionRequestListenerBuilder setInputChannelId( + InputChannelID inputChannelId) { + this.inputChannelId = inputChannelId; + return this; + } + + public TestingPartitionRequestListenerBuilder setNetworkSequenceViewReader( + NetworkSequenceViewReader reader) { + this.reader = reader; + return this; + } + + public TestingPartitionRequestListener build() { + return new TestingPartitionRequestListener(resultPartitionId, inputChannelId, reader); + } + } +} diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/TestingResultPartition.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/TestingResultPartition.java new file mode 100644 index 0000000000000..d29491739e346 --- /dev/null +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/TestingResultPartition.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.runtime.io.network.partition; + +import org.apache.flink.runtime.checkpoint.CheckpointException; +import org.apache.flink.runtime.event.AbstractEvent; + +import java.io.IOException; +import java.nio.ByteBuffer; + +/** {@link ResultPartition} class for testing purposes. */ +public class TestingResultPartition extends ResultPartition { + private final CreateSubpartitionView createSubpartitionViewFunction; + + public TestingResultPartition( + ResultPartitionID partitionId, + ResultPartitionManager partitionManager, + CreateSubpartitionView createSubpartitionViewFunction) { + super( + "test", + 0, + partitionId, + ResultPartitionType.PIPELINED, + 0, + 0, + partitionManager, + null, + null); + this.createSubpartitionViewFunction = createSubpartitionViewFunction; + } + + @Override + public void emitRecord(ByteBuffer record, int targetSubpartition) throws IOException {} + + @Override + public void broadcastRecord(ByteBuffer record) throws IOException {} + + @Override + public void broadcastEvent(AbstractEvent event, boolean isPriorityEvent) throws IOException {} + + @Override + public void alignedBarrierTimeout(long checkpointId) throws IOException {} + + @Override + public void abortCheckpoint(long checkpointId, CheckpointException cause) {} + + @Override + public ResultSubpartitionView createSubpartitionView( + int index, BufferAvailabilityListener availabilityListener) throws IOException { + return createSubpartitionViewFunction.createSubpartitionView(index, availabilityListener); + } + + @Override + public void flushAll() {} + + @Override + public void flush(int subpartitionIndex) {} + + @Override + protected void setupInternal() throws IOException {} + + @Override + public int getNumberOfQueuedBuffers() { + return 0; + } + + @Override + public long getSizeOfQueuedBuffersUnsafe() { + return 0; + } + + @Override + public int getNumberOfQueuedBuffers(int targetSubpartition) { + return 0; + } + + @Override + protected void releaseInternal() {} + + public static TestingResultPartitionBuilder newBuilder() { + return new TestingResultPartitionBuilder(); + } + + /** Factory for {@link TestingResultPartition}. */ + public static class TestingResultPartitionBuilder { + private ResultPartitionID resultPartitionId = new ResultPartitionID(); + private ResultPartitionManager resultPartitionManager = new ResultPartitionManager(); + private CreateSubpartitionView createSubpartitionViewFunction = + (index, availabilityListener) -> null; + + public TestingResultPartitionBuilder setCreateSubpartitionViewFunction( + CreateSubpartitionView createSubpartitionViewFunction) { + this.createSubpartitionViewFunction = createSubpartitionViewFunction; + return this; + } + + public TestingResultPartitionBuilder setResultPartitionID( + ResultPartitionID resultPartitionId) { + this.resultPartitionId = resultPartitionId; + return this; + } + + public TestingResultPartitionBuilder setResultPartitionManager( + ResultPartitionManager resultPartitionManager) { + this.resultPartitionManager = resultPartitionManager; + return this; + } + + public TestingResultPartition build() { + return new TestingResultPartition( + resultPartitionId, resultPartitionManager, createSubpartitionViewFunction); + } + } + + /** Testing interface for createSubpartitionView. */ + public interface CreateSubpartitionView { + ResultSubpartitionView createSubpartitionView( + int index, BufferAvailabilityListener availabilityListener) throws IOException; + } +} diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/TestingResultPartitionProvider.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/TestingResultPartitionProvider.java new file mode 100644 index 0000000000000..d5f10ad599f39 --- /dev/null +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/TestingResultPartitionProvider.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.runtime.io.network.partition; + +import java.io.IOException; +import java.util.Optional; + +/** {@link ResultPartitionProvider} implementation for testing purposes. */ +public class TestingResultPartitionProvider implements ResultPartitionProvider { + private final CreateSubpartitionView createSubpartitionViewFunction; + private final CreateSubpartitionViewOrRegisterListener + createSubpartitionViewOrRegisterListenerFunction; + private final ReleasePartitionRequestListener releasePartitionRequestListenerConsumer; + + public TestingResultPartitionProvider( + CreateSubpartitionView createSubpartitionViewFunction, + CreateSubpartitionViewOrRegisterListener + createSubpartitionViewOrRegisterListenerFunction, + ReleasePartitionRequestListener releasePartitionRequestListenerConsumer) { + this.createSubpartitionViewFunction = createSubpartitionViewFunction; + this.createSubpartitionViewOrRegisterListenerFunction = + createSubpartitionViewOrRegisterListenerFunction; + this.releasePartitionRequestListenerConsumer = releasePartitionRequestListenerConsumer; + } + + @Override + public ResultSubpartitionView createSubpartitionView( + ResultPartitionID partitionId, + int index, + BufferAvailabilityListener availabilityListener) + throws IOException { + return createSubpartitionViewFunction.createSubpartitionView( + partitionId, index, availabilityListener); + } + + @Override + public Optional createSubpartitionViewOrRegisterListener( + ResultPartitionID partitionId, + int index, + BufferAvailabilityListener availabilityListener, + PartitionRequestListener notifier) + throws IOException { + return createSubpartitionViewOrRegisterListenerFunction + .createSubpartitionViewOrRegisterListener( + partitionId, index, availabilityListener, notifier); + } + + @Override + public void releasePartitionRequestListener(PartitionRequestListener notifier) { + releasePartitionRequestListenerConsumer.releasePartitionRequestListener(notifier); + } + + public static TestingResultPartitionProviderBuilder newBuilder() { + return new TestingResultPartitionProviderBuilder(); + } + + /** Factory for {@link TestingResultPartitionProvider}. */ + public static class TestingResultPartitionProviderBuilder { + private CreateSubpartitionView createSubpartitionViewFunction = + (resultPartitionID, index, availabilityListener) -> null; + private CreateSubpartitionViewOrRegisterListener + createSubpartitionViewOrRegisterListenerFunction = + (partitionId, index, availabilityListener, partitionRequestListener) -> + Optional.empty(); + private ReleasePartitionRequestListener releasePartitionRequestListenerConsumer = + listener -> {}; + + public TestingResultPartitionProviderBuilder setCreateSubpartitionViewFunction( + CreateSubpartitionView createSubpartitionViewFunction) { + this.createSubpartitionViewFunction = createSubpartitionViewFunction; + return this; + } + + public TestingResultPartitionProviderBuilder setCreateSubpartitionViewOrNotifyFunction( + CreateSubpartitionViewOrRegisterListener + createSubpartitionViewOrRegisterListenerFunction) { + this.createSubpartitionViewOrRegisterListenerFunction = + createSubpartitionViewOrRegisterListenerFunction; + return this; + } + + public TestingResultPartitionProviderBuilder setReleasePartitionRequestListenerConsumer( + ReleasePartitionRequestListener releasePartitionRequestListenerConsumer) { + this.releasePartitionRequestListenerConsumer = releasePartitionRequestListenerConsumer; + return this; + } + + public TestingResultPartitionProvider build() { + return new TestingResultPartitionProvider( + createSubpartitionViewFunction, + createSubpartitionViewOrRegisterListenerFunction, + releasePartitionRequestListenerConsumer); + } + } + + /** Testing interface for createSubpartitionView. */ + public interface CreateSubpartitionView { + ResultSubpartitionView createSubpartitionView( + ResultPartitionID partitionId, + int index, + BufferAvailabilityListener availabilityListener) + throws IOException; + } + + /** Testing interface for createSubpartitionViewOrRegisterListener. */ + public interface CreateSubpartitionViewOrRegisterListener { + Optional createSubpartitionViewOrRegisterListener( + ResultPartitionID partitionId, + int index, + BufferAvailabilityListener availabilityListener, + PartitionRequestListener partitionRequestListener) + throws IOException; + } + + /** Testing interface for releasePartitionRequestListener. */ + public interface ReleasePartitionRequestListener { + void releasePartitionRequestListener(PartitionRequestListener listener); + } +} diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/TestingSubpartitionCreatedViewReader.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/TestingSubpartitionCreatedViewReader.java new file mode 100644 index 0000000000000..bc5334f642023 --- /dev/null +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/TestingSubpartitionCreatedViewReader.java @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.runtime.io.network.partition; + +import org.apache.flink.api.java.tuple.Tuple2; +import org.apache.flink.runtime.io.network.NetworkSequenceViewReader; +import org.apache.flink.runtime.io.network.partition.consumer.InputChannel; +import org.apache.flink.runtime.io.network.partition.consumer.InputChannelID; + +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.function.Consumer; + +/** Testing view reader for partition request notifier. */ +public class TestingSubpartitionCreatedViewReader implements NetworkSequenceViewReader { + private final InputChannelID receiverId; + private final Consumer partitionRequestListenerTimeoutConsumer; + private final Consumer> notifySubpartitionCreatedConsumer; + + private TestingSubpartitionCreatedViewReader( + InputChannelID receiverId, + Consumer partitionRequestListenerTimeoutConsumer, + Consumer> notifySubpartitionCreatedConsumer) { + this.receiverId = receiverId; + this.partitionRequestListenerTimeoutConsumer = partitionRequestListenerTimeoutConsumer; + this.notifySubpartitionCreatedConsumer = notifySubpartitionCreatedConsumer; + } + + @Override + public void notifySubpartitionCreated(ResultPartition partition, int subPartitionIndex) + throws IOException { + notifySubpartitionCreatedConsumer.accept(Tuple2.of(partition, subPartitionIndex)); + } + + @Override + public void requestSubpartitionViewOrRegisterListener( + ResultPartitionProvider partitionProvider, + ResultPartitionID resultPartitionId, + int subPartitionIndex) + throws IOException { + throw new UnsupportedOperationException(); + } + + @Nullable + @Override + public InputChannel.BufferAndAvailability getNextBuffer() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean needAnnounceBacklog() { + throw new UnsupportedOperationException(); + } + + @Override + public void addCredit(int creditDeltas) { + throw new UnsupportedOperationException(); + } + + @Override + public void notifyRequiredSegmentId(int segmentId) { + throw new UnsupportedOperationException(); + } + + @Override + public void resumeConsumption() { + throw new UnsupportedOperationException(); + } + + @Override + public void acknowledgeAllRecordsProcessed() { + throw new UnsupportedOperationException(); + } + + @Override + public ResultSubpartitionView.AvailabilityWithBacklog getAvailabilityAndBacklog() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isRegisteredAsAvailable() { + throw new UnsupportedOperationException(); + } + + @Override + public void setRegisteredAsAvailable(boolean isRegisteredAvailable) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isReleased() { + throw new UnsupportedOperationException(); + } + + @Override + public void releaseAllResources() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public Throwable getFailureCause() { + throw new UnsupportedOperationException(); + } + + @Override + public InputChannelID getReceiverId() { + return receiverId; + } + + @Override + public void notifyNewBufferSize(int newBufferSize) { + throw new UnsupportedOperationException(); + } + + @Override + public void notifyPartitionRequestTimeout(PartitionRequestListener partitionRequestListener) { + partitionRequestListenerTimeoutConsumer.accept(partitionRequestListener); + } + + public static TestingSubpartitionCreatedViewReaderBuilder newBuilder() { + return new TestingSubpartitionCreatedViewReaderBuilder(); + } + + /** Builder for {@link TestingSubpartitionCreatedViewReader}. */ + public static class TestingSubpartitionCreatedViewReaderBuilder { + private InputChannelID receiverId; + private Consumer partitionRequestListenerTimeoutConsumer = + listener -> {}; + private Consumer> notifySubpartitionCreatedConsumer = + tuple -> {}; + + public TestingSubpartitionCreatedViewReaderBuilder setReceiverId( + InputChannelID receiverId) { + this.receiverId = receiverId; + return this; + } + + public TestingSubpartitionCreatedViewReaderBuilder + setPartitionRequestListenerTimeoutConsumer( + Consumer + partitionRequestListenerTimeoutConsumer) { + this.partitionRequestListenerTimeoutConsumer = partitionRequestListenerTimeoutConsumer; + return this; + } + + public TestingSubpartitionCreatedViewReaderBuilder setNotifySubpartitionCreatedConsumer( + Consumer> notifySubpartitionCreatedConsumer) { + this.notifySubpartitionCreatedConsumer = notifySubpartitionCreatedConsumer; + return this; + } + + public TestingSubpartitionCreatedViewReader build() { + return new TestingSubpartitionCreatedViewReader( + receiverId, + partitionRequestListenerTimeoutConsumer, + notifySubpartitionCreatedConsumer); + } + } +} diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/consumer/InputChannelBuilder.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/consumer/InputChannelBuilder.java index 622a1392a0394..3f85ff0650c11 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/consumer/InputChannelBuilder.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/consumer/InputChannelBuilder.java @@ -52,6 +52,7 @@ public class InputChannelBuilder { private ConnectionManager connectionManager = new TestingConnectionManager(); private int initialBackoff = 0; private int maxBackoff = 0; + private int partitionRequestListenerTimeout = 0; private int networkBuffersPerChannel = 2; private InputChannelMetrics metrics = InputChannelTestUtils.newUnregisteredInputChannelMetrics(); @@ -100,6 +101,12 @@ public InputChannelBuilder setMaxBackoff(int maxBackoff) { return this; } + public InputChannelBuilder setPartitionRequestListenerTimeout( + int partitionRequestListenerTimeout) { + this.partitionRequestListenerTimeout = partitionRequestListenerTimeout; + return this; + } + public InputChannelBuilder setNetworkBuffersPerChannel(int networkBuffersPerChannel) { this.networkBuffersPerChannel = networkBuffersPerChannel; return this; @@ -136,6 +143,7 @@ UnknownInputChannel buildUnknownChannel(SingleInputGate inputGate) { connectionManager, initialBackoff, maxBackoff, + partitionRequestListenerTimeout, networkBuffersPerChannel, metrics); channel.setChannelStateWriter(stateWriter); @@ -167,6 +175,7 @@ public RemoteInputChannel buildRemoteChannel(SingleInputGate inputGate) { connectionManager, initialBackoff, maxBackoff, + partitionRequestListenerTimeout, networkBuffersPerChannel, metrics.getNumBytesInRemoteCounter(), metrics.getNumBuffersInRemoteCounter(), @@ -201,6 +210,7 @@ public RemoteRecoveredInputChannel buildRemoteRecoveredChannel(SingleInputGate i connectionManager, initialBackoff, maxBackoff, + partitionRequestListenerTimeout, networkBuffersPerChannel, metrics); channel.setChannelStateWriter(stateWriter); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/consumer/RemoteInputChannelTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/consumer/RemoteInputChannelTest.java index 8b2e42e5544a9..65d0d2bdfe8b4 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/consumer/RemoteInputChannelTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/consumer/RemoteInputChannelTest.java @@ -356,9 +356,10 @@ public void testRetriggerWithoutPartitionRequest() throws Exception { } @Test - public void testPartitionRequestExponentialBackoff() throws Exception { - // Start with initial backoff, then keep doubling, and cap at max. - int[] expectedDelays = {500, 1000, 2000, 3000}; + public void testPartitionRequestLinearBackoff() throws Exception { + // Start with initial backoff, then keep adding the partition request timeout, and cap at + // max. + int[] expectedDelays = {500, 1000, 1500, 2000}; // Setup SingleInputGate inputGate = createSingleInputGate(1); @@ -366,17 +367,16 @@ public void testPartitionRequestExponentialBackoff() throws Exception { TestVerifyPartitionRequestClient client = new TestVerifyPartitionRequestClient(); ConnectionManager connectionManager = new TestVerifyConnectionManager(client); RemoteInputChannel ch = - createRemoteInputChannel(inputGate, connectionManager, partitionId, 500, 3000); + createRemoteInputChannel(inputGate, connectionManager, partitionId, 500, 1000); // Initial request ch.requestSubpartition(); client.verifyResult(partitionId, 0, 0); - // Request subpartition and verify that the actual requests are delayed. + // Request subpartition and verify that the actual back off. for (int expected : expectedDelays) { ch.retriggerSubpartitionRequest(); - - client.verifyResult(partitionId, 0, expected); + assertEquals(expected, ch.getCurrentBackoff()); } // Exception after backoff is greater than the maximum backoff. @@ -402,9 +402,9 @@ public void testPartitionRequestSingleBackoff() throws Exception { ch.requestSubpartition(); client.verifyResult(partitionId, 0, 0); - // Initial delay for second request + // The current backoff for second request ch.retriggerSubpartitionRequest(); - client.verifyResult(partitionId, 0, 500); + assertEquals(500, ch.getCurrentBackoff()); // Exception after backoff is greater than the maximum backoff. try { @@ -1744,11 +1744,11 @@ private RemoteInputChannel createRemoteInputChannel( private RemoteInputChannel createRemoteInputChannel( SingleInputGate inputGate, int consumedSubpartitionIndex, - int initialBackoff, + int partitionRequestTimeout, int maxBackoff) { return InputChannelBuilder.newBuilder() .setConsumedSubpartitionIndex(consumedSubpartitionIndex) - .setInitialBackoff(initialBackoff) + .setPartitionRequestListenerTimeout(partitionRequestTimeout) .setMaxBackoff(maxBackoff) .buildRemoteChannel(inputGate); } @@ -1757,10 +1757,10 @@ private RemoteInputChannel createRemoteInputChannel( SingleInputGate inputGate, ConnectionManager connectionManager, ResultPartitionID partitionId, - int initialBackoff, + int partitionRequestTimeout, int maxBackoff) { return InputChannelBuilder.newBuilder() - .setInitialBackoff(initialBackoff) + .setPartitionRequestListenerTimeout(partitionRequestTimeout) .setMaxBackoff(maxBackoff) .setPartitionId(partitionId) .setConnectionManager(connectionManager) diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/consumer/SingleInputGateTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/consumer/SingleInputGateTest.java index 0f03b3d223d83..0e452f0dde770 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/consumer/SingleInputGateTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/consumer/SingleInputGateTest.java @@ -635,11 +635,13 @@ void testRequestBackoffConfiguration() throws Exception { }; int initialBackoff = 137; + int partitionRequestTimeout = 600; int maxBackoff = 1001; final NettyShuffleEnvironment netEnv = new NettyShuffleEnvironmentBuilder() .setPartitionRequestInitialBackoff(initialBackoff) + .setPartitionRequestTimeout(partitionRequestTimeout) .setPartitionRequestMaxBackoff(maxBackoff) .build(); @@ -675,14 +677,10 @@ void testRequestBackoffConfiguration() throws Exception { InputChannel localChannel = channelMap.get(createSubpartitionInfo(partitionIds[0])); assertThat(localChannel.getClass()).isEqualTo(LocalInputChannel.class); - InputChannel remoteChannel = channelMap.get(createSubpartitionInfo(partitionIds[1])); - assertThat(remoteChannel.getClass()).isEqualTo(RemoteInputChannel.class); - InputChannel unknownChannel = channelMap.get(createSubpartitionInfo(partitionIds[2])); assertThat(unknownChannel.getClass()).isEqualTo(UnknownInputChannel.class); - InputChannel[] channels = - new InputChannel[] {localChannel, remoteChannel, unknownChannel}; + InputChannel[] channels = new InputChannel[] {localChannel, unknownChannel}; for (InputChannel ch : channels) { assertThat(ch.getCurrentBackoff()).isEqualTo(0); @@ -700,6 +698,22 @@ void testRequestBackoffConfiguration() throws Exception { assertThat(ch.increaseBackoff()).isFalse(); } + + InputChannel remoteChannel = channelMap.get(createSubpartitionInfo(partitionIds[1])); + assertThat(remoteChannel.getClass()).isEqualTo(RemoteInputChannel.class); + + assertThat(remoteChannel.getCurrentBackoff()).isEqualTo(0); + + assertThat(remoteChannel.increaseBackoff()).isTrue(); + assertThat(remoteChannel.getCurrentBackoff()).isEqualTo(partitionRequestTimeout); + + assertThat(remoteChannel.increaseBackoff()).isTrue(); + assertThat(remoteChannel.getCurrentBackoff()).isEqualTo(partitionRequestTimeout * 2); + + assertThat(remoteChannel.increaseBackoff()).isTrue(); + assertThat(remoteChannel.getCurrentBackoff()).isEqualTo(partitionRequestTimeout * 3); + + assertThat(remoteChannel.increaseBackoff()).isFalse(); } } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/hybrid/TestingSpillingInfoProvider.java b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/hybrid/TestingSpillingInfoProvider.java index 9980f4176fa52..53b9df0f55a3d 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/hybrid/TestingSpillingInfoProvider.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/hybrid/TestingSpillingInfoProvider.java @@ -198,14 +198,16 @@ public Builder setGetNumSubpartitionsSupplier( public Builder addSubpartitionBuffers( int subpartitionId, List subpartitionBuffers) { - allBuffers.computeIfAbsent(subpartitionId, ArrayList::new).addAll(subpartitionBuffers); + allBuffers + .computeIfAbsent(subpartitionId, k -> new ArrayList<>()) + .addAll(subpartitionBuffers); return this; } public Builder addSpillBuffers( int subpartitionId, List subpartitionSpillBufferIndexes) { spillBufferIndexes - .computeIfAbsent(subpartitionId, HashSet::new) + .computeIfAbsent(subpartitionId, k -> new HashSet<>()) .addAll(subpartitionSpillBufferIndexes); return this; } @@ -213,7 +215,7 @@ public Builder addSpillBuffers( public Builder addConsumedBuffers( int subpartitionId, List subpartitionConsumedBufferIndexes) { consumedBufferIndexes - .computeIfAbsent(subpartitionId, HashSet::new) + .computeIfAbsent(subpartitionId, k -> new HashSet<>()) .addAll(subpartitionConsumedBufferIndexes); return this; } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/metrics/groups/MetricsGroupTestUtils.java b/flink-runtime/src/test/java/org/apache/flink/runtime/metrics/groups/MetricsGroupTestUtils.java new file mode 100644 index 0000000000000..fae09a39e9b23 --- /dev/null +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/metrics/groups/MetricsGroupTestUtils.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.runtime.metrics.groups; + +import org.apache.flink.annotation.VisibleForTesting; +import org.apache.flink.metrics.MetricGroup; +import org.apache.flink.metrics.groups.OperatorIOMetricGroup; +import org.apache.flink.metrics.groups.UnregisteredMetricsGroup; + +/** Util class to create metric groups for SinkV2 tests. */ +public class MetricsGroupTestUtils { + + @VisibleForTesting + public static InternalSinkWriterMetricGroup mockWriterMetricGroup() { + return new InternalSinkWriterMetricGroup( + new UnregisteredMetricsGroup(), + UnregisteredMetricsGroup.createOperatorIOMetricGroup()); + } + + @VisibleForTesting + public static InternalSinkWriterMetricGroup mockWriterMetricGroup(MetricGroup metricGroup) { + return new InternalSinkWriterMetricGroup( + metricGroup, UnregisteredMetricsGroup.createOperatorIOMetricGroup()); + } + + @VisibleForTesting + public static InternalSinkWriterMetricGroup mockWriterMetricGroup( + MetricGroup metricGroup, OperatorIOMetricGroup operatorIOMetricGroup) { + return new InternalSinkWriterMetricGroup(metricGroup, operatorIOMetricGroup); + } +} diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/metrics/util/MetricUtilsTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/metrics/util/MetricUtilsTest.java index e0c70638ed9e9..c98637fc37cde 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/metrics/util/MetricUtilsTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/metrics/util/MetricUtilsTest.java @@ -21,6 +21,7 @@ import org.apache.flink.configuration.Configuration; import org.apache.flink.configuration.MetricOptions; import org.apache.flink.metrics.Gauge; +import org.apache.flink.metrics.MeterView; import org.apache.flink.metrics.MetricGroup; import org.apache.flink.runtime.clusterframework.types.AllocationID; import org.apache.flink.runtime.memory.MemoryAllocationException; @@ -42,10 +43,15 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import javax.management.ObjectName; + +import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; import static org.apache.flink.runtime.metrics.util.MetricUtils.METRIC_GROUP_FLINK; @@ -110,7 +116,6 @@ void testMetaspaceCompleteness() { assertThat(hasMetaspaceMemoryPool()) .withFailMessage("Requires JVM with Metaspace memory pool") .isTrue(); - final InterceptingOperatorMetricGroup metaspaceMetrics = new InterceptingOperatorMetricGroup() { @Override @@ -126,6 +131,42 @@ public MetricGroup addGroup(String name) { assertThat(metaspaceMetrics.get(MetricNames.MEMORY_MAX)).isNotNull(); } + @Test + public void testGcMetricCompleteness() { + Map addedGroups = new HashMap<>(); + InterceptingOperatorMetricGroup gcGroup = + new InterceptingOperatorMetricGroup() { + @Override + public MetricGroup addGroup(String name) { + return addedGroups.computeIfAbsent( + name, k -> new InterceptingOperatorMetricGroup()); + } + }; + + List garbageCollectors = new ArrayList<>(); + garbageCollectors.add(new TestGcBean("gc1", 100, 500)); + garbageCollectors.add(new TestGcBean("gc2", 50, 250)); + + MetricUtils.instantiateGarbageCollectorMetrics(gcGroup, garbageCollectors); + assertThat(addedGroups).containsOnlyKeys("gc1", "gc2", "All"); + + // Make sure individual collector metrics are correct + validateCollectorMetric(addedGroups.get("gc1"), 100, 500L); + validateCollectorMetric(addedGroups.get("gc2"), 50L, 250L); + + // Make sure all/total collector metrics are correct + validateCollectorMetric(addedGroups.get("All"), 150L, 750L); + } + + private static void validateCollectorMetric( + InterceptingOperatorMetricGroup group, long count, long time) { + assertThat(((Gauge) group.get("Count")).getValue()).isEqualTo(count); + assertThat(((Gauge) group.get("Time")).getValue()).isEqualTo(time); + MeterView perSecond = ((MeterView) group.get("TimeMsPerSecond")); + perSecond.update(); + assertThat(perSecond.getRate()).isGreaterThan(0.); + } + @Test void testHeapMetricsCompleteness() { final InterceptingOperatorMetricGroup heapMetrics = new InterceptingOperatorMetricGroup(); @@ -296,4 +337,47 @@ private void runUntilMetricChanged( "%s usage metric never changed its value after %d runs.", name, maxRuns); fail(msg); } + + static class TestGcBean implements GarbageCollectorMXBean { + + final String name; + final long collectionCount; + final long collectionTime; + + public TestGcBean(String name, long collectionCount, long collectionTime) { + this.name = name; + this.collectionCount = collectionCount; + this.collectionTime = collectionTime; + } + + @Override + public long getCollectionCount() { + return collectionCount; + } + + @Override + public long getCollectionTime() { + return collectionTime; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isValid() { + throw new UnsupportedOperationException(); + } + + @Override + public String[] getMemoryPoolNames() { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectName getObjectName() { + throw new UnsupportedOperationException(); + } + } } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/DeclarativeSlotManagerBuilder.java b/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/DeclarativeSlotManagerBuilder.java index 57492af56f175..aaa240a2b6d66 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/DeclarativeSlotManagerBuilder.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/DeclarativeSlotManagerBuilder.java @@ -34,9 +34,11 @@ import java.time.Duration; import java.util.concurrent.Executor; +import static org.apache.flink.configuration.TaskManagerOptions.TaskManagerLoadBalanceMode; + /** Builder for {@link DeclarativeSlotManager}. */ public class DeclarativeSlotManagerBuilder { - private boolean evenlySpreadOutSlots; + private TaskManagerLoadBalanceMode taskManagerLoadBalanceMode; private final ScheduledExecutor scheduledExecutor; private Time taskManagerRequestTimeout; private Time taskManagerTimeout; @@ -53,7 +55,7 @@ public class DeclarativeSlotManagerBuilder { private Duration declareNeededResourceDelay; private DeclarativeSlotManagerBuilder(ScheduledExecutor scheduledExecutor) { - this.evenlySpreadOutSlots = false; + this.taskManagerLoadBalanceMode = TaskManagerLoadBalanceMode.NONE; this.scheduledExecutor = scheduledExecutor; this.taskManagerRequestTimeout = TestingUtils.infiniteTime(); this.taskManagerTimeout = TestingUtils.infiniteTime(); @@ -93,8 +95,9 @@ public DeclarativeSlotManagerBuilder setWaitResultConsumedBeforeRelease( return this; } - public DeclarativeSlotManagerBuilder setEvenlySpreadOutSlots(boolean evenlySpreadOutSlots) { - this.evenlySpreadOutSlots = evenlySpreadOutSlots; + public DeclarativeSlotManagerBuilder setTaskManagerLoadBalanceMode( + TaskManagerLoadBalanceMode taskManagerLoadBalanceMode) { + this.taskManagerLoadBalanceMode = taskManagerLoadBalanceMode; return this; } @@ -159,10 +162,10 @@ public DeclarativeSlotManager build() { requirementCheckDelay, declareNeededResourceDelay, waitResultConsumedBeforeRelease, - evenlySpreadOutSlots + taskManagerLoadBalanceMode == TaskManagerLoadBalanceMode.SLOTS ? LeastUtilizationSlotMatchingStrategy.INSTANCE : AnyMatchingSlotMatchingStrategy.INSTANCE, - evenlySpreadOutSlots, + taskManagerLoadBalanceMode, defaultWorkerResourceSpec, numSlotsPerWorker, minSlotNum, diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/DeclarativeSlotManagerTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/DeclarativeSlotManagerTest.java index 339509125eb11..5bbb678b34396 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/DeclarativeSlotManagerTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/DeclarativeSlotManagerTest.java @@ -81,6 +81,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; +import static org.apache.flink.configuration.TaskManagerOptions.TaskManagerLoadBalanceMode; import static org.assertj.core.api.Assertions.assertThat; /** Tests for the {@link DeclarativeSlotManager}. */ @@ -1121,7 +1122,7 @@ private TaskExecutorConnection createTaskExecutorConnection( void testSpreadOutSlotAllocationStrategy() throws Exception { try (DeclarativeSlotManager slotManager = createDeclarativeSlotManagerBuilder() - .setEvenlySpreadOutSlots(true) + .setTaskManagerLoadBalanceMode(TaskManagerLoadBalanceMode.SLOTS) .buildAndStartWithDirectExec()) { final List> requestSlotFutures = new ArrayList<>(); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/DefaultResourceAllocationStrategyTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/DefaultResourceAllocationStrategyTest.java index be46dfe6fd073..b4e61a89e8eda 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/DefaultResourceAllocationStrategyTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/DefaultResourceAllocationStrategyTest.java @@ -36,6 +36,7 @@ import java.util.List; import java.util.Map; +import static org.apache.flink.configuration.TaskManagerOptions.TaskManagerLoadBalanceMode; import static org.assertj.core.api.Assertions.assertThat; /** Tests for the {@link DefaultResourceAllocationStrategy}. */ @@ -44,9 +45,10 @@ class DefaultResourceAllocationStrategyTest { ResourceProfile.fromResources(1, 100); private static final int NUM_OF_SLOTS = 5; private static final DefaultResourceAllocationStrategy ANY_MATCHING_STRATEGY = - createStrategy(false); + createStrategy(TaskManagerLoadBalanceMode.NONE); - private static final DefaultResourceAllocationStrategy EVENLY_STRATEGY = createStrategy(true); + private static final DefaultResourceAllocationStrategy EVENLY_STRATEGY = + createStrategy(TaskManagerLoadBalanceMode.SLOTS); @Test void testFulfillRequirementWithRegisteredResources() { @@ -694,20 +696,21 @@ void testMinRequiredResourceLimitInFulfillRequirements( .hasSize(pendingTaskManagersToAllocate); } - private static DefaultResourceAllocationStrategy createStrategy(boolean evenlySpreadOutSlots) { - return createStrategy(evenlySpreadOutSlots, 0); + private static DefaultResourceAllocationStrategy createStrategy( + TaskManagerLoadBalanceMode taskManagerLoadBalanceMode) { + return createStrategy(taskManagerLoadBalanceMode, 0); } private static DefaultResourceAllocationStrategy createStrategy(int redundantTaskManagerNum) { - return createStrategy(false, redundantTaskManagerNum); + return createStrategy(TaskManagerLoadBalanceMode.NONE, redundantTaskManagerNum); } private static DefaultResourceAllocationStrategy createStrategy( - boolean evenlySpreadOutSlots, int redundantTaskManagerNum) { + TaskManagerLoadBalanceMode taskManagerLoadBalanceMode, int redundantTaskManagerNum) { return new DefaultResourceAllocationStrategy( DEFAULT_SLOT_RESOURCE.multiply(NUM_OF_SLOTS), NUM_OF_SLOTS, - evenlySpreadOutSlots, + taskManagerLoadBalanceMode, Time.milliseconds(0), redundantTaskManagerNum, new CPUResource(0.0), @@ -719,7 +722,7 @@ private static DefaultResourceAllocationStrategy createStrategy( return new DefaultResourceAllocationStrategy( DEFAULT_SLOT_RESOURCE.multiply(NUM_OF_SLOTS), NUM_OF_SLOTS, - false, + TaskManagerLoadBalanceMode.NONE, Time.milliseconds(0), 0, minRequiredCPU, diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/FineGrainedSlotManagerDefaultResourceAllocationStrategyITCase.java b/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/FineGrainedSlotManagerDefaultResourceAllocationStrategyITCase.java index f4e91a6ac70a6..d0eaeb5020da3 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/FineGrainedSlotManagerDefaultResourceAllocationStrategyITCase.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/FineGrainedSlotManagerDefaultResourceAllocationStrategyITCase.java @@ -55,7 +55,7 @@ protected Optional getResourceAllocationStrategy( new DefaultResourceAllocationStrategy( DEFAULT_TOTAL_RESOURCE_PROFILE, DEFAULT_NUM_SLOTS_PER_WORKER, - slotManagerConfiguration.isEvenlySpreadOutSlots(), + slotManagerConfiguration.getTaskManagerLoadBalanceMode(), slotManagerConfiguration.getTaskManagerTimeout(), slotManagerConfiguration.getRedundantTaskManagerNum(), slotManagerConfiguration.getMinTotalCpu(), diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/SlotManagerConfigurationBuilder.java b/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/SlotManagerConfigurationBuilder.java index d5d70e164ebd8..260592d44dcf3 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/SlotManagerConfigurationBuilder.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/resourcemanager/slotmanager/SlotManagerConfigurationBuilder.java @@ -27,6 +27,8 @@ import java.time.Duration; +import static org.apache.flink.configuration.TaskManagerOptions.TaskManagerLoadBalanceMode; + /** Builder for {@link SlotManagerConfiguration}. */ public class SlotManagerConfigurationBuilder { private Time taskManagerRequestTimeout; @@ -43,7 +45,7 @@ public class SlotManagerConfigurationBuilder { private MemorySize minTotalMem; private MemorySize maxTotalMem; private int redundantTaskManagerNum; - private boolean evenlySpreadOutSlots; + private TaskManagerLoadBalanceMode taskManagerLoadBalanceMode; private SlotManagerConfigurationBuilder() { this.taskManagerRequestTimeout = TestingUtils.infiniteTime(); @@ -62,7 +64,7 @@ private SlotManagerConfigurationBuilder() { this.maxTotalMem = MemorySize.MAX_VALUE; this.redundantTaskManagerNum = ResourceManagerOptions.REDUNDANT_TASK_MANAGER_NUM.defaultValue(); - this.evenlySpreadOutSlots = false; + this.taskManagerLoadBalanceMode = TaskManagerLoadBalanceMode.NONE; } public static SlotManagerConfigurationBuilder newBuilder() { @@ -141,8 +143,9 @@ public SlotManagerConfigurationBuilder setRedundantTaskManagerNum(int redundantT return this; } - public SlotManagerConfigurationBuilder setEvenlySpreadOutSlots(boolean evenlySpreadOutSlots) { - this.evenlySpreadOutSlots = evenlySpreadOutSlots; + public SlotManagerConfigurationBuilder setTaskManagerLoadBalanceMode( + TaskManagerLoadBalanceMode taskManagerLoadBalanceMode) { + this.taskManagerLoadBalanceMode = taskManagerLoadBalanceMode; return this; } @@ -153,10 +156,10 @@ public SlotManagerConfiguration build() { requirementCheckDelay, declareNeededResourceDelay, waitResultConsumedBeforeRelease, - evenlySpreadOutSlots + taskManagerLoadBalanceMode == TaskManagerLoadBalanceMode.SLOTS ? LeastUtilizationSlotMatchingStrategy.INSTANCE : AnyMatchingSlotMatchingStrategy.INSTANCE, - evenlySpreadOutSlots, + taskManagerLoadBalanceMode, defaultWorkerResourceSpec, numSlotsPerWorker, minSlotNum, diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/handler/job/SubtaskCurrentAttemptDetailsHandlerTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/handler/job/SubtaskCurrentAttemptDetailsHandlerTest.java index be4938ab96faa..63e53d889158a 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/handler/job/SubtaskCurrentAttemptDetailsHandlerTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/handler/job/SubtaskCurrentAttemptDetailsHandlerTest.java @@ -186,6 +186,7 @@ void testHandleRequest() throws Exception { expectedState, attempt, assignedResourceLocation.getHostname(), + assignedResourceLocation.getEndpoint(), deployingTs, finishedTs, finishedTs - deployingTs, diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/handler/job/SubtaskExecutionAttemptDetailsHandlerTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/handler/job/SubtaskExecutionAttemptDetailsHandlerTest.java index ba828377f4409..661e9e3982cf8 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/handler/job/SubtaskExecutionAttemptDetailsHandlerTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/handler/job/SubtaskExecutionAttemptDetailsHandlerTest.java @@ -189,6 +189,7 @@ void testHandleRequest() throws Exception { expectedState, attempt, "(unassigned)", + "(unassigned)", -1L, 0L, -1L, diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/AggregatedTaskDetailsInfoTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/AggregatedTaskDetailsInfoTest.java index cc916be116adc..32c42d6a0b6fb 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/AggregatedTaskDetailsInfoTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/AggregatedTaskDetailsInfoTest.java @@ -74,6 +74,7 @@ protected AggregatedTaskDetailsInfo getTestResponseInstance() throws Exception { ExecutionState.values()[ random.nextInt(ExecutionState.values().length)], Math.abs(random.nextInt()), + "localhost", "localhost:" + random.nextInt(65536), Math.abs(random.nextLong()), Math.abs(random.nextLong()), diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/JobExceptionsInfoWithHistoryNoRootTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/JobExceptionsInfoWithHistoryNoRootTest.java index eb35780142cfb..3bb7d1be0b9c9 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/JobExceptionsInfoWithHistoryNoRootTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/JobExceptionsInfoWithHistoryNoRootTest.java @@ -77,6 +77,7 @@ protected JobExceptionsInfoWithHistory getTestResponseInstance() throws Exceptio Collections.emptyMap(), "task name #2", "location #2", + "location #2", "taskManagerId #2"))), new JobExceptionsInfoWithHistory.RootExceptionInfo( "local task failure #1", @@ -85,6 +86,7 @@ protected JobExceptionsInfoWithHistory getTestResponseInstance() throws Exceptio Collections.emptyMap(), "task name", "location", + "location", "taskManagerId", Collections.emptyList())), false)); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/JobVertexDetailsInfoTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/JobVertexDetailsInfoTest.java index 155a31621ce58..bcce56ffccb17 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/JobVertexDetailsInfoTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/JobVertexDetailsInfoTest.java @@ -70,6 +70,7 @@ protected JobVertexDetailsInfo getTestResponseInstance() throws Exception { ExecutionState.CREATED, random.nextInt(), "local1", + "local1:123", System.currentTimeMillis(), System.currentTimeMillis(), 1L, @@ -83,6 +84,7 @@ protected JobVertexDetailsInfo getTestResponseInstance() throws Exception { ExecutionState.RUNNING, random.nextInt(), "local2", + "local2:123", System.currentTimeMillis(), System.currentTimeMillis(), 1L, @@ -95,6 +97,7 @@ protected JobVertexDetailsInfo getTestResponseInstance() throws Exception { ExecutionState.FAILED, random.nextInt(), "local2", + "local2:123", System.currentTimeMillis(), System.currentTimeMillis(), 1L, @@ -108,6 +111,7 @@ protected JobVertexDetailsInfo getTestResponseInstance() throws Exception { ExecutionState.FINISHED, random.nextInt(), "local3", + "local3:123", System.currentTimeMillis(), System.currentTimeMillis(), 1L, diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/JobVertexTaskManagersInfoTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/JobVertexTaskManagersInfoTest.java index 16a9fb243d20a..9f739e1af19e2 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/JobVertexTaskManagersInfoTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/JobVertexTaskManagersInfoTest.java @@ -70,6 +70,7 @@ protected JobVertexTaskManagersInfo getTestResponseInstance() throws Exception { taskManagersInfoList.add( new TaskManagersInfo( "host1", + "host1:123", ExecutionState.CANCELING, 1L, 2L, diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/SubtasksTimesInfoTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/SubtasksTimesInfoTest.java index a411527bca4ec..24c9805438bc0 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/SubtasksTimesInfoTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/SubtasksTimesInfoTest.java @@ -45,19 +45,22 @@ protected SubtasksTimesInfo getTestResponseInstance() throws Exception { subTimeMap1.put(ExecutionState.RUNNING, 1L); subTimeMap1.put(ExecutionState.FAILED, 2L); subTimeMap1.put(ExecutionState.CANCELED, 3L); - subtasks.add(new SubtasksTimesInfo.SubtaskTimeInfo(0, "local1", 1L, subTimeMap1)); + subtasks.add( + new SubtasksTimesInfo.SubtaskTimeInfo(0, "local1", "local1:123", 1L, subTimeMap1)); Map subTimeMap2 = new HashMap<>(); subTimeMap2.put(ExecutionState.RUNNING, 4L); subTimeMap2.put(ExecutionState.FAILED, 5L); subTimeMap2.put(ExecutionState.CANCELED, 6L); - subtasks.add(new SubtasksTimesInfo.SubtaskTimeInfo(1, "local2", 2L, subTimeMap2)); + subtasks.add( + new SubtasksTimesInfo.SubtaskTimeInfo(1, "local2", "local2:123", 2L, subTimeMap2)); Map subTimeMap3 = new HashMap<>(); subTimeMap3.put(ExecutionState.SCHEDULED, 1L); subTimeMap3.put(ExecutionState.FAILED, 2L); subTimeMap3.put(ExecutionState.CANCELING, 3L); - subtasks.add(new SubtasksTimesInfo.SubtaskTimeInfo(2, "local3", 3L, subTimeMap3)); + subtasks.add( + new SubtasksTimesInfo.SubtaskTimeInfo(2, "local3", "local3:123", 3L, subTimeMap3)); return new SubtasksTimesInfo("testId", "testName", System.currentTimeMillis(), subtasks); } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/job/SubtaskExecutionAttemptDetailsInfoTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/job/SubtaskExecutionAttemptDetailsInfoTest.java index 36a92b54cb545..90fe77b9155a2 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/job/SubtaskExecutionAttemptDetailsInfoTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/job/SubtaskExecutionAttemptDetailsInfoTest.java @@ -68,6 +68,7 @@ protected SubtaskExecutionAttemptDetailsInfo getTestResponseInstance() throws Ex Math.abs(random.nextInt()), ExecutionState.values()[random.nextInt(ExecutionState.values().length)], Math.abs(random.nextInt()), + "localhost", "localhost:" + random.nextInt(65536), Math.abs(random.nextLong()), Math.abs(random.nextLong()), diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/job/SubtasksAllAccumulatorsInfoTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/job/SubtasksAllAccumulatorsInfoTest.java index 651a34c61946e..5e3e0a9376cc8 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/job/SubtasksAllAccumulatorsInfoTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/messages/job/SubtasksAllAccumulatorsInfoTest.java @@ -48,7 +48,7 @@ protected SubtasksAllAccumulatorsInfo getTestResponseInstance() throws Exception for (int i = 0; i < 3; ++i) { subtaskAccumulatorsInfos.add( new SubtasksAllAccumulatorsInfo.SubtaskAccumulatorsInfo( - i, i, "host-" + String.valueOf(i), userAccumulators)); + i, i, "host-" + i, "host-" + i + ":123", userAccumulators)); } return new SubtasksAllAccumulatorsInfo(new JobVertexID(), 4, subtaskAccumulatorsInfos); } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/scheduler/adaptive/ExecutingTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/scheduler/adaptive/ExecutingTest.java index f89cd0bb54a6c..73ab5c7922ce0 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/scheduler/adaptive/ExecutingTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/scheduler/adaptive/ExecutingTest.java @@ -82,6 +82,7 @@ import javax.annotation.Nullable; import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -146,7 +147,10 @@ public void testNoDeploymentCallOnEnterWhenVertexRunning() throws Exception { log, ctx, ClassLoader.getSystemClassLoader(), - new ArrayList<>()); + new ArrayList<>(), + Duration.ZERO, + null, + Instant.now()); assertThat(mockExecutionVertex.isDeployCalled(), is(false)); } } @@ -164,7 +168,10 @@ public void testIllegalStateExceptionOnNotRunningExecutionGraph() throws Excepti log, ctx, ClassLoader.getSystemClassLoader(), - new ArrayList<>()); + new ArrayList<>(), + Duration.ZERO, + null, + Instant.now()); } } @@ -252,28 +259,121 @@ public void testTransitionToFinishedOnSuspend() throws Exception { } @Test - public void testNotifyNewResourcesAvailableWithCanScaleUpTransitionsToRestarting() + public void testNotifyNewResourcesAvailableBeforeCooldownIsOverScheduledStateChange() throws Exception { try (MockExecutingContext ctx = new MockExecutingContext()) { - Executing exec = new ExecutingStateBuilder().build(ctx); + // do not wait too long in the test + final Duration scalingIntervalMin = Duration.ofSeconds(1L); + final ExecutingStateBuilder executingStateBuilder = + new ExecutingStateBuilder().setScalingIntervalMin(scalingIntervalMin); + Executing exec = executingStateBuilder.build(ctx); + // => rescale + ctx.setCanScaleUp(true); + // scheduled rescale should restart the job after cooldown + ctx.setExpectRestarting( + restartingArguments -> { + assertThat(restartingArguments.getBackoffTime(), is(Duration.ZERO)); + assertThat(ctx.actionWasScheduled, is(true)); + }); + exec.onNewResourcesAvailable(); + } + } + @Test + public void testNotifyNewResourcesAvailableAfterCooldownIsOverStateChange() throws Exception { + try (MockExecutingContext ctx = new MockExecutingContext()) { + final ExecutingStateBuilder executingStateBuilder = + new ExecutingStateBuilder() + .setScalingIntervalMin(Duration.ofSeconds(20L)) + .setLastRescale(Instant.now().minus(Duration.ofSeconds(30L))); + Executing exec = executingStateBuilder.build(ctx); + // => rescale + ctx.setCanScaleUp(true); + // immediate rescale ctx.setExpectRestarting( restartingArguments -> { - // expect immediate restart on scale up assertThat(restartingArguments.getBackoffTime(), is(Duration.ZERO)); + assertThat(ctx.actionWasScheduled, is(false)); }); - ctx.setCanScaleUp(() -> true); exec.onNewResourcesAvailable(); } } @Test - public void testNotifyNewResourcesAvailableWithNoResourcesAndNoStateChange() throws Exception { + public void testNotifyNewResourcesAvailableWithCanScaleUpWithoutForceTransitionsToRestarting() + throws Exception { try (MockExecutingContext ctx = new MockExecutingContext()) { Executing exec = new ExecutingStateBuilder().build(ctx); - ctx.setCanScaleUp(() -> false); + + ctx.setExpectRestarting( + // immediate rescale + restartingArguments -> { + assertThat(restartingArguments.getBackoffTime(), is(Duration.ZERO)); + assertThat(ctx.actionWasScheduled, is(false)); + }); + ctx.setCanScaleUp(true); // => rescale + exec.onNewResourcesAvailable(); + } + } + + @Test + public void testNotifyNewResourcesAvailableWithCantScaleUpWithoutForceAndCantScaleUpWithForce() + throws Exception { + try (MockExecutingContext ctx = new MockExecutingContext()) { + Executing exec = + new ExecutingStateBuilder() + .setScalingIntervalMax(Duration.ofSeconds(1L)) + .build(ctx); + // => schedule force rescale but resource lost on timeout => no rescale + ctx.setCanScaleUp(false, false); exec.onNewResourcesAvailable(); ctx.assertNoStateTransition(); + assertThat(ctx.actionWasScheduled, is(true)); + } + } + + @Test + public void + testNotifyNewResourcesAvailableWithCantScaleUpWithoutForceAndCanScaleUpWithForceScheduled() + throws Exception { + try (MockExecutingContext ctx = new MockExecutingContext()) { + final ExecutingStateBuilder executingStateBuilder = + new ExecutingStateBuilder() + .setScalingIntervalMin(Duration.ofSeconds(20L)) + .setScalingIntervalMax(Duration.ofSeconds(30L)) + .setLastRescale(Instant.now().minus(Duration.ofSeconds(25L))); + Executing exec = executingStateBuilder.build(ctx); + // => schedule force rescale and resource still there after timeout => rescale + ctx.setCanScaleUp(false, true); + // rescale after scaling-interval.max + ctx.setExpectRestarting( + restartingArguments -> { + assertThat(restartingArguments.getBackoffTime(), is(Duration.ZERO)); + assertThat(ctx.actionWasScheduled, is(true)); + }); + exec.onNewResourcesAvailable(); + } + } + + @Test + public void + testNotifyNewResourcesAvailableWithCantScaleUpWithoutForceAndCanScaleUpWithForceImmediate() + throws Exception { + try (MockExecutingContext ctx = new MockExecutingContext()) { + final ExecutingStateBuilder executingStateBuilder = + new ExecutingStateBuilder() + .setScalingIntervalMin(Duration.ofSeconds(20L)) + .setScalingIntervalMax(Duration.ofSeconds(30L)) + .setLastRescale(Instant.now().minus(Duration.ofSeconds(70L))); + Executing exec = executingStateBuilder.build(ctx); + // => immediate force rescale and resource still there after timeout => rescale + ctx.setCanScaleUp(false, true); + ctx.setExpectRestarting( + restartingArguments -> { + assertThat(restartingArguments.getBackoffTime(), is(Duration.ZERO)); + assertThat(ctx.actionWasScheduled, is(false)); + }); + exec.onNewResourcesAvailable(); } } @@ -468,15 +568,15 @@ public void testStateDoesNotExposeGloballyTerminalExecutionGraph() throws Except @Test public void testExecutingChecksForNewResourcesWhenBeingCreated() throws Exception { - try (MockExecutingContext context = new MockExecutingContext()) { - context.setCanScaleUp(() -> true); - context.setExpectRestarting( - restartingArguments -> { - // expect immediate restart on scale up + try (MockExecutingContext ctx = new MockExecutingContext()) { + ctx.setCanScaleUp(true); + ctx.setExpectRestarting( + restartingArguments -> { // immediate rescale assertThat(restartingArguments.getBackoffTime(), is(Duration.ZERO)); + assertThat(ctx.actionWasScheduled, is(false)); }); - final Executing executing = new ExecutingStateBuilder().build(context); + new ExecutingStateBuilder().build(ctx); } } @@ -497,6 +597,9 @@ private final class ExecutingStateBuilder { TestingDefaultExecutionGraphBuilder.newBuilder() .build(EXECUTOR_RESOURCE.getExecutor()); private OperatorCoordinatorHandler operatorCoordinatorHandler; + private Duration scalingIntervalMin = Duration.ZERO; + @Nullable private Duration scalingIntervalMax; + private Instant lastRescale = Instant.now(); private ExecutingStateBuilder() throws JobException, JobExecutionException { operatorCoordinatorHandler = new TestingOperatorCoordinatorHandler(); @@ -513,6 +616,21 @@ public ExecutingStateBuilder setOperatorCoordinatorHandler( return this; } + public ExecutingStateBuilder setScalingIntervalMin(Duration scalingIntervalMin) { + this.scalingIntervalMin = scalingIntervalMin; + return this; + } + + public ExecutingStateBuilder setScalingIntervalMax(Duration scalingIntervalMax) { + this.scalingIntervalMax = scalingIntervalMax; + return this; + } + + public ExecutingStateBuilder setLastRescale(Instant lastRescale) { + this.lastRescale = lastRescale; + return this; + } + private Executing build(MockExecutingContext ctx) { executionGraph.transitionToRunning(); @@ -523,7 +641,10 @@ private Executing build(MockExecutingContext ctx) { log, ctx, ClassLoader.getSystemClassLoader(), - new ArrayList<>()); + new ArrayList<>(), + scalingIntervalMin, + scalingIntervalMax, + lastRescale); } } @@ -544,7 +665,9 @@ private static class MockExecutingContext extends MockStateWithExecutionGraphCon new StateValidator<>("cancelling"); private Function howToHandleFailure; - private Supplier canScaleUp = () -> false; + private boolean canScaleUpWithoutForce = false; + private boolean canScaleUpWithForce = false; + private boolean actionWasScheduled = false; private StateValidator stopWithSavepointValidator = new StateValidator<>("stopWithSavepoint"); private CompletableFuture mockedStopWithSavepointOperationFuture = @@ -570,8 +693,13 @@ public void setHowToHandleFailure(Function function) { this.howToHandleFailure = function; } - public void setCanScaleUp(Supplier supplier) { - this.canScaleUp = supplier; + public void setCanScaleUp(boolean canScaleUpWithoutForce, boolean canScaleUpWithForce) { + this.canScaleUpWithoutForce = canScaleUpWithoutForce; + this.canScaleUpWithForce = canScaleUpWithForce; + } + + public void setCanScaleUp(boolean canScaleUpWithoutForce) { + this.canScaleUpWithoutForce = canScaleUpWithoutForce; } // --------- Interface Implementations ------- // @@ -594,8 +722,12 @@ public FailureResult howToHandleFailure(Throwable failure) { } @Override - public boolean shouldRescale(ExecutionGraph executionGraph) { - return canScaleUp.get(); + public boolean shouldRescale(ExecutionGraph executionGraph, boolean forceRescale) { + if (forceRescale) { + return canScaleUpWithForce; + } else { + return canScaleUpWithoutForce; + } } @Override @@ -651,6 +783,7 @@ public CompletableFuture goToStopWithSavepoint( @Override public ScheduledFuture runIfState(State expectedState, Runnable action, Duration delay) { + actionWasScheduled = !delay.isZero(); if (!hadStateTransition) { action.run(); } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/scheduler/adaptive/scalingpolicy/EnforceParallelismChangeRescalingControllerTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/scheduler/adaptive/scalingpolicy/EnforceParallelismChangeRescalingControllerTest.java new file mode 100644 index 0000000000000..0d980702af3bd --- /dev/null +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/scheduler/adaptive/scalingpolicy/EnforceParallelismChangeRescalingControllerTest.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.runtime.scheduler.adaptive.scalingpolicy; + +import org.apache.flink.runtime.jobgraph.JobVertexID; +import org.apache.flink.runtime.scheduler.adaptive.allocator.VertexParallelism; + +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +/** Tests for the {@link RescalingController}. */ +class EnforceParallelismChangeRescalingControllerTest { + + private static final JobVertexID jobVertexId = new JobVertexID(); + + @Test + void testScaleUp() { + final RescalingController rescalingController = + new EnforceParallelismChangeRescalingController(); + assertThat(rescalingController.shouldRescale(forParallelism(1), forParallelism(2))) + .isTrue(); + } + + @Test + void testAlwaysScaleDown() { + final RescalingController rescalingController = + new EnforceParallelismChangeRescalingController(); + assertThat(rescalingController.shouldRescale(forParallelism(2), forParallelism(1))) + .isTrue(); + } + + @Test + void testNoScaleOnSameParallelism() { + final RescalingController rescalingController = + new EnforceParallelismChangeRescalingController(); + assertThat(rescalingController.shouldRescale(forParallelism(2), forParallelism(2))) + .isFalse(); + } + + private static VertexParallelism forParallelism(int parallelism) { + return new VertexParallelism(Collections.singletonMap(jobVertexId, parallelism)); + } +} diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/source/coordinator/SourceCoordinatorConcurrentAttemptsTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/source/coordinator/SourceCoordinatorConcurrentAttemptsTest.java index 65a82027c13a7..3f3d0a1f63e43 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/source/coordinator/SourceCoordinatorConcurrentAttemptsTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/source/coordinator/SourceCoordinatorConcurrentAttemptsTest.java @@ -266,7 +266,9 @@ private TestEnumerator( @Override public void handleSourceEvent(int subtaskId, int attemptNumber, SourceEvent sourceEvent) { - sourceEvents.computeIfAbsent(subtaskId, HashMap::new).put(attemptNumber, sourceEvent); + sourceEvents + .computeIfAbsent(subtaskId, k -> new HashMap<>()) + .put(attemptNumber, sourceEvent); handleSourceEvent(subtaskId, sourceEvent); } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/state/ChangelogTestUtils.java b/flink-runtime/src/test/java/org/apache/flink/runtime/state/ChangelogTestUtils.java index 890010e1f6def..1329665ea219c 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/state/ChangelogTestUtils.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/state/ChangelogTestUtils.java @@ -80,7 +80,7 @@ public static class IncrementalStateHandleWrapper extends IncrementalRemoteKeyed stateHandle.getCheckpointId(), stateHandle.getSharedState(), stateHandle.getPrivateState(), - stateHandle.getMetaStateHandle(), + stateHandle.getMetaDataStateHandle(), stateHandle.getCheckpointedSize(), stateHandle.getStateHandleId()); this.stateHandle = stateHandle; diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/state/IncrementalRemoteKeyedStateHandleTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/state/IncrementalRemoteKeyedStateHandleTest.java index 9b12b3a43a15c..1422bc8bcf074 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/state/IncrementalRemoteKeyedStateHandleTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/state/IncrementalRemoteKeyedStateHandleTest.java @@ -61,7 +61,7 @@ void testUnregisteredDiscarding() throws Exception { verifyDiscard(handleAndLocalPath.getHandle(), TernaryBoolean.TRUE); } - verify(stateHandle.getMetaStateHandle()).discardState(); + verify(stateHandle.getMetaDataStateHandle()).discardState(); } /** @@ -130,8 +130,8 @@ void testSharedStateDeRegistration() throws Exception { verify(handleAndLocalPath.getHandle(), times(0)).discardState(); } - verify(stateHandle1.getMetaStateHandle(), times(1)).discardState(); - verify(stateHandle2.getMetaStateHandle(), times(0)).discardState(); + verify(stateHandle1.getMetaDataStateHandle(), times(1)).discardState(); + verify(stateHandle2.getMetaDataStateHandle(), times(0)).discardState(); // We discard the second stateHandle2.discardState(); @@ -146,8 +146,8 @@ void testSharedStateDeRegistration() throws Exception { verifyDiscard(handleAndLocalPath.getHandle(), TernaryBoolean.TRUE); } - verify(stateHandle1.getMetaStateHandle(), times(1)).discardState(); - verify(stateHandle2.getMetaStateHandle(), times(1)).discardState(); + verify(stateHandle1.getMetaDataStateHandle(), times(1)).discardState(); + verify(stateHandle2.getMetaDataStateHandle(), times(1)).discardState(); } /** @@ -176,7 +176,7 @@ void testSharedStateReRegistration() throws Exception { // Everything should be discarded for this handle stateHandleZ.discardState(); - verify(stateHandleZ.getMetaStateHandle(), times(1)).discardState(); + verify(stateHandleZ.getMetaDataStateHandle(), times(1)).discardState(); // Close the first registry stateRegistryA.close(); @@ -188,16 +188,16 @@ void testSharedStateReRegistration() throws Exception { // Private state should still get discarded stateHandleY.discardState(); - verify(stateHandleY.getMetaStateHandle(), times(1)).discardState(); + verify(stateHandleY.getMetaDataStateHandle(), times(1)).discardState(); // This should still be unaffected - verify(stateHandleX.getMetaStateHandle(), never()).discardState(); + verify(stateHandleX.getMetaDataStateHandle(), never()).discardState(); // We re-register the handle with a new registry SharedStateRegistry sharedStateRegistryB = spy(new SharedStateRegistryImpl()); stateHandleX.registerSharedStates(sharedStateRegistryB, 0L); stateHandleX.discardState(); - verify(stateHandleX.getMetaStateHandle(), times(1)).discardState(); + verify(stateHandleX.getMetaDataStateHandle(), times(1)).discardState(); // Should be completely discarded because it is tracked through the new registry sharedStateRegistryB.unregisterUnusedState(1L); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/state/TaskExecutorLocalStateStoresManagerTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/state/TaskExecutorLocalStateStoresManagerTest.java index 11a511831d585..c7a86387657eb 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/state/TaskExecutorLocalStateStoresManagerTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/state/TaskExecutorLocalStateStoresManagerTest.java @@ -415,6 +415,7 @@ private TaskManagerServices createTaskManagerServices( VoidPermanentBlobService.INSTANCE, UnregisteredMetricGroups.createUnregisteredTaskManagerMetricGroup(), Executors.newDirectExecutorService(), + null, throwable -> {}, workingDirectory); } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorBuilder.java b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorBuilder.java index 8051e538dfddb..73f0b7d6e9a14 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorBuilder.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorBuilder.java @@ -139,6 +139,7 @@ public TaskExecutor build() throws Exception { VoidPermanentBlobService.INSTANCE, UnregisteredMetricGroups.createUnregisteredTaskManagerMetricGroup(), Executors.newDirectExecutorService(), + rpcService.getScheduledExecutor(), throwable -> {}, workingDirectory); } else { diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorOperatorEventHandlingTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorOperatorEventHandlingTest.java index 5fd5bef8e405a..b3d71a4a3c5ca 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorOperatorEventHandlingTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorOperatorEventHandlingTest.java @@ -179,7 +179,8 @@ private TaskSubmissionTestEnvironment createExecutorWithRunningTask( .build()) .build(EXECUTOR_EXTENSION.getExecutor()); - env.getTaskSlotTable().allocateSlot(0, jobId, tdd.getAllocationId(), Time.seconds(60)); + env.getTaskSlotTable() + .allocateSlot(0, jobId, tdd.getAllocationId(), Duration.ofSeconds(60)); final TaskExecutorGateway tmGateway = env.getTaskExecutorGateway(); tmGateway.submitTask(tdd, env.getJobMasterId(), Time.seconds(10)).get(); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorSubmissionTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorSubmissionTest.java index 002e680c6f7c3..3fb4c2d3b3a5b 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorSubmissionTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorSubmissionTest.java @@ -70,6 +70,7 @@ import java.io.IOException; import java.net.URL; +import java.time.Duration; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -124,7 +125,7 @@ void testTaskSubmission() throws Exception { TaskExecutorGateway tmGateway = env.getTaskExecutorGateway(); TaskSlotTable taskSlotTable = env.getTaskSlotTable(); - taskSlotTable.allocateSlot(0, jobId, tdd.getAllocationId(), Time.seconds(60)); + taskSlotTable.allocateSlot(0, jobId, tdd.getAllocationId(), Duration.ofSeconds(60)); tmGateway.submitTask(tdd, env.getJobMasterId(), timeout).get(); taskRunningFuture.get(); @@ -153,7 +154,7 @@ void testSubmitTaskFailure() throws Exception { TaskExecutorGateway tmGateway = env.getTaskExecutorGateway(); TaskSlotTable taskSlotTable = env.getTaskSlotTable(); - taskSlotTable.allocateSlot(0, jobId, tdd.getAllocationId(), Time.seconds(60)); + taskSlotTable.allocateSlot(0, jobId, tdd.getAllocationId(), Duration.ofSeconds(60)); assertThatFuture(tmGateway.submitTask(tdd, env.getJobMasterId(), timeout)) .eventuallyFailsWith(ExecutionException.class) @@ -189,11 +190,11 @@ void testTaskSubmissionAndCancelling() throws Exception { TaskExecutorGateway tmGateway = env.getTaskExecutorGateway(); TaskSlotTable taskSlotTable = env.getTaskSlotTable(); - taskSlotTable.allocateSlot(0, jobId, tdd1.getAllocationId(), Time.seconds(60)); + taskSlotTable.allocateSlot(0, jobId, tdd1.getAllocationId(), Duration.ofSeconds(60)); tmGateway.submitTask(tdd1, env.getJobMasterId(), timeout).get(); task1RunningFuture.get(); - taskSlotTable.allocateSlot(1, jobId, tdd2.getAllocationId(), Time.seconds(60)); + taskSlotTable.allocateSlot(1, jobId, tdd2.getAllocationId(), Duration.ofSeconds(60)); tmGateway.submitTask(tdd2, env.getJobMasterId(), timeout).get(); task2RunningFuture.get(); @@ -248,11 +249,11 @@ void testGateChannelEdgeMismatch() throws Exception { TaskExecutorGateway tmGateway = env.getTaskExecutorGateway(); TaskSlotTable taskSlotTable = env.getTaskSlotTable(); - taskSlotTable.allocateSlot(0, jobId, tdd1.getAllocationId(), Time.seconds(60)); + taskSlotTable.allocateSlot(0, jobId, tdd1.getAllocationId(), Duration.ofSeconds(60)); tmGateway.submitTask(tdd1, env.getJobMasterId(), timeout).get(); task1RunningFuture.get(); - taskSlotTable.allocateSlot(1, jobId, tdd2.getAllocationId(), Time.seconds(60)); + taskSlotTable.allocateSlot(1, jobId, tdd2.getAllocationId(), Duration.ofSeconds(60)); tmGateway.submitTask(tdd2, env.getJobMasterId(), timeout).get(); task2RunningFuture.get(); @@ -308,11 +309,11 @@ void testRunJobWithForwardChannel() throws Exception { TaskExecutorGateway tmGateway = env.getTaskExecutorGateway(); TaskSlotTable taskSlotTable = env.getTaskSlotTable(); - taskSlotTable.allocateSlot(0, jobId, tdd1.getAllocationId(), Time.seconds(60)); + taskSlotTable.allocateSlot(0, jobId, tdd1.getAllocationId(), Duration.ofSeconds(60)); tmGateway.submitTask(tdd1, jobMasterId, timeout).get(); task1RunningFuture.get(); - taskSlotTable.allocateSlot(1, jobId, tdd2.getAllocationId(), Time.seconds(60)); + taskSlotTable.allocateSlot(1, jobId, tdd2.getAllocationId(), Duration.ofSeconds(60)); tmGateway.submitTask(tdd2, jobMasterId, timeout).get(); task2RunningFuture.get(); @@ -387,11 +388,11 @@ void testCancellingDependentAndStateUpdateFails() throws Exception { TaskExecutorGateway tmGateway = env.getTaskExecutorGateway(); TaskSlotTable taskSlotTable = env.getTaskSlotTable(); - taskSlotTable.allocateSlot(0, jobId, tdd1.getAllocationId(), Time.seconds(60)); + taskSlotTable.allocateSlot(0, jobId, tdd1.getAllocationId(), Duration.ofSeconds(60)); tmGateway.submitTask(tdd1, jobMasterId, timeout).get(); task1RunningFuture.get(); - taskSlotTable.allocateSlot(1, jobId, tdd2.getAllocationId(), Time.seconds(60)); + taskSlotTable.allocateSlot(1, jobId, tdd2.getAllocationId(), Duration.ofSeconds(60)); tmGateway.submitTask(tdd2, jobMasterId, timeout).get(); task2RunningFuture.get(); @@ -443,7 +444,7 @@ void testRemotePartitionNotFound() throws Exception { TaskExecutorGateway tmGateway = env.getTaskExecutorGateway(); TaskSlotTable taskSlotTable = env.getTaskSlotTable(); - taskSlotTable.allocateSlot(0, jobId, tdd.getAllocationId(), Time.seconds(60)); + taskSlotTable.allocateSlot(0, jobId, tdd.getAllocationId(), Duration.ofSeconds(60)); tmGateway.submitTask(tdd, env.getJobMasterId(), timeout).get(); taskRunningFuture.get(); @@ -478,7 +479,7 @@ void testUpdateTaskInputPartitionsFailure() throws Exception { TaskExecutorGateway tmGateway = env.getTaskExecutorGateway(); TaskSlotTable taskSlotTable = env.getTaskSlotTable(); - taskSlotTable.allocateSlot(0, jobId, tdd.getAllocationId(), Time.seconds(60)); + taskSlotTable.allocateSlot(0, jobId, tdd.getAllocationId(), Duration.ofSeconds(60)); tmGateway.submitTask(tdd, env.getJobMasterId(), timeout).get(); taskRunningFuture.get(); @@ -536,7 +537,7 @@ void testLocalPartitionNotFound() throws Exception { TaskExecutorGateway tmGateway = env.getTaskExecutorGateway(); TaskSlotTable taskSlotTable = env.getTaskSlotTable(); - taskSlotTable.allocateSlot(0, jobId, tdd.getAllocationId(), Time.seconds(60)); + taskSlotTable.allocateSlot(0, jobId, tdd.getAllocationId(), Duration.ofSeconds(60)); tmGateway.submitTask(tdd, env.getJobMasterId(), timeout).get(); taskRunningFuture.get(); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorTest.java index 6719c33a81c42..25ae5986d567f 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskExecutorTest.java @@ -2942,7 +2942,7 @@ private AllocateSlotNotifyingTaskSlotTable(OneShotLatch allocateSlotLatch) { @Override public boolean allocateSlot( - int index, JobID jobId, AllocationID allocationId, Time slotTimeout) { + int index, JobID jobId, AllocationID allocationId, Duration slotTimeout) { final boolean result = super.allocateSlot(index, jobId, allocationId, slotTimeout); allocateSlotLatch.trigger(); @@ -2955,7 +2955,7 @@ public boolean allocateSlot( JobID jobId, AllocationID allocationId, ResourceProfile resourceProfile, - Time slotTimeout) { + Duration slotTimeout) { final boolean result = super.allocateSlot(index, jobId, allocationId, resourceProfile, slotTimeout); allocateSlotLatch.trigger(); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskManagerServicesBuilder.java b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskManagerServicesBuilder.java index 2bd18f543a1d5..68d9d5a275de7 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskManagerServicesBuilder.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskManagerServicesBuilder.java @@ -39,6 +39,7 @@ import org.apache.flink.runtime.taskmanager.LocalUnresolvedTaskManagerLocation; import org.apache.flink.runtime.taskmanager.Task; import org.apache.flink.runtime.taskmanager.UnresolvedTaskManagerLocation; +import org.apache.flink.runtime.util.NoOpGroupCache; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; @@ -200,6 +201,8 @@ public TaskManagerServices build() { libraryCacheManager, slotAllocationSnapshotPersistenceService, sharedResources, - NoOpShuffleDescriptorsCache.INSTANCE); + new NoOpGroupCache<>(), + new NoOpGroupCache<>(), + new NoOpGroupCache<>()); } } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskSubmissionTestEnvironment.java b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskSubmissionTestEnvironment.java index 7b152a9adc112..0cbf60279c593 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskSubmissionTestEnvironment.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/TaskSubmissionTestEnvironment.java @@ -35,6 +35,7 @@ import org.apache.flink.runtime.highavailability.TestingHighAvailabilityServices; import org.apache.flink.runtime.io.network.NettyShuffleEnvironmentBuilder; import org.apache.flink.runtime.io.network.netty.NettyConfig; +import org.apache.flink.runtime.io.network.partition.ResultPartitionManager; import org.apache.flink.runtime.io.network.partition.TaskExecutorPartitionTrackerImpl; import org.apache.flink.runtime.jobmaster.JobMasterGateway; import org.apache.flink.runtime.jobmaster.JobMasterId; @@ -309,6 +310,15 @@ private TestingTaskExecutor createTaskExecutor( configuration.getInteger( NettyShuffleEnvironmentOptions .NETWORK_REQUEST_BACKOFF_MAX)) + .setResultPartitionManager( + new ResultPartitionManager( + (int) + configuration + .get( + NettyShuffleEnvironmentOptions + .NETWORK_PARTITION_REQUEST_TIMEOUT) + .toMillis(), + testingRpcService.getScheduledExecutor())) .setNettyConfig(localCommunication ? null : nettyConfig) .build(); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/slot/TaskSlotTableImplTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/slot/TaskSlotTableImplTest.java index 7e61b06ded82c..6cd166cc5ace8 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/slot/TaskSlotTableImplTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/slot/TaskSlotTableImplTest.java @@ -19,7 +19,6 @@ package org.apache.flink.runtime.taskexecutor.slot; import org.apache.flink.api.common.JobID; -import org.apache.flink.api.common.time.Time; import org.apache.flink.runtime.clusterframework.types.AllocationID; import org.apache.flink.runtime.clusterframework.types.ResourceID; import org.apache.flink.runtime.clusterframework.types.ResourceProfile; @@ -38,6 +37,7 @@ import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -57,7 +57,7 @@ class TaskSlotTableImplTest { private static final TestExecutorExtension EXECUTOR_EXTENSION = TestingUtils.defaultExecutorExtension(); - private static final Time SLOT_TIMEOUT = Time.seconds(100L); + private static final Duration SLOT_TIMEOUT = Duration.ofSeconds(100L); /** Tests that one can can mark allocated slots as active. */ @Test @@ -455,7 +455,7 @@ void testAllocatedSlotTimeout() throws Exception { final AllocationID allocationId = new AllocationID(); assertThat( taskSlotTable.allocateSlot( - 0, new JobID(), allocationId, Time.milliseconds(1L))) + 0, new JobID(), allocationId, Duration.ofMillis(1L))) .isTrue(); assertThatFuture(timeoutFuture).eventuallySucceeds().isEqualTo(allocationId); } @@ -495,7 +495,7 @@ private void runDeactivateSlotTimeoutTest( final JobID jobId = new JobID(); assertThat( taskSlotTable.allocateSlot( - 0, jobId, allocationId, Time.milliseconds(timeout))) + 0, jobId, allocationId, Duration.ofMillis(timeout))) .isTrue(); assertThat(taskSlotTableAction.apply(taskSlotTable, jobId, allocationId)).isTrue(); diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/slot/TestingTaskSlotTable.java b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/slot/TestingTaskSlotTable.java index 2dbda2f898be1..5408e8b437321 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/slot/TestingTaskSlotTable.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/slot/TestingTaskSlotTable.java @@ -19,7 +19,6 @@ package org.apache.flink.runtime.taskexecutor.slot; import org.apache.flink.api.common.JobID; -import org.apache.flink.api.common.time.Time; import org.apache.flink.runtime.clusterframework.types.AllocationID; import org.apache.flink.runtime.clusterframework.types.ResourceID; import org.apache.flink.runtime.clusterframework.types.ResourceProfile; @@ -31,6 +30,7 @@ import javax.annotation.Nullable; +import java.time.Duration; import java.util.Collections; import java.util.Iterator; import java.util.Set; @@ -99,7 +99,7 @@ public SlotReport createSlotReport(ResourceID resourceId) { @Override public boolean allocateSlot( - int index, JobID jobId, AllocationID allocationId, Time slotTimeout) { + int index, JobID jobId, AllocationID allocationId, Duration slotTimeout) { return allocateSlotSupplier.get(); } @@ -109,7 +109,7 @@ public boolean allocateSlot( JobID jobId, AllocationID allocationId, ResourceProfile resourceProfile, - Time slotTimeout) { + Duration slotTimeout) { return allocateSlotSupplier.get(); } @@ -119,7 +119,7 @@ public boolean markSlotActive(AllocationID allocationId) { } @Override - public boolean markSlotInactive(AllocationID allocationId, Time slotTimeout) { + public boolean markSlotInactive(AllocationID allocationId, Duration slotTimeout) { throw new UnsupportedOperationException(); } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/slot/ThreadSafeTaskSlotTable.java b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/slot/ThreadSafeTaskSlotTable.java index 3623662af8fcc..32df96931e957 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/slot/ThreadSafeTaskSlotTable.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/slot/ThreadSafeTaskSlotTable.java @@ -19,7 +19,6 @@ package org.apache.flink.runtime.taskexecutor.slot; import org.apache.flink.api.common.JobID; -import org.apache.flink.api.common.time.Time; import org.apache.flink.runtime.clusterframework.types.AllocationID; import org.apache.flink.runtime.clusterframework.types.ResourceID; import org.apache.flink.runtime.clusterframework.types.ResourceProfile; @@ -99,7 +98,7 @@ public SlotReport createSlotReport(ResourceID resourceId) { @Override public boolean allocateSlot( - int index, JobID jobId, AllocationID allocationId, Time slotTimeout) { + int index, JobID jobId, AllocationID allocationId, Duration slotTimeout) { return callAsync(() -> taskSlotTable.allocateSlot(index, jobId, allocationId, slotTimeout)); } @@ -109,7 +108,7 @@ public boolean allocateSlot( JobID jobId, AllocationID allocationId, ResourceProfile resourceProfile, - Time slotTimeout) { + Duration slotTimeout) { return callAsync( () -> taskSlotTable.allocateSlot( @@ -122,7 +121,7 @@ public boolean markSlotActive(AllocationID allocationId) throws SlotNotFoundExce } @Override - public boolean markSlotInactive(AllocationID allocationId, Time slotTimeout) + public boolean markSlotInactive(AllocationID allocationId, Duration slotTimeout) throws SlotNotFoundException { return callAsync(() -> taskSlotTable.markSlotInactive(allocationId, slotTimeout)); } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/testutils/CommonTestUtils.java b/flink-runtime/src/test/java/org/apache/flink/runtime/testutils/CommonTestUtils.java index a101a453ff004..da01615050065 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/testutils/CommonTestUtils.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/testutils/CommonTestUtils.java @@ -21,6 +21,7 @@ import org.apache.flink.api.common.JobID; import org.apache.flink.api.common.JobStatus; import org.apache.flink.core.execution.JobClient; +import org.apache.flink.runtime.checkpoint.CheckpointStatsSnapshot; import org.apache.flink.runtime.checkpoint.CompletedCheckpointStats; import org.apache.flink.runtime.execution.ExecutionState; import org.apache.flink.runtime.executiongraph.AccessExecutionGraph; @@ -366,6 +367,32 @@ public static void waitForCheckpoint(JobID jobID, MiniCluster miniCluster, int n }); } + /** Wait for on more completed checkpoint. */ + public static void waitForOneMoreCheckpoint(JobID jobID, MiniCluster miniCluster) + throws Exception { + final long[] currentCheckpoint = new long[] {-1L}; + waitUntilCondition( + () -> { + AccessExecutionGraph graph = miniCluster.getExecutionGraph(jobID).get(); + CheckpointStatsSnapshot snapshot = graph.getCheckpointStatsSnapshot(); + if (snapshot != null) { + long currentCount = snapshot.getCounts().getNumberOfCompletedCheckpoints(); + if (currentCheckpoint[0] < 0L) { + currentCheckpoint[0] = currentCount; + } else { + return currentCount > currentCheckpoint[0]; + } + } else if (graph.getState().isGloballyTerminalState()) { + checkState( + graph.getFailureInfo() != null, + "Job terminated before taking required checkpoint.", + graph.getState()); + throw graph.getFailureInfo().getException(); + } + return false; + }); + } + /** * @return the path as {@link java.net.URI} to the latest checkpoint. * @throws FlinkJobNotFoundException if job not found diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/DefaultShuffleDescriptorsCacheTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/util/DefaultGroupCacheTest.java similarity index 76% rename from flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/DefaultShuffleDescriptorsCacheTest.java rename to flink-runtime/src/test/java/org/apache/flink/runtime/util/DefaultGroupCacheTest.java index 84c1acaa4e118..b5a3b717094fc 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/DefaultShuffleDescriptorsCacheTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/util/DefaultGroupCacheTest.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.flink.runtime.taskexecutor; +package org.apache.flink.runtime.util; import org.apache.flink.api.common.JobID; import org.apache.flink.runtime.blob.PermanentBlobKey; @@ -35,14 +35,14 @@ import static org.assertj.core.api.Assertions.assertThat; -/** Tests for {@link DefaultShuffleDescriptorsCache}. */ -class DefaultShuffleDescriptorsCacheTest { +/** Tests for {@link DefaultGroupCache}. */ +class DefaultGroupCacheTest { private final Duration expireTimeout = Duration.ofSeconds(10); @Test void testGetEntry() { - DefaultShuffleDescriptorsCache cache = - new DefaultShuffleDescriptorsCache.Factory( + DefaultGroupCache cache = + new DefaultGroupCache.Factory( expireTimeout, Integer.MAX_VALUE, Ticker.systemTicker()) .create(); @@ -56,16 +56,16 @@ void testGetEntry() { PermanentBlobKey blobKey = new PermanentBlobKey(); - assertThat(cache.get(blobKey)).isNull(); + assertThat(cache.get(jobId, blobKey)).isNull(); cache.put(jobId, blobKey, shuffleDescriptorGroup); - assertThat(cache.get(blobKey)).isEqualTo(shuffleDescriptorGroup); + assertThat(cache.get(jobId, blobKey)).isEqualTo(shuffleDescriptorGroup); } @Test void testClearCacheForJob() { - DefaultShuffleDescriptorsCache cache = - new DefaultShuffleDescriptorsCache.Factory( + DefaultGroupCache cache = + new DefaultGroupCache.Factory( expireTimeout, Integer.MAX_VALUE, Ticker.systemTicker()) .create(); @@ -78,19 +78,20 @@ void testClearCacheForJob() { }); PermanentBlobKey blobKey = new PermanentBlobKey(); - assertThat(cache.get(blobKey)).isNull(); + assertThat(cache.get(jobId, blobKey)).isNull(); cache.put(jobId, blobKey, shuffleDescriptorGroup); - assertThat(cache.get(blobKey)).isEqualTo(shuffleDescriptorGroup); + assertThat(cache.get(jobId, blobKey)).isEqualTo(shuffleDescriptorGroup); - cache.clearCacheForJob(jobId); - assertThat(cache.get(blobKey)).isNull(); + cache.clearCacheForGroup(jobId); + assertThat(cache.get(jobId, blobKey)).isNull(); } @Test void testPutWhenOverLimit() { - DefaultShuffleDescriptorsCache cache = - new DefaultShuffleDescriptorsCache.Factory(expireTimeout, 1, Ticker.systemTicker()) + DefaultGroupCache cache = + new DefaultGroupCache.Factory( + expireTimeout, 1, Ticker.systemTicker()) .create(); JobID jobId = new JobID(); @@ -104,7 +105,7 @@ void testPutWhenOverLimit() { PermanentBlobKey blobKey = new PermanentBlobKey(); cache.put(jobId, blobKey, shuffleDescriptorGroup); - assertThat(cache.get(blobKey)).isEqualTo(shuffleDescriptorGroup); + assertThat(cache.get(jobId, blobKey)).isEqualTo(shuffleDescriptorGroup); ShuffleDescriptorGroup otherShuffleDescriptorGroup = new ShuffleDescriptorGroup( @@ -115,15 +116,15 @@ void testPutWhenOverLimit() { PermanentBlobKey otherBlobKey = new PermanentBlobKey(); cache.put(jobId, otherBlobKey, otherShuffleDescriptorGroup); - assertThat(cache.get(blobKey)).isNull(); - assertThat(cache.get(otherBlobKey)).isEqualTo(otherShuffleDescriptorGroup); + assertThat(cache.get(jobId, blobKey)).isNull(); + assertThat(cache.get(jobId, otherBlobKey)).isEqualTo(otherShuffleDescriptorGroup); } @Test void testEntryExpired() { TestingTicker ticker = new TestingTicker(); - DefaultShuffleDescriptorsCache cache = - new DefaultShuffleDescriptorsCache.Factory( + DefaultGroupCache cache = + new DefaultGroupCache.Factory( Duration.ofSeconds(1), Integer.MAX_VALUE, ticker) .create(); @@ -138,10 +139,10 @@ void testEntryExpired() { PermanentBlobKey blobKey = new PermanentBlobKey(); cache.put(jobId, blobKey, shuffleDescriptorGroup); - assertThat(cache.get(blobKey)).isEqualTo(shuffleDescriptorGroup); + assertThat(cache.get(jobId, blobKey)).isEqualTo(shuffleDescriptorGroup); ticker.advance(Duration.ofSeconds(2)); - assertThat(cache.get(blobKey)).isNull(); + assertThat(cache.get(jobId, blobKey)).isNull(); } private static class TestingTicker extends Ticker { diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/NoOpShuffleDescriptorsCache.java b/flink-runtime/src/test/java/org/apache/flink/runtime/util/NoOpGroupCache.java similarity index 56% rename from flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/NoOpShuffleDescriptorsCache.java rename to flink-runtime/src/test/java/org/apache/flink/runtime/util/NoOpGroupCache.java index 5c7ad4236e3ea..93a0012bdd3d8 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/taskexecutor/NoOpShuffleDescriptorsCache.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/util/NoOpGroupCache.java @@ -16,29 +16,22 @@ * limitations under the License. */ -package org.apache.flink.runtime.taskexecutor; +package org.apache.flink.runtime.util; -import org.apache.flink.api.common.JobID; -import org.apache.flink.runtime.blob.PermanentBlobKey; -import org.apache.flink.runtime.deployment.TaskDeploymentDescriptorFactory.ShuffleDescriptorGroup; - -/** Non op implement of {@link ShuffleDescriptorsCache}. */ -public class NoOpShuffleDescriptorsCache implements ShuffleDescriptorsCache { - - public static final NoOpShuffleDescriptorsCache INSTANCE = new NoOpShuffleDescriptorsCache(); +/** Non op implement of {@link GroupCache}. */ +public class NoOpGroupCache implements GroupCache { @Override public void clear() {} @Override - public ShuffleDescriptorGroup get(PermanentBlobKey blobKey) { + public V get(G group, K key) { return null; } @Override - public void put( - JobID jobId, PermanentBlobKey blobKey, ShuffleDescriptorGroup shuffleDescriptorGroup) {} + public void put(G group, K key, V value) {} @Override - public void clearCacheForJob(JobID jobId) {} + public void clearCacheForGroup(G group) {} } diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/webmonitor/threadinfo/VertexFlameGraphFactoryTest.java b/flink-runtime/src/test/java/org/apache/flink/runtime/webmonitor/threadinfo/VertexFlameGraphFactoryTest.java index c0a3816052324..6a3f173a12c5e 100644 --- a/flink-runtime/src/test/java/org/apache/flink/runtime/webmonitor/threadinfo/VertexFlameGraphFactoryTest.java +++ b/flink-runtime/src/test/java/org/apache/flink/runtime/webmonitor/threadinfo/VertexFlameGraphFactoryTest.java @@ -58,7 +58,9 @@ void testLambdaClassNamesCleanUp() { private int verifyRecursively(VertexFlameGraph.Node node) { String location = node.getStackTraceLocation(); int lambdas = 0; - if (location.contains("$Lambda$")) { + final String javaVersion = System.getProperty("java.version"); + if (javaVersion.compareTo("21") < 0 && location.contains("$Lambda$") + || javaVersion.compareTo("21") >= 0 && location.contains("$$Lambda")) { lambdas++; // com.example.ClassName.method:123 // -> com.example.ClassName.method @@ -72,10 +74,12 @@ private int verifyRecursively(VertexFlameGraph.Node node) { new Condition() { @Override public boolean matches(String value) { - String javaVersion = System.getProperty("java.version"); + return javaVersion.startsWith("1.8") && value.endsWith("$Lambda$0/0") - || value.endsWith("$Lambda$0/0x0"); + || javaVersion.compareTo("21") < 0 + && value.endsWith("$Lambda$0/0x0") + || value.endsWith("$$Lambda0/0x0"); } }); } diff --git a/flink-scala/pom.xml b/flink-scala/pom.xml index 8adbbab4cc3e9..c4c801e089dc6 100644 --- a/flink-scala/pom.xml +++ b/flink-scala/pom.xml @@ -343,7 +343,7 @@ under the License. - io.github.zentol.japicmp + com.github.siom79.japicmp japicmp-maven-plugin diff --git a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/AbstractRocksDBAppendingState.java b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/AbstractRocksDBAppendingState.java index c5373f83b8bc1..5328a62d7b0aa 100644 --- a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/AbstractRocksDBAppendingState.java +++ b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/AbstractRocksDBAppendingState.java @@ -20,7 +20,6 @@ import org.apache.flink.api.common.typeutils.TypeSerializer; import org.apache.flink.runtime.state.internal.InternalAppendingState; -import org.apache.flink.util.FlinkRuntimeException; import org.rocksdb.ColumnFamilyHandle; import org.rocksdb.RocksDBException; @@ -50,34 +49,26 @@ protected AbstractRocksDBAppendingState( } @Override - public SV getInternal() { + public SV getInternal() throws IOException, RocksDBException { return getInternal(getKeyBytes()); } - SV getInternal(byte[] key) { - try { - byte[] valueBytes = backend.db.get(columnFamily, key); - if (valueBytes == null) { - return null; - } - dataInputView.setBuffer(valueBytes); - return valueSerializer.deserialize(dataInputView); - } catch (IOException | RocksDBException e) { - throw new FlinkRuntimeException("Error while retrieving data from RocksDB", e); + SV getInternal(byte[] key) throws IOException, RocksDBException { + byte[] valueBytes = backend.db.get(columnFamily, key); + if (valueBytes == null) { + return null; } + dataInputView.setBuffer(valueBytes); + return valueSerializer.deserialize(dataInputView); } @Override - public void updateInternal(SV valueToStore) { + public void updateInternal(SV valueToStore) throws RocksDBException { updateInternal(getKeyBytes(), valueToStore); } - void updateInternal(byte[] key, SV valueToStore) { - try { - // write the new value to RocksDB - backend.db.put(columnFamily, writeOptions, key, getValueBytes(valueToStore)); - } catch (RocksDBException e) { - throw new FlinkRuntimeException("Error while adding value to RocksDB", e); - } + void updateInternal(byte[] key, SV valueToStore) throws RocksDBException { + // write the new value to RocksDB + backend.db.put(columnFamily, writeOptions, key, getValueBytes(valueToStore)); } } diff --git a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBAggregatingState.java b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBAggregatingState.java index ba1e0e97ef219..fc556cae3f70b 100644 --- a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBAggregatingState.java +++ b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBAggregatingState.java @@ -27,10 +27,11 @@ import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.runtime.state.RegisteredKeyValueStateBackendMetaInfo; import org.apache.flink.runtime.state.internal.InternalAggregatingState; -import org.apache.flink.util.FlinkRuntimeException; import org.rocksdb.ColumnFamilyHandle; +import org.rocksdb.RocksDBException; +import java.io.IOException; import java.util.Collection; /** @@ -87,7 +88,7 @@ public TypeSerializer getValueSerializer() { } @Override - public R get() { + public R get() throws IOException, RocksDBException { ACC accumulator = getInternal(); if (accumulator == null) { return null; @@ -96,7 +97,7 @@ public R get() { } @Override - public void add(T value) { + public void add(T value) throws IOException, RocksDBException { byte[] key = getKeyBytes(); ACC accumulator = getInternal(key); accumulator = accumulator == null ? aggFunction.createAccumulator() : accumulator; @@ -104,60 +105,56 @@ public void add(T value) { } @Override - public void mergeNamespaces(N target, Collection sources) { + public void mergeNamespaces(N target, Collection sources) + throws IOException, RocksDBException { if (sources == null || sources.isEmpty()) { return; } - try { - ACC current = null; - - // merge the sources to the target - for (N source : sources) { - if (source != null) { - setCurrentNamespace(source); - final byte[] sourceKey = serializeCurrentKeyWithGroupAndNamespace(); - final byte[] valueBytes = backend.db.get(columnFamily, sourceKey); - - if (valueBytes != null) { - backend.db.delete(columnFamily, writeOptions, sourceKey); - dataInputView.setBuffer(valueBytes); - ACC value = valueSerializer.deserialize(dataInputView); - - if (current != null) { - current = aggFunction.merge(current, value); - } else { - current = value; - } - } - } - } + ACC current = null; - // if something came out of merging the sources, merge it or write it to the target - if (current != null) { - setCurrentNamespace(target); - // create the target full-binary-key - final byte[] targetKey = serializeCurrentKeyWithGroupAndNamespace(); - final byte[] targetValueBytes = backend.db.get(columnFamily, targetKey); + // merge the sources to the target + for (N source : sources) { + if (source != null) { + setCurrentNamespace(source); + final byte[] sourceKey = serializeCurrentKeyWithGroupAndNamespace(); + final byte[] valueBytes = backend.db.get(columnFamily, sourceKey); - if (targetValueBytes != null) { - // target also had a value, merge - dataInputView.setBuffer(targetValueBytes); + if (valueBytes != null) { + backend.db.delete(columnFamily, writeOptions, sourceKey); + dataInputView.setBuffer(valueBytes); ACC value = valueSerializer.deserialize(dataInputView); - current = aggFunction.merge(current, value); + if (current != null) { + current = aggFunction.merge(current, value); + } else { + current = value; + } } + } + } + + // if something came out of merging the sources, merge it or write it to the target + if (current != null) { + setCurrentNamespace(target); + // create the target full-binary-key + final byte[] targetKey = serializeCurrentKeyWithGroupAndNamespace(); + final byte[] targetValueBytes = backend.db.get(columnFamily, targetKey); - // serialize the resulting value - dataOutputView.clear(); - valueSerializer.serialize(current, dataOutputView); + if (targetValueBytes != null) { + // target also had a value, merge + dataInputView.setBuffer(targetValueBytes); + ACC value = valueSerializer.deserialize(dataInputView); - // write the resulting value - backend.db.put( - columnFamily, writeOptions, targetKey, dataOutputView.getCopyOfBuffer()); + current = aggFunction.merge(current, value); } - } catch (Exception e) { - throw new FlinkRuntimeException("Error while merging state in RocksDB", e); + + // serialize the resulting value + dataOutputView.clear(); + valueSerializer.serialize(current, dataOutputView); + + // write the resulting value + backend.db.put(columnFamily, writeOptions, targetKey, dataOutputView.getCopyOfBuffer()); } } diff --git a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBIncrementalCheckpointUtils.java b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBIncrementalCheckpointUtils.java index 5412170987604..a835d10c481a8 100644 --- a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBIncrementalCheckpointUtils.java +++ b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBIncrementalCheckpointUtils.java @@ -175,14 +175,14 @@ public static boolean beforeThePrefixBytes(@Nonnull byte[] bytes, @Nonnull byte[ * @return The best candidate or null if no candidate was a good fit. */ @Nullable - public static KeyedStateHandle chooseTheBestStateHandleForInitial( - @Nonnull Collection restoreStateHandles, + public static T chooseTheBestStateHandleForInitial( + @Nonnull Collection restoreStateHandles, @Nonnull KeyGroupRange targetKeyGroupRange, double overlapFractionThreshold) { - KeyedStateHandle bestStateHandle = null; + T bestStateHandle = null; Score bestScore = Score.MIN; - for (KeyedStateHandle rawStateHandle : restoreStateHandles) { + for (T rawStateHandle : restoreStateHandles) { Score handleScore = stateHandleEvaluator( rawStateHandle, targetKeyGroupRange, overlapFractionThreshold); diff --git a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBListState.java b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBListState.java index 553d86a63ce36..b5339fd4d25af 100644 --- a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBListState.java +++ b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBListState.java @@ -108,34 +108,26 @@ public TypeSerializer> getValueSerializer() { } @Override - public Iterable get() { + public Iterable get() throws IOException, RocksDBException { return getInternal(); } @Override - public List getInternal() { - try { - byte[] key = serializeCurrentKeyWithGroupAndNamespace(); - byte[] valueBytes = backend.db.get(columnFamily, key); - return listSerializer.deserializeList(valueBytes, elementSerializer); - } catch (RocksDBException e) { - throw new FlinkRuntimeException("Error while retrieving data from RocksDB", e); - } + public List getInternal() throws IOException, RocksDBException { + byte[] key = serializeCurrentKeyWithGroupAndNamespace(); + byte[] valueBytes = backend.db.get(columnFamily, key); + return listSerializer.deserializeList(valueBytes, elementSerializer); } @Override - public void add(V value) { + public void add(V value) throws IOException, RocksDBException { Preconditions.checkNotNull(value, "You cannot add null to a ListState."); - try { - backend.db.merge( - columnFamily, - writeOptions, - serializeCurrentKeyWithGroupAndNamespace(), - serializeValue(value, elementSerializer)); - } catch (Exception e) { - throw new FlinkRuntimeException("Error while adding data to RocksDB", e); - } + backend.db.merge( + columnFamily, + writeOptions, + serializeCurrentKeyWithGroupAndNamespace(), + serializeValue(value, elementSerializer)); } @Override @@ -169,43 +161,35 @@ public void mergeNamespaces(N target, Collection sources) { } @Override - public void update(List valueToStore) { + public void update(List valueToStore) throws IOException, RocksDBException { updateInternal(valueToStore); } @Override - public void updateInternal(List values) { + public void updateInternal(List values) throws IOException, RocksDBException { Preconditions.checkNotNull(values, "List of values to add cannot be null."); if (!values.isEmpty()) { - try { - backend.db.put( - columnFamily, - writeOptions, - serializeCurrentKeyWithGroupAndNamespace(), - listSerializer.serializeList(values, elementSerializer)); - } catch (IOException | RocksDBException e) { - throw new FlinkRuntimeException("Error while updating data to RocksDB", e); - } + backend.db.put( + columnFamily, + writeOptions, + serializeCurrentKeyWithGroupAndNamespace(), + listSerializer.serializeList(values, elementSerializer)); } else { clear(); } } @Override - public void addAll(List values) { + public void addAll(List values) throws IOException, RocksDBException { Preconditions.checkNotNull(values, "List of values to add cannot be null."); if (!values.isEmpty()) { - try { - backend.db.merge( - columnFamily, - writeOptions, - serializeCurrentKeyWithGroupAndNamespace(), - listSerializer.serializeList(values, elementSerializer)); - } catch (IOException | RocksDBException e) { - throw new FlinkRuntimeException("Error while updating data to RocksDB", e); - } + backend.db.merge( + columnFamily, + writeOptions, + serializeCurrentKeyWithGroupAndNamespace(), + listSerializer.serializeList(values, elementSerializer)); } } @@ -309,19 +293,21 @@ public byte[] filterOrTransform(@Nullable byte[] value) { in.setBuffer(value); T next; int prevPosition = 0; - while ((next = ListDelimitedSerializer.deserializeNextElement(in, elementSerializer)) - != null) { - T transformedElement = elementTransformer.filterOrTransform(next); - if (transformedElement != null) { - if (transformStrategy == STOP_ON_FIRST_INCLUDED) { - return Arrays.copyOfRange(value, prevPosition, value.length); - } else { - result.add(transformedElement); + try { + while ((next = + ListDelimitedSerializer.deserializeNextElement( + in, elementSerializer)) + != null) { + T transformedElement = elementTransformer.filterOrTransform(next); + if (transformedElement != null) { + if (transformStrategy == STOP_ON_FIRST_INCLUDED) { + return Arrays.copyOfRange(value, prevPosition, value.length); + } else { + result.add(transformedElement); + } } + prevPosition = in.getPosition(); } - prevPosition = in.getPosition(); - } - try { return result.isEmpty() ? null : listSerializer.serializeList(result, elementSerializer); diff --git a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBReducingState.java b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBReducingState.java index f50f11e9d72ad..e791b3f591a7e 100644 --- a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBReducingState.java +++ b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBReducingState.java @@ -27,10 +27,11 @@ import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.runtime.state.RegisteredKeyValueStateBackendMetaInfo; import org.apache.flink.runtime.state.internal.InternalReducingState; -import org.apache.flink.util.FlinkRuntimeException; import org.rocksdb.ColumnFamilyHandle; +import org.rocksdb.RocksDBException; +import java.io.IOException; import java.util.Collection; /** @@ -84,7 +85,7 @@ public TypeSerializer getValueSerializer() { } @Override - public V get() { + public V get() throws IOException, RocksDBException { return getInternal(); } @@ -97,60 +98,55 @@ public void add(V value) throws Exception { } @Override - public void mergeNamespaces(N target, Collection sources) { + public void mergeNamespaces(N target, Collection sources) throws Exception { if (sources == null || sources.isEmpty()) { return; } - try { - V current = null; - - // merge the sources to the target - for (N source : sources) { - if (source != null) { - setCurrentNamespace(source); - final byte[] sourceKey = serializeCurrentKeyWithGroupAndNamespace(); - final byte[] valueBytes = backend.db.get(columnFamily, sourceKey); - - if (valueBytes != null) { - backend.db.delete(columnFamily, writeOptions, sourceKey); - dataInputView.setBuffer(valueBytes); - V value = valueSerializer.deserialize(dataInputView); - - if (current != null) { - current = reduceFunction.reduce(current, value); - } else { - current = value; - } - } - } - } + V current = null; - // if something came out of merging the sources, merge it or write it to the target - if (current != null) { - // create the target full-binary-key - setCurrentNamespace(target); - final byte[] targetKey = serializeCurrentKeyWithGroupAndNamespace(); - final byte[] targetValueBytes = backend.db.get(columnFamily, targetKey); + // merge the sources to the target + for (N source : sources) { + if (source != null) { + setCurrentNamespace(source); + final byte[] sourceKey = serializeCurrentKeyWithGroupAndNamespace(); + final byte[] valueBytes = backend.db.get(columnFamily, sourceKey); - if (targetValueBytes != null) { - dataInputView.setBuffer(targetValueBytes); - // target also had a value, merge + if (valueBytes != null) { + backend.db.delete(columnFamily, writeOptions, sourceKey); + dataInputView.setBuffer(valueBytes); V value = valueSerializer.deserialize(dataInputView); - current = reduceFunction.reduce(current, value); + if (current != null) { + current = reduceFunction.reduce(current, value); + } else { + current = value; + } } + } + } + + // if something came out of merging the sources, merge it or write it to the target + if (current != null) { + // create the target full-binary-key + setCurrentNamespace(target); + final byte[] targetKey = serializeCurrentKeyWithGroupAndNamespace(); + final byte[] targetValueBytes = backend.db.get(columnFamily, targetKey); - // serialize the resulting value - dataOutputView.clear(); - valueSerializer.serialize(current, dataOutputView); + if (targetValueBytes != null) { + dataInputView.setBuffer(targetValueBytes); + // target also had a value, merge + V value = valueSerializer.deserialize(dataInputView); - // write the resulting value - backend.db.put( - columnFamily, writeOptions, targetKey, dataOutputView.getCopyOfBuffer()); + current = reduceFunction.reduce(current, value); } - } catch (Exception e) { - throw new FlinkRuntimeException("Error while merging state in RocksDB", e); + + // serialize the resulting value + dataOutputView.clear(); + valueSerializer.serialize(current, dataOutputView); + + // write the resulting value + backend.db.put(columnFamily, writeOptions, targetKey, dataOutputView.getCopyOfBuffer()); } } diff --git a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBValueState.java b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBValueState.java index 3cc780263cfdd..958efad068ab2 100644 --- a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBValueState.java +++ b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/RocksDBValueState.java @@ -25,7 +25,6 @@ import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.runtime.state.RegisteredKeyValueStateBackendMetaInfo; import org.apache.flink.runtime.state.internal.InternalValueState; -import org.apache.flink.util.FlinkRuntimeException; import org.rocksdb.ColumnFamilyHandle; import org.rocksdb.RocksDBException; @@ -77,7 +76,7 @@ public TypeSerializer getValueSerializer() { } @Override - public V value() { + public V value() throws IOException { try { byte[] valueBytes = backend.db.get(columnFamily, serializeCurrentKeyWithGroupAndNamespace()); @@ -87,13 +86,13 @@ public V value() { } dataInputView.setBuffer(valueBytes); return valueSerializer.deserialize(dataInputView); - } catch (IOException | RocksDBException e) { - throw new FlinkRuntimeException("Error while retrieving data from RocksDB.", e); + } catch (RocksDBException e) { + throw new IOException("Error while retrieving data from RocksDB.", e); } } @Override - public void update(V value) { + public void update(V value) throws IOException { if (value == null) { clear(); return; @@ -105,8 +104,8 @@ public void update(V value) { writeOptions, serializeCurrentKeyWithGroupAndNamespace(), serializeValue(value)); - } catch (Exception e) { - throw new FlinkRuntimeException("Error while adding data to RocksDB", e); + } catch (RocksDBException e) { + throw new IOException("Error while adding data to RocksDB", e); } } diff --git a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/StateHandleDownloadSpec.java b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/StateHandleDownloadSpec.java index 93a33fdc6fa97..5f37f84921ff4 100644 --- a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/StateHandleDownloadSpec.java +++ b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/StateHandleDownloadSpec.java @@ -18,6 +18,8 @@ package org.apache.flink.contrib.streaming.state; +import org.apache.flink.runtime.state.DirectoryStateHandle; +import org.apache.flink.runtime.state.IncrementalLocalKeyedStateHandle; import org.apache.flink.runtime.state.IncrementalRemoteKeyedStateHandle; import java.nio.file.Path; @@ -46,4 +48,14 @@ public IncrementalRemoteKeyedStateHandle getStateHandle() { public Path getDownloadDestination() { return downloadDestination; } + + public IncrementalLocalKeyedStateHandle createLocalStateHandleForDownloadedState() { + return new IncrementalLocalKeyedStateHandle( + stateHandle.getBackendIdentifier(), + stateHandle.getCheckpointId(), + new DirectoryStateHandle(downloadDestination), + stateHandle.getKeyGroupRange(), + stateHandle.getMetaDataStateHandle(), + stateHandle.getSharedState()); + } } diff --git a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/restore/RocksDBIncrementalRestoreOperation.java b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/restore/RocksDBIncrementalRestoreOperation.java index 3c36c754f3be1..11d4756ae1384 100644 --- a/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/restore/RocksDBIncrementalRestoreOperation.java +++ b/flink-state-backends/flink-statebackend-rocksdb/src/main/java/org/apache/flink/contrib/streaming/state/restore/RocksDBIncrementalRestoreOperation.java @@ -35,7 +35,6 @@ import org.apache.flink.metrics.MetricGroup; import org.apache.flink.runtime.state.BackendBuildingException; import org.apache.flink.runtime.state.CompositeKeySerializationUtils; -import org.apache.flink.runtime.state.DirectoryStateHandle; import org.apache.flink.runtime.state.IncrementalKeyedStateHandle; import org.apache.flink.runtime.state.IncrementalKeyedStateHandle.HandleAndLocalPath; import org.apache.flink.runtime.state.IncrementalLocalKeyedStateHandle; @@ -44,11 +43,9 @@ import org.apache.flink.runtime.state.KeyedBackendSerializationProxy; import org.apache.flink.runtime.state.KeyedStateHandle; import org.apache.flink.runtime.state.RegisteredStateMetaInfoBase; -import org.apache.flink.runtime.state.StateHandleID; import org.apache.flink.runtime.state.StateSerializerProvider; import org.apache.flink.runtime.state.StreamStateHandle; import org.apache.flink.runtime.state.metainfo.StateMetaInfoSnapshot; -import org.apache.flink.util.CollectionUtil; import org.apache.flink.util.FileUtils; import org.apache.flink.util.IOUtils; import org.apache.flink.util.Preconditions; @@ -227,40 +224,17 @@ private void restoreBaseDBFromRemoteState(IncrementalRemoteKeyedStateHandle stat new StateHandleDownloadSpec(stateHandle, tmpRestoreInstancePath); try { transferRemoteStateToLocalDirectory(Collections.singletonList(downloadRequest)); - restoreBaseDBFromDownloadedState(downloadRequest); + restoreBaseDBFromLocalState(downloadRequest.createLocalStateHandleForDownloadedState()); } finally { cleanUpPathQuietly(downloadRequest.getDownloadDestination()); } } - /** - * This helper method creates a {@link IncrementalLocalKeyedStateHandle} for state that was - * previously downloaded for a {@link IncrementalRemoteKeyedStateHandle} and then invokes the - * restore procedure for local state on the downloaded state. - * - * @param downloadedState the specification of a completed state download. - * @throws Exception for restore problems. - */ - private void restoreBaseDBFromDownloadedState(StateHandleDownloadSpec downloadedState) - throws Exception { - // since we transferred all remote state to a local directory, we can use the same code - // as for local recovery. - IncrementalRemoteKeyedStateHandle stateHandle = downloadedState.getStateHandle(); - restoreBaseDBFromLocalState( - new IncrementalLocalKeyedStateHandle( - stateHandle.getBackendIdentifier(), - stateHandle.getCheckpointId(), - new DirectoryStateHandle(downloadedState.getDownloadDestination()), - stateHandle.getKeyGroupRange(), - stateHandle.getMetaStateHandle(), - stateHandle.getSharedState())); - } - /** Restores RocksDB instance from local state. */ private void restoreBaseDBFromLocalState(IncrementalLocalKeyedStateHandle localKeyedStateHandle) throws Exception { KeyedBackendSerializationProxy serializationProxy = - readMetaData(localKeyedStateHandle.getMetaDataState()); + readMetaData(localKeyedStateHandle.getMetaDataStateHandle()); List stateMetaInfoSnapshots = serializationProxy.getStateMetaInfoSnapshots(); @@ -304,113 +278,133 @@ private void restoreWithRescaling(Collection restoreStateHandl Preconditions.checkArgument(restoreStateHandles != null && !restoreStateHandles.isEmpty()); - Map allDownloadSpecs = - CollectionUtil.newHashMapWithExpectedSize(restoreStateHandles.size()); - - // Choose the best state handle for the initial DB - final KeyedStateHandle selectedInitialHandle = - RocksDBIncrementalCheckpointUtils.chooseTheBestStateHandleForInitial( - restoreStateHandles, keyGroupRange, overlapFractionThreshold); + final List allDownloadSpecs = new ArrayList<>(); - Preconditions.checkNotNull(selectedInitialHandle); + final List localKeyedStateHandles = + new ArrayList<>(restoreStateHandles.size()); final Path absolutInstanceBasePath = instanceBasePath.getAbsoluteFile().toPath(); // Prepare and collect all the download request to pull remote state to a local directory for (KeyedStateHandle stateHandle : restoreStateHandles) { - if (!(stateHandle instanceof IncrementalRemoteKeyedStateHandle)) { + if (stateHandle instanceof IncrementalRemoteKeyedStateHandle) { + StateHandleDownloadSpec downloadRequest = + new StateHandleDownloadSpec( + (IncrementalRemoteKeyedStateHandle) stateHandle, + absolutInstanceBasePath.resolve(UUID.randomUUID().toString())); + allDownloadSpecs.add(downloadRequest); + } else if (stateHandle instanceof IncrementalLocalKeyedStateHandle) { + localKeyedStateHandles.add((IncrementalLocalKeyedStateHandle) stateHandle); + } else { throw unexpectedStateHandleException( IncrementalRemoteKeyedStateHandle.class, stateHandle.getClass()); } - StateHandleDownloadSpec downloadRequest = - new StateHandleDownloadSpec( - (IncrementalRemoteKeyedStateHandle) stateHandle, - absolutInstanceBasePath.resolve(UUID.randomUUID().toString())); - allDownloadSpecs.put(stateHandle.getStateHandleId(), downloadRequest); } - // Process all state downloads - transferRemoteStateToLocalDirectory(allDownloadSpecs.values()); - - // Init the base DB instance with the initial state - initBaseDBForRescaling(allDownloadSpecs.remove(selectedInitialHandle.getStateHandleId())); - - // Transfer remaining key-groups from temporary instance into base DB - byte[] startKeyGroupPrefixBytes = new byte[keyGroupPrefixBytes]; - CompositeKeySerializationUtils.serializeKeyGroup( - keyGroupRange.getStartKeyGroup(), startKeyGroupPrefixBytes); - - byte[] stopKeyGroupPrefixBytes = new byte[keyGroupPrefixBytes]; - CompositeKeySerializationUtils.serializeKeyGroup( - keyGroupRange.getEndKeyGroup() + 1, stopKeyGroupPrefixBytes); - - // Insert all remaining state through creating temporary RocksDB instances - for (StateHandleDownloadSpec downloadRequest : allDownloadSpecs.values()) { - logger.info( - "Starting to restore from state handle: {} with rescaling.", - downloadRequest.getStateHandle()); - - try (RestoredDBInstance tmpRestoreDBInfo = - restoreTempDBInstanceFromDownloadedState(downloadRequest); - RocksDBWriteBatchWrapper writeBatchWrapper = - new RocksDBWriteBatchWrapper( - this.rocksHandle.getDb(), writeBatchSize)) { - - List tmpColumnFamilyDescriptors = - tmpRestoreDBInfo.columnFamilyDescriptors; - List tmpColumnFamilyHandles = - tmpRestoreDBInfo.columnFamilyHandles; - - // iterating only the requested descriptors automatically skips the default column - // family handle - for (int descIdx = 0; descIdx < tmpColumnFamilyDescriptors.size(); ++descIdx) { - ColumnFamilyHandle tmpColumnFamilyHandle = tmpColumnFamilyHandles.get(descIdx); - - ColumnFamilyHandle targetColumnFamilyHandle = - this.rocksHandle.getOrRegisterStateColumnFamilyHandle( - null, - tmpRestoreDBInfo.stateMetaInfoSnapshots.get(descIdx)) - .columnFamilyHandle; - - try (RocksIteratorWrapper iterator = - RocksDBOperationUtils.getRocksIterator( - tmpRestoreDBInfo.db, - tmpColumnFamilyHandle, - tmpRestoreDBInfo.readOptions)) { - - iterator.seek(startKeyGroupPrefixBytes); - - while (iterator.isValid()) { - - if (RocksDBIncrementalCheckpointUtils.beforeThePrefixBytes( - iterator.key(), stopKeyGroupPrefixBytes)) { - writeBatchWrapper.put( - targetColumnFamilyHandle, iterator.key(), iterator.value()); - } else { - // Since the iterator will visit the record according to the sorted - // order, - // we can just break here. - break; - } + allDownloadSpecs.stream() + .map(StateHandleDownloadSpec::createLocalStateHandleForDownloadedState) + .forEach(localKeyedStateHandles::add); - iterator.next(); - } - } // releases native iterator resources - } + // Choose the best state handle for the initial DB + final IncrementalLocalKeyedStateHandle selectedInitialHandle = + RocksDBIncrementalCheckpointUtils.chooseTheBestStateHandleForInitial( + localKeyedStateHandles, keyGroupRange, overlapFractionThreshold); + Preconditions.checkNotNull(selectedInitialHandle); + // Remove the selected handle from the list so that we don't restore it twice. + localKeyedStateHandles.remove(selectedInitialHandle); + + try { + // Process all state downloads + transferRemoteStateToLocalDirectory(allDownloadSpecs); + + // Init the base DB instance with the initial state + initBaseDBForRescaling(selectedInitialHandle); + + // Transfer remaining key-groups from temporary instance into base DB + byte[] startKeyGroupPrefixBytes = new byte[keyGroupPrefixBytes]; + CompositeKeySerializationUtils.serializeKeyGroup( + keyGroupRange.getStartKeyGroup(), startKeyGroupPrefixBytes); + + byte[] stopKeyGroupPrefixBytes = new byte[keyGroupPrefixBytes]; + CompositeKeySerializationUtils.serializeKeyGroup( + keyGroupRange.getEndKeyGroup() + 1, stopKeyGroupPrefixBytes); + + // Insert all remaining state through creating temporary RocksDB instances + for (IncrementalLocalKeyedStateHandle stateHandle : localKeyedStateHandles) { logger.info( - "Finished restoring from state handle: {} with rescaling.", - downloadRequest.getStateHandle()); - } finally { - cleanUpPathQuietly(downloadRequest.getDownloadDestination()); + "Starting to restore from state handle: {} with rescaling.", stateHandle); + + try (RestoredDBInstance tmpRestoreDBInfo = + restoreTempDBInstanceFromLocalState(stateHandle); + RocksDBWriteBatchWrapper writeBatchWrapper = + new RocksDBWriteBatchWrapper( + this.rocksHandle.getDb(), writeBatchSize)) { + + List tmpColumnFamilyDescriptors = + tmpRestoreDBInfo.columnFamilyDescriptors; + List tmpColumnFamilyHandles = + tmpRestoreDBInfo.columnFamilyHandles; + + // iterating only the requested descriptors automatically skips the default + // column + // family handle + for (int descIdx = 0; descIdx < tmpColumnFamilyDescriptors.size(); ++descIdx) { + ColumnFamilyHandle tmpColumnFamilyHandle = + tmpColumnFamilyHandles.get(descIdx); + + ColumnFamilyHandle targetColumnFamilyHandle = + this.rocksHandle.getOrRegisterStateColumnFamilyHandle( + null, + tmpRestoreDBInfo.stateMetaInfoSnapshots.get( + descIdx)) + .columnFamilyHandle; + + try (RocksIteratorWrapper iterator = + RocksDBOperationUtils.getRocksIterator( + tmpRestoreDBInfo.db, + tmpColumnFamilyHandle, + tmpRestoreDBInfo.readOptions)) { + + iterator.seek(startKeyGroupPrefixBytes); + + while (iterator.isValid()) { + + if (RocksDBIncrementalCheckpointUtils.beforeThePrefixBytes( + iterator.key(), stopKeyGroupPrefixBytes)) { + writeBatchWrapper.put( + targetColumnFamilyHandle, + iterator.key(), + iterator.value()); + } else { + // Since the iterator will visit the record according to the + // sorted + // order, + // we can just break here. + break; + } + + iterator.next(); + } + } // releases native iterator resources + } + logger.info( + "Finished restoring from state handle: {} with rescaling.", + stateHandle); + } } + } finally { + // Cleanup all download directories + allDownloadSpecs.stream() + .map(StateHandleDownloadSpec::getDownloadDestination) + .forEach(this::cleanUpPathQuietly); } } - private void initBaseDBForRescaling(StateHandleDownloadSpec downloadedInitialState) + private void initBaseDBForRescaling(IncrementalLocalKeyedStateHandle stateHandle) throws Exception { // 1. Restore base DB from selected initial handle - restoreBaseDBFromDownloadedState(downloadedInitialState); + restoreBaseDBFromLocalState(stateHandle); // 2. Clip the base DB instance try { @@ -418,7 +412,7 @@ private void initBaseDBForRescaling(StateHandleDownloadSpec downloadedInitialSta this.rocksHandle.getDb(), this.rocksHandle.getColumnFamilyHandles(), keyGroupRange, - downloadedInitialState.getStateHandle().getKeyGroupRange(), + stateHandle.getKeyGroupRange(), keyGroupPrefixBytes); } catch (RocksDBException e) { String errMsg = "Failed to clip DB after initialization."; @@ -470,11 +464,10 @@ public void close() { } } - private RestoredDBInstance restoreTempDBInstanceFromDownloadedState( - StateHandleDownloadSpec downloadRequest) throws Exception { - + private RestoredDBInstance restoreTempDBInstanceFromLocalState( + IncrementalLocalKeyedStateHandle stateHandle) throws Exception { KeyedBackendSerializationProxy serializationProxy = - readMetaData(downloadRequest.getStateHandle().getMetaStateHandle()); + readMetaData(stateHandle.getMetaDataStateHandle()); // read meta data List stateMetaInfoSnapshots = serializationProxy.getStateMetaInfoSnapshots(); @@ -487,7 +480,7 @@ private RestoredDBInstance restoreTempDBInstanceFromDownloadedState( RocksDB restoreDb = RocksDBOperationUtils.openDB( - downloadRequest.getDownloadDestination().toString(), + stateHandle.getDirectoryStateHandle().getDirectory().toString(), columnFamilyDescriptors, columnFamilyHandles, RocksDBOperationUtils.createColumnFamilyOptions( diff --git a/flink-streaming-java/pom.xml b/flink-streaming-java/pom.xml index f66ed3b48b819..4a7644dfb453b 100644 --- a/flink-streaming-java/pom.xml +++ b/flink-streaming-java/pom.xml @@ -34,7 +34,14 @@ under the License. jar - + + ${surefire.module.config.jdk21} --add-opens=java.base/java.lang=ALL-UNNAMED + -Djava.security.manager=allow + + diff --git a/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/environment/StreamExecutionEnvironment.java b/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/environment/StreamExecutionEnvironment.java index 891aabc52f671..343308edcf2d8 100644 --- a/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/environment/StreamExecutionEnvironment.java +++ b/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/environment/StreamExecutionEnvironment.java @@ -73,6 +73,8 @@ import org.apache.flink.core.fs.Path; import org.apache.flink.runtime.clusterframework.types.ResourceProfile; import org.apache.flink.runtime.scheduler.ClusterDatasetCorruptedException; +import org.apache.flink.runtime.state.CheckpointStorage; +import org.apache.flink.runtime.state.CheckpointStorageLoader; import org.apache.flink.runtime.state.KeyGroupRangeAssignment; import org.apache.flink.runtime.state.StateBackend; import org.apache.flink.runtime.state.StateBackendLoader; @@ -1065,6 +1067,11 @@ public void configure(ReadableConfig configuration, ClassLoader classLoader) { config.configure(configuration, classLoader); checkpointCfg.configure(configuration); + + // here we should make sure the configured checkpoint storage will take effect + // this needs to happen after checkpointCfg#configure(...) to override the effect of + // checkpointCfg#setCheckpointStorage(checkpointDirectory) + configureCheckpointStorage(configuration, checkpointCfg); } private void registerCustomListeners( @@ -1098,6 +1105,17 @@ private void configBufferTimeout(ReadableConfig configuration) { } } + protected void configureCheckpointStorage( + ReadableConfig configuration, CheckpointConfig checkpointCfg) { + try { + Optional storageOptional = + CheckpointStorageLoader.fromConfig(configuration, userClassloader, null); + storageOptional.ifPresent(checkpointCfg::setCheckpointStorage); + } catch (DynamicCodeLoadingException e) { + throw new WrappingRuntimeException(e); + } + } + // -------------------------------------------------------------------------------------------- // Data stream creations // -------------------------------------------------------------------------------------------- diff --git a/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/functions/source/StatefulSequenceSource.java b/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/functions/source/StatefulSequenceSource.java index a9f4d1e7023b5..983f49c7eca99 100644 --- a/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/functions/source/StatefulSequenceSource.java +++ b/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/functions/source/StatefulSequenceSource.java @@ -42,6 +42,9 @@ *

This strategy guarantees that each element will be emitted exactly-once, but elements will not * necessarily be emitted in ascending order, even for the same tasks. * + *

NOTE: this source will be removed together with the deprecated + * StreamExecutionEnvironmetn#generateSequence() method. + * * @deprecated This class is based on the {@link * org.apache.flink.streaming.api.functions.source.SourceFunction} API, which is due to be * removed. Use the new {@link org.apache.flink.api.connector.source.Source} API instead. diff --git a/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/graph/StreamConfig.java b/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/graph/StreamConfig.java index 267289c181f30..91ed4ce3b4797 100644 --- a/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/graph/StreamConfig.java +++ b/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/graph/StreamConfig.java @@ -52,6 +52,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -78,7 +79,12 @@ public class StreamConfig implements Serializable { // Config Keys // ------------------------------------------------------------------------ - @VisibleForTesting public static final String SERIALIZEDUDF = "serializedUDF"; + public static final String SERIALIZED_UDF = "serializedUDF"; + /** + * Introduce serializedUdfClassName to avoid unnecessarily heavy {@link + * #getStreamOperatorFactory}. + */ + public static final String SERIALIZED_UDF_CLASS = "serializedUdfClass"; private static final String NUMBER_OF_OUTPUTS = "numberOfOutputs"; private static final String NUMBER_OF_NETWORK_INPUTS = "numberOfNetworkInputs"; @@ -146,6 +152,13 @@ public class StreamConfig implements Serializable { private final transient CompletableFuture serializationFuture = new CompletableFuture<>(); + /** + * In order to release memory during processing data, some keys are removed in {@link + * #clearInitialConfigs()}. Recording these keys here to prevent they are accessed after + * removing. + */ + private final Set removedKeys = new HashSet<>(); + public StreamConfig(Configuration config) { this.config = config; } @@ -368,7 +381,8 @@ public void setStreamOperator(StreamOperator operator) { public void setStreamOperatorFactory(StreamOperatorFactory factory) { if (factory != null) { - toBeSerializedConfigObjects.put(SERIALIZEDUDF, factory); + toBeSerializedConfigObjects.put(SERIALIZED_UDF, factory); + toBeSerializedConfigObjects.put(SERIALIZED_UDF_CLASS, factory.getClass()); } } @@ -380,7 +394,10 @@ public > T getStreamOperator(ClassLoader cl) { public > T getStreamOperatorFactory(ClassLoader cl) { try { - return InstantiationUtil.readObjectFromConfig(this.config, SERIALIZEDUDF, cl); + checkState( + !removedKeys.contains(SERIALIZED_UDF), + String.format("%s has been removed.", SERIALIZED_UDF)); + return InstantiationUtil.readObjectFromConfig(this.config, SERIALIZED_UDF, cl); } catch (ClassNotFoundException e) { String classLoaderInfo = ClassLoaderUtil.getUserCodeClassLoaderInfo(cl); boolean loadableDoubleCheck = ClassLoaderUtil.validateClassLoadable(e, cl); @@ -400,6 +417,15 @@ public > T getStreamOperatorFactory(ClassLoad } } + public > Class getStreamOperatorFactoryClass( + ClassLoader cl) { + try { + return InstantiationUtil.readObjectFromConfig(this.config, SERIALIZED_UDF_CLASS, cl); + } catch (Exception e) { + throw new StreamTaskException("Could not instantiate serialized udf class.", e); + } + } + public void setIterationId(String iterationId) { config.setString(ITERATION_ID, iterationId); } @@ -569,6 +595,9 @@ public void setTransitiveChainedTaskConfigs(Map chainedTa public Map getTransitiveChainedTaskConfigs(ClassLoader cl) { try { + checkState( + !removedKeys.contains(CHAINED_TASK_CONFIG), + String.format("%s has been removed.", CHAINED_TASK_CONFIG)); Map confs = InstantiationUtil.readObjectFromConfig(this.config, CHAINED_TASK_CONFIG, cl); return confs == null ? new HashMap() : confs; @@ -758,7 +787,7 @@ public String toString() { try { builder.append("\nOperator: ") - .append(getStreamOperatorFactory(cl).getClass().getSimpleName()); + .append(getStreamOperatorFactoryClass(cl).getSimpleName()); } catch (Exception e) { builder.append("\nOperator: Missing"); } @@ -780,6 +809,23 @@ public boolean isGraphContainingLoops() { return config.getBoolean(GRAPH_CONTAINING_LOOPS, false); } + /** + * In general, we don't clear any configuration. However, the {@link #SERIALIZED_UDF} may be + * very large when operator includes some large objects, the SERIALIZED_UDF is used to create a + * StreamOperator and usually only needs to be called once. {@link #CHAINED_TASK_CONFIG} may be + * large as well due to the StreamConfig of all non-head operators in OperatorChain will be + * serialized and stored in CHAINED_TASK_CONFIG. They can be cleared to reduce the memory after + * StreamTask is initialized. If so, TM will have more memory during running. See FLINK-33315 + * and FLINK-33317 for more information. + */ + public void clearInitialConfigs() { + removedKeys.add(SERIALIZED_UDF); + config.removeKey(SERIALIZED_UDF); + + removedKeys.add(CHAINED_TASK_CONFIG); + config.removeKey(CHAINED_TASK_CONFIG); + } + /** * Requirements of the different inputs of an operator. Each input can have a different * requirement. For all {@link #SORTED} inputs, records are sorted/grouped by key and all diff --git a/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/operators/sorted/state/BatchExecutionKeyAggregatingState.java b/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/operators/sorted/state/BatchExecutionKeyAggregatingState.java index 08e34f04b38e9..7dec9f21d373c 100644 --- a/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/operators/sorted/state/BatchExecutionKeyAggregatingState.java +++ b/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/operators/sorted/state/BatchExecutionKeyAggregatingState.java @@ -26,8 +26,6 @@ import org.apache.flink.api.common.typeutils.TypeSerializer; import org.apache.flink.runtime.state.internal.InternalAggregatingState; -import java.io.IOException; - /** An {@link AggregatingState} which keeps value for a single key at a time. */ class BatchExecutionKeyAggregatingState extends MergingAbstractBatchExecutionKeyState @@ -52,21 +50,15 @@ public OUT get() { } @Override - public void add(IN value) throws IOException { + public void add(IN value) { if (value == null) { clear(); return; } - - try { - if (getCurrentNamespaceValue() == null) { - setCurrentNamespaceValue(aggFunction.createAccumulator()); - } - setCurrentNamespaceValue(aggFunction.add(value, getCurrentNamespaceValue())); - } catch (Exception e) { - throw new IOException( - "Exception while applying AggregateFunction in aggregating state", e); + if (getCurrentNamespaceValue() == null) { + setCurrentNamespaceValue(aggFunction.createAccumulator()); } + setCurrentNamespaceValue(aggFunction.add(value, getCurrentNamespaceValue())); } @SuppressWarnings("unchecked") diff --git a/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/operators/sorted/state/BatchExecutionKeyListState.java b/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/operators/sorted/state/BatchExecutionKeyListState.java index 2fecbc2c50706..cdc17d68a2c85 100644 --- a/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/operators/sorted/state/BatchExecutionKeyListState.java +++ b/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/operators/sorted/state/BatchExecutionKeyListState.java @@ -47,6 +47,7 @@ public void update(List values) { checkNotNull(values); clear(); for (T value : values) { + checkNotNull(value); add(value); } } @@ -57,6 +58,7 @@ public void addAll(List values) { return; } for (T value : values) { + checkNotNull(value); add(value); } } diff --git a/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/operators/sorted/state/BatchExecutionKeyReducingState.java b/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/operators/sorted/state/BatchExecutionKeyReducingState.java index 54a7bef89171d..8ff81b6338906 100644 --- a/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/operators/sorted/state/BatchExecutionKeyReducingState.java +++ b/flink-streaming-java/src/main/java/org/apache/flink/streaming/api/operators/sorted/state/BatchExecutionKeyReducingState.java @@ -26,8 +26,6 @@ import org.apache.flink.api.common.typeutils.TypeSerializer; import org.apache.flink.runtime.state.internal.InternalReducingState; -import java.io.IOException; - /** A {@link ReducingState} which keeps value for a single key at a time. */ class BatchExecutionKeyReducingState extends MergingAbstractBatchExecutionKeyState @@ -50,21 +48,17 @@ public T get() { } @Override - public void add(T value) throws IOException { + public void add(T value) throws Exception { if (value == null) { clear(); return; } - try { - T currentNamespaceValue = getCurrentNamespaceValue(); - if (currentNamespaceValue != null) { - setCurrentNamespaceValue(reduceFunction.reduce(currentNamespaceValue, value)); - } else { - setCurrentNamespaceValue(value); - } - } catch (Exception e) { - throw new IOException("Exception while applying ReduceFunction in reducing state", e); + T currentNamespaceValue = getCurrentNamespaceValue(); + if (currentNamespaceValue != null) { + setCurrentNamespaceValue(reduceFunction.reduce(currentNamespaceValue, value)); + } else { + setCurrentNamespaceValue(value); } } diff --git a/flink-streaming-java/src/main/java/org/apache/flink/streaming/runtime/tasks/OperatorChain.java b/flink-streaming-java/src/main/java/org/apache/flink/streaming/runtime/tasks/OperatorChain.java index b581990e8c84d..2a7a8dc3c1b54 100644 --- a/flink-streaming-java/src/main/java/org/apache/flink/streaming/runtime/tasks/OperatorChain.java +++ b/flink-streaming-java/src/main/java/org/apache/flink/streaming/runtime/tasks/OperatorChain.java @@ -642,8 +642,9 @@ private Map createChainedSources( private Counter getOperatorRecordsOutCounter( StreamTask containingTask, StreamConfig operatorConfig) { ClassLoader userCodeClassloader = containingTask.getUserCodeClassLoader(); - StreamOperatorFactory operatorFactory = - operatorConfig.getStreamOperatorFactory(userCodeClassloader); + Class> streamOperatorFactoryClass = + operatorConfig.getStreamOperatorFactoryClass(userCodeClassloader); + // Do not use the numRecordsOut counter on output if this operator is SinkWriterOperator. // // Metric "numRecordsOut" is defined as the total number of records written to the @@ -651,8 +652,15 @@ private Counter getOperatorRecordsOutCounter( // number of records sent to downstream operators, which is number of Committable batches // sent to SinkCommitter. So we skip registering this metric on output and leave this metric // to sink writer implementations to report. - if (operatorFactory instanceof SinkWriterOperatorFactory) { - return null; + try { + Class sinkWriterFactoryClass = + userCodeClassloader.loadClass(SinkWriterOperatorFactory.class.getName()); + if (sinkWriterFactoryClass.isAssignableFrom(streamOperatorFactoryClass)) { + return null; + } + } catch (ClassNotFoundException e) { + throw new StreamTaskException( + "Could not load SinkWriterOperatorFactory class from userCodeClassloader.", e); } InternalOperatorMetricGroup operatorMetricGroup = diff --git a/flink-streaming-java/src/main/java/org/apache/flink/streaming/runtime/tasks/StreamTask.java b/flink-streaming-java/src/main/java/org/apache/flink/streaming/runtime/tasks/StreamTask.java index 39b460337dd02..eef96812fc43b 100644 --- a/flink-streaming-java/src/main/java/org/apache/flink/streaming/runtime/tasks/StreamTask.java +++ b/flink-streaming-java/src/main/java/org/apache/flink/streaming/runtime/tasks/StreamTask.java @@ -743,6 +743,7 @@ void restoreInternal() throws Exception { // task specific initialization init(); + configuration.clearInitialConfigs(); // save the work of reloading state, etc, if the task is already canceled ensureNotCanceled(); diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/datastream/DataStreamSinkTest.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/datastream/DataStreamSinkTest.java index fb2e9d4ccebf1..401ed55215c1b 100644 --- a/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/datastream/DataStreamSinkTest.java +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/datastream/DataStreamSinkTest.java @@ -20,7 +20,7 @@ import org.apache.flink.api.dag.Transformation; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.api.transformations.SinkTransformation; -import org.apache.flink.streaming.runtime.operators.sink.TestSink; +import org.apache.flink.streaming.runtime.operators.sink.TestSinkV2; import org.junit.Test; @@ -33,13 +33,15 @@ public class DataStreamSinkTest { public void testGettingTransformationWithNewSinkAPI() { StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); final Transformation transformation = - env.fromElements(1, 2).sinkTo(TestSink.newBuilder().build()).getTransformation(); + env.fromElements(1, 2) + .sinkTo(TestSinkV2.newBuilder().build()) + .getTransformation(); assertTrue(transformation instanceof SinkTransformation); } @Test(expected = UnsupportedOperationException.class) public void throwExceptionWhenSetUidWithNewSinkAPI() { StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); - env.fromElements(1, 2).sinkTo(TestSink.newBuilder().build()).setUidHash("Test"); + env.fromElements(1, 2).sinkTo(TestSinkV2.newBuilder().build()).setUidHash("Test"); } } diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/environment/StreamExecutionEnvironmentTest.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/environment/StreamExecutionEnvironmentTest.java index 4cb61a7d4acf5..f04ce7a7883a1 100644 --- a/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/environment/StreamExecutionEnvironmentTest.java +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/environment/StreamExecutionEnvironmentTest.java @@ -25,12 +25,16 @@ import org.apache.flink.api.common.typeinfo.Types; import org.apache.flink.api.java.typeutils.GenericTypeInfo; import org.apache.flink.api.java.typeutils.ResultTypeQueryable; +import org.apache.flink.configuration.CheckpointingOptions; import org.apache.flink.configuration.Configuration; import org.apache.flink.configuration.ExecutionOptions; import org.apache.flink.configuration.PipelineOptions; +import org.apache.flink.core.fs.Path; import org.apache.flink.core.testutils.CheckedThread; import org.apache.flink.core.testutils.OneShotLatch; import org.apache.flink.runtime.clusterframework.types.ResourceProfile; +import org.apache.flink.runtime.state.CheckpointStorage; +import org.apache.flink.runtime.state.storage.JobManagerCheckpointStorage; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.datastream.DataStreamSource; import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator; @@ -473,6 +477,21 @@ public void go() { } } + @Test + void testConfigureCheckpointStorage() { + Configuration configuration = new Configuration(); + String path = "file:///valid"; + configuration.set(CheckpointingOptions.CHECKPOINT_STORAGE, "jobmanager"); + configuration.set(CheckpointingOptions.CHECKPOINTS_DIRECTORY, path); + StreamExecutionEnvironment env = + StreamExecutionEnvironment.getExecutionEnvironment(configuration); + + CheckpointStorage storage = env.getCheckpointConfig().getCheckpointStorage(); + assertThat(storage).isInstanceOf(JobManagerCheckpointStorage.class); + assertThat(((JobManagerCheckpointStorage) storage).getCheckpointPath()) + .isEqualTo(new Path(path)); + } + ///////////////////////////////////////////////////////////// // Utilities ///////////////////////////////////////////////////////////// diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/functions/PrintSinkTest.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/functions/PrintSinkTest.java index e8760d6d5de99..352118db5d473 100644 --- a/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/functions/PrintSinkTest.java +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/functions/PrintSinkTest.java @@ -26,8 +26,7 @@ import org.apache.flink.api.connector.sink2.SinkWriter; import org.apache.flink.metrics.MetricGroup; import org.apache.flink.metrics.groups.SinkWriterMetricGroup; -import org.apache.flink.metrics.groups.UnregisteredMetricsGroup; -import org.apache.flink.runtime.metrics.groups.InternalSinkWriterMetricGroup; +import org.apache.flink.runtime.metrics.groups.MetricsGroupTestUtils; import org.apache.flink.streaming.api.functions.sink.PrintSink; import org.apache.flink.streaming.runtime.tasks.TestProcessingTimeService; import org.apache.flink.util.FlinkRuntimeException; @@ -200,7 +199,7 @@ public int getAttemptNumber() { @Override public SinkWriterMetricGroup metricGroup() { - return InternalSinkWriterMetricGroup.mock(new UnregisteredMetricsGroup()); + return MetricsGroupTestUtils.mockWriterMetricGroup(); } @Override diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/graph/SinkTransformationTranslatorITCaseBase.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/graph/SinkTransformationTranslatorITCaseBase.java new file mode 100644 index 0000000000000..3c93b178b518e --- /dev/null +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/graph/SinkTransformationTranslatorITCaseBase.java @@ -0,0 +1,225 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.streaming.api.graph; + +import org.apache.flink.api.common.RuntimeExecutionMode; +import org.apache.flink.api.common.typeutils.base.IntSerializer; +import org.apache.flink.configuration.Configuration; +import org.apache.flink.configuration.ExecutionOptions; +import org.apache.flink.core.io.SimpleVersionedSerializerTypeSerializerProxy; +import org.apache.flink.streaming.api.datastream.DataStream; +import org.apache.flink.streaming.api.datastream.DataStreamSink; +import org.apache.flink.streaming.api.datastream.DataStreamSource; +import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; +import org.apache.flink.streaming.api.operators.ChainingStrategy; +import org.apache.flink.streaming.api.operators.StreamOperatorFactory; +import org.apache.flink.streaming.runtime.operators.sink.CommitterOperatorFactory; +import org.apache.flink.streaming.runtime.operators.sink.SinkWriterOperatorFactory; +import org.apache.flink.util.TestLogger; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; +import java.util.function.Predicate; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * Tests for {@link org.apache.flink.streaming.api.transformations.SinkTransformation}. + * + *

ATTENTION: This test is extremely brittle. Do NOT remove, add or re-order test cases. + */ +@RunWith(Parameterized.class) +public abstract class SinkTransformationTranslatorITCaseBase extends TestLogger { + + @Parameterized.Parameters(name = "Execution Mode: {0}") + public static Collection data() { + return Arrays.asList(RuntimeExecutionMode.STREAMING, RuntimeExecutionMode.BATCH); + } + + @Parameterized.Parameter() public RuntimeExecutionMode runtimeExecutionMode; + + static final String NAME = "FileSink"; + static final String SLOT_SHARE_GROUP = "FileGroup"; + static final String UID = "FileUid"; + static final int PARALLELISM = 2; + + abstract SinkT simpleSink(); + + abstract SinkT sinkWithCommitter(); + + abstract DataStreamSink sinkTo(DataStream stream, SinkT sink); + + @Test + public void generateWriterTopology() { + final StreamGraph streamGraph = buildGraph(simpleSink(), runtimeExecutionMode); + + final StreamNode sourceNode = findNodeName(streamGraph, node -> node.contains("Source")); + final StreamNode writerNode = findWriter(streamGraph); + + assertThat(streamGraph.getStreamNodes().size(), equalTo(2)); + + validateTopology( + sourceNode, + IntSerializer.class, + writerNode, + SinkWriterOperatorFactory.class, + PARALLELISM, + -1); + } + + @Test + public void generateWriterCommitterTopology() { + + final StreamGraph streamGraph = buildGraph(sinkWithCommitter(), runtimeExecutionMode); + + final StreamNode sourceNode = findNodeName(streamGraph, node -> node.contains("Source")); + final StreamNode writerNode = findWriter(streamGraph); + + validateTopology( + sourceNode, + IntSerializer.class, + writerNode, + SinkWriterOperatorFactory.class, + PARALLELISM, + -1); + + final StreamNode committerNode = + findNodeName(streamGraph, name -> name.contains("Committer")); + + assertThat(streamGraph.getStreamNodes().size(), equalTo(3)); + + validateTopology( + writerNode, + SimpleVersionedSerializerTypeSerializerProxy.class, + committerNode, + CommitterOperatorFactory.class, + PARALLELISM, + -1); + } + + StreamNode findWriter(StreamGraph streamGraph) { + return findNodeName( + streamGraph, name -> name.contains("Writer") && !name.contains("Committer")); + } + + StreamNode findCommitter(StreamGraph streamGraph) { + return findNodeName( + streamGraph, + name -> name.contains("Committer") && !name.contains("Global Committer")); + } + + StreamNode findGlobalCommitter(StreamGraph streamGraph) { + return findNodeName(streamGraph, name -> name.contains("Global Committer")); + } + + @Test(expected = IllegalStateException.class) + public void throwExceptionWithoutSettingUid() { + StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); + + final Configuration config = new Configuration(); + config.set(ExecutionOptions.RUNTIME_MODE, runtimeExecutionMode); + env.configure(config, getClass().getClassLoader()); + // disable auto generating uid + env.getConfig().disableAutoGeneratedUIDs(); + sinkTo(env.fromElements(1, 2), simpleSink()); + env.getStreamGraph(); + } + + @Test + public void disableOperatorChain() { + StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); + + final DataStreamSource src = env.fromElements(1, 2); + final DataStreamSink dataStreamSink = sinkTo(src, sinkWithCommitter()).name(NAME); + dataStreamSink.disableChaining(); + + final StreamGraph streamGraph = env.getStreamGraph(); + final StreamNode writer = findWriter(streamGraph); + final StreamNode committer = findCommitter(streamGraph); + + assertThat(writer.getOperatorFactory().getChainingStrategy(), is(ChainingStrategy.NEVER)); + assertThat( + committer.getOperatorFactory().getChainingStrategy(), is(ChainingStrategy.NEVER)); + } + + void validateTopology( + StreamNode src, + Class srcOutTypeInfo, + StreamNode dest, + Class operatorFactoryClass, + int expectedParallelism, + int expectedMaxParallelism) { + + // verify src node + final StreamEdge srcOutEdge = src.getOutEdges().get(0); + assertThat(srcOutEdge.getTargetId(), equalTo(dest.getId())); + assertThat(src.getTypeSerializerOut(), instanceOf(srcOutTypeInfo)); + + // verify dest node input + final StreamEdge destInputEdge = dest.getInEdges().get(0); + assertThat(destInputEdge.getSourceId(), equalTo(src.getId())); + assertThat(dest.getTypeSerializersIn()[0], instanceOf(srcOutTypeInfo)); + + // make sure 2 sink operators have different names/uid + assertThat(dest.getOperatorName(), not(equalTo(src.getOperatorName()))); + assertThat(dest.getTransformationUID(), not(equalTo(src.getTransformationUID()))); + + assertThat(dest.getOperatorFactory(), instanceOf(operatorFactoryClass)); + assertThat(dest.getParallelism(), equalTo(expectedParallelism)); + assertThat(dest.getMaxParallelism(), equalTo(expectedMaxParallelism)); + assertThat(dest.getOperatorFactory().getChainingStrategy(), is(ChainingStrategy.ALWAYS)); + assertThat(dest.getSlotSharingGroup(), equalTo(SLOT_SHARE_GROUP)); + } + + StreamGraph buildGraph(SinkT sink, RuntimeExecutionMode runtimeExecutionMode) { + StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); + + final Configuration config = new Configuration(); + config.set(ExecutionOptions.RUNTIME_MODE, runtimeExecutionMode); + env.configure(config, getClass().getClassLoader()); + final DataStreamSource src = env.fromElements(1, 2); + final DataStreamSink dataStreamSink = sinkTo(src.rebalance(), sink); + setSinkProperty(dataStreamSink); + // Trigger the plan generation but do not clear the transformations + env.getExecutionPlan(); + return env.getStreamGraph(); + } + + private void setSinkProperty(DataStreamSink dataStreamSink) { + dataStreamSink.name(NAME); + dataStreamSink.uid(UID); + dataStreamSink.setParallelism(SinkTransformationTranslatorITCaseBase.PARALLELISM); + dataStreamSink.slotSharingGroup(SLOT_SHARE_GROUP); + } + + StreamNode findNodeName(StreamGraph streamGraph, Predicate predicate) { + return streamGraph.getStreamNodes().stream() + .filter(node -> predicate.test(node.getOperatorName())) + .findFirst() + .orElseThrow(() -> new IllegalStateException("Can not find the node")); + } +} diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/graph/SinkTransformationTranslatorITCase.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/graph/SinkV1TransformationTranslatorITCase.java similarity index 52% rename from flink-streaming-java/src/test/java/org/apache/flink/streaming/api/graph/SinkTransformationTranslatorITCase.java rename to flink-streaming-java/src/test/java/org/apache/flink/streaming/api/graph/SinkV1TransformationTranslatorITCase.java index 4d0b2323c7824..1b5bd22142f24 100644 --- a/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/graph/SinkTransformationTranslatorITCase.java +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/graph/SinkV1TransformationTranslatorITCase.java @@ -20,33 +20,23 @@ import org.apache.flink.api.common.RuntimeExecutionMode; import org.apache.flink.api.common.typeutils.base.IntSerializer; -import org.apache.flink.configuration.Configuration; -import org.apache.flink.configuration.ExecutionOptions; +import org.apache.flink.api.connector.sink.Sink; import org.apache.flink.core.io.SimpleVersionedSerializerTypeSerializerProxy; import org.apache.flink.streaming.api.datastream.CustomSinkOperatorUidHashes; +import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.datastream.DataStreamSink; import org.apache.flink.streaming.api.datastream.DataStreamSource; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; -import org.apache.flink.streaming.api.operators.ChainingStrategy; import org.apache.flink.streaming.api.operators.SimpleOperatorFactory; -import org.apache.flink.streaming.api.operators.StreamOperatorFactory; import org.apache.flink.streaming.runtime.operators.sink.CommitterOperatorFactory; import org.apache.flink.streaming.runtime.operators.sink.SinkWriterOperatorFactory; import org.apache.flink.streaming.runtime.operators.sink.TestSink; -import org.apache.flink.util.TestLogger; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.util.Arrays; -import java.util.Collection; -import java.util.function.Predicate; - import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; @@ -56,69 +46,22 @@ *

ATTENTION: This test is extremely brittle. Do NOT remove, add or re-order test cases. */ @RunWith(Parameterized.class) -public class SinkTransformationTranslatorITCase extends TestLogger { +public class SinkV1TransformationTranslatorITCase + extends SinkTransformationTranslatorITCaseBase> { - @Parameterized.Parameters(name = "Execution Mode: {0}") - public static Collection data() { - return Arrays.asList(RuntimeExecutionMode.STREAMING, RuntimeExecutionMode.BATCH); + @Override + Sink simpleSink() { + return TestSink.newBuilder().build(); } - @Parameterized.Parameter() public RuntimeExecutionMode runtimeExecutionMode; - - static final String NAME = "FileSink"; - static final String SLOT_SHARE_GROUP = "FileGroup"; - static final String UID = "FileUid"; - static final int PARALLELISM = 2; - - @Test - public void generateWriterTopology() { - final StreamGraph streamGraph = - buildGraph(TestSink.newBuilder().build(), runtimeExecutionMode); - - final StreamNode sourceNode = findNodeName(streamGraph, node -> node.contains("Source")); - final StreamNode writerNode = findWriter(streamGraph); - - assertThat(streamGraph.getStreamNodes().size(), equalTo(2)); - - validateTopology( - sourceNode, - IntSerializer.class, - writerNode, - SinkWriterOperatorFactory.class, - PARALLELISM, - -1); + @Override + Sink sinkWithCommitter() { + return TestSink.newBuilder().setDefaultCommitter().build(); } - @Test - public void generateWriterCommitterTopology() { - - final StreamGraph streamGraph = - buildGraph( - TestSink.newBuilder().setDefaultCommitter().build(), runtimeExecutionMode); - - final StreamNode sourceNode = findNodeName(streamGraph, node -> node.contains("Source")); - final StreamNode writerNode = findWriter(streamGraph); - - validateTopology( - sourceNode, - IntSerializer.class, - writerNode, - SinkWriterOperatorFactory.class, - PARALLELISM, - -1); - - final StreamNode committerNode = - findNodeName(streamGraph, name -> name.contains("Committer")); - - assertThat(streamGraph.getStreamNodes().size(), equalTo(3)); - - validateTopology( - writerNode, - SimpleVersionedSerializerTypeSerializerProxy.class, - committerNode, - CommitterOperatorFactory.class, - PARALLELISM, - -1); + @Override + DataStreamSink sinkTo(DataStream stream, Sink sink) { + return stream.sinkTo(sink); } @Test @@ -219,56 +162,6 @@ public void generateWriterGlobalCommitterTopology() { 1); } - private StreamNode findWriter(StreamGraph streamGraph) { - return findNodeName( - streamGraph, name -> name.contains("Writer") && !name.contains("Committer")); - } - - private StreamNode findCommitter(StreamGraph streamGraph) { - return findNodeName(streamGraph, name -> name.contains("Committer")); - } - - private StreamNode findGlobalCommitter(StreamGraph streamGraph) { - return findNodeName(streamGraph, name -> name.contains("Global Committer")); - } - - @Test(expected = IllegalStateException.class) - public void throwExceptionWithoutSettingUid() { - StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); - - final Configuration config = new Configuration(); - config.set(ExecutionOptions.RUNTIME_MODE, runtimeExecutionMode); - env.configure(config, getClass().getClassLoader()); - // disable auto generating uid - env.getConfig().disableAutoGeneratedUIDs(); - env.fromElements(1, 2).sinkTo(TestSink.newBuilder().build()); - env.getStreamGraph(); - } - - @Test - public void disableOperatorChain() { - StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); - - final DataStreamSource src = env.fromElements(1, 2); - final DataStreamSink dataStreamSink = - src.sinkTo( - TestSink.newBuilder() - .setDefaultCommitter() - .setDefaultGlobalCommitter() - .build()) - .name(NAME); - dataStreamSink.disableChaining(); - - final StreamGraph streamGraph = env.getStreamGraph(); - final StreamNode writer = findWriter(streamGraph); - final StreamNode globalCommitter = findCommitter(streamGraph); - - assertThat(writer.getOperatorFactory().getChainingStrategy(), is(ChainingStrategy.NEVER)); - assertThat( - globalCommitter.getOperatorFactory().getChainingStrategy(), - is(ChainingStrategy.NEVER)); - } - @Test public void testSettingOperatorUidHash() { final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); @@ -319,61 +212,4 @@ public void testSettingOperatorUids() { findGlobalCommitter(streamGraph).getTransformationUID(), String.format("Sink %s Global Committer", sinkUid)); } - - private void validateTopology( - StreamNode src, - Class srcOutTypeInfo, - StreamNode dest, - Class operatorFactoryClass, - int expectedParallelism, - int expectedMaxParallelism) { - - // verify src node - final StreamEdge srcOutEdge = src.getOutEdges().get(0); - assertThat(srcOutEdge.getTargetId(), equalTo(dest.getId())); - assertThat(src.getTypeSerializerOut(), instanceOf(srcOutTypeInfo)); - - // verify dest node input - final StreamEdge destInputEdge = dest.getInEdges().get(0); - assertThat(destInputEdge.getSourceId(), equalTo(src.getId())); - assertThat(dest.getTypeSerializersIn()[0], instanceOf(srcOutTypeInfo)); - - // make sure 2 sink operators have different names/uid - assertThat(dest.getOperatorName(), not(equalTo(src.getOperatorName()))); - assertThat(dest.getTransformationUID(), not(equalTo(src.getTransformationUID()))); - - assertThat(dest.getOperatorFactory(), instanceOf(operatorFactoryClass)); - assertThat(dest.getParallelism(), equalTo(expectedParallelism)); - assertThat(dest.getMaxParallelism(), equalTo(expectedMaxParallelism)); - assertThat(dest.getOperatorFactory().getChainingStrategy(), is(ChainingStrategy.ALWAYS)); - assertThat(dest.getSlotSharingGroup(), equalTo(SLOT_SHARE_GROUP)); - } - - private StreamGraph buildGraph(TestSink sink, RuntimeExecutionMode runtimeExecutionMode) { - StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); - - final Configuration config = new Configuration(); - config.set(ExecutionOptions.RUNTIME_MODE, runtimeExecutionMode); - env.configure(config, getClass().getClassLoader()); - final DataStreamSource src = env.fromElements(1, 2); - final DataStreamSink dataStreamSink = src.rebalance().sinkTo(sink); - setSinkProperty(dataStreamSink); - // Trigger the plan generation but do not clear the transformations - env.getExecutionPlan(); - return env.getStreamGraph(); - } - - private void setSinkProperty(DataStreamSink dataStreamSink) { - dataStreamSink.name(NAME); - dataStreamSink.uid(UID); - dataStreamSink.setParallelism(SinkTransformationTranslatorITCase.PARALLELISM); - dataStreamSink.slotSharingGroup(SLOT_SHARE_GROUP); - } - - private StreamNode findNodeName(StreamGraph streamGraph, Predicate predicate) { - return streamGraph.getStreamNodes().stream() - .filter(node -> predicate.test(node.getOperatorName())) - .findFirst() - .orElseThrow(() -> new IllegalStateException("Can not find the node")); - } } diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/graph/SinkV2TransformationTranslatorITCase.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/graph/SinkV2TransformationTranslatorITCase.java new file mode 100644 index 0000000000000..612cb9d780816 --- /dev/null +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/api/graph/SinkV2TransformationTranslatorITCase.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.streaming.api.graph; + +import org.apache.flink.api.connector.sink2.Sink; +import org.apache.flink.streaming.api.datastream.CustomSinkOperatorUidHashes; +import org.apache.flink.streaming.api.datastream.DataStream; +import org.apache.flink.streaming.api.datastream.DataStreamSink; +import org.apache.flink.streaming.api.datastream.DataStreamSource; +import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; +import org.apache.flink.streaming.runtime.operators.sink.TestSinkV2; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import static org.junit.Assert.assertEquals; + +/** + * Tests for {@link org.apache.flink.streaming.api.transformations.SinkTransformation}. + * + *

ATTENTION: This test is extremely brittle. Do NOT remove, add or re-order test cases. + */ +@RunWith(Parameterized.class) +public class SinkV2TransformationTranslatorITCase + extends SinkTransformationTranslatorITCaseBase> { + + @Override + Sink simpleSink() { + return TestSinkV2.newBuilder().build(); + } + + @Override + Sink sinkWithCommitter() { + return TestSinkV2.newBuilder().setDefaultCommitter().build(); + } + + @Override + DataStreamSink sinkTo(DataStream stream, Sink sink) { + return stream.sinkTo(sink); + } + + @Test + public void testSettingOperatorUidHash() { + final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); + final DataStreamSource src = env.fromElements(1, 2); + final String writerHash = "f6b178ce445dc3ffaa06bad27a51fead"; + final String committerHash = "68ac8ae79eae4e3135a54f9689c4aa10"; + final CustomSinkOperatorUidHashes operatorsUidHashes = + CustomSinkOperatorUidHashes.builder() + .setWriterUidHash(writerHash) + .setCommitterUidHash(committerHash) + .build(); + src.sinkTo( + TestSinkV2.newBuilder().setDefaultCommitter().build(), + operatorsUidHashes) + .name(NAME); + + final StreamGraph streamGraph = env.getStreamGraph(); + + assertEquals(findWriter(streamGraph).getUserHash(), writerHash); + assertEquals(findCommitter(streamGraph).getUserHash(), committerHash); + } + + /** + * When ever you need to change something in this test case please think about possible state + * upgrade problems introduced by your changes. + */ + @Test + public void testSettingOperatorUids() { + final String sinkUid = "f6b178ce445dc3ffaa06bad27a51fead"; + final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); + final DataStreamSource src = env.fromElements(1, 2); + src.sinkTo(TestSinkV2.newBuilder().setDefaultCommitter().build()) + .name(NAME) + .uid(sinkUid); + + final StreamGraph streamGraph = env.getStreamGraph(); + assertEquals(findWriter(streamGraph).getTransformationUID(), sinkUid); + assertEquals( + findCommitter(streamGraph).getTransformationUID(), + String.format("Sink Committer: %s", sinkUid)); + } +} diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/graph/StreamConfigTest.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/graph/StreamConfigTest.java new file mode 100644 index 0000000000000..d3eb45f1990f9 --- /dev/null +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/graph/StreamConfigTest.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.streaming.graph; + +import org.apache.flink.configuration.Configuration; +import org.apache.flink.streaming.api.graph.StreamConfig; +import org.apache.flink.streaming.api.operators.StreamOperatorFactory; +import org.apache.flink.streaming.util.MockStreamConfig; + +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** Test for {@link StreamConfig}. */ +class StreamConfigTest { + + @Test + void testClearInitialConfigs() { + int chainedTaskId = 3456; + MockStreamConfig streamConfig = + new MockStreamConfig( + new Configuration(), + 1, + Collections.singletonMap( + chainedTaskId, new MockStreamConfig(new Configuration(), 1))); + + ClassLoader classLoader = getClass().getClassLoader(); + StreamOperatorFactory streamOperatorFactory = + streamConfig.getStreamOperatorFactory(classLoader); + assertThat(streamOperatorFactory).isNotNull(); + assertThat(streamConfig.getStreamOperatorFactoryClass(classLoader)).isNotNull(); + assertThat(streamConfig.getTransitiveChainedTaskConfigs(classLoader)) + .hasSize(1) + .containsKey(chainedTaskId); + + // StreamOperatorFactory and ChainedTaskConfigs should be cleared after clearInitialConfigs, + // but the factory class shouldn't be cleared. + streamConfig.clearInitialConfigs(); + assertThatThrownBy(() -> streamConfig.getStreamOperatorFactory(classLoader)) + .hasCauseInstanceOf(IllegalStateException.class) + .hasRootCauseMessage("serializedUDF has been removed."); + assertThat(streamConfig.getStreamOperatorFactoryClass(classLoader)).isNotNull(); + assertThatThrownBy(() -> streamConfig.getTransitiveChainedTaskConfigs(classLoader)) + .hasCauseInstanceOf(IllegalStateException.class) + .hasRootCauseMessage("chainedTaskConfig_ has been removed."); + } +} diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/io/benchmark/SingleInputGateBenchmarkFactory.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/io/benchmark/SingleInputGateBenchmarkFactory.java index 49f56cbb840bc..1754184138797 100644 --- a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/io/benchmark/SingleInputGateBenchmarkFactory.java +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/io/benchmark/SingleInputGateBenchmarkFactory.java @@ -92,6 +92,7 @@ protected InputChannel createKnownInputChannel( connectionManager, partitionRequestInitialBackoff, partitionRequestMaxBackoff, + partitionRequestListenerTimeout, configuredNetworkBuffersPerChannel, metrics); } @@ -165,6 +166,7 @@ public TestRemoteInputChannel( ConnectionManager connectionManager, int initialBackOff, int maxBackoff, + int partitionRequestListenerTimeout, int networkBuffersPerChannel, InputChannelMetrics metrics) { super( @@ -176,6 +178,7 @@ public TestRemoteInputChannel( connectionManager, initialBackOff, maxBackoff, + partitionRequestListenerTimeout, networkBuffersPerChannel, metrics.getNumBytesInRemoteCounter(), metrics.getNumBuffersInRemoteCounter(), diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/CommitterOperatorTest.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/CommitterOperatorTestBase.java similarity index 77% rename from flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/CommitterOperatorTest.java rename to flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/CommitterOperatorTestBase.java index 7ae860c54e0c4..fe3625ec8ce17 100644 --- a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/CommitterOperatorTest.java +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/CommitterOperatorTestBase.java @@ -18,8 +18,6 @@ package org.apache.flink.streaming.runtime.operators.sink; -import org.apache.flink.api.connector.sink.Committer; -import org.apache.flink.api.connector.sink2.Sink; import org.apache.flink.api.connector.sink2.TwoPhaseCommittingSink; import org.apache.flink.runtime.checkpoint.OperatorSubtaskState; import org.apache.flink.streaming.api.connector.sink2.CommittableMessage; @@ -34,45 +32,31 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import java.util.Collections; import java.util.List; +import java.util.function.IntSupplier; import static org.apache.flink.streaming.runtime.operators.sink.SinkTestUtil.fromOutput; import static org.apache.flink.streaming.runtime.operators.sink.SinkTestUtil.toCommittableSummary; import static org.apache.flink.streaming.runtime.operators.sink.SinkTestUtil.toCommittableWithLinage; import static org.assertj.core.api.Assertions.assertThat; -class CommitterOperatorTest { +abstract class CommitterOperatorTestBase { @ParameterizedTest @ValueSource(booleans = {true, false}) void testEmitCommittables(boolean withPostCommitTopology) throws Exception { - final ForwardingCommitter committer = new ForwardingCommitter(); - - Sink sink; + SinkAndCounters sinkAndCounters; if (withPostCommitTopology) { // Insert global committer to simulate post commit topology - sink = - TestSink.newBuilder() - .setCommitter(committer) - .setDefaultGlobalCommitter() - .setCommittableSerializer(TestSink.StringCommittableSerializer.INSTANCE) - .build() - .asV2(); + sinkAndCounters = sinkWithPostCommit(); } else { - sink = - TestSink.newBuilder() - .setCommitter(committer) - .setCommittableSerializer(TestSink.StringCommittableSerializer.INSTANCE) - .build() - .asV2(); + sinkAndCounters = sinkWithoutPostCommit(); } final OneInputStreamOperatorTestHarness< CommittableMessage, CommittableMessage> testHarness = new OneInputStreamOperatorTestHarness<>( - new CommitterOperatorFactory<>( - (TwoPhaseCommittingSink) sink, false, true)); + new CommitterOperatorFactory<>(sinkAndCounters.sink, false, true)); testHarness.open(); final CommittableSummary committableSummary = @@ -85,7 +69,7 @@ void testEmitCommittables(boolean withPostCommitTopology) throws Exception { // Trigger commit testHarness.notifyOfCompletedCheckpoint(1); - assertThat(committer.getSuccessfulCommits()).isEqualTo(1); + assertThat(sinkAndCounters.commitCounter.getAsInt()).isEqualTo(1); if (withPostCommitTopology) { final List output = fromOutput(testHarness.getOutput()); SinkV2Assertions.assertThat(toCommittableSummary(output.get(0))) @@ -102,10 +86,10 @@ void testEmitCommittables(boolean withPostCommitTopology) throws Exception { @Test void testWaitForCommittablesOfLatestCheckpointBeforeCommitting() throws Exception { - final ForwardingCommitter committer = new ForwardingCommitter(); + SinkAndCounters sinkAndCounters = sinkWithPostCommit(); final OneInputStreamOperatorTestHarness< CommittableMessage, CommittableMessage> - testHarness = createTestHarness(committer, false, true); + testHarness = createTestHarness(sinkAndCounters.sink, false, true); testHarness.open(); testHarness.setProcessingTime(0); @@ -119,7 +103,7 @@ void testWaitForCommittablesOfLatestCheckpointBeforeCommitting() throws Exceptio testHarness.notifyOfCompletedCheckpoint(1); assertThat(testHarness.getOutput()).isEmpty(); - assertThat(committer.getSuccessfulCommits()).isEqualTo(0); + assertThat(sinkAndCounters.commitCounter.getAsInt()).isZero(); final CommittableWithLineage second = new CommittableWithLineage<>("2", 1L, 1); testHarness.processElement(new StreamRecord<>(second)); @@ -129,7 +113,7 @@ void testWaitForCommittablesOfLatestCheckpointBeforeCommitting() throws Exceptio final List output = fromOutput(testHarness.getOutput()); assertThat(output).hasSize(3); - assertThat(committer.getSuccessfulCommits()).isEqualTo(2); + assertThat(sinkAndCounters.commitCounter.getAsInt()).isEqualTo(2); SinkV2Assertions.assertThat(toCommittableSummary(output.get(0))) .hasFailedCommittables(committableSummary.getNumberOfFailedCommittables()) .hasOverallCommittables(committableSummary.getNumberOfCommittables()) @@ -143,10 +127,11 @@ void testWaitForCommittablesOfLatestCheckpointBeforeCommitting() throws Exceptio @Test void testImmediatelyCommitLateCommittables() throws Exception { - final ForwardingCommitter committer = new ForwardingCommitter(); + SinkAndCounters sinkAndCounters = sinkWithPostCommit(); + final OneInputStreamOperatorTestHarness< CommittableMessage, CommittableMessage> - testHarness = createTestHarness(committer, false, true); + testHarness = createTestHarness(sinkAndCounters.sink, false, true); testHarness.open(); final CommittableSummary committableSummary = @@ -166,7 +151,7 @@ void testImmediatelyCommitLateCommittables() throws Exception { final List output = fromOutput(testHarness.getOutput()); assertThat(output).hasSize(2); - assertThat(committer.getSuccessfulCommits()).isEqualTo(1); + assertThat(sinkAndCounters.commitCounter.getAsInt()).isEqualTo(1); SinkV2Assertions.assertThat(toCommittableSummary(output.get(0))) .hasFailedCommittables(committableSummary.getNumberOfFailedCommittables()) .hasOverallCommittables(committableSummary.getNumberOfCommittables()) @@ -179,10 +164,10 @@ void testImmediatelyCommitLateCommittables() throws Exception { @ParameterizedTest @ValueSource(booleans = {true, false}) void testEmitAllCommittablesOnEndOfInput(boolean isBatchMode) throws Exception { - final ForwardingCommitter committer = new ForwardingCommitter(); + SinkAndCounters sinkAndCounters = sinkWithPostCommit(); final OneInputStreamOperatorTestHarness< CommittableMessage, CommittableMessage> - testHarness = createTestHarness(committer, isBatchMode, !isBatchMode); + testHarness = createTestHarness(sinkAndCounters.sink, isBatchMode, !isBatchMode); testHarness.open(); final CommittableSummary committableSummary = @@ -199,7 +184,7 @@ void testEmitAllCommittablesOnEndOfInput(boolean isBatchMode) throws Exception { testHarness.endInput(); if (!isBatchMode) { - assertThat(testHarness.getOutput()).hasSize(0); + assertThat(testHarness.getOutput()).isEmpty(); // notify final checkpoint complete testHarness.notifyOfCompletedCheckpoint(1); } @@ -227,7 +212,7 @@ void testStateRestore() throws Exception { CommittableMessage, CommittableMessage> testHarness = createTestHarness( - new TestSink.RetryOnceCommitter(), + sinkWithPostCommitWithRetry().sink, false, true, 1, @@ -262,15 +247,15 @@ void testStateRestore() throws Exception { assertThat(testHarness.getOutput()).isEmpty(); testHarness.close(); - final ForwardingCommitter committer = new ForwardingCommitter(); - // create new testHarness but with different parallelism level and subtaskId that original // one. // we will make sure that new subtaskId was used during committable recovery. + SinkAndCounters sinkAndCounters = sinkWithPostCommit(); final OneInputStreamOperatorTestHarness< CommittableMessage, CommittableMessage> restored = - createTestHarness(committer, false, true, 10, 10, subtaskIdAfterRecovery); + createTestHarness( + sinkAndCounters.sink, false, true, 10, 10, subtaskIdAfterRecovery); restored.initializeState(snapshot); restored.open(); @@ -278,7 +263,7 @@ void testStateRestore() throws Exception { // Previous committables are immediately committed if possible final List output = fromOutput(restored.getOutput()); assertThat(output).hasSize(3); - assertThat(committer.getSuccessfulCommits()).isEqualTo(2); + assertThat(sinkAndCounters.commitCounter.getAsInt()).isEqualTo(2); SinkV2Assertions.assertThat(toCommittableSummary(output.get(0))) .hasCheckpointId(checkpointId) .hasFailedCommittables(0) @@ -300,22 +285,14 @@ void testStateRestore() throws Exception { @ParameterizedTest @ValueSource(booleans = {true, false}) void testHandleEndInputInStreamingMode(boolean isCheckpointingEnabled) throws Exception { - final Sink sink = - TestSink.newBuilder() - .setDefaultCommitter() - .setDefaultGlobalCommitter() - .setCommittableSerializer(TestSink.StringCommittableSerializer.INSTANCE) - .build() - .asV2(); + final SinkAndCounters sinkAndCounters = sinkWithPostCommit(); final OneInputStreamOperatorTestHarness< CommittableMessage, CommittableMessage> testHarness = new OneInputStreamOperatorTestHarness<>( new CommitterOperatorFactory<>( - (TwoPhaseCommittingSink) sink, - false, - isCheckpointingEnabled)); + sinkAndCounters.sink, false, isCheckpointingEnabled)); testHarness.open(); final CommittableSummary committableSummary = @@ -363,28 +340,18 @@ CommittableWithLineage copyCommittableWithDifferentOrigin( private OneInputStreamOperatorTestHarness< CommittableMessage, CommittableMessage> createTestHarness( - Committer committer, + TwoPhaseCommittingSink sink, boolean isBatchMode, boolean isCheckpointingEnabled) throws Exception { return new OneInputStreamOperatorTestHarness<>( - new CommitterOperatorFactory<>( - (TwoPhaseCommittingSink) - TestSink.newBuilder() - .setCommitter(committer) - .setDefaultGlobalCommitter() - .setCommittableSerializer( - TestSink.StringCommittableSerializer.INSTANCE) - .build() - .asV2(), - isBatchMode, - isCheckpointingEnabled)); + new CommitterOperatorFactory<>(sink, isBatchMode, isCheckpointingEnabled)); } private OneInputStreamOperatorTestHarness< CommittableMessage, CommittableMessage> createTestHarness( - Committer committer, + TwoPhaseCommittingSink sink, boolean isBatchMode, boolean isCheckpointingEnabled, int maxParallelism, @@ -392,36 +359,25 @@ CommittableWithLineage copyCommittableWithDifferentOrigin( int subtaskId) throws Exception { return new OneInputStreamOperatorTestHarness<>( - new CommitterOperatorFactory<>( - (TwoPhaseCommittingSink) - TestSink.newBuilder() - .setCommitter(committer) - .setDefaultGlobalCommitter() - .setCommittableSerializer( - TestSink.StringCommittableSerializer.INSTANCE) - .build() - .asV2(), - isBatchMode, - isCheckpointingEnabled), + new CommitterOperatorFactory<>(sink, isBatchMode, isCheckpointingEnabled), maxParallelism, parallelism, subtaskId); } - private static class ForwardingCommitter extends TestSink.DefaultCommitter { - private int successfulCommits = 0; + abstract SinkAndCounters sinkWithPostCommit(); - @Override - public List commit(List committables) { - successfulCommits += committables.size(); - return Collections.emptyList(); - } + abstract SinkAndCounters sinkWithPostCommitWithRetry(); + + abstract SinkAndCounters sinkWithoutPostCommit(); - @Override - public void close() throws Exception {} + static class SinkAndCounters { + TwoPhaseCommittingSink sink; + IntSupplier commitCounter; - public int getSuccessfulCommits() { - return successfulCommits; + public SinkAndCounters(TwoPhaseCommittingSink sink, IntSupplier commitCounter) { + this.sink = sink; + this.commitCounter = commitCounter; } } } diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkTestUtil.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkTestUtil.java index c309835cc7591..b457b5a0b7825 100644 --- a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkTestUtil.java +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkTestUtil.java @@ -52,7 +52,7 @@ static List toBytes(Collection elements) { static byte[] toBytes(String obj) { try { return SimpleVersionedSerialization.writeVersionAndSerialize( - TestSink.StringCommittableSerializer.INSTANCE, obj); + TestSinkV2.StringSerializer.INSTANCE, obj); } catch (IOException e) { throw new IllegalStateException(e); } @@ -83,7 +83,7 @@ static String fromRecord(StreamRecord obj) { static String fromBytes(byte[] obj) { try { return SimpleVersionedSerialization.readVersionAndDeSerialize( - TestSink.StringCommittableSerializer.INSTANCE, obj); + TestSinkV2.StringSerializer.INSTANCE, obj); } catch (IOException e) { throw new IllegalStateException(e); } diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkV2CommitterOperatorTest.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkV2CommitterOperatorTest.java new file mode 100644 index 0000000000000..ed8e53ff342ce --- /dev/null +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkV2CommitterOperatorTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.streaming.runtime.operators.sink; + +import org.apache.flink.api.connector.sink2.TwoPhaseCommittingSink; + +import java.util.Collection; + +class SinkV2CommitterOperatorTest extends CommitterOperatorTestBase { + @Override + SinkAndCounters sinkWithPostCommit() { + ForwardingCommitter committer = new ForwardingCommitter(); + return new SinkAndCounters( + (TwoPhaseCommittingSink) + TestSinkV2.newBuilder() + .setCommitter(committer) + .setCommittableSerializer(TestSinkV2.StringSerializer.INSTANCE) + .setWithPostCommitTopology(true) + .build(), + () -> committer.successfulCommits); + } + + @Override + SinkAndCounters sinkWithPostCommitWithRetry() { + return new SinkAndCounters( + (TwoPhaseCommittingSink) + TestSinkV2.newBuilder() + .setCommitter(new TestSinkV2.RetryOnceCommitter()) + .setCommittableSerializer(TestSinkV2.StringSerializer.INSTANCE) + .setWithPostCommitTopology(true) + .build(), + () -> 0); + } + + @Override + SinkAndCounters sinkWithoutPostCommit() { + ForwardingCommitter committer = new ForwardingCommitter(); + return new SinkAndCounters( + (TwoPhaseCommittingSink) + TestSinkV2.newBuilder() + .setCommitter(committer) + .setCommittableSerializer(TestSinkV2.StringSerializer.INSTANCE) + .setWithPostCommitTopology(false) + .build(), + () -> committer.successfulCommits); + } + + private static class ForwardingCommitter extends TestSinkV2.DefaultCommitter { + private int successfulCommits = 0; + + @Override + public void commit(Collection> committables) { + successfulCommits += committables.size(); + } + + @Override + public void close() throws Exception {} + } +} diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkV2SinkWriterOperatorTest.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkV2SinkWriterOperatorTest.java new file mode 100644 index 0000000000000..5bb0135d69b4a --- /dev/null +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkV2SinkWriterOperatorTest.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.streaming.runtime.operators.sink; + +import org.apache.flink.api.common.operators.ProcessingTimeService; +import org.apache.flink.api.java.tuple.Tuple3; + +import org.apache.flink.shaded.guava31.com.google.common.collect.ImmutableList; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +class SinkV2SinkWriterOperatorTest extends SinkWriterOperatorTestBase { + + @Override + SinkAndSuppliers sinkWithoutCommitter() { + TestSinkV2.DefaultSinkWriter sinkWriter = new TestSinkV2.DefaultSinkWriter<>(); + return new SinkAndSuppliers( + TestSinkV2.newBuilder().setWriter(sinkWriter).build(), + () -> sinkWriter.elements, + () -> sinkWriter.watermarks, + () -> -1, + TestSinkV2.StringSerializer::new); + } + + @Override + SinkAndSuppliers sinkWithCommitter() { + TestSinkV2.DefaultSinkWriter sinkWriter = + new TestSinkV2.DefaultCommittingSinkWriter<>(); + return new SinkAndSuppliers( + TestSinkV2.newBuilder() + .setWriter(sinkWriter) + .setDefaultCommitter() + .build(), + () -> sinkWriter.elements, + () -> sinkWriter.watermarks, + () -> -1, + TestSinkV2.StringSerializer::new); + } + + @Override + SinkAndSuppliers sinkWithTimeBasedWriter() { + TestSinkV2.DefaultSinkWriter sinkWriter = new TimeBasedBufferingSinkWriter(); + return new SinkAndSuppliers( + TestSinkV2.newBuilder() + .setWriter(sinkWriter) + .setDefaultCommitter() + .build(), + () -> sinkWriter.elements, + () -> sinkWriter.watermarks, + () -> -1, + TestSinkV2.StringSerializer::new); + } + + @Override + SinkAndSuppliers sinkWithSnapshottingWriter(boolean withState, String stateName) { + SnapshottingBufferingSinkWriter sinkWriter = new SnapshottingBufferingSinkWriter(); + TestSinkV2.Builder builder = + TestSinkV2.newBuilder() + .setWriter(sinkWriter) + .setDefaultCommitter() + .setWithPostCommitTopology(true); + if (withState) { + builder.setWriterState(true); + } + if (stateName != null) { + builder.setCompatibleStateNames(stateName); + } + return new SinkAndSuppliers( + builder.build(), + () -> sinkWriter.elements, + () -> sinkWriter.watermarks, + () -> sinkWriter.lastCheckpointId, + () -> new TestSinkV2.StringSerializer()); + } + + private static class TimeBasedBufferingSinkWriter + extends TestSinkV2.DefaultCommittingSinkWriter + implements ProcessingTimeService.ProcessingTimeCallback { + + private final List cachedCommittables = new ArrayList<>(); + private ProcessingTimeService processingTimeService; + + @Override + public void write(Integer element, Context context) { + cachedCommittables.add( + Tuple3.of(element, context.timestamp(), context.currentWatermark()).toString()); + } + + @Override + public void onProcessingTime(long time) { + elements.addAll(cachedCommittables); + cachedCommittables.clear(); + this.processingTimeService.registerTimer(time + 1000, this); + } + + @Override + public void init(org.apache.flink.api.connector.sink2.Sink.InitContext context) { + this.processingTimeService = context.getProcessingTimeService(); + this.processingTimeService.registerTimer(1000, this); + } + } + + private static class SnapshottingBufferingSinkWriter + extends TestSinkV2.DefaultStatefulSinkWriter { + public static final int NOT_SNAPSHOTTED = -1; + long lastCheckpointId = NOT_SNAPSHOTTED; + boolean endOfInput = false; + + @Override + public void flush(boolean endOfInput) throws IOException, InterruptedException { + this.endOfInput = endOfInput; + } + + @Override + public List snapshotState(long checkpointId) throws IOException { + lastCheckpointId = checkpointId; + return super.snapshotState(checkpointId); + } + + @Override + public Collection prepareCommit() { + if (!endOfInput) { + return ImmutableList.of(); + } + List result = elements; + elements = new ArrayList<>(); + return result; + } + } +} diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkWriterOperatorTest.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkWriterOperatorTestBase.java similarity index 77% rename from flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkWriterOperatorTest.java rename to flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkWriterOperatorTestBase.java index 86cc85c3155c7..464b08c8f67b0 100644 --- a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkWriterOperatorTest.java +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/SinkWriterOperatorTestBase.java @@ -25,7 +25,7 @@ import org.apache.flink.api.common.typeutils.TypeSerializer; import org.apache.flink.api.common.typeutils.base.StringSerializer; import org.apache.flink.api.common.typeutils.base.array.BytePrimitiveArraySerializer; -import org.apache.flink.api.connector.sink.Sink; +import org.apache.flink.api.connector.sink2.Sink; import org.apache.flink.api.java.tuple.Tuple3; import org.apache.flink.core.io.SimpleVersionedSerialization; import org.apache.flink.core.io.SimpleVersionedSerializer; @@ -62,29 +62,31 @@ import java.util.List; import java.util.Queue; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.LongSupplier; +import java.util.function.Supplier; import static org.apache.flink.streaming.runtime.operators.sink.SinkTestUtil.fromOutput; import static org.assertj.core.api.Assertions.assertThat; -class SinkWriterOperatorTest { +abstract class SinkWriterOperatorTestBase { @Test void testNotEmitCommittablesWithoutCommitter() throws Exception { - final TestSink.DefaultSinkWriter sinkWriter = new TestSink.DefaultSinkWriter<>(); + SinkAndSuppliers sinkAndSuppliers = sinkWithoutCommitter(); final OneInputStreamOperatorTestHarness> testHarness = new OneInputStreamOperatorTestHarness<>( - new SinkWriterOperatorFactory<>( - TestSink.newBuilder().setWriter(sinkWriter).build().asV2())); + new SinkWriterOperatorFactory<>(sinkAndSuppliers.sink)); testHarness.open(); testHarness.processElement(1, 1); assertThat(testHarness.getOutput()).isEmpty(); - assertThat(sinkWriter.elements).containsOnly("(1,1," + Long.MIN_VALUE + ")"); + assertThat(sinkAndSuppliers.elementSupplier.get()) + .containsOnly("(1,1," + Long.MIN_VALUE + ")"); testHarness.prepareSnapshotPreBarrier(1); assertThat(testHarness.getOutput()).isEmpty(); // Elements are flushed - assertThat(sinkWriter.elements).isEmpty(); + assertThat(sinkAndSuppliers.elementSupplier.get()).isEmpty(); testHarness.close(); } @@ -92,11 +94,10 @@ void testNotEmitCommittablesWithoutCommitter() throws Exception { void testWatermarkPropagatedToSinkWriter() throws Exception { final long initialTime = 0; - final TestSink.DefaultSinkWriter writer = new TestSink.DefaultSinkWriter<>(); + SinkAndSuppliers sinkAndSuppliers = sinkWithoutCommitter(); final OneInputStreamOperatorTestHarness> testHarness = new OneInputStreamOperatorTestHarness<>( - new SinkWriterOperatorFactory<>( - TestSink.newBuilder().setWriter(writer).build().asV2())); + new SinkWriterOperatorFactory<>(sinkAndSuppliers.sink)); testHarness.open(); testHarness.processWatermark(initialTime); @@ -104,7 +105,7 @@ void testWatermarkPropagatedToSinkWriter() throws Exception { assertThat(testHarness.getOutput()) .containsExactly(new Watermark(initialTime), new Watermark(initialTime + 1)); - assertThat(writer.watermarks) + assertThat(sinkAndSuppliers.watermarkSupplier.get()) .containsExactly( new org.apache.flink.api.common.eventtime.Watermark(initialTime), new org.apache.flink.api.common.eventtime.Watermark(initialTime + 1)); @@ -117,12 +118,7 @@ public void testTimeBasedBufferingSinkWriter() throws Exception { final OneInputStreamOperatorTestHarness> testHarness = new OneInputStreamOperatorTestHarness<>( - new SinkWriterOperatorFactory<>( - TestSink.newBuilder() - .setDefaultCommitter() - .setWriter(new TimeBasedBufferingSinkWriter()) - .build() - .asV2())); + new SinkWriterOperatorFactory<>(sinkWithTimeBasedWriter().sink)); testHarness.open(); @@ -149,8 +145,7 @@ public void testTimeBasedBufferingSinkWriter() throws Exception { void testEmitOnFlushWithCommitter() throws Exception { final OneInputStreamOperatorTestHarness> testHarness = new OneInputStreamOperatorTestHarness<>( - new SinkWriterOperatorFactory<>( - TestSink.newBuilder().setDefaultCommitter().build().asV2())); + new SinkWriterOperatorFactory<>(sinkWithCommitter().sink)); testHarness.open(); assertThat(testHarness.getOutput()).isEmpty(); @@ -168,8 +163,7 @@ void testEmitOnFlushWithCommitter() throws Exception { @Test void testEmitOnEndOfInputInBatchMode() throws Exception { final SinkWriterOperatorFactory writerOperatorFactory = - new SinkWriterOperatorFactory<>( - TestSink.newBuilder().setDefaultCommitter().build().asV2()); + new SinkWriterOperatorFactory<>(sinkWithCommitter().sink); final OneInputStreamOperatorTestHarness> testHarness = new OneInputStreamOperatorTestHarness<>(writerOperatorFactory); @@ -187,10 +181,9 @@ void testStateRestore(boolean stateful) throws Exception { final long initialTime = 0; - final SnapshottingBufferingSinkWriter snapshottingWriter = - new SnapshottingBufferingSinkWriter(); + final SinkAndSuppliers sinkAndSuppliers = sinkWithSnapshottingWriter(stateful, null); final OneInputStreamOperatorTestHarness> testHarness = - createTestHarnessWithBufferingSinkWriter(snapshottingWriter, stateful); + createTestHarnessWithBufferingSinkWriter(sinkAndSuppliers.sink); testHarness.open(); @@ -204,15 +197,14 @@ void testStateRestore(boolean stateful) throws Exception { // we see the watermark and the committable summary, so the committables must be stored in // state assertThat(testHarness.getOutput()).hasSize(2).contains(new Watermark(initialTime)); - assertThat(snapshottingWriter.lastCheckpointId) - .isEqualTo(stateful ? 1L : SnapshottingBufferingSinkWriter.NOT_SNAPSHOTTED); + assertThat(sinkAndSuppliers.lastCheckpointSupplier.getAsLong()) + .isEqualTo(stateful ? 1L : -1L); testHarness.close(); + final SinkAndSuppliers restoredSink = sinkWithSnapshottingWriter(stateful, null); final OneInputStreamOperatorTestHarness> - restoredTestHarness = - createTestHarnessWithBufferingSinkWriter( - new SnapshottingBufferingSinkWriter(), stateful); + restoredTestHarness = createTestHarnessWithBufferingSinkWriter(restoredSink.sink); restoredTestHarness.initializeState(snapshot); restoredTestHarness.open(); @@ -221,6 +213,7 @@ void testStateRestore(boolean stateful) throws Exception { restoredTestHarness.endInput(); final long checkpointId = 2; restoredTestHarness.prepareSnapshotPreBarrier(checkpointId); + restoredTestHarness.notifyOfCompletedCheckpoint(checkpointId); if (stateful) { assertBasicOutput(restoredTestHarness.getOutput(), 2, Long.MAX_VALUE); @@ -246,16 +239,20 @@ public void testLoadPreviousSinkState(boolean stateful) throws Exception { "bit", "mention", "thick", "stick", "stir", "easy", "sleep", "forth", "cost", "prompt"); + SinkAndSuppliers sinkAndSuppliers = + sinkWithSnapshottingWriter(stateful, DummySinkOperator.DUMMY_SINK_STATE_NAME); final OneInputStreamOperatorTestHarness previousSink = new OneInputStreamOperatorTestHarness<>( - new DummySinkOperator(), StringSerializer.INSTANCE); + new DummySinkOperator(sinkAndSuppliers.serializerSupplier.get()), + StringSerializer.INSTANCE); OperatorSubtaskState previousSinkState = TestHarnessUtil.buildSubtaskState(previousSink, previousSinkInputs); // 2. Load previous sink state and verify the output final OneInputStreamOperatorTestHarness> - compatibleWriterOperator = createCompatibleStateTestHarness(stateful); + compatibleWriterOperator = + createTestHarnessWithBufferingSinkWriter(sinkAndSuppliers.sink); final List expectedOutput1 = stateful ? new ArrayList<>(previousSinkInputs) : new ArrayList<>(); @@ -280,8 +277,11 @@ public void testLoadPreviousSinkState(boolean stateful) throws Exception { assertEmitted(expectedOutput1, compatibleWriterOperator.getOutput()); // 3. Restore the sink without previous sink's state + SinkAndSuppliers sinkAndSuppliers2 = + sinkWithSnapshottingWriter(stateful, DummySinkOperator.DUMMY_SINK_STATE_NAME); final OneInputStreamOperatorTestHarness> - restoredSinkOperator = createCompatibleStateTestHarness(stateful); + restoredSinkOperator = + createTestHarnessWithBufferingSinkWriter(sinkAndSuppliers2.sink); final List expectedOutput2 = Arrays.asList( Tuple3.of(2, 2, Long.MIN_VALUE).toString(), @@ -306,22 +306,18 @@ public void testLoadPreviousSinkState(boolean stateful) throws Exception { void testRestoreCommitterState() throws Exception { final List committables = Arrays.asList("state1", "state2"); + SinkAndSuppliers sinkAndSuppliers = sinkWithCommitter(); final OneInputStreamOperatorTestHarness committer = new OneInputStreamOperatorTestHarness<>( - new TestCommitterOperator(), StringSerializer.INSTANCE); + new TestCommitterOperator(sinkAndSuppliers.serializerSupplier.get()), + StringSerializer.INSTANCE); final OperatorSubtaskState committerState = TestHarnessUtil.buildSubtaskState(committer, committables); - final TestSink.DefaultSinkWriter sinkWriter = new TestSink.DefaultSinkWriter<>(); final OneInputStreamOperatorTestHarness> testHarness = new OneInputStreamOperatorTestHarness<>( - new SinkWriterOperatorFactory<>( - TestSink.newBuilder() - .setDefaultCommitter() - .setWriter(sinkWriter) - .build() - .asV2())); + new SinkWriterOperatorFactory<>(sinkAndSuppliers.sink)); testHarness.initializeState(committerState); @@ -361,21 +357,16 @@ void testRestoreCommitterState() throws Exception { @ParameterizedTest @ValueSource(booleans = {true, false}) void testHandleEndInputInStreamingMode(boolean isCheckpointingEnabled) throws Exception { - final TestSink.DefaultSinkWriter sinkWriter = new TestSink.DefaultSinkWriter<>(); + SinkAndSuppliers sinkAndSuppliers = sinkWithCommitter(); final OneInputStreamOperatorTestHarness> testHarness = new OneInputStreamOperatorTestHarness<>( - new SinkWriterOperatorFactory<>( - TestSink.newBuilder() - .setWriter(sinkWriter) - .setDefaultCommitter() - .build() - .asV2())); + new SinkWriterOperatorFactory<>(sinkAndSuppliers.sink)); testHarness.open(); testHarness.processElement(1, 1); assertThat(testHarness.getOutput()).isEmpty(); final String record = "(1,1," + Long.MIN_VALUE + ")"; - assertThat(sinkWriter.elements).containsOnly(record); + assertThat(sinkAndSuppliers.elementSupplier.get()).containsOnly(record); testHarness.endInput(); @@ -384,7 +375,7 @@ void testHandleEndInputInStreamingMode(boolean isCheckpointingEnabled) throws Ex } assertEmitted(Collections.singletonList(record), testHarness.getOutput()); - assertThat(sinkWriter.elements).isEmpty(); + assertThat(sinkAndSuppliers.elementSupplier.get()).isEmpty(); testHarness.close(); } @@ -472,33 +463,9 @@ private static void assertEmitted(List records, Queue output) { } private static OneInputStreamOperatorTestHarness> - createTestHarnessWithBufferingSinkWriter( - SnapshottingBufferingSinkWriter snapshottingWriter, boolean stateful) - throws Exception { - final TestSink.Builder builder = - TestSink.newBuilder().setDefaultCommitter().setWriter(snapshottingWriter); - if (stateful) { - builder.withWriterState(); - } + createTestHarnessWithBufferingSinkWriter(Sink sink) throws Exception { final SinkWriterOperatorFactory writerOperatorFactory = - new SinkWriterOperatorFactory<>(builder.build().asV2()); - return new OneInputStreamOperatorTestHarness<>(writerOperatorFactory); - } - - private static OneInputStreamOperatorTestHarness> - createCompatibleStateTestHarness(boolean stateful) throws Exception { - final SnapshottingBufferingSinkWriter snapshottingWriter = - new SnapshottingBufferingSinkWriter(); - final TestSink.Builder builder = - TestSink.newBuilder() - .setDefaultCommitter() - .setCompatibleStateNames(DummySinkOperator.DUMMY_SINK_STATE_NAME) - .setWriter(snapshottingWriter); - if (stateful) { - builder.withWriterState(); - } - final SinkWriterOperatorFactory writerOperatorFactory = - new SinkWriterOperatorFactory<>(builder.build().asV2()); + new SinkWriterOperatorFactory<>(sink); return new OneInputStreamOperatorTestHarness<>(writerOperatorFactory); } @@ -527,30 +494,6 @@ private static void assertBasicOutput( } } - private static class TimeBasedBufferingSinkWriter extends TestSink.DefaultSinkWriter - implements Sink.ProcessingTimeService.ProcessingTimeCallback { - - private final List cachedCommittables = new ArrayList<>(); - - @Override - public void write(Integer element, Context context) { - cachedCommittables.add( - Tuple3.of(element, context.timestamp(), context.currentWatermark()).toString()); - } - - void setProcessingTimerService(Sink.ProcessingTimeService processingTimerService) { - super.setProcessingTimerService(processingTimerService); - this.processingTimerService.registerProcessingTimer(1000, this); - } - - @Override - public void onProcessingTime(long time) { - elements.addAll(cachedCommittables); - cachedCommittables.clear(); - this.processingTimerService.registerProcessingTimer(time + 1000, this); - } - } - private static class TestCommitterOperator extends AbstractStreamOperator implements OneInputStreamOperator { @@ -559,6 +502,11 @@ private static class TestCommitterOperator extends AbstractStreamOperator> committerState; private final List buffer = new ArrayList<>(); + private final SimpleVersionedSerializer serializer; + + public TestCommitterOperator(SimpleVersionedSerializer serializer) { + this.serializer = serializer; + } @Override public void initializeState(StateInitializationContext context) throws Exception { @@ -567,8 +515,7 @@ public void initializeState(StateInitializationContext context) throws Exception new SimpleVersionedListState<>( context.getOperatorStateStore() .getListState(STREAMING_COMMITTER_RAW_STATES_DESC), - new TestingCommittableSerializer( - TestSink.StringCommittableSerializer.INSTANCE)); + new TestingCommittableSerializer(serializer)); } @Override @@ -592,13 +539,18 @@ private static class DummySinkOperator extends AbstractStreamOperator new ListStateDescriptor<>( DUMMY_SINK_STATE_NAME, BytePrimitiveArraySerializer.INSTANCE); ListState sinkState; + private final SimpleVersionedSerializer serializer; + + public DummySinkOperator(SimpleVersionedSerializer serializer) { + this.serializer = serializer; + } public void initializeState(StateInitializationContext context) throws Exception { super.initializeState(context); sinkState = new SimpleVersionedListState<>( context.getOperatorStateStore().getListState(SINK_STATE_DESC), - TestSink.StringCommittableSerializer.INSTANCE); + serializer); } @Override @@ -607,33 +559,6 @@ public void processElement(StreamRecord element) throws Exception { } } - private static class SnapshottingBufferingSinkWriter - extends TestSink.DefaultSinkWriter { - public static final int NOT_SNAPSHOTTED = -1; - long lastCheckpointId = NOT_SNAPSHOTTED; - - @Override - public List snapshotState(long checkpointId) { - lastCheckpointId = checkpointId; - return elements; - } - - @Override - void restoredFrom(List states) { - this.elements = new ArrayList<>(states); - } - - @Override - public List prepareCommit(boolean flush) { - if (!flush) { - return Collections.emptyList(); - } - List result = elements; - elements = new ArrayList<>(); - return result; - } - } - private static class TestingCommittableSerializer extends SinkV1WriterCommittableSerializer { @@ -654,4 +579,33 @@ public byte[] serialize(List obj) throws IOException { return out.getCopyOfBuffer(); } } + + abstract SinkAndSuppliers sinkWithoutCommitter(); + + abstract SinkAndSuppliers sinkWithTimeBasedWriter(); + + abstract SinkAndSuppliers sinkWithSnapshottingWriter(boolean withState, String stateName); + + abstract SinkAndSuppliers sinkWithCommitter(); + + static class SinkAndSuppliers { + org.apache.flink.api.connector.sink2.Sink sink; + Supplier> elementSupplier; + Supplier> watermarkSupplier; + LongSupplier lastCheckpointSupplier; + Supplier> serializerSupplier; + + public SinkAndSuppliers( + org.apache.flink.api.connector.sink2.Sink sink, + Supplier> elementSupplier, + Supplier> watermarkSupplier, + LongSupplier lastCheckpointSupplier, + Supplier> serializerSupplier) { + this.sink = sink; + this.elementSupplier = elementSupplier; + this.watermarkSupplier = watermarkSupplier; + this.lastCheckpointSupplier = lastCheckpointSupplier; + this.serializerSupplier = serializerSupplier; + } + } } diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/TestSink.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/TestSink.java index ce00362f6a058..742e4438b5d36 100644 --- a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/TestSink.java +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/TestSink.java @@ -49,7 +49,13 @@ import static org.apache.flink.util.Preconditions.checkNotNull; import static org.junit.Assert.assertNotNull; -/** A {@link Sink TestSink} for all the sink related tests. */ +/** + * A {@link Sink TestSink} for all the sink related tests. Use only for tests where {@link + * SinkV1Adapter} should be tested. + * + * @deprecated Use {@link TestSinkV2} instead. + */ +@Deprecated public class TestSink implements Sink { public static final String END_OF_INPUT_STR = "end of input"; @@ -181,11 +187,6 @@ public Builder setDefaultCommitter(Supplier> queueSupplier) { return this; } - public Builder setGlobalCommitter(GlobalCommitter globalCommitter) { - this.globalCommitter = globalCommitter; - return this; - } - public Builder setGlobalCommittableSerializer( SimpleVersionedSerializer globalCommittableSerializer) { this.globalCommittableSerializer = globalCommittableSerializer; @@ -363,10 +364,6 @@ static class DefaultGlobalCommitter extends DefaultCommitter private final String committedSuccessData; - DefaultGlobalCommitter() { - this(""); - } - DefaultGlobalCommitter(String committedSuccessData) { this.committedSuccessData = committedSuccessData; } @@ -397,39 +394,6 @@ public void endOfInput() { } } - /** A {@link GlobalCommitter} that always re-commits global committables it received. */ - static class RetryOnceGlobalCommitter extends DefaultGlobalCommitter { - - private final Set seen = new LinkedHashSet<>(); - - @Override - public List filterRecoveredCommittables(List globalCommittables) { - return globalCommittables; - } - - @Override - public String combine(List committables) { - return String.join("|", committables); - } - - @Override - public void endOfInput() {} - - @Override - public List commit(List committables) { - committables.forEach( - c -> { - if (seen.remove(c)) { - checkNotNull(committedData); - committedData.add(c); - } else { - seen.add(c); - } - }); - return new ArrayList<>(seen); - } - } - /** * We introduce this {@link StringCommittableSerializer} is because that all the fields of * {@link TestSink} should be serializable. diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/TestSinkV2.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/TestSinkV2.java new file mode 100644 index 0000000000000..bb89bf0fe7cc0 --- /dev/null +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/TestSinkV2.java @@ -0,0 +1,434 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.streaming.runtime.operators.sink; + +import org.apache.flink.api.common.eventtime.Watermark; +import org.apache.flink.api.connector.sink2.Committer; +import org.apache.flink.api.connector.sink2.Sink; +import org.apache.flink.api.connector.sink2.SinkWriter; +import org.apache.flink.api.connector.sink2.StatefulSink; +import org.apache.flink.api.connector.sink2.TwoPhaseCommittingSink; +import org.apache.flink.api.java.tuple.Tuple3; +import org.apache.flink.core.io.SimpleVersionedSerializer; +import org.apache.flink.streaming.api.connector.sink2.CommittableMessage; +import org.apache.flink.streaming.api.connector.sink2.WithPostCommitTopology; +import org.apache.flink.streaming.api.datastream.DataStream; +import org.apache.flink.streaming.api.functions.sink.filesystem.bucketassigners.SimpleVersionedStringSerializer; +import org.apache.flink.util.Preconditions; + +import org.apache.flink.shaded.guava31.com.google.common.collect.ImmutableSet; + +import javax.annotation.Nullable; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.function.Supplier; + +import static org.apache.flink.util.Preconditions.checkNotNull; +import static org.junit.Assert.assertNotNull; + +/** A {@link org.apache.flink.api.connector.sink2.Sink} for all the sink related tests. */ +public class TestSinkV2 implements Sink { + + private final DefaultSinkWriter writer; + + private TestSinkV2(DefaultSinkWriter writer) { + this.writer = writer; + } + + @Override + public SinkWriter createWriter(InitContext context) { + writer.init(context); + return writer; + } + + DefaultSinkWriter getWriter() { + return writer; + } + + public static Builder newBuilder() { + return new Builder<>(); + } + + /** A builder class for {@link TestSinkV2}. */ + public static class Builder { + private DefaultSinkWriter writer = null; + private DefaultCommitter committer; + private SimpleVersionedSerializer committableSerializer; + private boolean withPostCommitTopology = false; + private boolean withWriterState = false; + private String compatibleStateNames; + + public Builder setWriter(DefaultSinkWriter writer) { + this.writer = checkNotNull(writer); + return this; + } + + public Builder setCommitter(DefaultCommitter committer) { + this.committer = committer; + return this; + } + + public Builder setCommittableSerializer( + SimpleVersionedSerializer committableSerializer) { + this.committableSerializer = committableSerializer; + return this; + } + + public Builder setDefaultCommitter() { + this.committer = new DefaultCommitter(); + this.committableSerializer = StringSerializer.INSTANCE; + return this; + } + + public Builder setDefaultCommitter( + Supplier>> queueSupplier) { + this.committer = new DefaultCommitter(queueSupplier); + this.committableSerializer = StringSerializer.INSTANCE; + return this; + } + + public Builder setWithPostCommitTopology(boolean withPostCommitTopology) { + this.withPostCommitTopology = withPostCommitTopology; + return this; + } + + public Builder setWriterState(boolean withWriterState) { + this.withWriterState = withWriterState; + return this; + } + + public Builder setCompatibleStateNames(String compatibleStateNames) { + this.compatibleStateNames = compatibleStateNames; + return this; + } + + public TestSinkV2 build() { + if (committer == null) { + if (writer == null) { + writer = new DefaultSinkWriter<>(); + } + // SinkV2 with a simple writer + return new TestSinkV2<>(writer); + } else { + if (writer == null) { + writer = new DefaultCommittingSinkWriter<>(); + } + if (!withPostCommitTopology) { + // TwoPhaseCommittingSink with a stateless writer and a committer + return new TestSinkV2TwoPhaseCommittingSink<>( + writer, committableSerializer, committer); + } else { + if (withWriterState) { + // TwoPhaseCommittingSink with a stateful writer and a committer and post + // commit topology + Preconditions.checkArgument( + writer instanceof DefaultStatefulSinkWriter, + "Please provide a DefaultStatefulSinkWriter instance"); + return new TestStatefulSinkV2( + (DefaultStatefulSinkWriter) writer, + committableSerializer, + committer, + compatibleStateNames); + } else { + // TwoPhaseCommittingSink with a stateless writer and a committer and post + // commit topology + Preconditions.checkArgument( + writer instanceof DefaultCommittingSinkWriter, + "Please provide a DefaultCommittingSinkWriter instance"); + return new TestSinkV2WithPostCommitTopology<>( + (DefaultCommittingSinkWriter) writer, + committableSerializer, + committer); + } + } + } + } + } + + private static class TestSinkV2TwoPhaseCommittingSink extends TestSinkV2 + implements TwoPhaseCommittingSink { + private final DefaultCommitter committer; + private final SimpleVersionedSerializer committableSerializer; + + public TestSinkV2TwoPhaseCommittingSink( + DefaultSinkWriter writer, + SimpleVersionedSerializer committableSerializer, + DefaultCommitter committer) { + super(writer); + this.committer = committer; + this.committableSerializer = committableSerializer; + } + + @Override + public Committer createCommitter() { + committer.init(); + return committer; + } + + @Override + public SimpleVersionedSerializer getCommittableSerializer() { + return committableSerializer; + } + + @Override + public PrecommittingSinkWriter createWriter(InitContext context) { + return (PrecommittingSinkWriter) super.createWriter(context); + } + } + + // -------------------------------------- Sink With PostCommitTopology ------------------------- + + private static class TestSinkV2WithPostCommitTopology + extends TestSinkV2TwoPhaseCommittingSink + implements WithPostCommitTopology { + public TestSinkV2WithPostCommitTopology( + DefaultSinkWriter writer, + SimpleVersionedSerializer committableSerializer, + DefaultCommitter committer) { + super(writer, committableSerializer, committer); + } + + @Override + public void addPostCommitTopology(DataStream> committables) { + // We do not need to do anything for tests + } + } + + private static class TestStatefulSinkV2 extends TestSinkV2WithPostCommitTopology + implements StatefulSink, StatefulSink.WithCompatibleState { + private String compatibleState; + + public TestStatefulSinkV2( + DefaultStatefulSinkWriter writer, + SimpleVersionedSerializer committableSerializer, + DefaultCommitter committer, + String compatibleState) { + super(writer, committableSerializer, committer); + this.compatibleState = compatibleState; + } + + @Override + public DefaultStatefulSinkWriter createWriter(InitContext context) { + return (DefaultStatefulSinkWriter) super.createWriter(context); + } + + @Override + public StatefulSinkWriter restoreWriter( + InitContext context, Collection recoveredState) { + DefaultStatefulSinkWriter statefulWriter = + (DefaultStatefulSinkWriter) getWriter(); + + statefulWriter.restore(recoveredState); + return statefulWriter; + } + + @Override + public SimpleVersionedSerializer getWriterStateSerializer() { + return new StringSerializer(); + } + + @Override + public Collection getCompatibleWriterStateNames() { + return compatibleState == null ? ImmutableSet.of() : ImmutableSet.of(compatibleState); + } + } + + // -------------------------------------- Sink Writer ------------------------------------------ + + /** Base class for out testing {@link SinkWriter}. */ + public static class DefaultSinkWriter implements SinkWriter, Serializable { + + protected List elements; + + protected List watermarks; + + protected DefaultSinkWriter() { + this.elements = new ArrayList<>(); + this.watermarks = new ArrayList<>(); + } + + @Override + public void write(InputT element, Context context) { + elements.add( + Tuple3.of(element, context.timestamp(), context.currentWatermark()).toString()); + } + + @Override + public void flush(boolean endOfInput) throws IOException, InterruptedException { + elements = new ArrayList<>(); + } + + @Override + public void writeWatermark(Watermark watermark) { + watermarks.add(watermark); + } + + @Override + public void close() throws Exception { + // noting to do here + } + + public void init(InitContext context) { + // context is not used in default case + } + } + + /** Base class for out testing {@link TwoPhaseCommittingSink.PrecommittingSinkWriter}. */ + protected static class DefaultCommittingSinkWriter extends DefaultSinkWriter + implements TwoPhaseCommittingSink.PrecommittingSinkWriter, + Serializable { + + @Override + public void flush(boolean endOfInput) throws IOException, InterruptedException { + // We empty the elements on prepareCommit + } + + @Override + public Collection prepareCommit() { + List result = elements; + elements = new ArrayList<>(); + return result; + } + } + + /** + * Base class for out testing {@link StatefulSink.StatefulSinkWriter}. Extends the {@link + * DefaultCommittingSinkWriter} for simplicity. + */ + protected static class DefaultStatefulSinkWriter + extends DefaultCommittingSinkWriter + implements StatefulSink.StatefulSinkWriter { + + @Override + public List snapshotState(long checkpointId) throws IOException { + return elements; + } + + protected void restore(Collection recoveredState) { + this.elements = new ArrayList<>(recoveredState); + } + } + + // -------------------------------------- Sink Committer --------------------------------------- + + /** Base class for testing {@link Committer}. */ + static class DefaultCommitter implements Committer, Serializable { + + @Nullable protected Queue> committedData; + + private boolean isClosed; + + @Nullable private final Supplier>> queueSupplier; + + public DefaultCommitter() { + this.committedData = new ConcurrentLinkedQueue<>(); + this.isClosed = false; + this.queueSupplier = null; + } + + public DefaultCommitter(@Nullable Supplier>> queueSupplier) { + this.queueSupplier = queueSupplier; + this.isClosed = false; + this.committedData = null; + } + + public List> getCommittedData() { + if (committedData != null) { + return new ArrayList<>(committedData); + } else { + return Collections.emptyList(); + } + } + + @Override + public void commit(Collection> committables) { + if (committedData == null) { + assertNotNull(queueSupplier); + committedData = queueSupplier.get(); + } + committedData.addAll(committables); + } + + public void close() throws Exception { + isClosed = true; + } + + public boolean isClosed() { + return isClosed; + } + + public void init() { + // context is not used for this implementation + } + } + + /** A {@link Committer} that always re-commits the committables data it received. */ + static class RetryOnceCommitter extends DefaultCommitter { + + private final Set> seen = new LinkedHashSet<>(); + + @Override + public void commit(Collection> committables) { + committables.forEach( + c -> { + if (seen.remove(c)) { + checkNotNull(committedData); + committedData.add(c); + } else { + seen.add(c); + c.retryLater(); + } + }); + } + } + + /** + * We introduce this {@link StringSerializer} is because that all the fields of {@link + * TestSinkV2} should be serializable. + */ + public static class StringSerializer + implements SimpleVersionedSerializer, Serializable { + + public static final StringSerializer INSTANCE = new StringSerializer(); + + @Override + public int getVersion() { + return SimpleVersionedStringSerializer.INSTANCE.getVersion(); + } + + @Override + public byte[] serialize(String obj) { + return SimpleVersionedStringSerializer.INSTANCE.serialize(obj); + } + + @Override + public String deserialize(int version, byte[] serialized) throws IOException { + return SimpleVersionedStringSerializer.INSTANCE.deserialize(version, serialized); + } + } +} diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/WithAdapterCommitterOperatorTest.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/WithAdapterCommitterOperatorTest.java new file mode 100644 index 0000000000000..c516db8746743 --- /dev/null +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/WithAdapterCommitterOperatorTest.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.streaming.runtime.operators.sink; + +import org.apache.flink.api.connector.sink2.TwoPhaseCommittingSink; + +import java.util.Collections; +import java.util.List; + +class WithAdapterCommitterOperatorTest extends CommitterOperatorTestBase { + + @Override + SinkAndCounters sinkWithPostCommit() { + ForwardingCommitter committer = new ForwardingCommitter(); + return new SinkAndCounters( + (TwoPhaseCommittingSink) + TestSink.newBuilder() + .setCommitter(committer) + .setDefaultGlobalCommitter() + .setCommittableSerializer( + TestSink.StringCommittableSerializer.INSTANCE) + .build() + .asV2(), + () -> committer.successfulCommits); + } + + @Override + SinkAndCounters sinkWithPostCommitWithRetry() { + return new SinkAndCounters( + (TwoPhaseCommittingSink) + TestSink.newBuilder() + .setCommitter(new TestSink.RetryOnceCommitter()) + .setDefaultGlobalCommitter() + .setCommittableSerializer( + TestSink.StringCommittableSerializer.INSTANCE) + .build() + .asV2(), + () -> 0); + } + + @Override + SinkAndCounters sinkWithoutPostCommit() { + ForwardingCommitter committer = new ForwardingCommitter(); + return new SinkAndCounters( + (TwoPhaseCommittingSink) + TestSink.newBuilder() + .setCommitter(committer) + .setCommittableSerializer( + TestSink.StringCommittableSerializer.INSTANCE) + .build() + .asV2(), + () -> committer.successfulCommits); + } + + private static class ForwardingCommitter extends TestSink.DefaultCommitter { + private int successfulCommits = 0; + + @Override + public List commit(List committables) { + successfulCommits += committables.size(); + return Collections.emptyList(); + } + + @Override + public void close() throws Exception {} + } +} diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/WithAdapterSinkWriterOperatorTest.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/WithAdapterSinkWriterOperatorTest.java new file mode 100644 index 0000000000000..5af5ac5a679a4 --- /dev/null +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/operators/sink/WithAdapterSinkWriterOperatorTest.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.streaming.runtime.operators.sink; + +import org.apache.flink.api.connector.sink.Sink; +import org.apache.flink.api.java.tuple.Tuple3; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +class WithAdapterSinkWriterOperatorTest extends SinkWriterOperatorTestBase { + + @Override + SinkAndSuppliers sinkWithoutCommitter() { + TestSink.DefaultSinkWriter sinkWriter = new TestSink.DefaultSinkWriter<>(); + return new SinkAndSuppliers( + TestSink.newBuilder().setWriter(sinkWriter).build().asV2(), + () -> sinkWriter.elements, + () -> sinkWriter.watermarks, + () -> -1, + () -> new TestSink.StringCommittableSerializer()); + } + + @Override + SinkAndSuppliers sinkWithCommitter() { + TestSink.DefaultSinkWriter sinkWriter = new TestSink.DefaultSinkWriter<>(); + return new SinkAndSuppliers( + TestSink.newBuilder().setWriter(sinkWriter).setDefaultCommitter().build().asV2(), + () -> sinkWriter.elements, + () -> sinkWriter.watermarks, + () -> -1, + () -> new TestSink.StringCommittableSerializer()); + } + + @Override + SinkAndSuppliers sinkWithTimeBasedWriter() { + TestSink.DefaultSinkWriter sinkWriter = new TimeBasedBufferingSinkWriter(); + return new SinkAndSuppliers( + TestSink.newBuilder().setWriter(sinkWriter).setDefaultCommitter().build().asV2(), + () -> sinkWriter.elements, + () -> sinkWriter.watermarks, + () -> -1, + () -> new TestSink.StringCommittableSerializer()); + } + + @Override + SinkAndSuppliers sinkWithSnapshottingWriter(boolean withState, String stateName) { + SnapshottingBufferingSinkWriter sinkWriter = new SnapshottingBufferingSinkWriter(); + TestSink.Builder builder = + TestSink.newBuilder().setWriter(sinkWriter).setDefaultCommitter(); + if (withState) { + builder.withWriterState(); + } + if (stateName != null) { + builder.setCompatibleStateNames(stateName); + } + return new SinkAndSuppliers( + builder.build().asV2(), + () -> sinkWriter.elements, + () -> sinkWriter.watermarks, + () -> sinkWriter.lastCheckpointId, + () -> new TestSink.StringCommittableSerializer()); + } + + private static class TimeBasedBufferingSinkWriter extends TestSink.DefaultSinkWriter + implements Sink.ProcessingTimeService.ProcessingTimeCallback { + + private final List cachedCommittables = new ArrayList<>(); + + @Override + public void write(Integer element, Context context) { + cachedCommittables.add( + Tuple3.of(element, context.timestamp(), context.currentWatermark()).toString()); + } + + void setProcessingTimerService(Sink.ProcessingTimeService processingTimerService) { + super.setProcessingTimerService(processingTimerService); + this.processingTimerService.registerProcessingTimer(1000, this); + } + + @Override + public void onProcessingTime(long time) { + elements.addAll(cachedCommittables); + cachedCommittables.clear(); + this.processingTimerService.registerProcessingTimer(time + 1000, this); + } + } + + private static class SnapshottingBufferingSinkWriter + extends TestSink.DefaultSinkWriter { + public static final int NOT_SNAPSHOTTED = -1; + long lastCheckpointId = NOT_SNAPSHOTTED; + + @Override + public List snapshotState(long checkpointId) { + lastCheckpointId = checkpointId; + return elements; + } + + @Override + void restoredFrom(List states) { + this.elements = new ArrayList<>(states); + } + + @Override + public List prepareCommit(boolean flush) { + if (!flush) { + return Collections.emptyList(); + } + List result = elements; + elements = new ArrayList<>(); + return result; + } + } +} diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/tasks/SubtaskCheckpointCoordinatorTest.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/tasks/SubtaskCheckpointCoordinatorTest.java index 5bb1110922bf0..2cba71b4e908f 100644 --- a/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/tasks/SubtaskCheckpointCoordinatorTest.java +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/runtime/tasks/SubtaskCheckpointCoordinatorTest.java @@ -44,6 +44,7 @@ import org.apache.flink.runtime.metrics.groups.UnregisteredMetricGroups; import org.apache.flink.runtime.operators.testutils.DummyEnvironment; import org.apache.flink.runtime.operators.testutils.MockEnvironment; +import org.apache.flink.runtime.operators.testutils.MockInputSplitProvider; import org.apache.flink.runtime.state.CheckpointStorageLocationReference; import org.apache.flink.runtime.state.CheckpointStreamFactory; import org.apache.flink.runtime.state.DoneFuture; @@ -72,7 +73,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.Map; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; @@ -389,10 +389,15 @@ public void testBroadcastCancelCheckpointMarkerOnAbortingFromCoordinator() throw StreamConfig streamConfig = testHarness.getStreamConfig(); streamConfig.setStreamOperator(new MapOperator()); - testHarness.invoke(); - testHarness.waitForTaskRunning(); - - MockEnvironment mockEnvironment = MockEnvironment.builder().build(); + StreamMockEnvironment mockEnvironment = + new StreamMockEnvironment( + testHarness.jobConfig, + testHarness.taskConfig, + testHarness.executionConfig, + testHarness.memorySize, + new MockInputSplitProvider(), + testHarness.bufferSize, + testHarness.taskStateManager); try (SubtaskCheckpointCoordinator subtaskCheckpointCoordinator = new MockSubtaskCheckpointCoordinatorBuilder() @@ -404,13 +409,14 @@ public void testBroadcastCancelCheckpointMarkerOnAbortingFromCoordinator() throw ResultPartitionWriter resultPartitionWriter = new RecordOrEventCollectingResultPartitionWriter<>( recordOrEvents, stringStreamElementSerializer); - mockEnvironment.addOutputs(Collections.singletonList(resultPartitionWriter)); + mockEnvironment.addOutput(resultPartitionWriter); + + testHarness.invoke(mockEnvironment); + testHarness.waitForTaskRunning(); OneInputStreamTask task = testHarness.getTask(); OperatorChain> operatorChain = - new RegularOperatorChain<>( - task, - StreamTask.createRecordWriterDelegate(streamConfig, mockEnvironment)); + task.operatorChain; long checkpointId = 42L; // notify checkpoint aborted before execution. subtaskCheckpointCoordinator.notifyCheckpointAborted( diff --git a/flink-streaming-java/src/test/java/org/apache/flink/streaming/util/MockStreamConfig.java b/flink-streaming-java/src/test/java/org/apache/flink/streaming/util/MockStreamConfig.java index 3080e55f512c5..ec35ce681e202 100644 --- a/flink-streaming-java/src/test/java/org/apache/flink/streaming/util/MockStreamConfig.java +++ b/flink-streaming-java/src/test/java/org/apache/flink/streaming/util/MockStreamConfig.java @@ -30,13 +30,24 @@ import org.apache.flink.streaming.runtime.partitioner.BroadcastPartitioner; import org.apache.flink.streaming.runtime.tasks.SourceStreamTask; +import javax.annotation.Nullable; + import java.util.ArrayList; import java.util.List; +import java.util.Map; /** A dummy stream config implementation for specifying the number of outputs in tests. */ public class MockStreamConfig extends StreamConfig { public MockStreamConfig(Configuration configuration, int numberOfOutputs) { + this(configuration, numberOfOutputs, null); + } + + public MockStreamConfig( + Configuration configuration, + int numberOfOutputs, + @Nullable Map chainedTaskConfigs) { + super(configuration); setChainStart(); @@ -71,6 +82,9 @@ public MockStreamConfig(Configuration configuration, int numberOfOutputs) { } setVertexNonChainedOutputs(streamOutputs); setOperatorNonChainedOutputs(streamOutputs); + if (chainedTaskConfigs != null) { + setAndSerializeTransitiveChainedTaskConfigs(chainedTaskConfigs); + } serializeAllConfigs(); } } diff --git a/flink-streaming-scala/pom.xml b/flink-streaming-scala/pom.xml index c3d0e8e2cb75e..a40a76e4b0321 100644 --- a/flink-streaming-scala/pom.xml +++ b/flink-streaming-scala/pom.xml @@ -263,7 +263,7 @@ under the License. - io.github.zentol.japicmp + com.github.siom79.japicmp japicmp-maven-plugin @@ -305,7 +305,7 @@ under the License. - io.github.zentol.japicmp + com.github.siom79.japicmp japicmp-maven-plugin diff --git a/flink-table/flink-sql-client/src/test/java/org/apache/flink/table/client/cli/CliClientTest.java b/flink-table/flink-sql-client/src/test/java/org/apache/flink/table/client/cli/CliClientTest.java index 5cf6b527a6cce..f2920c21d180f 100644 --- a/flink-table/flink-sql-client/src/test/java/org/apache/flink/table/client/cli/CliClientTest.java +++ b/flink-table/flink-sql-client/src/test/java/org/apache/flink/table/client/cli/CliClientTest.java @@ -21,6 +21,7 @@ import org.apache.flink.api.common.JobID; import org.apache.flink.configuration.Configuration; import org.apache.flink.configuration.ReadableConfig; +import org.apache.flink.core.testutils.CheckedThread; import org.apache.flink.runtime.testutils.CommonTestUtils; import org.apache.flink.table.api.DataTypes; import org.apache.flink.table.api.ResultKind; @@ -294,14 +295,13 @@ void testCancelExecutionInteractiveMode() throws Exception { try (Terminal terminal = TerminalUtils.createDumbTerminal(inputStream, outputStream); CliClient client = new CliClient(() -> terminal, mockExecutor, historyFilePath, null)) { - Thread thread = - new Thread( - () -> { - try { - client.executeInInteractiveMode(); - } catch (Exception ignore) { - } - }); + CheckedThread thread = + new CheckedThread() { + @Override + public void go() { + client.executeInInteractiveMode(); + } + }; thread.start(); while (!mockExecutor.isAwait) { @@ -311,6 +311,8 @@ void testCancelExecutionInteractiveMode() throws Exception { terminal.raise(Terminal.Signal.INT); CommonTestUtils.waitUntilCondition( () -> outputStream.toString().contains(CliStrings.MESSAGE_HELP)); + // Prevent NPE when closing the terminal. See FLINK-33116 for more information. + thread.sync(); } } diff --git a/flink-table/flink-sql-client/src/test/resources/sql/function.q b/flink-table/flink-sql-client/src/test/resources/sql/function.q index 320fbefc8989b..e3e21ed8ccdf5 100644 --- a/flink-table/flink-sql-client/src/test/resources/sql/function.q +++ b/flink-table/flink-sql-client/src/test/resources/sql/function.q @@ -306,6 +306,11 @@ create function upperudf AS 'UpperUDF' using jar '$VAR_UDF_JAR_PATH'; [INFO] Execute statement succeed. !info +# `SHOW JARS` does not list the jars being used by function, it only list all the jars added by `ADD JAR` +SHOW JARS; +Empty set +!ok + # run a query to verify the registered UDF works SELECT id, upperudf(str) FROM (VALUES (1, 'hello world'), (2, 'hi')) as T(id, str); +----+-------------+--------------------------------+ @@ -317,19 +322,27 @@ SELECT id, upperudf(str) FROM (VALUES (1, 'hello world'), (2, 'hi')) as T(id, st Received a total of 2 rows !ok +# Each query registers its jar to resource manager could not affect the session in sql gateway SHOW JARS; -+-$VAR_UDF_JAR_PATH_DASH-----+ -| $VAR_UDF_JAR_PATH_SPACEjars | -+-$VAR_UDF_JAR_PATH_DASH-----+ -| $VAR_UDF_JAR_PATH | -+-$VAR_UDF_JAR_PATH_DASH-----+ -1 row in set +Empty set !ok -REMOVE JAR '$VAR_UDF_JAR_PATH'; -[INFO] Execute statement succeed. -!info +# Show all users functions which should not add function jars to session resource manager +show user functions; ++---------------+ +| function name | ++---------------+ +| func11 | +| func3 | +| func4 | +| temp_upperudf | +| tmp_func | +| upperudf | ++---------------+ +6 rows in set +!ok +# Show functions will not affect the session in sql gateway SHOW JARS; Empty set !ok diff --git a/flink-table/flink-sql-gateway/src/main/java/org/apache/flink/table/gateway/service/operation/OperationExecutor.java b/flink-table/flink-sql-gateway/src/main/java/org/apache/flink/table/gateway/service/operation/OperationExecutor.java index 96a9003f81676..1d29e555a7463 100644 --- a/flink-table/flink-sql-gateway/src/main/java/org/apache/flink/table/gateway/service/operation/OperationExecutor.java +++ b/flink-table/flink-sql-gateway/src/main/java/org/apache/flink/table/gateway/service/operation/OperationExecutor.java @@ -37,6 +37,7 @@ import org.apache.flink.table.api.TableConfig; import org.apache.flink.table.api.TableException; import org.apache.flink.table.api.bridge.java.internal.StreamTableEnvironmentImpl; +import org.apache.flink.table.api.internal.ExecutableOperationContextImpl; import org.apache.flink.table.api.internal.TableEnvironmentInternal; import org.apache.flink.table.api.internal.TableResultInternal; import org.apache.flink.table.catalog.CatalogBaseTable.TableKind; @@ -70,10 +71,12 @@ import org.apache.flink.table.operations.CompileAndExecutePlanOperation; import org.apache.flink.table.operations.DeleteFromFilterOperation; import org.apache.flink.table.operations.EndStatementSetOperation; +import org.apache.flink.table.operations.ExecutableOperation; import org.apache.flink.table.operations.LoadModuleOperation; import org.apache.flink.table.operations.ModifyOperation; import org.apache.flink.table.operations.Operation; import org.apache.flink.table.operations.QueryOperation; +import org.apache.flink.table.operations.ShowFunctionsOperation; import org.apache.flink.table.operations.StatementSetOperation; import org.apache.flink.table.operations.UnloadModuleOperation; import org.apache.flink.table.operations.UseOperation; @@ -82,10 +85,13 @@ import org.apache.flink.table.operations.command.RemoveJarOperation; import org.apache.flink.table.operations.command.ResetOperation; import org.apache.flink.table.operations.command.SetOperation; +import org.apache.flink.table.operations.command.ShowJarsOperation; import org.apache.flink.table.operations.command.ShowJobsOperation; import org.apache.flink.table.operations.command.StopJobOperation; import org.apache.flink.table.operations.ddl.AlterOperation; +import org.apache.flink.table.operations.ddl.CreateCatalogFunctionOperation; import org.apache.flink.table.operations.ddl.CreateOperation; +import org.apache.flink.table.operations.ddl.CreateTempSystemFunctionOperation; import org.apache.flink.table.operations.ddl.DropOperation; import org.apache.flink.table.resource.ResourceManager; import org.apache.flink.table.utils.DateTimeUtils; @@ -110,6 +116,8 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import static org.apache.flink.api.common.RuntimeExecutionMode.STREAMING; +import static org.apache.flink.configuration.ExecutionOptions.RUNTIME_MODE; import static org.apache.flink.table.api.internal.TableResultInternal.TABLE_RESULT_OK; import static org.apache.flink.table.gateway.service.utils.Constants.COMPLETION_CANDIDATES; import static org.apache.flink.table.gateway.service.utils.Constants.JOB_ID; @@ -183,7 +191,8 @@ public ResultFetcher configureSession(OperationHandle handle, String statement) public ResultFetcher executeStatement(OperationHandle handle, String statement) { // Instantiate the TableEnvironment lazily - TableEnvironmentInternal tableEnv = getTableEnvironment(); + ResourceManager resourceManager = sessionContext.getSessionState().resourceManager.copy(); + TableEnvironmentInternal tableEnv = getTableEnvironment(resourceManager); List parsedOperations = tableEnv.getParser().parse(statement); if (parsedOperations.size() > 1) { throw new UnsupportedOperationException( @@ -197,14 +206,15 @@ public ResultFetcher executeStatement(OperationHandle handle, String statement) try { SqlGatewayStreamExecutionEnvironment.setAsContext( sessionContext.getUserClassloader()); - return executeOperation(tableEnv, handle, op); + return executeOperation(tableEnv, handle, op).withResourceManager(resourceManager); } finally { SqlGatewayStreamExecutionEnvironment.unsetAsContext(); } } else { return sessionContext.isStatementSetState() ? executeOperationInStatementSetState(tableEnv, handle, op) - : executeOperation(tableEnv, handle, op); + .withResourceManager(resourceManager) + : executeOperation(tableEnv, handle, op).withResourceManager(resourceManager); } } @@ -315,6 +325,10 @@ public ResultFetcher getCompletionHints( // -------------------------------------------------------------------------------------------- public TableEnvironmentInternal getTableEnvironment() { + return getTableEnvironment(sessionContext.getSessionState().resourceManager); + } + + public TableEnvironmentInternal getTableEnvironment(ResourceManager resourceManager) { // checks the value of RUNTIME_MODE Configuration operationConfig = sessionContext.getSessionConf().clone(); operationConfig.addAll(executionConfig); @@ -342,8 +356,8 @@ public TableEnvironmentInternal getTableEnvironment() { executor, sessionContext.getSessionState().catalogManager, sessionContext.getSessionState().moduleManager, - sessionContext.getSessionState().resourceManager, - sessionContext.getSessionState().functionCatalog); + resourceManager, + sessionContext.getSessionState().functionCatalog.copy(resourceManager)); } private static Executor lookupExecutor( @@ -440,11 +454,41 @@ private ResultFetcher executeOperation( return callShowJobsOperation(tableEnv, handle, (ShowJobsOperation) op); } else if (op instanceof RemoveJarOperation) { return callRemoveJar(handle, ((RemoveJarOperation) op).getPath()); + } else if (op instanceof AddJarOperation + || op instanceof ShowJarsOperation + || op instanceof CreateTempSystemFunctionOperation + || op instanceof CreateCatalogFunctionOperation + || op instanceof ShowFunctionsOperation) { + return callExecutableOperation(handle, (ExecutableOperation) op); } else { return callOperation(tableEnv, handle, op); } } + private ResultFetcher callExecutableOperation(OperationHandle handle, ExecutableOperation op) { + TableResultInternal result = + op.execute( + new ExecutableOperationContextImpl( + sessionContext.getSessionState().catalogManager, + sessionContext.getSessionState().functionCatalog, + sessionContext.getSessionState().moduleManager, + sessionContext.getSessionState().resourceManager, + tableConfig(), + sessionContext.getSessionConf().get(RUNTIME_MODE) == STREAMING)); + return ResultFetcher.fromTableResult(handle, result, false); + } + + private TableConfig tableConfig() { + Configuration operationConfig = sessionContext.getSessionConf().clone(); + operationConfig.addAll(executionConfig); + + TableConfig tableConfig = TableConfig.getDefault(); + tableConfig.setRootConfiguration(sessionContext.getDefaultContext().getFlinkConfig()); + tableConfig.addConfiguration(operationConfig); + + return tableConfig; + } + private ResultFetcher callSetOperation( TableEnvironmentInternal tableEnv, OperationHandle handle, SetOperation setOp) { if (setOp.getKey().isPresent() && setOp.getValue().isPresent()) { diff --git a/flink-table/flink-sql-gateway/src/main/java/org/apache/flink/table/gateway/service/result/ResultFetcher.java b/flink-table/flink-sql-gateway/src/main/java/org/apache/flink/table/gateway/service/result/ResultFetcher.java index 9fa810c9c8cba..f901091f18ed8 100644 --- a/flink-table/flink-sql-gateway/src/main/java/org/apache/flink/table/gateway/service/result/ResultFetcher.java +++ b/flink-table/flink-sql-gateway/src/main/java/org/apache/flink/table/gateway/service/result/ResultFetcher.java @@ -30,6 +30,7 @@ import org.apache.flink.table.gateway.api.results.ResultSet; import org.apache.flink.table.gateway.api.results.ResultSetImpl; import org.apache.flink.table.gateway.service.utils.SqlExecutionException; +import org.apache.flink.table.resource.ResourceManager; import org.apache.flink.table.utils.print.RowDataToStringConverter; import org.apache.flink.util.CloseableIterator; import org.apache.flink.util.CollectionUtil; @@ -39,6 +40,7 @@ import javax.annotation.Nullable; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -78,6 +80,7 @@ public class ResultFetcher { private long currentToken = 0; private boolean noMoreResults = false; + @Nullable private ResourceManager resourceManager; private ResultFetcher( OperationHandle operationHandle, @@ -181,8 +184,20 @@ public static ResultFetcher fromResults( return new ResultFetcher(operationHandle, resultSchema, results, jobID, resultKind); } + public ResultFetcher withResourceManager(ResourceManager resourceManager) { + this.resourceManager = resourceManager; + return this; + } + public void close() { resultStore.close(); + if (resourceManager != null) { + try { + resourceManager.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } public ResolvedSchema getResultSchema() { diff --git a/flink-table/flink-sql-gateway/src/test/java/org/apache/flink/table/gateway/rest/OperationRelatedITCase.java b/flink-table/flink-sql-gateway/src/test/java/org/apache/flink/table/gateway/rest/OperationRelatedITCase.java index a26799462dad1..b7e9e2fef4b9e 100644 --- a/flink-table/flink-sql-gateway/src/test/java/org/apache/flink/table/gateway/rest/OperationRelatedITCase.java +++ b/flink-table/flink-sql-gateway/src/test/java/org/apache/flink/table/gateway/rest/OperationRelatedITCase.java @@ -18,6 +18,7 @@ package org.apache.flink.table.gateway.rest; +import org.apache.flink.core.testutils.OneShotLatch; import org.apache.flink.runtime.rest.messages.EmptyMessageParameters; import org.apache.flink.runtime.rest.messages.EmptyRequestBody; import org.apache.flink.table.gateway.api.operation.OperationHandle; @@ -43,7 +44,6 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -119,6 +119,9 @@ List submitOperation() throws Exception { SessionHandle sessionHandle = new SessionHandle(UUID.fromString(sessionHandleId)); assertThat(SQL_GATEWAY_SERVICE_EXTENSION.getSessionManager().getSession(sessionHandle)) .isNotNull(); + + OneShotLatch startLatch = new OneShotLatch(); + Thread main = Thread.currentThread(); OperationHandle operationHandle = SQL_GATEWAY_SERVICE_EXTENSION .getService() @@ -126,11 +129,15 @@ List submitOperation() throws Exception { sessionHandle, () -> { try { - TimeUnit.SECONDS.sleep(10); + startLatch.trigger(); + // keep operation in RUNNING state in response to cancel + // or close operations. + main.join(); } catch (InterruptedException ignored) { } return NotReadyResult.INSTANCE; }); + startLatch.await(); assertThat(operationHandle).isNotNull(); return Arrays.asList(sessionHandleId, operationHandle.getIdentifier().toString()); } diff --git a/flink-table/flink-sql-jdbc-driver-bundle/pom.xml b/flink-table/flink-sql-jdbc-driver-bundle/pom.xml index 77585f330321d..d310182adff90 100644 --- a/flink-table/flink-sql-jdbc-driver-bundle/pom.xml +++ b/flink-table/flink-sql-jdbc-driver-bundle/pom.xml @@ -84,7 +84,7 @@ - io.github.zentol.japicmp + com.github.siom79.japicmp japicmp-maven-plugin true diff --git a/flink-table/flink-table-api-java-bridge/src/main/java/org/apache/flink/connector/datagen/table/DataGenTableSource.java b/flink-table/flink-table-api-java-bridge/src/main/java/org/apache/flink/connector/datagen/table/DataGenTableSource.java index f9c1bf4deee09..9163c2a63f2fd 100644 --- a/flink-table/flink-table-api-java-bridge/src/main/java/org/apache/flink/connector/datagen/table/DataGenTableSource.java +++ b/flink-table/flink-table-api-java-bridge/src/main/java/org/apache/flink/connector/datagen/table/DataGenTableSource.java @@ -21,7 +21,6 @@ import org.apache.flink.annotation.Internal; import org.apache.flink.annotation.VisibleForTesting; import org.apache.flink.connector.datagen.table.types.RowDataGenerator; -import org.apache.flink.streaming.api.functions.source.StatefulSequenceSource; import org.apache.flink.streaming.api.functions.source.datagen.DataGenerator; import org.apache.flink.streaming.api.functions.source.datagen.DataGeneratorSource; import org.apache.flink.table.connector.ChangelogMode; @@ -33,10 +32,7 @@ import org.apache.flink.table.sources.StreamTableSource; import org.apache.flink.table.types.DataType; -/** - * A {@link StreamTableSource} that emits each number from a given interval exactly once, possibly - * in parallel. See {@link StatefulSequenceSource}. - */ +/** A {@link StreamTableSource} that emits generated data rows. */ @Internal public class DataGenTableSource implements ScanTableSource, SupportsLimitPushDown { diff --git a/flink-table/flink-table-api-java-bridge/src/main/java/org/apache/flink/table/connector/source/DataStreamScanProvider.java b/flink-table/flink-table-api-java-bridge/src/main/java/org/apache/flink/table/connector/source/DataStreamScanProvider.java index 7fc4687363f28..213e3806327a1 100644 --- a/flink-table/flink-table-api-java-bridge/src/main/java/org/apache/flink/table/connector/source/DataStreamScanProvider.java +++ b/flink-table/flink-table-api-java-bridge/src/main/java/org/apache/flink/table/connector/source/DataStreamScanProvider.java @@ -23,6 +23,7 @@ import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.table.api.CompiledPlan; +import org.apache.flink.table.connector.ParallelismProvider; import org.apache.flink.table.connector.ProviderContext; import org.apache.flink.table.data.RowData; @@ -35,7 +36,8 @@ * or {@link InputFormatProvider}. */ @PublicEvolving -public interface DataStreamScanProvider extends ScanTableSource.ScanRuntimeProvider { +public interface DataStreamScanProvider + extends ScanTableSource.ScanRuntimeProvider, ParallelismProvider { /** * Creates a scan Java {@link DataStream} from a {@link StreamExecutionEnvironment}. diff --git a/flink-table/flink-table-api-java-bridge/src/main/java/org/apache/flink/table/connector/source/SourceFunctionProvider.java b/flink-table/flink-table-api-java-bridge/src/main/java/org/apache/flink/table/connector/source/SourceFunctionProvider.java index ff7238d8a2f7c..e5c35525e162d 100644 --- a/flink-table/flink-table-api-java-bridge/src/main/java/org/apache/flink/table/connector/source/SourceFunctionProvider.java +++ b/flink-table/flink-table-api-java-bridge/src/main/java/org/apache/flink/table/connector/source/SourceFunctionProvider.java @@ -20,8 +20,13 @@ import org.apache.flink.annotation.PublicEvolving; import org.apache.flink.streaming.api.functions.source.SourceFunction; +import org.apache.flink.table.connector.ParallelismProvider; import org.apache.flink.table.data.RowData; +import javax.annotation.Nullable; + +import java.util.Optional; + /** * Provider of a {@link SourceFunction} instance as a runtime implementation for {@link * ScanTableSource}. @@ -32,10 +37,19 @@ */ @Deprecated @PublicEvolving -public interface SourceFunctionProvider extends ScanTableSource.ScanRuntimeProvider { +public interface SourceFunctionProvider + extends ScanTableSource.ScanRuntimeProvider, ParallelismProvider { /** Helper method for creating a static provider. */ static SourceFunctionProvider of(SourceFunction sourceFunction, boolean isBounded) { + return of(sourceFunction, isBounded, null); + } + + /** Helper method for creating a Source provider with a provided source parallelism. */ + static SourceFunctionProvider of( + SourceFunction sourceFunction, + boolean isBounded, + @Nullable Integer sourceParallelism) { return new SourceFunctionProvider() { @Override public SourceFunction createSourceFunction() { @@ -46,6 +60,11 @@ public SourceFunction createSourceFunction() { public boolean isBounded() { return isBounded; } + + @Override + public Optional getParallelism() { + return Optional.ofNullable(sourceParallelism); + } }; } diff --git a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/FunctionCatalog.java b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/FunctionCatalog.java index 8eb8f82351f6a..55507ad1e6d78 100644 --- a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/FunctionCatalog.java +++ b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/FunctionCatalog.java @@ -78,9 +78,8 @@ public final class FunctionCatalog { private final CatalogManager catalogManager; private final ModuleManager moduleManager; - private final Map tempSystemFunctions = new LinkedHashMap<>(); - private final Map tempCatalogFunctions = - new LinkedHashMap<>(); + private final Map tempSystemFunctions; + private final Map tempCatalogFunctions; /** * Temporary utility until the new type inference is fully functional. It needs to be set by the @@ -93,10 +92,31 @@ public FunctionCatalog( ResourceManager resourceManager, CatalogManager catalogManager, ModuleManager moduleManager) { + this( + config, + resourceManager, + catalogManager, + moduleManager, + new LinkedHashMap<>(), + new LinkedHashMap<>(), + null); + } + + private FunctionCatalog( + ReadableConfig config, + ResourceManager resourceManager, + CatalogManager catalogManager, + ModuleManager moduleManager, + Map tempSystemFunctions, + Map tempCatalogFunctions, + PlannerTypeInferenceUtil plannerTypeInferenceUtil) { this.config = checkNotNull(config); this.resourceManager = checkNotNull(resourceManager); this.catalogManager = checkNotNull(catalogManager); this.moduleManager = checkNotNull(moduleManager); + this.tempSystemFunctions = tempSystemFunctions; + this.tempCatalogFunctions = tempCatalogFunctions; + this.plannerTypeInferenceUtil = plannerTypeInferenceUtil; } public void setPlannerTypeInferenceUtil(PlannerTypeInferenceUtil plannerTypeInferenceUtil) { @@ -777,6 +797,17 @@ public void registerFunctionJarResources(String functionName, List } } + public FunctionCatalog copy(ResourceManager newResourceManager) { + return new FunctionCatalog( + config, + newResourceManager, + catalogManager, + moduleManager, + tempSystemFunctions, + tempCatalogFunctions, + plannerTypeInferenceUtil); + } + private void registerCatalogFunction( ObjectIdentifier identifier, CatalogFunction catalogFunction, boolean ignoreIfExists) { final ObjectIdentifier normalizedIdentifier = diff --git a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/resource/ResourceManager.java b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/resource/ResourceManager.java index 5de2b5d6ef4b9..307380af41b8a 100644 --- a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/resource/ResourceManager.java +++ b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/resource/ResourceManager.java @@ -69,10 +69,12 @@ public class ResourceManager implements Closeable { private static final String JAR_SUFFIX = "jar"; private static final String FILE_SCHEME = "file"; - private final Path localResourceDir; + protected final Path localResourceDir; /** Resource infos for functions. */ private final Map functionResourceInfos; + private final boolean cleanLocalResource; + protected final Map resourceInfos; protected final MutableURLClassLoader userClassLoader; @@ -84,13 +86,27 @@ public static ResourceManager createResourceManager( } public ResourceManager(ReadableConfig config, MutableURLClassLoader userClassLoader) { - this.localResourceDir = + this( new Path( config.get(TableConfigOptions.RESOURCES_DOWNLOAD_DIR), - String.format("flink-table-%s", UUID.randomUUID())); - this.functionResourceInfos = new HashMap<>(); - this.resourceInfos = new HashMap<>(); + String.format("flink-table-%s", UUID.randomUUID())), + new HashMap<>(), + new HashMap<>(), + userClassLoader, + true); + } + + private ResourceManager( + Path localResourceDir, + Map resourceInfos, + Map functionResourceInfos, + MutableURLClassLoader userClassLoader, + boolean cleanLocalResource) { + this.localResourceDir = localResourceDir; + this.functionResourceInfos = functionResourceInfos; + this.resourceInfos = resourceInfos; this.userClassLoader = userClassLoader; + this.cleanLocalResource = cleanLocalResource; } /** @@ -232,6 +248,15 @@ public void addJarConfiguration(TableConfig tableConfig) { tableConfig.set(PipelineOptions.JARS, new ArrayList<>(jarFiles)); } + public ResourceManager copy() { + return new ResourceManager( + localResourceDir, + new HashMap<>(resourceInfos), + new HashMap<>(functionResourceInfos), + userClassLoader.copy(), + false); + } + @Override public void close() throws IOException { resourceInfos.clear(); @@ -245,14 +270,17 @@ public void close() throws IOException { exception = e; } - FileSystem fileSystem = FileSystem.getLocalFileSystem(); - try { - if (fileSystem.exists(localResourceDir)) { - fileSystem.delete(localResourceDir, true); + if (cleanLocalResource) { + FileSystem fileSystem = FileSystem.getLocalFileSystem(); + try { + if (fileSystem.exists(localResourceDir)) { + fileSystem.delete(localResourceDir, true); + } + } catch (IOException ioe) { + LOG.debug( + String.format("Error while delete directory [%s].", localResourceDir), ioe); + exception = ExceptionUtils.firstOrSuppressed(ioe, exception); } - } catch (IOException ioe) { - LOG.debug(String.format("Error while delete directory [%s].", localResourceDir), ioe); - exception = ExceptionUtils.firstOrSuppressed(ioe, exception); } if (exception != null) { diff --git a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/resource/ResourceManagerTest.java b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/resource/ResourceManagerTest.java index 42305d806af06..400e865b41624 100644 --- a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/resource/ResourceManagerTest.java +++ b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/resource/ResourceManagerTest.java @@ -431,6 +431,25 @@ void testRegisterFunctionWithResource() { assertThat(functionResourceInfos.containsKey(resourceUri)).isFalse(); } + @Test + void testCloseCopiedResourceManager() throws Exception { + ResourceUri resourceUri = new ResourceUri(ResourceType.JAR, udfJar.getPath()); + resourceManager.declareFunctionResources(Collections.singleton(resourceUri)); + resourceManager.registerJarResources(Collections.singletonList(resourceUri)); + assertThat(resourceManager.functionResourceInfos().size()).isEqualTo(1); + assertThat(resourceManager.resourceInfos.size()).isEqualTo(1); + + ResourceManager copiedResourceManager = resourceManager.copy(); + assertThat(copiedResourceManager.functionResourceInfos().size()).isEqualTo(1); + assertThat(copiedResourceManager.resourceInfos.size()).isEqualTo(1); + copiedResourceManager.close(); + assertThat(copiedResourceManager.functionResourceInfos().size()).isEqualTo(0); + assertThat(copiedResourceManager.resourceInfos.size()).isEqualTo(0); + + assertThat(resourceManager.functionResourceInfos().size()).isEqualTo(1); + assertThat(resourceManager.resourceInfos.size()).isEqualTo(1); + } + @Test public void testCloseResourceManagerCleanDownloadedResources() throws Exception { resourceManager.close(); diff --git a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/ConfigOptionTestStep.java b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/ConfigOptionTestStep.java new file mode 100644 index 0000000000000..7f66122f1cd99 --- /dev/null +++ b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/ConfigOptionTestStep.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.test.program; + +import org.apache.flink.configuration.ConfigOption; +import org.apache.flink.table.api.TableEnvironment; + +/** Test step for setting a {@link ConfigOption}. */ +public final class ConfigOptionTestStep implements TestStep { + + public final ConfigOption option; + public final T value; + + ConfigOptionTestStep(ConfigOption option, T value) { + this.option = option; + this.value = value; + } + + @Override + public TestKind getKind() { + return TestKind.CONFIG; + } + + public void apply(TableEnvironment env) { + env.getConfig().set(option, value); + } +} diff --git a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/FunctionTestStep.java b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/FunctionTestStep.java new file mode 100644 index 0000000000000..ad377bae4cda5 --- /dev/null +++ b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/FunctionTestStep.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.test.program; + +import org.apache.flink.table.api.TableEnvironment; +import org.apache.flink.table.functions.UserDefinedFunction; + +/** Test step for registering a (temporary) (system or catalog) function. */ +public final class FunctionTestStep implements TestStep { + + /** Whether function should be temporary or not. */ + enum FunctionPersistence { + TEMPORARY, + PERSISTENT + } + + /** Whether function should be persisted in a catalog or not. */ + enum FunctionBehavior { + SYSTEM, + CATALOG + } + + public final FunctionPersistence persistence; + public final FunctionBehavior behavior; + public final String name; + public final Class function; + + FunctionTestStep( + FunctionPersistence persistence, + FunctionBehavior behavior, + String name, + Class function) { + this.persistence = persistence; + this.behavior = behavior; + this.name = name; + this.function = function; + } + + @Override + public TestKind getKind() { + return TestKind.FUNCTION; + } + + public void apply(TableEnvironment env) { + if (behavior == FunctionBehavior.SYSTEM) { + if (persistence == FunctionPersistence.TEMPORARY) { + env.createTemporarySystemFunction(name, function); + } else { + throw new UnsupportedOperationException("System functions must be temporary."); + } + } else { + if (persistence == FunctionPersistence.TEMPORARY) { + env.createTemporaryFunction(name, function); + } else { + env.createFunction(name, function); + } + } + } +} diff --git a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/SinkTestStep.java b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/SinkTestStep.java new file mode 100644 index 0000000000000..11ae1bb055d15 --- /dev/null +++ b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/SinkTestStep.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.test.program; + +import org.apache.flink.types.Row; + +import javax.annotation.Nullable; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** Test step for creating a table sink. */ +public final class SinkTestStep extends TableTestStep { + + public final @Nullable List expectedBeforeRestore; + public final @Nullable List expectedAfterRestore; + public final @Nullable List expectedBeforeRestoreStrings; + public final @Nullable List expectedAfterRestoreStrings; + + SinkTestStep( + String name, + List schemaComponents, + List partitionKeys, + Map options, + @Nullable List expectedBeforeRestore, + @Nullable List expectedAfterRestore, + @Nullable List expectedBeforeRestoreStrings, + @Nullable List expectedAfterRestoreStrings) { + super(name, schemaComponents, partitionKeys, options); + if (expectedBeforeRestore != null && expectedAfterRestoreStrings != null) { + throw new IllegalArgumentException( + "You can not mix Row/String representation in before/after restore data."); + } + if (expectedBeforeRestoreStrings != null && expectedAfterRestore != null) { + throw new IllegalArgumentException( + "You can not mix Row/String representation in before/after restore data."); + } + this.expectedBeforeRestore = expectedBeforeRestore; + this.expectedAfterRestore = expectedAfterRestore; + this.expectedBeforeRestoreStrings = expectedBeforeRestoreStrings; + this.expectedAfterRestoreStrings = expectedAfterRestoreStrings; + } + + /** Builder for creating a {@link SinkTestStep}. */ + public static SinkTestStep.Builder newBuilder(String name) { + return new SinkTestStep.Builder(name); + } + + public List getExpectedBeforeRestoreAsStrings() { + if (expectedBeforeRestoreStrings != null) { + return expectedBeforeRestoreStrings; + } + + if (expectedBeforeRestore != null) { + return expectedBeforeRestore.stream().map(Row::toString).collect(Collectors.toList()); + } + + return null; + } + + public List getExpectedAfterRestoreAsStrings() { + if (expectedAfterRestoreStrings != null) { + return expectedAfterRestoreStrings; + } + + if (expectedAfterRestore != null) { + return expectedAfterRestore.stream().map(Row::toString).collect(Collectors.toList()); + } + + return null; + } + + @Override + public TestKind getKind() { + return expectedBeforeRestore == null && expectedBeforeRestoreStrings == null + ? TestKind.SINK_WITHOUT_DATA + : expectedAfterRestore == null && expectedAfterRestoreStrings == null + ? TestKind.SINK_WITH_DATA + : TestKind.SINK_WITH_RESTORE_DATA; + } + + /** Builder pattern for {@link SinkTestStep}. */ + public static final class Builder extends AbstractBuilder { + + private List expectedBeforeRestore; + private List expectedAfterRestore; + + private List expectedBeforeRestoreStrings; + private List expectedAfterRestoreStrings; + + private Builder(String name) { + super(name); + } + + public Builder consumedValues(Row... expectedRows) { + return consumedBeforeRestore(expectedRows); + } + + public Builder consumedValues(String... expectedRows) { + return consumedBeforeRestore(expectedRows); + } + + public Builder consumedBeforeRestore(Row... expectedRows) { + this.expectedBeforeRestore = Arrays.asList(expectedRows); + return this; + } + + public Builder consumedBeforeRestore(String... expectedRows) { + this.expectedBeforeRestoreStrings = Arrays.asList(expectedRows); + return this; + } + + public Builder consumedAfterRestore(Row... expectedRows) { + this.expectedAfterRestore = Arrays.asList(expectedRows); + return this; + } + + public Builder consumedAfterRestore(String... expectedRows) { + this.expectedAfterRestoreStrings = Arrays.asList(expectedRows); + return this; + } + + public SinkTestStep build() { + return new SinkTestStep( + name, + schemaComponents, + partitionKeys, + options, + expectedBeforeRestore, + expectedAfterRestore, + expectedBeforeRestoreStrings, + expectedAfterRestoreStrings); + } + } +} diff --git a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/SourceTestStep.java b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/SourceTestStep.java new file mode 100644 index 0000000000000..6653eb174c5ec --- /dev/null +++ b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/SourceTestStep.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.test.program; + +import org.apache.flink.types.Row; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** Test step for creating a table source. */ +public final class SourceTestStep extends TableTestStep { + + public final List dataBeforeRestore; + public final List dataAfterRestore; + + SourceTestStep( + String name, + List schemaComponents, + List partitionKeys, + Map options, + List dataBeforeRestore, + List dataAfterRestore) { + super(name, schemaComponents, partitionKeys, options); + this.dataBeforeRestore = dataBeforeRestore; + this.dataAfterRestore = dataAfterRestore; + } + + /** Builder for creating a {@link SourceTestStep}. */ + public static Builder newBuilder(String name) { + return new Builder(name); + } + + @Override + public TestKind getKind() { + return dataBeforeRestore.isEmpty() + ? TestKind.SOURCE_WITHOUT_DATA + : dataAfterRestore.isEmpty() + ? TestKind.SOURCE_WITH_DATA + : TestKind.SOURCE_WITH_RESTORE_DATA; + } + + /** Builder pattern for {@link SourceTestStep}. */ + public static final class Builder extends AbstractBuilder { + + private final List dataBeforeRestore = new ArrayList<>(); + private final List dataAfterRestore = new ArrayList<>(); + + private Builder(String name) { + super(name); + } + + public Builder producedValues(Row... data) { + return producedBeforeRestore(data); + } + + public Builder producedBeforeRestore(Row... data) { + this.dataBeforeRestore.addAll(Arrays.asList(data)); + return this; + } + + public Builder producedAfterRestore(Row... data) { + this.dataAfterRestore.addAll(Arrays.asList(data)); + return this; + } + + public SourceTestStep build() { + return new SourceTestStep( + name, + schemaComponents, + partitionKeys, + options, + dataBeforeRestore, + dataAfterRestore); + } + } +} diff --git a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/SqlTestStep.java b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/SqlTestStep.java new file mode 100644 index 0000000000000..c6809dc16cfba --- /dev/null +++ b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/SqlTestStep.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.test.program; + +import org.apache.flink.table.api.TableEnvironment; +import org.apache.flink.table.api.TableResult; + +/** + * Test step for execution SQL. + * + *

Note: Not every runner supports generic SQL statements. Sometimes the runner would like to + * enrich properties e.g. of a CREATE TABLE. Use this step with caution. + */ +public final class SqlTestStep implements TestStep { + + public final String sql; + + SqlTestStep(String sql) { + this.sql = sql; + } + + @Override + public TestKind getKind() { + return TestKind.SQL; + } + + public TableResult apply(TableEnvironment env) { + return env.executeSql(sql); + } +} diff --git a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/StatementSetTestStep.java b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/StatementSetTestStep.java new file mode 100644 index 0000000000000..98cf44ecc5ae1 --- /dev/null +++ b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/StatementSetTestStep.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.test.program; + +import org.apache.flink.table.api.StatementSet; +import org.apache.flink.table.api.TableEnvironment; +import org.apache.flink.table.api.TableResult; + +import java.util.List; + +/** Test step for creating a statement set. */ +public final class StatementSetTestStep implements TestStep { + + public final List statements; + + StatementSetTestStep(List statements) { + this.statements = statements; + } + + @Override + public TestKind getKind() { + return TestKind.STATEMENT_SET; + } + + public TableResult apply(TableEnvironment env) { + final StatementSet statementSet = env.createStatementSet(); + statements.forEach(statementSet::addInsertSql); + return statementSet.execute(); + } +} diff --git a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/TableTestProgram.java b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/TableTestProgram.java new file mode 100644 index 0000000000000..5af08b2999888 --- /dev/null +++ b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/TableTestProgram.java @@ -0,0 +1,289 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.test.program; + +import org.apache.flink.configuration.ConfigOption; +import org.apache.flink.table.functions.UserDefinedFunction; +import org.apache.flink.table.test.program.FunctionTestStep.FunctionBehavior; +import org.apache.flink.table.test.program.FunctionTestStep.FunctionPersistence; +import org.apache.flink.table.test.program.TestStep.TestKind; +import org.apache.flink.util.Preconditions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; +import java.util.stream.Collectors; + +/** + * A generic declaration of a table program for testing. + * + *

A test program defines the basic test pipeline (from source to sink) and required artifacts + * such as table sources and sinks, configuration options, and user-defined functions. Because some + * programs need to create artifacts in a certain order, a program consists of individual {@link + * TestStep}s for setting up the test and the actual running of the test. + * + *

Tests programs are intended to reduce code duplication and test the same SQL statement though + * different layers of the stack. Different {@link TableTestProgramRunner}s can share the same + * program and enrich it with custom implementation and assertions. + * + *

For example, a SQL query such as {@code SELECT * FROM (VALUES (1), (2), (3))} can be declared + * once and can be shared among different tests for integration testing, optimizer plan testing, + * compiled plan testing, transformation testing, and others. + * + *

A typical implementation looks like: + * + *

{@code
+ * // Define the behavior and configuration of an operation.
+ * public class CalcTestPrograms {
+ *     public static final TableTestProgram CALC_SIMPLE = TableTestProgram.of("calc-simple") ...;
+ *     public static final TableTestProgram CALC_COMPLEX = TableTestProgram.of("calc-complex") ...;
+ * }
+ *
+ * // Define a test base for example for plan testing
+ * public abstract class PlanTestBase implements TableTestProgramRunner {
+ *     // The test base declares what kind of steps it can apply.
+ *     public Set supportedSetupSteps() { return EnumSet.of(SOURCE_WITH_DATA, SINK_WITH_DATA); }
+ *     public Set supportedRunSteps() { return EnumSet.of(SQL); }
+ *
+ *     // Leave the list of programs up to the concrete test
+ *     public abstract List programs();
+ *
+ *     @ParameterizedTest
+ *     @MethodSource("supportedPrograms")
+ *     public void test(TableTestProgram program) {
+ *         TableEnvironment env = ...;
+ *         program.getSetupSourceTestSteps().forEach(s -> s.apply(env));
+ *         program.getSetupSinkTestSteps().forEach(s -> s.apply(env));
+ *         assertThat(program.getRunSqlTestStep().apply(env)).contains(...);
+ *     }
+ * }
+ *
+ * // Run the test base for a category of test programs.
+ * public class CalcPlanTest extends PlanTestBase {
+ *     public List programs() = { return Arrays.asList(CALC_SIMPLE, CALC_COMPLEX); }
+ * }
+ * }
+ */ +public class TableTestProgram { + + /** Identifier of the test program (e.g. for naming generated files). */ + public final String id; + + /** Description for internal documentation. */ + public final String description; + + /** Steps to be executed for setting up an environment. */ + public final List setupSteps; + + /** Steps to be executed for running the actual test. */ + public final List runSteps; + + private TableTestProgram( + String id, String description, List setupSteps, List runSteps) { + this.id = id; + this.description = description; + this.setupSteps = setupSteps; + this.runSteps = runSteps; + } + + @Override + public String toString() { + return id; + } + + /** + * Entrypoint for a {@link TableTestProgram} that forces an identifier and description of the + * test program. + * + *

The identifier is necessary to (ideally globally) identify the test program in outputs. + * For example, a runner for plan tests can create directories and use the name as file names. + * The identifier must start with the name of the exec node under testing. + * + *

The description should give more context and should start with a verb and "s" suffix. + * + *

For example: + * + *

    + *
  • TableTestProgram.of("join-outer", "tests outer joins") + *
  • TableTestProgram.of("rank-x-enabled", "validates a rank with config flag 'x' set") + *
  • TableTestProgram.of("calc-with-projection", "verifies FLINK-12345 is fixed due to + * missing row projection") + *
+ */ + public static Builder of(String id, String description) { + return new Builder(id, description); + } + + /** Convenience method to avoid casting. It assumes that the order of steps is not important. */ + public List getSetupSourceTestSteps() { + final EnumSet sourceKinds = + EnumSet.of( + TestKind.SOURCE_WITHOUT_DATA, + TestKind.SOURCE_WITH_DATA, + TestKind.SOURCE_WITH_RESTORE_DATA); + return setupSteps.stream() + .filter(s -> sourceKinds.contains(s.getKind())) + .map(SourceTestStep.class::cast) + .collect(Collectors.toList()); + } + + /** Convenience method to avoid casting. It assumes that the order of steps is not important. */ + public List getSetupSinkTestSteps() { + final EnumSet sinkKinds = + EnumSet.of( + TestKind.SINK_WITHOUT_DATA, + TestKind.SINK_WITH_DATA, + TestKind.SINK_WITH_RESTORE_DATA); + return setupSteps.stream() + .filter(s -> sinkKinds.contains(s.getKind())) + .map(SinkTestStep.class::cast) + .collect(Collectors.toList()); + } + + /** Convenience method to avoid casting. It assumes that the order of steps is not important. */ + public List> getSetupConfigOptionTestSteps() { + return setupSteps.stream() + .filter(s -> s.getKind() == TestKind.CONFIG) + .map(s -> (ConfigOptionTestStep) s) + .collect(Collectors.toList()); + } + + /** Convenience method to avoid casting. It assumes that the order of steps is not important. */ + public List getSetupFunctionTestSteps() { + return setupSteps.stream() + .filter(s -> s.getKind() == TestKind.FUNCTION) + .map(FunctionTestStep.class::cast) + .collect(Collectors.toList()); + } + + /** + * Convenience method to avoid boilerplate code. It assumes that only a single SQL statement is + * tested. + */ + public SqlTestStep getRunSqlTestStep() { + Preconditions.checkArgument( + runSteps.size() == 1 && runSteps.get(0).getKind() == TestKind.SQL, + "Single SQL step expected."); + return (SqlTestStep) runSteps.get(0); + } + + /** Builder pattern for {@link TableTestProgram}. */ + public static class Builder { + + private final String id; + private final String description; + private final List setupSteps = new ArrayList<>(); + private final List runSteps = new ArrayList<>(); + + private Builder(String id, String description) { + this.id = id; + this.description = description; + } + + /** + * Setup step for execution SQL. + * + *

Note: Not every runner supports generic SQL statements. Sometimes the runner would + * like to enrich properties e.g. of a CREATE TABLE. Use this step with caution. + */ + public Builder setupSql(String sql) { + this.setupSteps.add(new SqlTestStep(sql)); + return this; + } + + /** Setup step for setting a {@link ConfigOption}. */ + public Builder setupConfig(ConfigOption option, T value) { + this.setupSteps.add(new ConfigOptionTestStep<>(option, value)); + return this; + } + + /** Setup step for registering a temporary system function. */ + public Builder setupTemporarySystemFunction( + String name, Class function) { + this.setupSteps.add( + new FunctionTestStep( + FunctionPersistence.TEMPORARY, + FunctionBehavior.SYSTEM, + name, + function)); + return this; + } + + /** Setup step for registering a temporary catalog function. */ + public Builder setupTemporaryCatalogFunction( + String name, Class function) { + this.setupSteps.add( + new FunctionTestStep( + FunctionPersistence.TEMPORARY, + FunctionBehavior.CATALOG, + name, + function)); + return this; + } + + /** Setup step for registering a catalog function. */ + public Builder setupCatalogFunction( + String name, Class function) { + this.setupSteps.add( + new FunctionTestStep( + FunctionPersistence.PERSISTENT, + FunctionBehavior.CATALOG, + name, + function)); + return this; + } + + /** + * Setup step for a table source. + * + *

Use {@link SourceTestStep.Builder} to construct this step. + */ + public Builder setupTableSource(SourceTestStep sourceTestStep) { + setupSteps.add(sourceTestStep); + return this; + } + + /** + * Setup step for a table sink. + * + *

Use {@link SinkTestStep.Builder} to construct this step. + */ + public Builder setupTableSink(SinkTestStep sinkTestStep) { + setupSteps.add(sinkTestStep); + return this; + } + + /** Run step for executing SQL. */ + public Builder runSql(String sql) { + this.runSteps.add(new SqlTestStep(sql)); + return this; + } + + /** Run step for executing a statement set. */ + public Builder runStatementSet(String... sql) { + this.runSteps.add(new StatementSetTestStep(Arrays.asList(sql))); + return this; + } + + public TableTestProgram build() { + return new TableTestProgram(id, description, setupSteps, runSteps); + } + } +} diff --git a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/TableTestProgramRunner.java b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/TableTestProgramRunner.java new file mode 100644 index 0000000000000..cd874739135ee --- /dev/null +++ b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/TableTestProgramRunner.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.test.program; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Interface for test bases that want to run lists of {@link TableTestProgram}s. + * + *

NOTE: See {@link TableTestProgram} for a full example. + * + *

Use {@link #supportedPrograms()} for assertions (usually in test bases), and {@link + * #programs()} for program lists (usually in final tests). + */ +public interface TableTestProgramRunner { + + /** + * List of {@link TableTestProgram}s that this runner should run. + * + *

Usually, this list should reference some test programs stored in static variables that can + * be shared across runners. + */ + List programs(); + + /** + * Runners should call this method to get started. + * + *

Compared to {@link #programs()}, this method will perform some pre-checks. + */ + default List supportedPrograms() { + final List programs = programs(); + + final List ids = programs.stream().map(p -> p.id).collect(Collectors.toList()); + final List duplicates = + ids.stream() + .filter(id -> Collections.frequency(ids, id) > 1) + .distinct() + .collect(Collectors.toList()); + if (!duplicates.isEmpty()) { + throw new IllegalArgumentException("Duplicate test program id found: " + duplicates); + } + + final Set setupSteps = supportedSetupSteps(); + final Set runSteps = supportedRunSteps(); + + programs.forEach( + p -> { + p.setupSteps.stream() + .map(TestStep::getKind) + .filter(k -> !setupSteps.contains(k)) + .findFirst() + .ifPresent( + k -> { + throw new UnsupportedOperationException( + "Test runner does not support setup step: " + k); + }); + p.runSteps.stream() + .map(TestStep::getKind) + .filter(k -> !runSteps.contains(k)) + .findFirst() + .ifPresent( + k -> { + throw new UnsupportedOperationException( + "Test runner does not support run step: " + k); + }); + }); + + return programs; + } + + /** + * Lists setup steps that are supported by this runner. + * + *

E.g. some runners might not want to run generic {@link TestStep.TestKind#SQL} because they + * want to enrich CREATE TABLE statements. + * + *

This also ensures that runners don't need to be updated when a new kind of step is added, + * or steps get silently dropped. + */ + EnumSet supportedSetupSteps(); + + /** + * Lists run steps that are supported by this runner. + * + *

E.g. some runners might not want to run generic {@link TestStep.TestKind#SQL} because they + * want to enrich CREATE TABLE statements. + * + *

This also ensures that runners don't need to be updated when a new kind of step is added, + * or steps get silently dropped. + */ + EnumSet supportedRunSteps(); +} diff --git a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/TableTestStep.java b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/TableTestStep.java new file mode 100644 index 0000000000000..1d0207f71261e --- /dev/null +++ b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/TableTestStep.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.test.program; + +import org.apache.flink.configuration.ConfigOption; +import org.apache.flink.configuration.ConfigurationUtils; +import org.apache.flink.table.api.TableEnvironment; +import org.apache.flink.table.api.TableResult; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** Abstract class for {@link SourceTestStep} and {@link SinkTestStep}. */ +public abstract class TableTestStep implements TestStep { + + public final String name; + public final List schemaComponents; + public final List partitionKeys; + public final Map options; + + TableTestStep( + String name, + List schemaComponents, + List partitionKeys, + Map options) { + this.name = name; + this.schemaComponents = schemaComponents; + this.partitionKeys = partitionKeys; + this.options = options; + } + + public TableResult apply(TableEnvironment env) { + return apply(env, Collections.emptyMap()); + } + + public TableResult apply(TableEnvironment env, Map extraOptions) { + final Map allOptions = new HashMap<>(options); + allOptions.putAll(extraOptions); + + final String partitionedBy = + partitionKeys.isEmpty() + ? "" + : "PARTITIONED BY (" + String.join(", ", partitionKeys) + ")\n"; + final String createTable = + String.format( + "CREATE TABLE %s (\n%s)\n%sWITH (\n%s)", + name, + String.join(",\n", schemaComponents), + partitionedBy, + allOptions.entrySet().stream() + .map(e -> String.format("'%s'='%s'", e.getKey(), e.getValue())) + .collect(Collectors.joining(",\n"))); + + return env.executeSql(createTable); + } + + /** Builder pattern for {@link SourceTestStep} and {@link SinkTestStep}. */ + @SuppressWarnings("unchecked") + protected abstract static class AbstractBuilder< + SpecificBuilder extends AbstractBuilder> { + + protected final String name; + + protected final List schemaComponents = new ArrayList<>(); + protected final List partitionKeys = new ArrayList<>(); + protected final Map options = new HashMap<>(); + + protected AbstractBuilder(String name) { + this.name = name; + } + + /** + * Define the schema like you would in SQL e.g. "my_col INT", "PRIMARY KEY (uid) NOT + * ENFORCED", or "WATERMARK FOR ts AS ts". + */ + public SpecificBuilder addSchema(String... schemaComponents) { + this.schemaComponents.addAll(Arrays.asList(schemaComponents)); + return (SpecificBuilder) this; + } + + /** + * Unless the test requires a very specific configuration, try to avoid calling this method + * and fill in options later via {@link TableTestStep#apply(TableEnvironment, Map)}. + */ + public SpecificBuilder addOptions(Map options) { + this.options.putAll(options); + return (SpecificBuilder) this; + } + + /** + * Unless the test requires a very specific configuration, try to avoid calling this method + * and fill in options later via {@link TableTestStep#apply(TableEnvironment, Map)}. + */ + public SpecificBuilder addOption(String key, String value) { + this.options.put(key, value); + return (SpecificBuilder) this; + } + + /** + * Unless the test requires a very specific configuration, try to avoid calling this method + * and fill in options later via {@link TableTestStep#apply(TableEnvironment, Map)}. + */ + public SpecificBuilder addOption(ConfigOption option, String value) { + this.options.put(option.key(), ConfigurationUtils.convertValue(value, String.class)); + return (SpecificBuilder) this; + } + + public SpecificBuilder addPartitionKeys(String... partitionKeys) { + this.partitionKeys.addAll(Arrays.asList(partitionKeys)); + return (SpecificBuilder) this; + } + } +} diff --git a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/TestStep.java b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/TestStep.java new file mode 100644 index 0000000000000..67105b68d53db --- /dev/null +++ b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/test/program/TestStep.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.test.program; + +/** + * Test step that makes up a {@link TableTestProgram}. + * + *

It describes a task that should be executed either before running the actual test or as the + * main ingredient of the test. + * + *

Some steps provide {@code apply()} methods for convenience. But in the end, the {@link + * TableTestProgramRunner} decides whether to call them or not. + * + *

Not every {@link TableTestProgramRunner} might support every {@link TestKind}. + */ +public interface TestStep { + + /** + * Enum to identify important properties of a {@link TestStep}. + * + *

Used in {@link TableTestProgramRunner#supportedSetupSteps()} and {@link + * TableTestProgramRunner#supportedRunSteps()}. + */ + enum TestKind { + SQL, + STATEMENT_SET, + CONFIG, + FUNCTION, + SOURCE_WITHOUT_DATA, + SOURCE_WITH_DATA, + SOURCE_WITH_RESTORE_DATA, + SINK_WITHOUT_DATA, + SINK_WITH_DATA, + SINK_WITH_RESTORE_DATA, + } + + TestKind getKind(); +} diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/connector/ParallelismProvider.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/connector/ParallelismProvider.java index f9c4684383a49..27e4047a016d6 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/connector/ParallelismProvider.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/connector/ParallelismProvider.java @@ -20,14 +20,13 @@ import org.apache.flink.annotation.PublicEvolving; import org.apache.flink.table.connector.sink.DynamicTableSink.SinkRuntimeProvider; +import org.apache.flink.table.connector.source.ScanTableSource.ScanRuntimeProvider; import java.util.Optional; /** * Parallelism provider for other connector providers. It allows to express a custom parallelism for - * the connector runtime implementation. Otherwise the parallelism is determined by the planner. - * - *

Note: Currently, this interface only works with {@link SinkRuntimeProvider}. + * the connector runtime implementation. Otherwise, the parallelism is determined by the planner. */ @PublicEvolving public interface ParallelismProvider { @@ -38,9 +37,10 @@ public interface ParallelismProvider { *

The parallelism denotes how many parallel instances of a source or sink will be spawned * during the execution. * - *

Enforcing a different parallelism for sinks might mess up the changelog if the input is - * not {@link ChangelogMode#insertOnly()}. Therefore, a primary key is required by which the - * input will be shuffled before records enter the {@link SinkRuntimeProvider} implementation. + *

Enforcing a different parallelism for sources/sinks might mess up the changelog if the + * output/input is not {@link ChangelogMode#insertOnly()}. Therefore, a primary key is required + * by which the output/input will be shuffled after/before records leave/enter the {@link + * ScanRuntimeProvider}/{@link SinkRuntimeProvider} implementation. * * @return empty if the connector does not provide a custom parallelism, then the planner will * decide the number of parallel instances by itself. diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/connector/source/InputFormatProvider.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/connector/source/InputFormatProvider.java index bbf20e1b1930b..a9775becc33c9 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/connector/source/InputFormatProvider.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/connector/source/InputFormatProvider.java @@ -20,18 +20,32 @@ import org.apache.flink.annotation.PublicEvolving; import org.apache.flink.api.common.io.InputFormat; +import org.apache.flink.table.connector.ParallelismProvider; import org.apache.flink.table.data.RowData; +import javax.annotation.Nullable; + +import java.util.Optional; + /** * Provider of an {@link InputFormat} instance as a runtime implementation for {@link * ScanTableSource}. */ @PublicEvolving -public interface InputFormatProvider extends ScanTableSource.ScanRuntimeProvider { +public interface InputFormatProvider + extends ScanTableSource.ScanRuntimeProvider, ParallelismProvider { /** Helper method for creating a static provider. */ static InputFormatProvider of(InputFormat inputFormat) { + return of(inputFormat, null); + } + + /** Helper method for creating a static provider with a provided source parallelism. */ + static InputFormatProvider of( + InputFormat inputFormat, @Nullable Integer sourceParallelism) { + return new InputFormatProvider() { + @Override public InputFormat createInputFormat() { return inputFormat; @@ -41,6 +55,11 @@ static InputFormatProvider of(InputFormat inputFormat) { public boolean isBounded() { return true; } + + @Override + public Optional getParallelism() { + return Optional.ofNullable(sourceParallelism); + } }; } diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/connector/source/SourceProvider.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/connector/source/SourceProvider.java index f0e85f88624d1..2d9f5143626cd 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/connector/source/SourceProvider.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/connector/source/SourceProvider.java @@ -21,8 +21,13 @@ import org.apache.flink.annotation.PublicEvolving; import org.apache.flink.api.connector.source.Boundedness; import org.apache.flink.api.connector.source.Source; +import org.apache.flink.table.connector.ParallelismProvider; import org.apache.flink.table.data.RowData; +import javax.annotation.Nullable; + +import java.util.Optional; + /** * Provider of a {@link Source} instance as a runtime implementation for {@link ScanTableSource}. * @@ -30,11 +35,17 @@ * advanced connector developers. */ @PublicEvolving -public interface SourceProvider extends ScanTableSource.ScanRuntimeProvider { +public interface SourceProvider extends ScanTableSource.ScanRuntimeProvider, ParallelismProvider { /** Helper method for creating a static provider. */ static SourceProvider of(Source source) { + return of(source, null); + } + + /** Helper method for creating a Source provider with a provided source parallelism. */ + static SourceProvider of(Source source, @Nullable Integer sourceParallelism) { return new SourceProvider() { + @Override public Source createSource() { return source; @@ -44,6 +55,11 @@ static SourceProvider of(Source source) { public boolean isBounded() { return Boundedness.BOUNDED.equals(source.getBoundedness()); } + + @Override + public Optional getParallelism() { + return Optional.ofNullable(sourceParallelism); + } }; } diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/factories/FactoryUtil.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/factories/FactoryUtil.java index 8f103be0c7e25..d8d6d7e90008d 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/factories/FactoryUtil.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/factories/FactoryUtil.java @@ -120,6 +120,15 @@ public final class FactoryUtil { .defaultValues("rest") .withDescription("Specify the endpoints that are used."); + public static final ConfigOption SOURCE_PARALLELISM = + ConfigOptions.key("scan.parallelism") + .intType() + .noDefaultValue() + .withDescription( + "Defines a custom parallelism for the source. " + + "By default, if this option is not defined, the planner will derive the parallelism " + + "for each statement individually by also considering the global configuration."); + public static final ConfigOption WATERMARK_EMIT_STRATEGY = ConfigOptions.key("scan.watermark.emit.strategy") .enumType(WatermarkEmitStrategy.class) diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java index e653d1d646331..b8012922df2e1 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java @@ -1826,14 +1826,25 @@ ANY, and(logical(LogicalTypeRoot.BOOLEAN), LITERAL) BuiltInFunctionDefinition.newBuilder() .name("at") .kind(SCALAR) - .outputTypeStrategy(TypeStrategies.MISSING) + .inputTypeStrategy( + sequence( + or( + logical(LogicalTypeRoot.ARRAY), + logical(LogicalTypeRoot.MAP)), + InputTypeStrategies.ITEM_AT_INDEX)) + .outputTypeStrategy(SpecificTypeStrategies.ITEM_AT) .build(); public static final BuiltInFunctionDefinition CARDINALITY = BuiltInFunctionDefinition.newBuilder() .name("cardinality") .kind(SCALAR) - .outputTypeStrategy(TypeStrategies.MISSING) + .inputTypeStrategy( + sequence( + or( + logical(LogicalTypeFamily.COLLECTION), + logical(LogicalTypeRoot.MAP)))) + .outputTypeStrategy(nullableIfArgs(TypeStrategies.explicit(DataTypes.INT()))) .build(); public static final BuiltInFunctionDefinition ARRAY = @@ -1848,7 +1859,8 @@ ANY, and(logical(LogicalTypeRoot.BOOLEAN), LITERAL) BuiltInFunctionDefinition.newBuilder() .name("element") .kind(SCALAR) - .outputTypeStrategy(TypeStrategies.MISSING) + .inputTypeStrategy(sequence(logical(LogicalTypeRoot.ARRAY))) + .outputTypeStrategy(forceNullable(SpecificTypeStrategies.ARRAY_ELEMENT)) .build(); public static final BuiltInFunctionDefinition MAP = @@ -2230,7 +2242,8 @@ ANY, and(logical(LogicalTypeRoot.BOOLEAN), LITERAL) BuiltInFunctionDefinition.newBuilder() .name("in") .kind(SCALAR) - .outputTypeStrategy(TypeStrategies.MISSING) + .inputTypeStrategy(SpecificInputTypeStrategies.IN) + .outputTypeStrategy(nullableIfArgs(explicit(DataTypes.BOOLEAN()))) .build(); public static final BuiltInFunctionDefinition CAST = @@ -2254,7 +2267,8 @@ ANY, and(logical(LogicalTypeRoot.BOOLEAN), LITERAL) BuiltInFunctionDefinition.newBuilder() .name("reinterpretCast") .kind(SCALAR) - .outputTypeStrategy(TypeStrategies.MISSING) + .inputTypeStrategy(SpecificInputTypeStrategies.REINTERPRET_CAST) + .outputTypeStrategy(TypeStrategies.argument(1)) .build(); public static final BuiltInFunctionDefinition AS = diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/InputTypeStrategies.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/InputTypeStrategies.java index 7ed5ba771f52f..f5477c508d4fc 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/InputTypeStrategies.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/InputTypeStrategies.java @@ -32,6 +32,7 @@ import org.apache.flink.table.types.inference.strategies.ConstraintArgumentTypeStrategy; import org.apache.flink.table.types.inference.strategies.ExplicitArgumentTypeStrategy; import org.apache.flink.table.types.inference.strategies.FamilyArgumentTypeStrategy; +import org.apache.flink.table.types.inference.strategies.ItemAtIndexArgumentTypeStrategy; import org.apache.flink.table.types.inference.strategies.LiteralArgumentTypeStrategy; import org.apache.flink.table.types.inference.strategies.OrArgumentTypeStrategy; import org.apache.flink.table.types.inference.strategies.OrInputTypeStrategy; @@ -369,6 +370,9 @@ public static InputTypeStrategy arrayFullyComparableElementType() { return new ArrayComparableElementTypeStrategy(StructuredComparison.FULL); } + /** @see ItemAtIndexArgumentTypeStrategy */ + public static final ArgumentTypeStrategy ITEM_AT_INDEX = new ItemAtIndexArgumentTypeStrategy(); + // -------------------------------------------------------------------------------------------- private InputTypeStrategies() { diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/TypeInferenceUtil.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/TypeInferenceUtil.java index dcaf52a71b5cb..8247ccad1b06f 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/TypeInferenceUtil.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/TypeInferenceUtil.java @@ -209,6 +209,48 @@ public static TableException createUnexpectedException( cause); } + /** + * Validates argument counts. + * + * @param argumentCount expected argument count + * @param actualCount actual argument count + * @param throwOnFailure if true, the function throws a {@link ValidationException} if the + * actual value does not meet the expected argument count + * @return a boolean indicating if expected argument counts match the actual counts + */ + public static boolean validateArgumentCount( + ArgumentCount argumentCount, int actualCount, boolean throwOnFailure) { + final int minCount = argumentCount.getMinCount().orElse(0); + if (actualCount < minCount) { + if (throwOnFailure) { + throw new ValidationException( + String.format( + "Invalid number of arguments. At least %d arguments expected but %d passed.", + minCount, actualCount)); + } + return false; + } + final int maxCount = argumentCount.getMaxCount().orElse(Integer.MAX_VALUE); + if (actualCount > maxCount) { + if (throwOnFailure) { + throw new ValidationException( + String.format( + "Invalid number of arguments. At most %d arguments expected but %d passed.", + maxCount, actualCount)); + } + return false; + } + if (!argumentCount.isValidCount(actualCount)) { + if (throwOnFailure) { + throw new ValidationException( + String.format( + "Invalid number of arguments. %d arguments passed.", actualCount)); + } + return false; + } + return true; + } + /** * Information what the outer world (i.e. an outer wrapping call) expects from the current * function call. This can be helpful for an {@link InputTypeStrategy}. @@ -385,39 +427,6 @@ private static String formatArgument(Signature.Argument arg) { return stringBuilder.toString(); } - private static boolean validateArgumentCount( - ArgumentCount argumentCount, int actualCount, boolean throwOnFailure) { - final int minCount = argumentCount.getMinCount().orElse(0); - if (actualCount < minCount) { - if (throwOnFailure) { - throw new ValidationException( - String.format( - "Invalid number of arguments. At least %d arguments expected but %d passed.", - minCount, actualCount)); - } - return false; - } - final int maxCount = argumentCount.getMaxCount().orElse(Integer.MAX_VALUE); - if (actualCount > maxCount) { - if (throwOnFailure) { - throw new ValidationException( - String.format( - "Invalid number of arguments. At most %d arguments expected but %d passed.", - maxCount, actualCount)); - } - return false; - } - if (!argumentCount.isValidCount(actualCount)) { - if (throwOnFailure) { - throw new ValidationException( - String.format( - "Invalid number of arguments. %d arguments passed.", actualCount)); - } - return false; - } - return true; - } - private static AdaptedCallContext inferInputTypes( TypeInference typeInference, CallContext callContext, diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ArrayElementTypeStrategy.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ArrayElementTypeStrategy.java new file mode 100644 index 0000000000000..b0515961e9ca9 --- /dev/null +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ArrayElementTypeStrategy.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.types.inference.strategies; + +import org.apache.flink.annotation.Internal; +import org.apache.flink.table.types.CollectionDataType; +import org.apache.flink.table.types.DataType; +import org.apache.flink.table.types.inference.CallContext; +import org.apache.flink.table.types.inference.TypeStrategy; +import org.apache.flink.table.types.logical.LogicalTypeFamily; + +import java.util.Optional; + +/** Returns the element of an {@link LogicalTypeFamily#COLLECTION} type. */ +@Internal +public final class ArrayElementTypeStrategy implements TypeStrategy { + @Override + public Optional inferType(CallContext callContext) { + + DataType arrayType = callContext.getArgumentDataTypes().get(0); + final Optional legacyArrayElement = + StrategyUtils.extractLegacyArrayElement(arrayType); + if (legacyArrayElement.isPresent()) { + return legacyArrayElement; + } + + if (!arrayType.getLogicalType().is(LogicalTypeFamily.COLLECTION)) { + return Optional.empty(); + } + + return Optional.of(((CollectionDataType) arrayType).getElementDataType().nullable()); + } +} diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ComparableTypeStrategy.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ComparableTypeStrategy.java index 88aa6877c3e15..cb62543cce5a3 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ComparableTypeStrategy.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ComparableTypeStrategy.java @@ -26,14 +26,9 @@ import org.apache.flink.table.types.inference.ConstantArgumentCount; import org.apache.flink.table.types.inference.InputTypeStrategy; import org.apache.flink.table.types.inference.Signature; -import org.apache.flink.table.types.logical.DistinctType; -import org.apache.flink.table.types.logical.LegacyTypeInformationType; import org.apache.flink.table.types.logical.LogicalType; -import org.apache.flink.table.types.logical.LogicalTypeFamily; -import org.apache.flink.table.types.logical.LogicalTypeRoot; -import org.apache.flink.table.types.logical.RawType; -import org.apache.flink.table.types.logical.StructuredType; import org.apache.flink.table.types.logical.StructuredType.StructuredComparison; +import org.apache.flink.table.types.logical.utils.LogicalTypeChecks; import org.apache.flink.util.Preconditions; import java.util.Collections; @@ -49,7 +44,7 @@ * with itself (e.g. for aggregations). * *

For the rules which types are comparable with which types see {@link - * #areComparable(LogicalType, LogicalType)}. + * LogicalTypeChecks#areComparable(LogicalType, LogicalType, StructuredComparison)}. */ @Internal public final class ComparableTypeStrategy implements InputTypeStrategy { @@ -78,7 +73,7 @@ public Optional> inferInputTypes( final List argumentDataTypes = callContext.getArgumentDataTypes(); if (argumentDataTypes.size() == 1) { final LogicalType argType = argumentDataTypes.get(0).getLogicalType(); - if (!areComparable(argType, argType)) { + if (!LogicalTypeChecks.areComparable(argType, argType, requiredComparison)) { return callContext.fail( throwOnFailure, "Type '%s' should support %s comparison with itself.", @@ -90,7 +85,7 @@ public Optional> inferInputTypes( final LogicalType firstType = argumentDataTypes.get(i).getLogicalType(); final LogicalType secondType = argumentDataTypes.get(i + 1).getLogicalType(); - if (!areComparable(firstType, secondType)) { + if (!LogicalTypeChecks.areComparable(firstType, secondType, requiredComparison)) { return callContext.fail( throwOnFailure, "All types in a comparison should support %s comparison with each other. " @@ -111,120 +106,9 @@ private String comparisonToString() { : "both 'EQUALS' and 'ORDER'"; } - private boolean areComparable(LogicalType firstType, LogicalType secondType) { - return areComparableWithNormalizedNullability(firstType.copy(true), secondType.copy(true)); - } - - private boolean areComparableWithNormalizedNullability( - LogicalType firstType, LogicalType secondType) { - // A hack to support legacy types. To be removed when we drop the legacy types. - if (firstType instanceof LegacyTypeInformationType - || secondType instanceof LegacyTypeInformationType) { - return true; - } - - // everything is comparable with null, it should return null in that case - if (firstType.is(LogicalTypeRoot.NULL) || secondType.is(LogicalTypeRoot.NULL)) { - return true; - } - - if (firstType.getTypeRoot() == secondType.getTypeRoot()) { - return areTypesOfSameRootComparable(firstType, secondType); - } - - if (firstType.is(LogicalTypeFamily.NUMERIC) && secondType.is(LogicalTypeFamily.NUMERIC)) { - return true; - } - - // DATE + ALL TIMESTAMPS - if (firstType.is(LogicalTypeFamily.DATETIME) && secondType.is(LogicalTypeFamily.DATETIME)) { - return true; - } - - // VARCHAR + CHAR (we do not compare collations here) - if (firstType.is(LogicalTypeFamily.CHARACTER_STRING) - && secondType.is(LogicalTypeFamily.CHARACTER_STRING)) { - return true; - } - - // VARBINARY + BINARY - if (firstType.is(LogicalTypeFamily.BINARY_STRING) - && secondType.is(LogicalTypeFamily.BINARY_STRING)) { - return true; - } - - return false; - } - - private boolean areTypesOfSameRootComparable(LogicalType firstType, LogicalType secondType) { - switch (firstType.getTypeRoot()) { - case ARRAY: - case MULTISET: - case MAP: - case ROW: - return areConstructedTypesComparable(firstType, secondType); - case DISTINCT_TYPE: - return areDistinctTypesComparable(firstType, secondType); - case STRUCTURED_TYPE: - return areStructuredTypesComparable(firstType, secondType); - case RAW: - return areRawTypesComparable(firstType, secondType); - default: - return true; - } - } - - private boolean areRawTypesComparable(LogicalType firstType, LogicalType secondType) { - return firstType.equals(secondType) - && Comparable.class.isAssignableFrom( - ((RawType) firstType).getOriginatingClass()); - } - - private boolean areDistinctTypesComparable(LogicalType firstType, LogicalType secondType) { - DistinctType firstDistinctType = (DistinctType) firstType; - DistinctType secondDistinctType = (DistinctType) secondType; - return firstType.equals(secondType) - && areComparable( - firstDistinctType.getSourceType(), secondDistinctType.getSourceType()); - } - - private boolean areStructuredTypesComparable(LogicalType firstType, LogicalType secondType) { - return firstType.equals(secondType) && hasRequiredComparison((StructuredType) firstType); - } - - private boolean areConstructedTypesComparable(LogicalType firstType, LogicalType secondType) { - List firstChildren = firstType.getChildren(); - List secondChildren = secondType.getChildren(); - - if (firstChildren.size() != secondChildren.size()) { - return false; - } - - for (int i = 0; i < firstChildren.size(); i++) { - if (!areComparable(firstChildren.get(i), secondChildren.get(i))) { - return false; - } - } - - return true; - } - @Override public List getExpectedSignatures(FunctionDefinition definition) { return Collections.singletonList( Signature.of(Signature.Argument.ofGroupVarying("COMPARABLE"))); } - - private Boolean hasRequiredComparison(StructuredType structuredType) { - switch (requiredComparison) { - case EQUALS: - return structuredType.getComparison().isEquality(); - case FULL: - return structuredType.getComparison().isComparison(); - case NONE: - default: - // this is not important, required comparison will never be NONE - return true; - } - } } diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ItemAtIndexArgumentTypeStrategy.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ItemAtIndexArgumentTypeStrategy.java new file mode 100644 index 0000000000000..82b360ccc4354 --- /dev/null +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ItemAtIndexArgumentTypeStrategy.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.types.inference.strategies; + +import org.apache.flink.annotation.Internal; +import org.apache.flink.table.functions.FunctionDefinition; +import org.apache.flink.table.types.DataType; +import org.apache.flink.table.types.KeyValueDataType; +import org.apache.flink.table.types.inference.ArgumentTypeStrategy; +import org.apache.flink.table.types.inference.CallContext; +import org.apache.flink.table.types.inference.Signature; +import org.apache.flink.table.types.logical.LogicalType; +import org.apache.flink.table.types.logical.LogicalTypeFamily; +import org.apache.flink.table.types.logical.LogicalTypeRoot; +import org.apache.flink.table.types.logical.MapType; +import org.apache.flink.table.types.logical.utils.LogicalTypeCasts; + +import java.util.List; +import java.util.Optional; + +/** + * An {@link ArgumentTypeStrategy} that expects: + * + *

a {@link LogicalTypeFamily#NUMERIC} if the first argument is an {@link LogicalTypeRoot#ARRAY} + * or {@link LogicalTypeRoot#MULTISET} + * + *

the type to be equal to the key type of {@link LogicalTypeRoot#MAP} if the first argument is a + * map. + */ +@Internal +public final class ItemAtIndexArgumentTypeStrategy implements ArgumentTypeStrategy { + @Override + public Optional inferArgumentType( + CallContext callContext, int argumentPos, boolean throwOnFailure) { + final List argumentDataTypes = callContext.getArgumentDataTypes(); + final LogicalType collectionType = argumentDataTypes.get(0).getLogicalType(); + final DataType indexType = argumentDataTypes.get(1); + + if (collectionType.is(LogicalTypeRoot.ARRAY)) { + if (indexType.getLogicalType().is(LogicalTypeFamily.INTEGER_NUMERIC)) { + + if (callContext.isArgumentLiteral(1)) { + Optional literalVal = callContext.getArgumentValue(1, Integer.class); + if (literalVal.isPresent() && literalVal.get() <= 0) { + return callContext.fail( + throwOnFailure, + "The provided index must be a valid SQL index starting from 1, but was '%s'", + literalVal.get()); + } + } + + return Optional.of(indexType); + } else { + return callContext.fail( + throwOnFailure, "Array can be indexed only using an INTEGER NUMERIC type."); + } + } + + if (collectionType.is(LogicalTypeRoot.MAP)) { + MapType mapType = (MapType) collectionType; + if (LogicalTypeCasts.supportsImplicitCast( + indexType.getLogicalType(), mapType.getKeyType())) { + final KeyValueDataType mapDataType = (KeyValueDataType) argumentDataTypes.get(0); + return Optional.of(mapDataType.getKeyDataType()); + } else { + return callContext.fail( + throwOnFailure, + "Expected index for a MAP to be of type: %s", + mapType.getKeyType()); + } + } + + return Optional.empty(); + } + + @Override + public Signature.Argument getExpectedArgument( + FunctionDefinition functionDefinition, int argumentPos) { + return Signature.Argument.of("[ | ]"); + } +} diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ItemAtTypeStrategy.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ItemAtTypeStrategy.java new file mode 100644 index 0000000000000..dc5a3ddebf8f4 --- /dev/null +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ItemAtTypeStrategy.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.types.inference.strategies; + +import org.apache.flink.annotation.Internal; +import org.apache.flink.table.functions.BuiltInFunctionDefinitions; +import org.apache.flink.table.types.CollectionDataType; +import org.apache.flink.table.types.DataType; +import org.apache.flink.table.types.KeyValueDataType; +import org.apache.flink.table.types.inference.CallContext; +import org.apache.flink.table.types.inference.TypeStrategy; +import org.apache.flink.table.types.logical.LogicalTypeFamily; +import org.apache.flink.table.types.logical.LogicalTypeRoot; + +import java.util.Optional; + +/** + * An output type strategy for {@link BuiltInFunctionDefinitions#AT}. + * + *

Returns either the element of an {@link LogicalTypeFamily#COLLECTION} type or the value of + * {@link LogicalTypeRoot#MAP}. + */ +@Internal +public final class ItemAtTypeStrategy implements TypeStrategy { + @Override + public Optional inferType(CallContext callContext) { + + DataType arrayOrMapType = callContext.getArgumentDataTypes().get(0); + final Optional legacyArrayElement = + StrategyUtils.extractLegacyArrayElement(arrayOrMapType); + + if (legacyArrayElement.isPresent()) { + return legacyArrayElement; + } + + if (arrayOrMapType.getLogicalType().is(LogicalTypeRoot.ARRAY)) { + return Optional.of( + ((CollectionDataType) arrayOrMapType).getElementDataType().nullable()); + } else if (arrayOrMapType instanceof KeyValueDataType) { + return Optional.of(((KeyValueDataType) arrayOrMapType).getValueDataType().nullable()); + } + + return Optional.empty(); + } +} diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ReinterpretCastInputTypeStrategy.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ReinterpretCastInputTypeStrategy.java new file mode 100644 index 0000000000000..d33ddcdb452d8 --- /dev/null +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ReinterpretCastInputTypeStrategy.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.types.inference.strategies; + +import org.apache.flink.annotation.Internal; +import org.apache.flink.table.functions.BuiltInFunctionDefinitions; +import org.apache.flink.table.functions.FunctionDefinition; +import org.apache.flink.table.types.DataType; +import org.apache.flink.table.types.inference.ArgumentCount; +import org.apache.flink.table.types.inference.CallContext; +import org.apache.flink.table.types.inference.ConstantArgumentCount; +import org.apache.flink.table.types.inference.InputTypeStrategy; +import org.apache.flink.table.types.inference.Signature; +import org.apache.flink.table.types.logical.LegacyTypeInformationType; +import org.apache.flink.table.types.logical.LogicalType; +import org.apache.flink.table.types.logical.LogicalTypeRoot; +import org.apache.flink.table.types.logical.utils.LogicalTypeCasts; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * {@link InputTypeStrategy} specific for {@link BuiltInFunctionDefinitions#REINTERPRET_CAST}. + * + *

It expects three arguments where the type of first one must be reinterpretable as the type of + * the second one. The second one must be a type literal. The third a BOOLEAN literal if the + * reinterpretation may result in an overflow. + */ +@Internal +public final class ReinterpretCastInputTypeStrategy implements InputTypeStrategy { + @Override + public ArgumentCount getArgumentCount() { + return ConstantArgumentCount.of(3); + } + + @Override + public Optional> inferInputTypes( + CallContext callContext, boolean throwOnFailure) { + final List argumentDataTypes = callContext.getArgumentDataTypes(); + + // check for type literal + if (!callContext.isArgumentLiteral(1) + || !callContext.getArgumentValue(1, DataType.class).isPresent()) { + return callContext.fail( + throwOnFailure, "Expected type literal for the second argument."); + } + + if (!argumentDataTypes.get(2).getLogicalType().is(LogicalTypeRoot.BOOLEAN) + || !callContext.isArgumentLiteral(2) + || callContext.isArgumentNull(2)) { + return callContext.fail( + throwOnFailure, "Not null boolean literal expected for overflow."); + } + + final LogicalType fromType = argumentDataTypes.get(0).getLogicalType(); + final LogicalType toType = argumentDataTypes.get(1).getLogicalType(); + + // A hack to support legacy types. To be removed when we drop the legacy types. + if (fromType instanceof LegacyTypeInformationType) { + return Optional.of(argumentDataTypes); + } + if (!LogicalTypeCasts.supportsReinterpretCast(fromType, toType)) { + return callContext.fail( + throwOnFailure, + "Unsupported reinterpret cast from '%s' to '%s'.", + fromType, + toType); + } + + return Optional.of(argumentDataTypes); + } + + @Override + public List getExpectedSignatures(FunctionDefinition definition) { + return Collections.singletonList( + Signature.of( + Signature.Argument.ofGroup("ANY"), + Signature.Argument.ofGroup("TYPE LITERAL"), + Signature.Argument.ofGroup("TRUE | FALSE"))); + } +} diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/SpecificInputTypeStrategies.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/SpecificInputTypeStrategies.java index 56809c7e69ce0..e0a07150d7869 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/SpecificInputTypeStrategies.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/SpecificInputTypeStrategies.java @@ -50,6 +50,8 @@ public final class SpecificInputTypeStrategies { /** See {@link CastInputTypeStrategy}. */ public static final InputTypeStrategy CAST = new CastInputTypeStrategy(); + public static final InputTypeStrategy REINTERPRET_CAST = new ReinterpretCastInputTypeStrategy(); + /** See {@link MapInputTypeStrategy}. */ public static final InputTypeStrategy MAP = new MapInputTypeStrategy(); @@ -120,6 +122,9 @@ public final class SpecificInputTypeStrategies { public static final InputTypeStrategy TWO_EQUALS_COMPARABLE = comparable(ConstantArgumentCount.of(2), StructuredType.StructuredComparison.EQUALS); + /** Type strategy specific for {@link BuiltInFunctionDefinitions#IN}. */ + public static final InputTypeStrategy IN = new SubQueryInputTypeStrategy(); + private SpecificInputTypeStrategies() { // no instantiation } diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/SpecificTypeStrategies.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/SpecificTypeStrategies.java index 911f0d643e06b..d6fba4409a533 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/SpecificTypeStrategies.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/SpecificTypeStrategies.java @@ -59,12 +59,9 @@ public final class SpecificTypeStrategies { public static final TypeStrategy ARRAY = new ArrayTypeStrategy(); /** Type strategy specific for array element. */ - public static final TypeStrategy ARRAY_ELEMENT = - callContext -> - Optional.of( - ((CollectionDataType) callContext.getArgumentDataTypes().get(0)) - .getElementDataType()); + public static final TypeStrategy ARRAY_ELEMENT = new ArrayElementTypeStrategy(); + public static final TypeStrategy ITEM_AT = new ItemAtTypeStrategy(); /** See {@link GetTypeStrategy}. */ public static final TypeStrategy GET = new GetTypeStrategy(); diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/StrategyUtils.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/StrategyUtils.java index fe2b3d85ca4fd..a4b302fc5b0dc 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/StrategyUtils.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/StrategyUtils.java @@ -18,18 +18,22 @@ package org.apache.flink.table.types.inference.strategies; +import org.apache.flink.api.common.typeinfo.BasicArrayTypeInfo; +import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.table.api.DataTypes; import org.apache.flink.table.types.DataType; import org.apache.flink.table.types.inference.CallContext; import org.apache.flink.table.types.logical.BinaryType; import org.apache.flink.table.types.logical.CharType; import org.apache.flink.table.types.logical.DecimalType; +import org.apache.flink.table.types.logical.LegacyTypeInformationType; import org.apache.flink.table.types.logical.LogicalType; import org.apache.flink.table.types.logical.LogicalTypeFamily; import org.apache.flink.table.types.logical.LogicalTypeRoot; import org.apache.flink.table.types.logical.VarBinaryType; import org.apache.flink.table.types.logical.VarCharType; import org.apache.flink.table.types.logical.utils.LogicalTypeCasts; +import org.apache.flink.table.types.utils.TypeConversions; import javax.annotation.Nullable; @@ -197,6 +201,22 @@ static boolean isDecimalComputation(LogicalType type1, LogicalType type2) { } } + static Optional extractLegacyArrayElement(DataType arrayType) { + final LogicalType logicalType = arrayType.getLogicalType(); + if (logicalType instanceof LegacyTypeInformationType) { + final TypeInformation typeInformation = + ((LegacyTypeInformationType) logicalType).getTypeInformation(); + if (typeInformation instanceof BasicArrayTypeInfo) { + return Optional.of( + TypeConversions.fromLegacyInfoToDataType( + ((BasicArrayTypeInfo) typeInformation) + .getComponentInfo()) + .nullable()); + } + } + return Optional.empty(); + } + private StrategyUtils() { // no instantiation } diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/SubQueryInputTypeStrategy.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/SubQueryInputTypeStrategy.java new file mode 100644 index 0000000000000..96ff9450d528a --- /dev/null +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/SubQueryInputTypeStrategy.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.types.inference.strategies; + +import org.apache.flink.annotation.Internal; +import org.apache.flink.table.functions.BuiltInFunctionDefinitions; +import org.apache.flink.table.functions.FunctionDefinition; +import org.apache.flink.table.types.DataType; +import org.apache.flink.table.types.inference.ArgumentCount; +import org.apache.flink.table.types.inference.CallContext; +import org.apache.flink.table.types.inference.ConstantArgumentCount; +import org.apache.flink.table.types.inference.InputTypeStrategy; +import org.apache.flink.table.types.inference.Signature; +import org.apache.flink.table.types.logical.LogicalType; +import org.apache.flink.table.types.logical.LogicalTypeRoot; +import org.apache.flink.table.types.logical.RowType; +import org.apache.flink.table.types.logical.StructuredType; +import org.apache.flink.table.types.logical.utils.LogicalTypeChecks; +import org.apache.flink.table.types.logical.utils.LogicalTypeMerging; +import org.apache.flink.table.types.utils.TypeConversions; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +/** {@link InputTypeStrategy} for {@link BuiltInFunctionDefinitions#IN}. */ +@Internal +public class SubQueryInputTypeStrategy implements InputTypeStrategy { + @Override + public ArgumentCount getArgumentCount() { + return ConstantArgumentCount.from(2); + } + + @Override + public Optional> inferInputTypes( + CallContext callContext, boolean throwOnFailure) { + final LogicalType rightType; + final DataType leftType = callContext.getArgumentDataTypes().get(0); + if (callContext.getArgumentDataTypes().size() > 2) { + final Optional commonType = + LogicalTypeMerging.findCommonType( + callContext.getArgumentDataTypes().stream() + .map(DataType::getLogicalType) + .collect(Collectors.toList())); + if (!commonType.isPresent()) { + return callContext.fail( + throwOnFailure, "Could not find a common type of the sublist."); + } + rightType = commonType.get(); + } else { + rightType = callContext.getArgumentDataTypes().get(1).getLogicalType(); + } + + // check if the types are comparable, if the types are not comparable, check if it is not + // a sub-query case like SELECT a IN (SELECT b FROM table1). We check if the result of the + // rightType is of a ROW type with a single column, and if that column is comparable with + // left type + if (!LogicalTypeChecks.areComparable( + leftType.getLogicalType(), + rightType, + StructuredType.StructuredComparison.EQUALS) + && !isComparableWithSubQuery(leftType.getLogicalType(), rightType)) { + return callContext.fail( + throwOnFailure, + "Types on the right side of IN operator (%s) are not comparable with %s.", + rightType, + leftType.getLogicalType()); + } + + return Optional.of( + Stream.concat( + Stream.of(leftType), + IntStream.range(1, callContext.getArgumentDataTypes().size()) + .mapToObj( + i -> + TypeConversions.fromLogicalToDataType( + rightType))) + .collect(Collectors.toList())); + } + + private static boolean isComparableWithSubQuery(LogicalType left, LogicalType right) { + if (right.is(LogicalTypeRoot.ROW) && right.getChildren().size() == 1) { + final RowType rowType = (RowType) right; + return LogicalTypeChecks.areComparable( + left, rowType.getTypeAt(0), StructuredType.StructuredComparison.EQUALS); + } + return false; + } + + @Override + public List getExpectedSignatures(FunctionDefinition definition) { + return Arrays.asList( + Signature.of( + Signature.Argument.ofGroup("COMPARABLE"), + Signature.Argument.ofGroupVarying("COMPARABLE")), + Signature.of( + Signature.Argument.ofGroup("COMPARABLE"), + Signature.Argument.ofGroup("SUBQUERY"))); + } +} diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/utils/LogicalTypeCasts.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/utils/LogicalTypeCasts.java index 72321b8accf51..54c9c7d12603c 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/utils/LogicalTypeCasts.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/utils/LogicalTypeCasts.java @@ -288,6 +288,54 @@ public static boolean supportsExplicitCast(LogicalType sourceType, LogicalType t return supportsCasting(sourceType, targetType, true); } + /** + * Returns whether the source type can be reinterpreted as the target type. + * + *

Reinterpret casts correspond to the SQL reinterpret_cast and represent the logic behind a + * {@code REINTERPRET_CAST(sourceType AS targetType)} operation. + */ + public static boolean supportsReinterpretCast(LogicalType sourceType, LogicalType targetType) { + if (sourceType.getTypeRoot() == targetType.getTypeRoot()) { + return true; + } + + switch (sourceType.getTypeRoot()) { + case INTEGER: + switch (targetType.getTypeRoot()) { + case DATE: + case TIME_WITHOUT_TIME_ZONE: + case INTERVAL_YEAR_MONTH: + return true; + default: + return false; + } + case BIGINT: + switch (targetType.getTypeRoot()) { + case TIMESTAMP_WITHOUT_TIME_ZONE: + case INTERVAL_DAY_TIME: + return true; + default: + return false; + } + case DATE: + case TIME_WITHOUT_TIME_ZONE: + case INTERVAL_YEAR_MONTH: + switch (targetType.getTypeRoot()) { + case INTEGER: + case BIGINT: + return true; + default: + return false; + } + + case TIMESTAMP_WITHOUT_TIME_ZONE: + case INTERVAL_DAY_TIME: + return targetType.getTypeRoot() == BIGINT; + default: + return false; + } + } + // -------------------------------------------------------------------------------------------- private static boolean supportsCasting( diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/utils/LogicalTypeChecks.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/utils/LogicalTypeChecks.java index cb2fd4c966894..d6e31ea92da9c 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/utils/LogicalTypeChecks.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/utils/LogicalTypeChecks.java @@ -32,11 +32,14 @@ import org.apache.flink.table.types.logical.LegacyTypeInformationType; import org.apache.flink.table.types.logical.LocalZonedTimestampType; import org.apache.flink.table.types.logical.LogicalType; +import org.apache.flink.table.types.logical.LogicalTypeFamily; import org.apache.flink.table.types.logical.LogicalTypeRoot; +import org.apache.flink.table.types.logical.RawType; import org.apache.flink.table.types.logical.RowType; import org.apache.flink.table.types.logical.SmallIntType; import org.apache.flink.table.types.logical.StructuredType; import org.apache.flink.table.types.logical.StructuredType.StructuredAttribute; +import org.apache.flink.table.types.logical.StructuredType.StructuredComparison; import org.apache.flink.table.types.logical.TimeType; import org.apache.flink.table.types.logical.TimestampKind; import org.apache.flink.table.types.logical.TimestampType; @@ -242,6 +245,139 @@ public static boolean hasWellDefinedString(LogicalType logicalType) { } } + public static boolean areComparable( + LogicalType firstType, + LogicalType secondType, + StructuredComparison requiredComparison) { + return areComparableWithNormalizedNullability( + firstType.copy(true), secondType.copy(true), requiredComparison); + } + + private static boolean areComparableWithNormalizedNullability( + LogicalType firstType, + LogicalType secondType, + StructuredComparison requiredComparison) { + // A hack to support legacy types. To be removed when we drop the legacy types. + if (firstType instanceof LegacyTypeInformationType + || secondType instanceof LegacyTypeInformationType) { + return true; + } + + // everything is comparable with null, it should return null in that case + if (firstType.is(LogicalTypeRoot.NULL) || secondType.is(LogicalTypeRoot.NULL)) { + return true; + } + + if (firstType.getTypeRoot() == secondType.getTypeRoot()) { + return areTypesOfSameRootComparable(firstType, secondType, requiredComparison); + } + + if (firstType.is(LogicalTypeFamily.NUMERIC) && secondType.is(LogicalTypeFamily.NUMERIC)) { + return true; + } + + // DATE + ALL TIMESTAMPS + if (firstType.is(LogicalTypeFamily.DATETIME) && secondType.is(LogicalTypeFamily.DATETIME)) { + return true; + } + + // VARCHAR + CHAR (we do not compare collations here) + if (firstType.is(LogicalTypeFamily.CHARACTER_STRING) + && secondType.is(LogicalTypeFamily.CHARACTER_STRING)) { + return true; + } + + // VARBINARY + BINARY + if (firstType.is(LogicalTypeFamily.BINARY_STRING) + && secondType.is(LogicalTypeFamily.BINARY_STRING)) { + return true; + } + + return false; + } + + private static boolean areTypesOfSameRootComparable( + LogicalType firstType, + LogicalType secondType, + StructuredComparison requiredComparison) { + switch (firstType.getTypeRoot()) { + case ARRAY: + case MULTISET: + case MAP: + case ROW: + return areConstructedTypesComparable(firstType, secondType, requiredComparison); + case DISTINCT_TYPE: + return areDistinctTypesComparable(firstType, secondType, requiredComparison); + case STRUCTURED_TYPE: + return areStructuredTypesComparable(firstType, secondType, requiredComparison); + case RAW: + return areRawTypesComparable(firstType, secondType); + default: + return true; + } + } + + private static boolean areRawTypesComparable(LogicalType firstType, LogicalType secondType) { + return firstType.equals(secondType) + && Comparable.class.isAssignableFrom( + ((RawType) firstType).getOriginatingClass()); + } + + private static boolean areDistinctTypesComparable( + LogicalType firstType, + LogicalType secondType, + StructuredComparison requiredComparison) { + DistinctType firstDistinctType = (DistinctType) firstType; + DistinctType secondDistinctType = (DistinctType) secondType; + return firstType.equals(secondType) + && areComparable( + firstDistinctType.getSourceType(), + secondDistinctType.getSourceType(), + requiredComparison); + } + + private static boolean areStructuredTypesComparable( + LogicalType firstType, + LogicalType secondType, + StructuredComparison requiredComparison) { + return firstType.equals(secondType) + && hasRequiredComparison((StructuredType) firstType, requiredComparison); + } + + private static boolean areConstructedTypesComparable( + LogicalType firstType, + LogicalType secondType, + StructuredComparison requiredComparison) { + List firstChildren = firstType.getChildren(); + List secondChildren = secondType.getChildren(); + + if (firstChildren.size() != secondChildren.size()) { + return false; + } + + for (int i = 0; i < firstChildren.size(); i++) { + if (!areComparable(firstChildren.get(i), secondChildren.get(i), requiredComparison)) { + return false; + } + } + + return true; + } + + private static Boolean hasRequiredComparison( + StructuredType structuredType, StructuredComparison requiredComparison) { + switch (requiredComparison) { + case EQUALS: + return structuredType.getComparison().isEquality(); + case FULL: + return structuredType.getComparison().isComparison(); + case NONE: + default: + // this is not important, required comparison will never be NONE + return true; + } + } + private LogicalTypeChecks() { // no instantiation } diff --git a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTest.java b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTest.java index 14c7c89a45119..3a9139a645fe2 100644 --- a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTest.java +++ b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTest.java @@ -636,7 +636,34 @@ ANY, explicit(DataTypes.INT()) .expectSignature("f(, )") .expectArgumentTypes( DataTypes.ARRAY(DataTypes.INT().notNull()).notNull(), - DataTypes.INT())); + DataTypes.INT()), + TestSpec.forStrategy( + "Reinterpret_cast strategy", + SpecificInputTypeStrategies.REINTERPRET_CAST) + .calledWithArgumentTypes( + DataTypes.DATE(), DataTypes.BIGINT(), DataTypes.BOOLEAN().notNull()) + .calledWithLiteralAt(1, DataTypes.BIGINT()) + .calledWithLiteralAt(2, true) + .expectSignature("f(, , )") + .expectArgumentTypes( + DataTypes.DATE(), + DataTypes.BIGINT(), + DataTypes.BOOLEAN().notNull()), + TestSpec.forStrategy( + "Reinterpret_cast strategy non literal overflow", + SpecificInputTypeStrategies.REINTERPRET_CAST) + .calledWithArgumentTypes( + DataTypes.DATE(), DataTypes.BIGINT(), DataTypes.BOOLEAN().notNull()) + .calledWithLiteralAt(1, DataTypes.BIGINT()) + .expectErrorMessage("Not null boolean literal expected for overflow."), + TestSpec.forStrategy( + "Reinterpret_cast strategy not supported cast", + SpecificInputTypeStrategies.REINTERPRET_CAST) + .calledWithArgumentTypes( + DataTypes.INT(), DataTypes.BIGINT(), DataTypes.BOOLEAN().notNull()) + .calledWithLiteralAt(1, DataTypes.BIGINT()) + .calledWithLiteralAt(2, true) + .expectErrorMessage("Unsupported reinterpret cast from 'INT' to 'BIGINT'")); } /** Simple pojo that should be converted to a Structured type. */ diff --git a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTestBase.java b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTestBase.java index 73c9e61011975..06ff25f9a9a72 100644 --- a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTestBase.java +++ b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTestBase.java @@ -35,7 +35,9 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -92,15 +94,11 @@ private TypeInferenceUtil.Result runTypeInference( callContextMock.argumentDataTypes = actualArgumentTypes; callContextMock.argumentLiterals = IntStream.range(0, actualArgumentTypes.size()) - .mapToObj(i -> testSpec.literalPos != null && i == testSpec.literalPos) + .mapToObj(i -> testSpec.literals.containsKey(i)) .collect(Collectors.toList()); callContextMock.argumentValues = IntStream.range(0, actualArgumentTypes.size()) - .mapToObj( - i -> - (testSpec.literalPos != null && i == testSpec.literalPos) - ? Optional.ofNullable(testSpec.literalValue) - : Optional.empty()) + .mapToObj(i -> Optional.ofNullable(testSpec.literals.get(i))) .collect(Collectors.toList()); callContextMock.argumentNulls = IntStream.range(0, actualArgumentTypes.size()) @@ -161,9 +159,7 @@ protected static class TestSpec { private List> actualArgumentTypes = new ArrayList<>(); - private @Nullable Integer literalPos; - - private @Nullable Object literalValue; + private Map literals = new HashMap<>(); private @Nullable InputTypeStrategy surroundingStrategy; @@ -207,13 +203,12 @@ public TestSpec calledWithArgumentTypes(AbstractDataType... dataTypes) { } public TestSpec calledWithLiteralAt(int pos) { - this.literalPos = pos; + this.literals.put(pos, null); return this; } public TestSpec calledWithLiteralAt(int pos, Object value) { - this.literalPos = pos; - this.literalValue = value; + this.literals.put(pos, value); return this; } diff --git a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/ItemAtIndexArgumentTypeStrategyTest.java b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/ItemAtIndexArgumentTypeStrategyTest.java new file mode 100644 index 0000000000000..d38ebd011f347 --- /dev/null +++ b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/ItemAtIndexArgumentTypeStrategyTest.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.types.inference; + +import org.apache.flink.table.api.DataTypes; +import org.apache.flink.table.functions.BuiltInFunctionDefinitions; +import org.apache.flink.table.types.inference.strategies.ItemAtIndexArgumentTypeStrategy; +import org.apache.flink.table.types.utils.DataTypeFactoryMock; + +import java.util.stream.Stream; + +/** Tests for {@link ItemAtIndexArgumentTypeStrategy}. */ +class ItemAtIndexArgumentTypeStrategyTest extends InputTypeStrategiesTestBase { + + private static final InputTypeStrategy ITEM_AT_INPUT_STRATEGY = + BuiltInFunctionDefinitions.AT + .getTypeInference(new DataTypeFactoryMock()) + .getInputTypeStrategy(); + + @Override + protected Stream testData() { + + return Stream.of( + TestSpec.forStrategy("Validate integer index for an array", ITEM_AT_INPUT_STRATEGY) + .calledWithArgumentTypes( + DataTypes.ARRAY(DataTypes.STRING().notNull()), + DataTypes.SMALLINT().notNull()) + .expectSignature( + "f([ | ], [ | ])") + .expectArgumentTypes( + DataTypes.ARRAY(DataTypes.STRING().notNull()), + DataTypes.SMALLINT().notNull()), + TestSpec.forStrategy( + "Validate not an integer index for an array", + ITEM_AT_INPUT_STRATEGY) + .calledWithArgumentTypes( + DataTypes.ARRAY(DataTypes.STRING().notNull()), DataTypes.STRING()) + .expectErrorMessage( + "Array can be indexed only using an INTEGER NUMERIC type."), + TestSpec.forStrategy("Validate correct map key", ITEM_AT_INPUT_STRATEGY) + .calledWithArgumentTypes( + DataTypes.MAP(DataTypes.BIGINT(), DataTypes.STRING().notNull()), + DataTypes.SMALLINT()) + .expectSignature( + "f([ | ], [ | ])") + .expectArgumentTypes( + DataTypes.MAP(DataTypes.BIGINT(), DataTypes.STRING().notNull()), + DataTypes.BIGINT()), + TestSpec.forStrategy("Validate incorrect map key", ITEM_AT_INPUT_STRATEGY) + .calledWithArgumentTypes( + DataTypes.MAP(DataTypes.BIGINT(), DataTypes.STRING().notNull()), + DataTypes.STRING()) + .expectErrorMessage("Expected index for a MAP to be of type: BIGINT"), + TestSpec.forStrategy("Validate incorrect index", ITEM_AT_INPUT_STRATEGY) + .calledWithArgumentTypes( + DataTypes.ARRAY(DataTypes.BIGINT()), DataTypes.INT().notNull()) + .calledWithLiteralAt(1, 0) + .expectErrorMessage( + "The provided index must be a valid SQL index starting from 1, but was '0'")); + } +} diff --git a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/TypeStrategiesTestBase.java b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/TypeStrategiesTestBase.java index 94a95e186de95..310c1faa7ea75 100644 --- a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/TypeStrategiesTestBase.java +++ b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/TypeStrategiesTestBase.java @@ -54,8 +54,13 @@ void testTypeStrategy(TestSpec testSpec) { anyCauseMatches( ValidationException.class, testSpec.expectedErrorMessage)); } else if (testSpec.expectedDataType != null) { - assertThat(runTypeInference(testSpec).getOutputDataType()) - .isEqualTo(testSpec.expectedDataType); + if (testSpec.compareConversionClass) { + assertThat(runTypeInference(testSpec).getOutputDataType()) + .isEqualTo(testSpec.expectedDataType); + } else { + assertThat(runTypeInference(testSpec).getOutputDataType().getLogicalType()) + .isEqualTo(testSpec.expectedDataType.getLogicalType()); + } } } @@ -114,6 +119,7 @@ public static class TestSpec { private @Nullable Object literalValue; private boolean isGroupedAggregation; + private boolean compareConversionClass = false; private TestSpec(@Nullable String description, TypeStrategy strategy) { this.description = description; @@ -154,6 +160,11 @@ public TestSpec expectErrorMessage(String expectedErrorMessage) { return this; } + public TestSpec compareConversionClass() { + this.compareConversionClass = true; + return this; + } + @Override public String toString() { return description != null ? description : ""; diff --git a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/strategies/ArrayElementTypeStrategyTest.java b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/strategies/ArrayElementTypeStrategyTest.java new file mode 100644 index 0000000000000..6f0f518f766a6 --- /dev/null +++ b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/strategies/ArrayElementTypeStrategyTest.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.types.inference.strategies; + +import org.apache.flink.table.api.DataTypes; +import org.apache.flink.table.types.inference.TypeStrategiesTestBase; + +import java.util.stream.Stream; + +/** Tests for {@link ArrayElementTypeStrategy}. */ +class ArrayElementTypeStrategyTest extends TypeStrategiesTestBase { + + @Override + protected Stream testData() { + return Stream.of( + TestSpec.forStrategy( + "Infer an element of an array type", + SpecificTypeStrategies.ARRAY_ELEMENT) + .inputTypes(DataTypes.ARRAY(DataTypes.BIGINT().notNull())) + .expectDataType(DataTypes.BIGINT().nullable()), + TestSpec.forStrategy( + "Infer an element of a multiset type", + SpecificTypeStrategies.ARRAY_ELEMENT) + .inputTypes(DataTypes.MULTISET(DataTypes.STRING().notNull())) + .expectDataType(DataTypes.STRING().nullable()), + TestSpec.forStrategy( + "Error on non collection type", + SpecificTypeStrategies.ARRAY_ELEMENT) + .inputTypes(DataTypes.BIGINT()) + .expectErrorMessage( + "Could not infer an output type for the given arguments.")); + } +} diff --git a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/strategies/ItemAtTypeStrategyTest.java b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/strategies/ItemAtTypeStrategyTest.java new file mode 100644 index 0000000000000..a908d98526b04 --- /dev/null +++ b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/strategies/ItemAtTypeStrategyTest.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.types.inference.strategies; + +import org.apache.flink.table.api.DataTypes; +import org.apache.flink.table.types.inference.TypeStrategiesTestBase; + +import java.util.stream.Stream; + +/** Tests for {@link ItemAtTypeStrategy}. */ +class ItemAtTypeStrategyTest extends TypeStrategiesTestBase { + + @Override + protected Stream testData() { + return Stream.of( + TestSpec.forStrategy("Infer an item at array type", SpecificTypeStrategies.ITEM_AT) + .inputTypes(DataTypes.ARRAY(DataTypes.BIGINT().notNull()), DataTypes.INT()) + .expectDataType(DataTypes.BIGINT().nullable()), + TestSpec.forStrategy("Infer an item at map type", SpecificTypeStrategies.ITEM_AT) + .inputTypes( + DataTypes.MAP( + DataTypes.STRING().notNull(), DataTypes.BIGINT().notNull()), + DataTypes.STRING()) + .expectDataType(DataTypes.BIGINT().nullable())); + } +} diff --git a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/strategies/SubQueryInputTypeStrategyTest.java b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/strategies/SubQueryInputTypeStrategyTest.java new file mode 100644 index 0000000000000..82ce646860b30 --- /dev/null +++ b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/strategies/SubQueryInputTypeStrategyTest.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.types.inference.strategies; + +import org.apache.flink.table.api.DataTypes; +import org.apache.flink.table.types.inference.InputTypeStrategiesTestBase; + +import java.util.stream.Stream; + +/** Tests for {@link SubQueryInputTypeStrategy}. */ +class SubQueryInputTypeStrategyTest extends InputTypeStrategiesTestBase { + + @Override + protected Stream testData() { + return Stream.of( + TestSpec.forStrategy("IN a set", SpecificInputTypeStrategies.IN) + .calledWithArgumentTypes( + DataTypes.INT(), + DataTypes.BIGINT(), + DataTypes.SMALLINT(), + DataTypes.INT()) + .expectArgumentTypes( + DataTypes.INT(), + DataTypes.BIGINT(), + DataTypes.BIGINT(), + DataTypes.BIGINT()), + TestSpec.forStrategy("IN a set, binary", SpecificInputTypeStrategies.IN) + .calledWithArgumentTypes( + DataTypes.BYTES(), + DataTypes.BYTES(), + DataTypes.BYTES(), + DataTypes.BYTES()) + .expectArgumentTypes( + DataTypes.BYTES(), + DataTypes.BYTES(), + DataTypes.BYTES(), + DataTypes.BYTES()), + TestSpec.forStrategy("IN a set, string", SpecificInputTypeStrategies.IN) + .calledWithArgumentTypes( + DataTypes.STRING(), + DataTypes.STRING(), + DataTypes.STRING(), + DataTypes.STRING()) + .expectArgumentTypes( + DataTypes.STRING(), + DataTypes.STRING(), + DataTypes.STRING(), + DataTypes.STRING()), + TestSpec.forStrategy( + "IN a set, multiset(timestamp)", SpecificInputTypeStrategies.IN) + .calledWithArgumentTypes( + DataTypes.MULTISET(DataTypes.TIMESTAMP()), + DataTypes.MULTISET(DataTypes.TIMESTAMP()), + DataTypes.MULTISET(DataTypes.TIMESTAMP()), + DataTypes.MULTISET(DataTypes.TIMESTAMP())) + .expectArgumentTypes( + DataTypes.MULTISET(DataTypes.TIMESTAMP()), + DataTypes.MULTISET(DataTypes.TIMESTAMP()), + DataTypes.MULTISET(DataTypes.TIMESTAMP()), + DataTypes.MULTISET(DataTypes.TIMESTAMP())), + TestSpec.forStrategy("IN a set, arrays", SpecificInputTypeStrategies.IN) + .calledWithArgumentTypes( + DataTypes.ARRAY(DataTypes.BIGINT()), + DataTypes.ARRAY(DataTypes.BIGINT()), + DataTypes.ARRAY(DataTypes.INT()), + DataTypes.ARRAY(DataTypes.SMALLINT())) + .expectArgumentTypes( + DataTypes.ARRAY(DataTypes.BIGINT()), + DataTypes.ARRAY(DataTypes.BIGINT()), + DataTypes.ARRAY(DataTypes.BIGINT()), + DataTypes.ARRAY(DataTypes.BIGINT())), + TestSpec.forStrategy("IN a set of ROWs", SpecificInputTypeStrategies.IN) + .calledWithArgumentTypes( + DataTypes.ROW(DataTypes.FIELD("f0", DataTypes.INT())), + DataTypes.ROW(DataTypes.FIELD("f0", DataTypes.INT()))) + .expectArgumentTypes( + DataTypes.ROW(DataTypes.FIELD("f0", DataTypes.INT())), + DataTypes.ROW(DataTypes.FIELD("f0", DataTypes.INT()))), + TestSpec.forStrategy("IN a subquery", SpecificInputTypeStrategies.IN) + .calledWithArgumentTypes( + DataTypes.INT(), + DataTypes.ROW(DataTypes.FIELD("f0", DataTypes.BIGINT()))) + .expectArgumentTypes( + DataTypes.INT(), + DataTypes.ROW(DataTypes.FIELD("f0", DataTypes.BIGINT()))), + TestSpec.forStrategy("IN a set not comparable", SpecificInputTypeStrategies.IN) + .calledWithArgumentTypes(DataTypes.INT(), DataTypes.STRING()) + .expectErrorMessage( + "Types on the right side of IN operator (STRING) are not comparable with INT."), + TestSpec.forStrategy("IN a subquery not comparable", SpecificInputTypeStrategies.IN) + .calledWithArgumentTypes( + DataTypes.INT(), + DataTypes.ROW(DataTypes.FIELD("f0", DataTypes.STRING()))) + .expectErrorMessage( + "Types on the right side of IN operator (ROW<`f0` STRING>) are not comparable with INT"), + TestSpec.forStrategy("IN a subquery of ROWs", SpecificInputTypeStrategies.IN) + .calledWithArgumentTypes( + DataTypes.ROW(DataTypes.FIELD("f0", DataTypes.INT())), + DataTypes.ROW( + DataTypes.FIELD( + "f0", + DataTypes.ROW( + DataTypes.FIELD("f0", DataTypes.INT()))))) + .expectArgumentTypes( + DataTypes.ROW(DataTypes.FIELD("f0", DataTypes.INT())), + DataTypes.ROW( + DataTypes.FIELD( + "f0", + DataTypes.ROW( + DataTypes.FIELD("f0", DataTypes.INT())))))); + } +} diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/validate/ExtraCalciteResource.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/validate/ExtraCalciteResource.java new file mode 100644 index 0000000000000..92c65c17825e4 --- /dev/null +++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/validate/ExtraCalciteResource.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.calcite.sql.validate; + +import org.apache.calcite.runtime.Resources; + +/** + * Compiler-checked resources similar to CalciteResource in Calcite project. These are extra + * exceptions we want to extend Calcite with. Ref: + * https://issues.apache.org/jira/browse/CALCITE-6069 + */ +public interface ExtraCalciteResource { + + @Resources.BaseMessage( + "No match found for function signature {0}.\nSupported signatures are:\n{1}") + Resources.ExInst validatorNoFunctionMatch( + String invocation, String allowedSignatures); +} diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java index 8b2dd917962aa..28c3504f97e73 100644 --- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java +++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java @@ -158,10 +158,13 @@ * Default implementation of {@link SqlValidator}, the class was copied over because of * CALCITE-4554. * - *

Lines 5079 ~ 5092, Flink enables TIMESTAMP and TIMESTAMP_LTZ for system time period + *

Lines 1954 ~ 1977, Flink improves error message for functions without appropriate arguments in + * handleUnresolvedFunction at {@link SqlValidatorImpl#handleUnresolvedFunction}. + * + *

Lines 5101 ~ 5114, Flink enables TIMESTAMP and TIMESTAMP_LTZ for system time period * specification type at {@link org.apache.calcite.sql.validate.SqlValidatorImpl#validateSnapshot}. * - *

Lines 5436 ~ 5442, Flink enables TIMESTAMP and TIMESTAMP_LTZ for first orderBy column in + *

Lines 5458 ~ 5464, Flink enables TIMESTAMP and TIMESTAMP_LTZ for first orderBy column in * matchRecognize at {@link SqlValidatorImpl#validateMatchRecognize}. */ public class SqlValidatorImpl implements SqlValidatorWithHints { @@ -181,6 +184,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints { /** Alias prefix generated for source columns when rewriting UPDATE to MERGE. */ public static final String UPDATE_ANON_PREFIX = "SYS$ANON"; + private static final ExtraCalciteResource EXTRA_RESOURCE = + Resources.create(ExtraCalciteResource.class); + // ~ Instance fields -------------------------------------------------------- private final SqlOperatorTable opTab; @@ -1946,11 +1952,27 @@ public CalciteException handleUnresolvedFunction( final String signature; if (unresolvedFunction instanceof SqlFunction) { + // ----- FLINK MODIFICATION BEGIN ----- final SqlOperandTypeChecker typeChecking = new AssignableOperandTypeChecker(argTypes, argNames); - signature = + final String invocation = typeChecking.getAllowedSignatures( unresolvedFunction, unresolvedFunction.getName()); + if (unresolvedFunction.getOperandTypeChecker() != null) { + final String allowedSignatures = + unresolvedFunction + .getOperandTypeChecker() + .getAllowedSignatures( + unresolvedFunction, unresolvedFunction.getName()); + throw newValidationError( + call, + EXTRA_RESOURCE.validatorNoFunctionMatch(invocation, allowedSignatures)); + } else { + signature = + typeChecking.getAllowedSignatures( + unresolvedFunction, unresolvedFunction.getName()); + } + // ----- FLINK MODIFICATION END ----- } else { signature = unresolvedFunction.getName(); } diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/calcite/FlinkCalciteSqlValidator.java b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/calcite/FlinkCalciteSqlValidator.java index 0b0075a4f6476..a8cd9265e693a 100644 --- a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/calcite/FlinkCalciteSqlValidator.java +++ b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/calcite/FlinkCalciteSqlValidator.java @@ -54,6 +54,7 @@ import org.apache.calcite.sql.SqlOperatorTable; import org.apache.calcite.sql.SqlSelect; import org.apache.calcite.sql.SqlSnapshot; +import org.apache.calcite.sql.SqlTableFunction; import org.apache.calcite.sql.SqlUtil; import org.apache.calcite.sql.SqlWindowTableFunction; import org.apache.calcite.sql.parser.SqlParserPos; @@ -441,7 +442,7 @@ private static List getExplicitTableOperands(SqlNode node) { } final SqlFunction function = (SqlFunction) call.getOperator(); - if (function.getFunctionType() != SqlFunctionCategory.USER_DEFINED_TABLE_FUNCTION) { + if (!isTableFunction(function)) { return null; } @@ -459,4 +460,9 @@ private static List getExplicitTableOperands(SqlNode node) { }) .collect(Collectors.toList()); } + + private static boolean isTableFunction(SqlFunction function) { + return function instanceof SqlTableFunction + || function.getFunctionType() == SqlFunctionCategory.USER_DEFINED_TABLE_FUNCTION; + } } diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/connectors/TransformationScanProvider.java b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/connectors/TransformationScanProvider.java index e6642bc2ab112..46e739ec54d89 100644 --- a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/connectors/TransformationScanProvider.java +++ b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/connectors/TransformationScanProvider.java @@ -21,6 +21,7 @@ import org.apache.flink.annotation.Internal; import org.apache.flink.api.dag.Transformation; import org.apache.flink.table.api.CompiledPlan; +import org.apache.flink.table.connector.ParallelismProvider; import org.apache.flink.table.connector.ProviderContext; import org.apache.flink.table.connector.source.InputFormatProvider; import org.apache.flink.table.connector.source.ScanTableSource; @@ -37,7 +38,8 @@ * SourceFunctionProvider}, or {@link SourceProvider}. */ @Internal -public interface TransformationScanProvider extends ScanTableSource.ScanRuntimeProvider { +public interface TransformationScanProvider + extends ScanTableSource.ScanRuntimeProvider, ParallelismProvider { /** * Creates a {@link Transformation} instance. diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/inference/TypeInferenceOperandInference.java b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/inference/TypeInferenceOperandInference.java index 5e6e3d83b4d24..b59ad75f8d433 100644 --- a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/inference/TypeInferenceOperandInference.java +++ b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/inference/TypeInferenceOperandInference.java @@ -68,7 +68,12 @@ public void inferOperandTypes( final CallContext callContext = new CallBindingCallContext(dataTypeFactory, definition, callBinding, returnType); try { - inferOperandTypesOrError(unwrapTypeFactory(callBinding), callContext, operandTypes); + if (TypeInferenceUtil.validateArgumentCount( + typeInference.getInputTypeStrategy().getArgumentCount(), + callContext.getArgumentDataTypes().size(), + false)) { + inferOperandTypesOrError(unwrapTypeFactory(callBinding), callContext, operandTypes); + } } catch (ValidationException | CalciteContextException e) { // let operand checker fail } catch (Throwable t) { diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/ExecNodeGraphInternalPlan.java b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/ExecNodeGraphInternalPlan.java index 7f138c6517a69..b921535b8b810 100644 --- a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/ExecNodeGraphInternalPlan.java +++ b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/ExecNodeGraphInternalPlan.java @@ -34,17 +34,21 @@ import java.nio.file.Files; import java.nio.file.StandardOpenOption; import java.util.List; +import java.util.function.Supplier; import java.util.stream.Collectors; /** Implementation of {@link CompiledPlan} backed by an {@link ExecNodeGraph}. */ @Internal public class ExecNodeGraphInternalPlan implements InternalPlan { - private final String serializedPlan; + private final Supplier serializedPlanSupplier; private final ExecNodeGraph execNodeGraph; - public ExecNodeGraphInternalPlan(String serializedPlan, ExecNodeGraph execNodeGraph) { - this.serializedPlan = serializedPlan; + private String serializedPlan; + + public ExecNodeGraphInternalPlan( + Supplier serializedPlanSupplier, ExecNodeGraph execNodeGraph) { + this.serializedPlanSupplier = serializedPlanSupplier; this.execNodeGraph = execNodeGraph; } @@ -54,6 +58,9 @@ public ExecNodeGraph getExecNodeGraph() { @Override public String asJsonString() { + if (serializedPlan == null) { + serializedPlan = serializedPlanSupplier.get(); + } return serializedPlan; } @@ -78,7 +85,7 @@ public void writeToFile(File file, boolean ignoreIfExists, boolean failIfExists) Files.createDirectories(file.toPath().getParent()); Files.write( file.toPath(), - serializedPlan.getBytes(StandardCharsets.UTF_8), + asJsonString().getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE); diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/nodes/exec/serde/JsonSerdeUtil.java b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/nodes/exec/serde/JsonSerdeUtil.java index dffd134e97a84..b55fccbff2865 100644 --- a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/nodes/exec/serde/JsonSerdeUtil.java +++ b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/nodes/exec/serde/JsonSerdeUtil.java @@ -55,6 +55,7 @@ import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.ObjectMapper; import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.ObjectReader; import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.ObjectWriter; +import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.SerializationFeature; import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.SerializerProvider; import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.deser.std.StdDeserializer; import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.jsontype.NamedType; @@ -107,6 +108,7 @@ public static boolean hasJsonCreatorAnnotation(Class clazz) { .getTypeFactory() .withClassLoader(JsonSerdeUtil.class.getClassLoader())); OBJECT_MAPPER_INSTANCE.configure(MapperFeature.USE_GETTERS_AS_SETTERS, false); + OBJECT_MAPPER_INSTANCE.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); OBJECT_MAPPER_INSTANCE.registerModule(createFlinkTableJacksonModule()); } diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/utils/ExecNodeMetadataUtil.java b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/utils/ExecNodeMetadataUtil.java index 5c467f29583cb..b478f637fc919 100644 --- a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/utils/ExecNodeMetadataUtil.java +++ b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/utils/ExecNodeMetadataUtil.java @@ -197,7 +197,7 @@ public static void addTestNode(Class> execNodeClass) { addToLookupMap(execNodeClass); } - private static > List extractMetadataFromAnnotation( + public static > List extractMetadataFromAnnotation( Class execNodeClass) { List metadata = new ArrayList<>(); ExecNodeMetadata annotation = execNodeClass.getDeclaredAnnotation(ExecNodeMetadata.class); diff --git a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/ScalarOperatorGens.scala b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/ScalarOperatorGens.scala index 084b3aa503ec0..230b09adccc11 100644 --- a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/ScalarOperatorGens.scala +++ b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/calls/ScalarOperatorGens.scala @@ -345,44 +345,83 @@ object ScalarOperatorGens { } } - def generateEquals( + private def wrapExpressionIfNonEq( + isNonEq: Boolean, + equalsExpr: GeneratedExpression, + resultType: LogicalType): GeneratedExpression = { + if (isNonEq) { + GeneratedExpression( + s"(!${equalsExpr.resultTerm})", + equalsExpr.nullTerm, + equalsExpr.code, + resultType) + } else { + equalsExpr + } + } + + private def generateEqualAndNonEqual( ctx: CodeGeneratorContext, left: GeneratedExpression, right: GeneratedExpression, + operator: String, resultType: LogicalType): GeneratedExpression = { + checkImplicitConversionValidity(left, right) + + val nonEq = operator match { + case "==" => false + case "!=" => true + case _ => throw new CodeGenException(s"Unsupported boolean comparison '$operator'.") + } val canEqual = isInteroperable(left.resultType, right.resultType) + if (isCharacterString(left.resultType) && isCharacterString(right.resultType)) { generateOperatorIfNotNull(ctx, resultType, left, right)( - (leftTerm, rightTerm) => s"$leftTerm.equals($rightTerm)") + (leftTerm, rightTerm) => s"${if (nonEq) "!" else ""}$leftTerm.equals($rightTerm)") } // numeric types else if (isNumeric(left.resultType) && isNumeric(right.resultType)) { - generateComparison(ctx, "==", left, right, resultType) + generateComparison(ctx, operator, left, right, resultType) } // array types else if (isArray(left.resultType) && canEqual) { - generateArrayComparison(ctx, left, right, resultType) + wrapExpressionIfNonEq( + nonEq, + generateArrayComparison(ctx, left, right, resultType), + resultType) } // map types else if (isMap(left.resultType) && canEqual) { val mapType = left.resultType.asInstanceOf[MapType] - generateMapComparison(ctx, left, right, mapType.getKeyType, mapType.getValueType, resultType) + wrapExpressionIfNonEq( + nonEq, + generateMapComparison( + ctx, + left, + right, + mapType.getKeyType, + mapType.getValueType, + resultType), + resultType) } // multiset types else if (isMultiset(left.resultType) && canEqual) { val multisetType = left.resultType.asInstanceOf[MultisetType] - generateMapComparison( - ctx, - left, - right, - multisetType.getElementType, - new IntType(false), + wrapExpressionIfNonEq( + nonEq, + generateMapComparison( + ctx, + left, + right, + multisetType.getElementType, + new IntType(false), + resultType), resultType) } // comparable types of same type else if (isComparable(left.resultType) && canEqual) { - generateComparison(ctx, "==", left, right, resultType) + generateComparison(ctx, operator, left, right, resultType) } // generic types of same type else if (isRaw(left.resultType) && canEqual) { @@ -399,7 +438,7 @@ object ScalarOperatorGens { | ${left.resultTerm}.ensureMaterialized($ser); | ${right.resultTerm}.ensureMaterialized($ser); | $resultTerm = - | ${left.resultTerm}.getBinarySection(). + | ${if (nonEq) "!" else ""}${left.resultTerm}.getBinarySection(). | equals(${right.resultTerm}.getBinarySection()); |} |""".stripMargin @@ -407,43 +446,58 @@ object ScalarOperatorGens { } // support date/time/timestamp equalTo string. // for performance, we cast literal string to literal time. - else if (isTimePoint(left.resultType) && isCharacterString(right.resultType)) { - if (right.literal) { - generateEquals(ctx, left, generateCastLiteral(ctx, right, left.resultType), resultType) - } else { - generateEquals( - ctx, - left, - generateCast(ctx, right, left.resultType, nullOnFailure = true), - resultType) - } - } else if (isTimePoint(right.resultType) && isCharacterString(left.resultType)) { - if (left.literal) { - generateEquals(ctx, generateCastLiteral(ctx, left, right.resultType), right, resultType) - } else { - generateEquals( - ctx, - generateCast(ctx, left, right.resultType, nullOnFailure = true), - right, - resultType) - } + else if ( + (isTimePoint(left.resultType) && isCharacterString(right.resultType)) || (isTimePoint( + right.resultType) && isCharacterString(left.resultType)) + ) { + val (newLeft, newRight) = + if (isTimePoint(left.resultType)) (left, right) + else (right, left) + generateEqualAndNonEqual( + ctx, + newLeft, + if (newRight.literal) { + generateCastLiteral(ctx, newRight, newLeft.resultType) + } else { + generateCast(ctx, newRight, newLeft.resultType, nullOnFailure = true) + }, + operator, + resultType + ) } // non comparable types else { - generateOperatorIfNotNull(ctx, resultType, left, right) { - if (isReference(left.resultType)) { - (leftTerm, rightTerm) => s"$leftTerm.equals($rightTerm)" - } else if (isReference(right.resultType)) { - (leftTerm, rightTerm) => s"$rightTerm.equals($leftTerm)" - } else { - throw new CodeGenException( - s"Incomparable types: ${left.resultType} and " + - s"${right.resultType}") - } + val (newLeft, newRight) = if (isReference(left.resultType)) { + (left, right) + } else if (isReference(right.resultType)) { + (right, left) + } else { + throw new CodeGenException( + s"Incomparable types: ${left.resultType} and " + + s"${right.resultType}") + } + generateOperatorIfNotNull(ctx, resultType, newLeft, newRight) { + (leftTerm, rightTerm) => s"${if (nonEq) "!" else ""}$leftTerm.equals($rightTerm)" } } } + def generateEquals( + ctx: CodeGeneratorContext, + left: GeneratedExpression, + right: GeneratedExpression, + resultType: LogicalType): GeneratedExpression = { + generateEqualAndNonEqual(ctx, left, right, "==", resultType) + } + + def generateNotEquals( + ctx: CodeGeneratorContext, + left: GeneratedExpression, + right: GeneratedExpression, + resultType: LogicalType): GeneratedExpression = { + generateEqualAndNonEqual(ctx, left, right, "!=", resultType) + } + def generateIsDistinctFrom( ctx: CodeGeneratorContext, left: GeneratedExpression, @@ -474,68 +528,6 @@ object ScalarOperatorGens { ) } - def generateNotEquals( - ctx: CodeGeneratorContext, - left: GeneratedExpression, - right: GeneratedExpression, - resultType: LogicalType): GeneratedExpression = { - checkImplicitConversionValidity(left, right) - if (isCharacterString(left.resultType) && isCharacterString(right.resultType)) { - generateOperatorIfNotNull(ctx, resultType, left, right)( - (leftTerm, rightTerm) => s"!$leftTerm.equals($rightTerm)") - } - // numeric types - else if (isNumeric(left.resultType) && isNumeric(right.resultType)) { - generateComparison(ctx, "!=", left, right, resultType) - } - // temporal types - else if ( - isTemporal(left.resultType) && - isInteroperable(left.resultType, right.resultType) - ) { - generateComparison(ctx, "!=", left, right, resultType) - } - // array types - else if (isArray(left.resultType) && isInteroperable(left.resultType, right.resultType)) { - val equalsExpr = generateEquals(ctx, left, right, resultType) - GeneratedExpression( - s"(!${equalsExpr.resultTerm})", - equalsExpr.nullTerm, - equalsExpr.code, - resultType) - } - // map types - else if (isMap(left.resultType) && isInteroperable(left.resultType, right.resultType)) { - val equalsExpr = generateEquals(ctx, left, right, resultType) - GeneratedExpression( - s"(!${equalsExpr.resultTerm})", - equalsExpr.nullTerm, - equalsExpr.code, - resultType) - } - // comparable types - else if ( - isComparable(left.resultType) && - isInteroperable(left.resultType, right.resultType) - ) { - generateComparison(ctx, "!=", left, right, resultType) - } - // non-comparable types - else { - generateOperatorIfNotNull(ctx, resultType, left, right) { - if (isReference(left.resultType)) { - (leftTerm, rightTerm) => s"!($leftTerm.equals($rightTerm))" - } else if (isReference(right.resultType)) { - (leftTerm, rightTerm) => s"!($rightTerm.equals($leftTerm))" - } else { - throw new CodeGenException( - s"Incomparable types: ${left.resultType} and " + - s"${right.resultType}") - } - } - } - } - /** Generates comparison code for numeric types and comparable types of same type. */ def generateComparison( ctx: CodeGeneratorContext, diff --git a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/delegation/StreamPlanner.scala b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/delegation/StreamPlanner.scala index 1029475b74827..fb32326f11787 100644 --- a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/delegation/StreamPlanner.scala +++ b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/delegation/StreamPlanner.scala @@ -22,7 +22,7 @@ import org.apache.flink.api.dag.Transformation import org.apache.flink.configuration.ExecutionOptions import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.ObjectReader import org.apache.flink.streaming.api.graph.StreamGraph -import org.apache.flink.table.api.{ExplainDetail, ExplainFormat, PlanReference, TableConfig, TableException} +import org.apache.flink.table.api._ import org.apache.flink.table.api.PlanReference.{ContentPlanReference, FilePlanReference, ResourcePlanReference} import org.apache.flink.table.catalog.{CatalogManager, FunctionCatalog} import org.apache.flink.table.delegation.{Executor, InternalPlan} @@ -190,10 +190,12 @@ class StreamPlanner( } new ExecNodeGraphInternalPlan( - JsonSerdeUtil - .createObjectWriter(ctx) - .withDefaultPrettyPrinter() - .writeValueAsString(execNodeGraph), + // ensures that the JSON output is always normalized + () => + JsonSerdeUtil + .createObjectWriter(ctx) + .withDefaultPrettyPrinter() + .writeValueAsString(execNodeGraph), execNodeGraph) } @@ -204,12 +206,12 @@ class StreamPlanner( val execGraph = translateToExecNodeGraph(optimizedRelNodes, isCompiled = true) afterTranslation() - new ExecNodeGraphInternalPlan( - JsonSerdeUtil - .createObjectWriter(createSerdeContext) - .withDefaultPrettyPrinter() - .writeValueAsString(execGraph), - execGraph) + val compiledJson = JsonSerdeUtil + .createObjectWriter(createSerdeContext) + .withDefaultPrettyPrinter() + .writeValueAsString(execGraph) + + new ExecNodeGraphInternalPlan(() => compiledJson, execGraph) } override def translatePlan(plan: InternalPlan): util.List[Transformation[_]] = { diff --git a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/PlannerExpressionConverter.scala b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/PlannerExpressionConverter.scala index 6a0d7b028ed91..cf95c97cf0db7 100644 --- a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/PlannerExpressionConverter.scala +++ b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/PlannerExpressionConverter.scala @@ -69,14 +69,6 @@ class PlannerExpressionConverter private extends ApiExpressionVisitor[PlannerExp // special case: requires individual handling of child expressions func match { - case REINTERPRET_CAST => - assert(children.size == 3) - return Reinterpret( - children.head.accept(this), - fromDataTypeToTypeInfo(children(1).asInstanceOf[TypeLiteralExpression].getOutputDataType), - getValue[Boolean](children(2).accept(this)) - ) - case WINDOW_START => assert(children.size == 1) val windowReference = translateWindowReference(children.head) @@ -135,10 +127,6 @@ class PlannerExpressionConverter private extends ApiExpressionVisitor[PlannerExp case fd: FunctionDefinition => fd match { - case IN => - assert(args.size > 1) - In(args.head, args.drop(1)) - case DISTINCT => assert(args.size == 1) DistinctAgg(args.head) @@ -147,58 +135,6 @@ class PlannerExpressionConverter private extends ApiExpressionVisitor[PlannerExp assert(args.size == 1) Collect(args.head) - case EXTRACT => - assert(args.size == 2) - Extract(args.head, args.last) - - case CURRENT_DATE => - assert(args.isEmpty) - CurrentDate() - - case CURRENT_TIME => - assert(args.isEmpty) - CurrentTime() - - case CURRENT_TIMESTAMP => - assert(args.isEmpty) - CurrentTimestamp() - - case LOCAL_TIME => - assert(args.isEmpty) - LocalTime() - - case LOCAL_TIMESTAMP => - assert(args.isEmpty) - LocalTimestamp() - - case TEMPORAL_OVERLAPS => - assert(args.size == 4) - TemporalOverlaps(args.head, args(1), args(2), args.last) - - case DATE_FORMAT => - assert(args.size == 2) - DateFormat(args.head, args.last) - - case TIMESTAMP_DIFF => - assert(args.size == 3) - TimestampDiff(args.head, args(1), args.last) - - case TO_TIMESTAMP_LTZ => - assert(args.size == 2) - ToTimestampLtz(args.head, args.last) - - case AT => - assert(args.size == 2) - ItemAt(args.head, args.last) - - case CARDINALITY => - assert(args.size == 1) - Cardinality(args.head) - - case ARRAY_ELEMENT => - assert(args.size == 1) - ArrayElement(args.head) - case ORDER_ASC => assert(args.size == 1) Asc(args.head) diff --git a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/Reinterpret.scala b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/Reinterpret.scala deleted file mode 100644 index 13998c7b7d6dd..0000000000000 --- a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/Reinterpret.scala +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.flink.table.planner.expressions - -import org.apache.flink.api.common.typeinfo.TypeInformation -import org.apache.flink.table.planner.typeutils.TypeCoercion -import org.apache.flink.table.planner.validate._ -import org.apache.flink.table.runtime.types.TypeInfoLogicalTypeConverter.fromTypeInfoToLogicalType - -case class Reinterpret( - child: PlannerExpression, - resultType: TypeInformation[_], - checkOverflow: Boolean) - extends UnaryExpression { - - override def toString = s"$child.reinterpret($resultType)" - - override private[flink] def makeCopy(anyRefs: Array[AnyRef]): this.type = { - val child: PlannerExpression = anyRefs.head.asInstanceOf[PlannerExpression] - copy(child, resultType).asInstanceOf[this.type] - } - - override private[flink] def validateInput(): ValidationResult = { - if ( - TypeCoercion.canReinterpret( - fromTypeInfoToLogicalType(child.resultType), - fromTypeInfoToLogicalType(resultType)) - ) { - ValidationSuccess - } else { - ValidationFailure(s"Unsupported reinterpret from ${child.resultType} to $resultType") - } - } -} diff --git a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/collection.scala b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/collection.scala deleted file mode 100644 index 35bf9c2d721fd..0000000000000 --- a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/collection.scala +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.flink.table.planner.expressions - -import org.apache.flink.api.common.typeinfo.{BasicArrayTypeInfo, BasicTypeInfo, PrimitiveArrayTypeInfo, TypeInformation} -import org.apache.flink.api.common.typeinfo.BasicTypeInfo.INT_TYPE_INFO -import org.apache.flink.api.java.typeutils.{MapTypeInfo, ObjectArrayTypeInfo, RowTypeInfo} -import org.apache.flink.table.planner.typeutils.TypeInfoCheckUtils.{isArray, isMap} -import org.apache.flink.table.planner.validate.{ValidationFailure, ValidationResult, ValidationSuccess} - -case class ArrayElement(array: PlannerExpression) extends PlannerExpression { - - override private[flink] def children: Seq[PlannerExpression] = Seq(array) - - override def toString = s"($array).element()" - - override private[flink] def resultType = array.resultType match { - case oati: ObjectArrayTypeInfo[_, _] => oati.getComponentInfo - case bati: BasicArrayTypeInfo[_, _] => bati.getComponentInfo - case pati: PrimitiveArrayTypeInfo[_] => pati.getComponentType - } - - override private[flink] def validateInput(): ValidationResult = { - array.resultType match { - case ati: TypeInformation[_] if isArray(ati) => ValidationSuccess - case other @ _ => ValidationFailure(s"Array expected but was '$other'.") - } - } -} - -case class Cardinality(container: PlannerExpression) extends PlannerExpression { - - override private[flink] def children: Seq[PlannerExpression] = Seq(container) - - override def toString = s"($container).cardinality()" - - override private[flink] def resultType = BasicTypeInfo.INT_TYPE_INFO - - override private[flink] def validateInput(): ValidationResult = { - container.resultType match { - case mti: TypeInformation[_] if isMap(mti) => ValidationSuccess - case ati: TypeInformation[_] if isArray(ati) => ValidationSuccess - case other @ _ => ValidationFailure(s"Array or map expected but was '$other'.") - } - } -} - -case class ItemAt(container: PlannerExpression, key: PlannerExpression) extends PlannerExpression { - - override private[flink] def children: Seq[PlannerExpression] = Seq(container, key) - - override def toString = s"($container).at($key)" - - override private[flink] def resultType = container.resultType match { - case mti: MapTypeInfo[_, _] => mti.getValueTypeInfo - case oati: ObjectArrayTypeInfo[_, _] => oati.getComponentInfo - case bati: BasicArrayTypeInfo[_, _] => bati.getComponentInfo - case pati: PrimitiveArrayTypeInfo[_] => pati.getComponentType - } - - override private[flink] def validateInput(): ValidationResult = { - container.resultType match { - - case ati: TypeInformation[_] if isArray(ati) => - if (key.resultType == INT_TYPE_INFO) { - // check for common user mistake - key match { - case Literal(value: Int, INT_TYPE_INFO) if value < 1 => - ValidationFailure( - s"Array element access needs an index starting at 1 but was $value.") - case _ => ValidationSuccess - } - } else { - ValidationFailure( - s"Array element access needs an integer index but was '${key.resultType}'.") - } - - case mti: MapTypeInfo[_, _] => - if (key.resultType == mti.getKeyTypeInfo) { - ValidationSuccess - } else { - ValidationFailure( - s"Map entry access needs a valid key of type " + - s"'${mti.getKeyTypeInfo}', found '${key.resultType}'.") - } - - case other @ _ => ValidationFailure(s"Array or map expected but was '$other'.") - } - } -} diff --git a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/subquery.scala b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/subquery.scala deleted file mode 100644 index a5595024e8d14..0000000000000 --- a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/subquery.scala +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.flink.table.planner.expressions - -import org.apache.flink.api.common.typeinfo.BasicTypeInfo._ -import org.apache.flink.api.common.typeinfo.TypeInformation -import org.apache.flink.table.operations.QueryOperation -import org.apache.flink.table.planner.typeutils.TypeInfoCheckUtils._ -import org.apache.flink.table.planner.validate.{ValidationFailure, ValidationResult, ValidationSuccess} -import org.apache.flink.table.types.utils.TypeConversions -import org.apache.flink.table.types.utils.TypeConversions.fromDataTypeToLegacyInfo - -case class In(expression: PlannerExpression, elements: Seq[PlannerExpression]) - extends PlannerExpression { - - override def toString = s"$expression.in(${elements.mkString(", ")})" - - override private[flink] def children: Seq[PlannerExpression] = expression +: elements.distinct - - override private[flink] def validateInput(): ValidationResult = { - // check if this is a sub-query expression or an element list - elements.head match { - - case TableReference(name, tableOperation: QueryOperation) => - if (elements.length != 1) { - return ValidationFailure("IN operator supports only one table reference.") - } - val resolvedSchema = tableOperation.getResolvedSchema - if (resolvedSchema.getColumnCount > 1) { - return ValidationFailure( - s"The sub-query table '$name' must not have more than one column.") - } - ( - expression.resultType, - fromDataTypeToLegacyInfo(resolvedSchema.getColumnDataTypes.get(0))) match { - case (lType, rType) if lType == rType => ValidationSuccess - case (lType, rType) if isNumeric(lType) && isNumeric(rType) => ValidationSuccess - case (lType, rType) if isArray(lType) && lType.getTypeClass == rType.getTypeClass => - ValidationSuccess - case (lType, rType) => - ValidationFailure(s"IN operator on incompatible types: $lType and $rType.") - } - - case _ => - val types = children.tail.map(_.resultType) - if (types.distinct.length != 1) { - return ValidationFailure( - s"Types on the right side of the IN operator must be the same, " + - s"got ${types.mkString(", ")}.") - } - (children.head.resultType, children.last.resultType) match { - case (lType, rType) if isNumeric(lType) && isNumeric(rType) => ValidationSuccess - case (lType, rType) if lType == rType => ValidationSuccess - case (lType, rType) if isArray(lType) && lType.getTypeClass == rType.getTypeClass => - ValidationSuccess - case (lType, rType) => - ValidationFailure(s"IN operator on incompatible types: $lType and $rType.") - } - } - } - - override private[flink] def resultType: TypeInformation[_] = BOOLEAN_TYPE_INFO -} diff --git a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/time.scala b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/time.scala deleted file mode 100644 index 19295dd36aa12..0000000000000 --- a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/expressions/time.scala +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.flink.table.planner.expressions - -import org.apache.flink.api.common.typeinfo.{LocalTimeTypeInfo, SqlTimeTypeInfo, TypeInformation} -import org.apache.flink.api.common.typeinfo.BasicTypeInfo._ -import org.apache.flink.table.planner.calcite.FlinkRelBuilder -import org.apache.flink.table.planner.functions.sql.FlinkSqlOperatorTable -import org.apache.flink.table.planner.typeutils.TypeInfoCheckUtils -import org.apache.flink.table.planner.typeutils.TypeInfoCheckUtils.isTimeInterval -import org.apache.flink.table.planner.validate.{ValidationFailure, ValidationResult, ValidationSuccess} -import org.apache.flink.table.runtime.typeutils.LegacyLocalDateTimeTypeInfo -import org.apache.flink.table.typeutils.TimeIntervalTypeInfo - -import org.apache.calcite.rex._ - -case class Extract(timeIntervalUnit: PlannerExpression, temporal: PlannerExpression) - extends PlannerExpression { - - override private[flink] def children: Seq[PlannerExpression] = timeIntervalUnit :: temporal :: Nil - - override private[flink] def resultType: TypeInformation[_] = LONG_TYPE_INFO - - override private[flink] def validateInput(): ValidationResult = { - if (!TypeInfoCheckUtils.isTemporal(temporal.resultType)) { - return ValidationFailure( - s"Extract operator requires Temporal input, " + - s"but $temporal is of type ${temporal.resultType}") - } - - timeIntervalUnit match { - case SymbolPlannerExpression(PlannerTimeIntervalUnit.YEAR) | SymbolPlannerExpression( - PlannerTimeIntervalUnit.QUARTER) | SymbolPlannerExpression( - PlannerTimeIntervalUnit.MONTH) | SymbolPlannerExpression(PlannerTimeIntervalUnit.WEEK) | - SymbolPlannerExpression(PlannerTimeIntervalUnit.DAY) - if temporal.resultType == SqlTimeTypeInfo.DATE - || temporal.resultType == SqlTimeTypeInfo.TIMESTAMP - || temporal.resultType == LocalTimeTypeInfo.LOCAL_DATE - || temporal.resultType == LocalTimeTypeInfo.LOCAL_DATE_TIME - || temporal.resultType.isInstanceOf[LegacyLocalDateTimeTypeInfo] - || temporal.resultType == TimeIntervalTypeInfo.INTERVAL_MILLIS - || temporal.resultType == TimeIntervalTypeInfo.INTERVAL_MONTHS => - ValidationSuccess - - case SymbolPlannerExpression(PlannerTimeIntervalUnit.HOUR) | SymbolPlannerExpression( - PlannerTimeIntervalUnit.MINUTE) | SymbolPlannerExpression( - PlannerTimeIntervalUnit.SECOND) - if temporal.resultType == SqlTimeTypeInfo.TIME - || temporal.resultType == SqlTimeTypeInfo.TIMESTAMP - || temporal.resultType == LocalTimeTypeInfo.LOCAL_TIME - || temporal.resultType == LocalTimeTypeInfo.LOCAL_DATE_TIME - || temporal.resultType.isInstanceOf[LegacyLocalDateTimeTypeInfo] - || temporal.resultType == TimeIntervalTypeInfo.INTERVAL_MILLIS => - ValidationSuccess - - case _ => - ValidationFailure( - s"Extract operator does not support unit '$timeIntervalUnit' for input" + - s" of type '${temporal.resultType}'.") - } - } - - override def toString: String = s"($temporal).extract($timeIntervalUnit)" -} - -abstract class CurrentTimePoint(targetType: TypeInformation[_], local: Boolean) - extends LeafExpression { - - override private[flink] def resultType: TypeInformation[_] = targetType - - override private[flink] def validateInput(): ValidationResult = { - if (!TypeInfoCheckUtils.isTimePoint(targetType)) { - ValidationFailure( - s"CurrentTimePoint operator requires Time Point target type, " + - s"but get $targetType.") - } else if (local && targetType == SqlTimeTypeInfo.DATE) { - ValidationFailure( - s"Localized CurrentTimePoint operator requires Time or Timestamp target " + - s"type, but get $targetType.") - } else { - ValidationSuccess - } - } - - override def toString: String = if (local) { - s"local$targetType()" - } else { - s"current$targetType()" - } -} - -case class CurrentDate() extends CurrentTimePoint(SqlTimeTypeInfo.DATE, local = false) - -case class CurrentTime() extends CurrentTimePoint(SqlTimeTypeInfo.TIME, local = false) - -case class CurrentTimestamp() extends CurrentTimePoint(SqlTimeTypeInfo.TIMESTAMP, local = false) - -case class LocalTime() extends CurrentTimePoint(SqlTimeTypeInfo.TIME, local = true) - -case class LocalTimestamp() extends CurrentTimePoint(SqlTimeTypeInfo.TIMESTAMP, local = true) - -/** Determines whether two anchored time intervals overlap. */ -case class TemporalOverlaps( - leftTimePoint: PlannerExpression, - leftTemporal: PlannerExpression, - rightTimePoint: PlannerExpression, - rightTemporal: PlannerExpression) - extends PlannerExpression { - - override private[flink] def children: Seq[PlannerExpression] = - Seq(leftTimePoint, leftTemporal, rightTimePoint, rightTemporal) - - override private[flink] def resultType: TypeInformation[_] = BOOLEAN_TYPE_INFO - - override private[flink] def validateInput(): ValidationResult = { - if (!TypeInfoCheckUtils.isTimePoint(leftTimePoint.resultType)) { - return ValidationFailure( - s"TemporalOverlaps operator requires leftTimePoint to be of type " + - s"Time Point, but get ${leftTimePoint.resultType}.") - } - if (!TypeInfoCheckUtils.isTimePoint(rightTimePoint.resultType)) { - return ValidationFailure( - s"TemporalOverlaps operator requires rightTimePoint to be of " + - s"type Time Point, but get ${rightTimePoint.resultType}.") - } - if (leftTimePoint.resultType != rightTimePoint.resultType) { - return ValidationFailure( - s"TemporalOverlaps operator requires leftTimePoint and " + - s"rightTimePoint to be of same type.") - } - - // leftTemporal is point, then it must be comparable with leftTimePoint - if (TypeInfoCheckUtils.isTimePoint(leftTemporal.resultType)) { - if (leftTemporal.resultType != leftTimePoint.resultType) { - return ValidationFailure( - s"TemporalOverlaps operator requires leftTemporal and " + - s"leftTimePoint to be of same type if leftTemporal is of type Time Point.") - } - } else if (!isTimeInterval(leftTemporal.resultType)) { - return ValidationFailure( - s"TemporalOverlaps operator requires leftTemporal to be of " + - s"type Time Point or Time Interval.") - } - - // rightTemporal is point, then it must be comparable with rightTimePoint - if (TypeInfoCheckUtils.isTimePoint(rightTemporal.resultType)) { - if (rightTemporal.resultType != rightTimePoint.resultType) { - return ValidationFailure( - s"TemporalOverlaps operator requires rightTemporal and " + - s"rightTimePoint to be of same type if rightTemporal is of type Time Point.") - } - } else if (!isTimeInterval(rightTemporal.resultType)) { - return ValidationFailure( - s"TemporalOverlaps operator requires rightTemporal to be of " + - s"type Time Point or Time Interval.") - } - ValidationSuccess - } - - override def toString: String = s"temporalOverlaps(${children.mkString(", ")})" - - /** - * Standard conversion of the OVERLAPS operator. Source: - * [[org.apache.calcite.sql2rel.StandardConvertletTable#convertOverlaps()]] - */ - private def convertOverlaps( - leftP: RexNode, - leftT: RexNode, - rightP: RexNode, - rightT: RexNode, - relBuilder: FlinkRelBuilder): RexNode = { - val convLeftT = convertOverlapsEnd(relBuilder, leftP, leftT, leftTemporal.resultType) - val convRightT = convertOverlapsEnd(relBuilder, rightP, rightT, rightTemporal.resultType) - - // sort end points into start and end, such that (s0 <= e0) and (s1 <= e1). - val (s0, e0) = buildSwap(relBuilder, leftP, convLeftT) - val (s1, e1) = buildSwap(relBuilder, rightP, convRightT) - - // (e0 >= s1) AND (e1 >= s0) - val leftPred = relBuilder.call(FlinkSqlOperatorTable.GREATER_THAN_OR_EQUAL, e0, s1) - val rightPred = relBuilder.call(FlinkSqlOperatorTable.GREATER_THAN_OR_EQUAL, e1, s0) - relBuilder.call(FlinkSqlOperatorTable.AND, leftPred, rightPred) - } - - private def convertOverlapsEnd( - relBuilder: FlinkRelBuilder, - start: RexNode, - end: RexNode, - endType: TypeInformation[_]) = { - if (isTimeInterval(endType)) { - relBuilder.call(FlinkSqlOperatorTable.DATETIME_PLUS, start, end) - } else { - end - } - } - - private def buildSwap(relBuilder: FlinkRelBuilder, start: RexNode, end: RexNode) = { - val le = relBuilder.call(FlinkSqlOperatorTable.LESS_THAN_OR_EQUAL, start, end) - val l = relBuilder.call(FlinkSqlOperatorTable.CASE, le, start, end) - val r = relBuilder.call(FlinkSqlOperatorTable.CASE, le, end, start) - (l, r) - } -} - -case class DateFormat(timestamp: PlannerExpression, format: PlannerExpression) - extends PlannerExpression { - override private[flink] def children = timestamp :: format :: Nil - - override def toString: String = s"$timestamp.dateFormat($format)" - - override private[flink] def resultType = STRING_TYPE_INFO -} - -case class TimestampDiff( - timePointUnit: PlannerExpression, - timePoint1: PlannerExpression, - timePoint2: PlannerExpression) - extends PlannerExpression { - - override private[flink] def children: Seq[PlannerExpression] = - timePointUnit :: timePoint1 :: timePoint2 :: Nil - - override private[flink] def validateInput(): ValidationResult = { - if (!TypeInfoCheckUtils.isTimePoint(timePoint1.resultType)) { - return ValidationFailure( - s"$this requires an input time point type, " + - s"but timePoint1 is of type '${timePoint1.resultType}'.") - } - - if (!TypeInfoCheckUtils.isTimePoint(timePoint2.resultType)) { - return ValidationFailure( - s"$this requires an input time point type, " + - s"but timePoint2 is of type '${timePoint2.resultType}'.") - } - - timePointUnit match { - case SymbolPlannerExpression(PlannerTimePointUnit.YEAR) | SymbolPlannerExpression( - PlannerTimePointUnit.QUARTER) | SymbolPlannerExpression(PlannerTimePointUnit.MONTH) | - SymbolPlannerExpression(PlannerTimePointUnit.WEEK) | SymbolPlannerExpression( - PlannerTimePointUnit.DAY) | SymbolPlannerExpression(PlannerTimePointUnit.HOUR) | - SymbolPlannerExpression(PlannerTimePointUnit.MINUTE) | SymbolPlannerExpression( - PlannerTimePointUnit.SECOND) - if timePoint1.resultType == SqlTimeTypeInfo.DATE - || timePoint1.resultType == SqlTimeTypeInfo.TIMESTAMP - || timePoint2.resultType == SqlTimeTypeInfo.DATE - || timePoint2.resultType == SqlTimeTypeInfo.TIMESTAMP - || timePoint1.resultType == LocalTimeTypeInfo.LOCAL_DATE - || timePoint1.resultType == LocalTimeTypeInfo.LOCAL_DATE_TIME - || timePoint2.resultType == LocalTimeTypeInfo.LOCAL_DATE - || timePoint2.resultType == LocalTimeTypeInfo.LOCAL_DATE_TIME => - ValidationSuccess - - case _ => - ValidationFailure( - s"$this operator does not support unit '$timePointUnit'" + - s" for input of type ('${timePoint1.resultType}', '${timePoint2.resultType}').") - } - } - - override def toString: String = s"timestampDiff(${children.mkString(", ")})" - - override private[flink] def resultType = INT_TYPE_INFO -} - -case class ToTimestampLtz(numericEpochTime: PlannerExpression, precision: PlannerExpression) - extends PlannerExpression { - - override private[flink] def children: Seq[PlannerExpression] = - numericEpochTime :: precision :: Nil - - override private[flink] def validateInput(): ValidationResult = { - if ( - TypeInfoCheckUtils.assertNumericExpr(numericEpochTime.resultType, "toTimestampLtz").isFailure - ) { - return ValidationFailure( - s"$this requires numeric type for the first input, " + - s"but the actual type '${numericEpochTime.resultType}'.") - } - if ( - TypeInfoCheckUtils - .assertNumericExpr(precision.resultType, "toTimestampLtz") - .isFailure - ) { - return ValidationFailure( - s"$this requires numeric type for the second input, " + - s"but the actual type '${precision.resultType}'.") - } - ValidationSuccess - } - - override def toString: String = s"toTimestampLtz(${children.mkString(", ")})" - - override private[flink] def resultType = SqlTimeTypeInfo.TIMESTAMP -} diff --git a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/utils/RexNodeExtractor.scala b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/utils/RexNodeExtractor.scala index 482ce56dc6389..481cbda8b8265 100644 --- a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/utils/RexNodeExtractor.scala +++ b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/utils/RexNodeExtractor.scala @@ -37,6 +37,7 @@ import org.apache.flink.table.types.logical.YearMonthIntervalType import org.apache.flink.table.types.utils.TypeConversions import org.apache.flink.util.Preconditions +import org.apache.calcite.avatica.util.ByteString import org.apache.calcite.plan.RelOptUtil import org.apache.calcite.rel.`type`.RelDataType import org.apache.calcite.rex._ @@ -502,6 +503,9 @@ class RexNodeToExpressionConverter( // convert to BigDecimal literal.getValueAs(classOf[java.math.BigDecimal]) + case BINARY | VARBINARY => + literal.getValueAs(classOf[Array[Byte]]) + case _ => literal.getValue } diff --git a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/typeutils/TypeCoercion.scala b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/typeutils/TypeCoercion.scala index 6e284464225d2..c6a06b8c64eb1 100644 --- a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/typeutils/TypeCoercion.scala +++ b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/typeutils/TypeCoercion.scala @@ -53,85 +53,4 @@ object TypeCoercion { case _ => false } - - /** - * All the supported cast types in flink-table. - * - * Note: No distinction between explicit and implicit conversions Note: This is a subset of - * SqlTypeAssignmentRule Note: This may lose type during the cast. - */ - def canCast(from: LogicalType, to: LogicalType): Boolean = - (from.getTypeRoot, to.getTypeRoot) match { - case (_, _) if from == to => true - - case (_, VARCHAR | CHAR) => true - - case (VARCHAR | CHAR, _) if isNumeric(to) => true - case (VARCHAR | CHAR, BOOLEAN) => true - case (VARCHAR | CHAR, DECIMAL) => true - case (VARCHAR | CHAR, DATE) => true - case (VARCHAR | CHAR, TIME_WITHOUT_TIME_ZONE) => true - case (VARCHAR | CHAR, TIMESTAMP_WITHOUT_TIME_ZONE) => true - case (VARCHAR | CHAR, TIMESTAMP_WITH_LOCAL_TIME_ZONE) => true - - case (BOOLEAN, _) if isNumeric(to) => true - case (BOOLEAN, DECIMAL) => true - case (_, BOOLEAN) if isNumeric(from) => true - case (DECIMAL, BOOLEAN) => true - - case (_, _) if isNumeric(from) && isNumeric(to) => true - case (_, DECIMAL) if isNumeric(from) => true - case (DECIMAL, _) if isNumeric(to) => true - case (DECIMAL, DECIMAL) => true - case (INTEGER, DATE) => true - case (INTEGER, TIME_WITHOUT_TIME_ZONE) => true - case (TINYINT, TIMESTAMP_WITHOUT_TIME_ZONE) => true - case (SMALLINT, TIMESTAMP_WITHOUT_TIME_ZONE) => true - case (INTEGER, TIMESTAMP_WITHOUT_TIME_ZONE) => true - case (BIGINT, TIMESTAMP_WITHOUT_TIME_ZONE) => true - case (DOUBLE, TIMESTAMP_WITHOUT_TIME_ZONE) => true - case (FLOAT, TIMESTAMP_WITHOUT_TIME_ZONE) => true - case (INTEGER, INTERVAL_YEAR_MONTH) => true - case (BIGINT, INTERVAL_DAY_TIME) => true - - case (DATE, TIME_WITHOUT_TIME_ZONE) => false - case (TIME_WITHOUT_TIME_ZONE, DATE) => false - case (_, _) if isTimePoint(from) && isTimePoint(to) => true - case (DATE, INTEGER) => true - case (TIME_WITHOUT_TIME_ZONE, INTEGER) => true - case (TIMESTAMP_WITHOUT_TIME_ZONE, TINYINT) => true - case (TIMESTAMP_WITHOUT_TIME_ZONE, INTEGER) => true - case (TIMESTAMP_WITHOUT_TIME_ZONE, SMALLINT) => true - case (TIMESTAMP_WITHOUT_TIME_ZONE, BIGINT) => true - case (TIMESTAMP_WITHOUT_TIME_ZONE, DOUBLE) => true - case (TIMESTAMP_WITHOUT_TIME_ZONE, FLOAT) => true - - case (INTERVAL_YEAR_MONTH, INTEGER) => true - case (INTERVAL_DAY_TIME, BIGINT) => true - - case _ => false - } - - /** All the supported reinterpret types in flink-table. */ - def canReinterpret(from: LogicalType, to: LogicalType): Boolean = - (from.getTypeRoot, to.getTypeRoot) match { - case (_, _) if from == to => true - - case (DATE, INTEGER) => true - case (TIME_WITHOUT_TIME_ZONE, INTEGER) => true - case (TIMESTAMP_WITHOUT_TIME_ZONE, BIGINT) => true - case (INTEGER, DATE) => true - case (INTEGER, TIME_WITHOUT_TIME_ZONE) => true - case (BIGINT, TIMESTAMP_WITHOUT_TIME_ZONE) => true - case (INTEGER, INTERVAL_YEAR_MONTH) => true - case (BIGINT, INTERVAL_DAY_TIME) => true - case (INTERVAL_YEAR_MONTH, INTEGER) => true - case (INTERVAL_DAY_TIME, BIGINT) => true - - case (DATE, BIGINT) => true - case (TIME_WITHOUT_TIME_ZONE, BIGINT) => true - case (INTERVAL_YEAR_MONTH, BIGINT) => true - - case _ => false - } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/file/table/FileSystemTableSourceTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/file/table/FileSystemTableSourceTest.java index 3b3b71a7e10b4..358f5ed5776c0 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/file/table/FileSystemTableSourceTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/file/table/FileSystemTableSourceTest.java @@ -23,16 +23,16 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test for {@link FileSystemTableSource}. */ -public class FileSystemTableSourceTest extends TableTestBase { +class FileSystemTableSourceTest extends TableTestBase { private StreamTableTestUtil util; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); TableEnvironment tEnv = util.getTableEnv(); @@ -71,12 +71,12 @@ public void setup() { } @Test - public void testFilterPushDown() { + void testFilterPushDown() { util.verifyRelPlanInsert("insert into MySink select * from MyTable where a > 10"); } @Test - public void testMetadataReading() { + void testMetadataReading() { util.verifyRelPlanInsert( "insert into MySink(a, b, c) select a, b, filemeta from MyTableWithMeta"); } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/DynamicFilteringValuesSource.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/DynamicFilteringValuesSource.java index 0622169b8ccb9..303bf92f546a5 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/DynamicFilteringValuesSource.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/DynamicFilteringValuesSource.java @@ -52,16 +52,22 @@ public class DynamicFilteringValuesSource implements Source { private final TypeSerializer serializer; + private final TerminatingLogic terminatingLogic; + private final Boundedness boundedness; private Map, byte[]> serializedElements; private Map, Integer> counts; private final List dynamicFilteringFields; public DynamicFilteringValuesSource( + TerminatingLogic terminatingLogic, + Boundedness boundedness, Map, Collection> elements, TypeSerializer serializer, List dynamicFilteringFields) { this.serializer = serializer; this.dynamicFilteringFields = dynamicFilteringFields; + this.terminatingLogic = terminatingLogic; + this.boundedness = boundedness; serializeElements(serializer, elements); } @@ -91,7 +97,7 @@ private void serializeElements( @Override public Boundedness getBoundedness() { - return Boundedness.BOUNDED; + return boundedness; } @Override @@ -108,13 +114,15 @@ public SplitEnumerator createEnumerat serializedElements.keySet().stream() .map(ValuesSourcePartitionSplit::new) .collect(Collectors.toList()); - return new DynamicFilteringValuesSourceEnumerator(context, splits, dynamicFilteringFields); + return new DynamicFilteringValuesSourceEnumerator( + context, terminatingLogic, splits, dynamicFilteringFields); } @Override public SplitEnumerator restoreEnumerator( - SplitEnumeratorContext context, NoOpEnumState checkpoint) { - throw new UnsupportedOperationException("Unsupported now."); + SplitEnumeratorContext context, NoOpEnumState checkpoint) + throws Exception { + return createEnumerator(context); } @Override diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/DynamicFilteringValuesSourceReader.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/DynamicFilteringValuesSourceReader.java index 71e0dd05ee462..29ffa8a70a0c5 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/DynamicFilteringValuesSourceReader.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/DynamicFilteringValuesSourceReader.java @@ -64,6 +64,7 @@ public class DynamicFilteringValuesSourceReader private transient ValuesSourcePartitionSplit currentSplit; private transient Iterator iterator; private transient boolean noMoreSplits; + private transient boolean reachedInfiniteEnd; public DynamicFilteringValuesSourceReader( Map, byte[]> serializedElements, @@ -88,6 +89,10 @@ public void start() { @Override public InputStatus pollNext(ReaderOutput output) { + if (reachedInfiniteEnd) { + return InputStatus.NOTHING_AVAILABLE; + } + if (iterator != null) { if (iterator.hasNext()) { output.collect(iterator.next()); @@ -115,23 +120,32 @@ private void finishSplit() { private InputStatus tryMoveToNextSplit() { currentSplit = remainingSplits.poll(); if (currentSplit != null) { - Map partition = currentSplit.getPartition(); - List list = - deserialize(serializedElements.get(partition), counts.get(partition)); - iterator = list.iterator(); - return InputStatus.MORE_AVAILABLE; + if (currentSplit.isInfinite()) { + this.reachedInfiniteEnd = true; + resetAvailability(); + return InputStatus.NOTHING_AVAILABLE; + } else { + Map partition = currentSplit.getPartition(); + List list = + deserialize(serializedElements.get(partition), counts.get(partition)); + iterator = list.iterator(); + return InputStatus.MORE_AVAILABLE; + } } else if (noMoreSplits) { return InputStatus.END_OF_INPUT; } else { - // ensure we are not called in a loop by resetting the availability future - if (availability.isDone()) { - availability = new CompletableFuture<>(); - } - + resetAvailability(); return InputStatus.NOTHING_AVAILABLE; } } + private void resetAvailability() { + // ensure we are not called in a loop by resetting the availability future + if (availability.isDone()) { + availability = new CompletableFuture<>(); + } + } + private List deserialize(byte[] data, int count) { List list = new ArrayList<>(); try (ByteArrayInputStream bais = new ByteArrayInputStream(data)) { diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/TerminatingLogic.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/TerminatingLogic.java new file mode 100644 index 0000000000000..124a2bdff582e --- /dev/null +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/TerminatingLogic.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.connector.source; + +import org.apache.flink.table.planner.factories.TestValuesTableFactory; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Tells sources created from {@link TestValuesTableFactory} how they should behave after they + * produced all data. It is separate from 'bounded', because even if a source is unbounded it can + * stop producing records and shutdown. + */ +public enum TerminatingLogic { + INFINITE, + FINITE; + + public static TerminatingLogic readFrom(DataInputStream in) throws IOException { + final boolean isInfinite = in.readBoolean(); + return isInfinite ? TerminatingLogic.INFINITE : TerminatingLogic.FINITE; + } + + public static void writeTo(DataOutputStream out, TerminatingLogic terminatingLogic) + throws IOException { + out.writeBoolean(terminatingLogic == TerminatingLogic.INFINITE); + } +} diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/ValuesSource.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/ValuesSource.java index 6d9a3e186e256..e4b24d82e7cfe 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/ValuesSource.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/ValuesSource.java @@ -57,10 +57,19 @@ public class ValuesSource implements Source serializedElements; - public ValuesSource(Collection elements, TypeSerializer serializer) { + private final TerminatingLogic terminatingLogic; + private final Boundedness boundedness; + + public ValuesSource( + TerminatingLogic terminatingLogic, + Boundedness boundedness, + Collection elements, + TypeSerializer serializer) { Preconditions.checkState(serializer != null, "serializer not set"); this.serializedElements = serializeElements(elements, serializer); this.serializer = serializer; + this.terminatingLogic = terminatingLogic; + this.boundedness = boundedness; } private List serializeElements( @@ -82,7 +91,7 @@ private List serializeElements( @Override public Boundedness getBoundedness() { - return Boundedness.BOUNDED; + return boundedness; } @Override @@ -98,14 +107,14 @@ public SplitEnumerator createEnumerator( IntStream.range(0, serializedElements.size()) .mapToObj(ValuesSourceSplit::new) .collect(Collectors.toList()); - return new ValuesSourceEnumerator(enumContext, splits); + return new ValuesSourceEnumerator(enumContext, splits, terminatingLogic); } @Override public SplitEnumerator restoreEnumerator( SplitEnumeratorContext enumContext, NoOpEnumState checkpoint) throws Exception { - throw new UnsupportedOperationException("Unsupported now."); + return createEnumerator(enumContext); } @Override diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/ValuesSourceReader.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/ValuesSourceReader.java index 84e33e529dadf..dcaca67fd1ea6 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/ValuesSourceReader.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/ValuesSourceReader.java @@ -97,22 +97,32 @@ public void start() { public InputStatus pollNext(ReaderOutput output) throws Exception { ValuesSourceSplit currentSplit = remainingSplits.poll(); if (currentSplit != null) { - output.collect(elements.get(currentSplit.getIndex())); - // request another split - context.sendSplitRequest(); - return InputStatus.MORE_AVAILABLE; + if (currentSplit.isInfinite()) { + remainingSplits.add(currentSplit); + resetAvailability(); + return InputStatus.NOTHING_AVAILABLE; + } else { + output.collect(elements.get(currentSplit.getIndex())); + // request another split + context.sendSplitRequest(); + return InputStatus.MORE_AVAILABLE; + } } else if (noMoreSplits) { return InputStatus.END_OF_INPUT; } else { - // ensure we are not called in a loop by resetting the availability future - if (availability.isDone()) { - availability = new CompletableFuture<>(); - } + resetAvailability(); return InputStatus.NOTHING_AVAILABLE; } } + private void resetAvailability() { + // ensure we are not called in a loop by resetting the availability future + if (availability.isDone()) { + availability = new CompletableFuture<>(); + } + } + @Override public List snapshotState(long checkpointId) { return Collections.emptyList(); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/enumerator/DynamicFilteringValuesSourceEnumerator.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/enumerator/DynamicFilteringValuesSourceEnumerator.java index 83b09b0ffcc91..793f46fa19590 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/enumerator/DynamicFilteringValuesSourceEnumerator.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/enumerator/DynamicFilteringValuesSourceEnumerator.java @@ -21,6 +21,7 @@ import org.apache.flink.api.connector.source.SourceEvent; import org.apache.flink.api.connector.source.SplitEnumerator; import org.apache.flink.api.connector.source.SplitEnumeratorContext; +import org.apache.flink.connector.source.TerminatingLogic; import org.apache.flink.connector.source.split.ValuesSourcePartitionSplit; import org.apache.flink.table.connector.source.DynamicFilteringData; import org.apache.flink.table.connector.source.DynamicFilteringEvent; @@ -35,6 +36,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -47,15 +49,18 @@ public class DynamicFilteringValuesSourceEnumerator private final SplitEnumeratorContext context; private final List allSplits; private final List dynamicFilteringFields; + private final TerminatingLogic terminatingLogic; private transient boolean receivedDynamicFilteringEvent; private transient List remainingSplits; public DynamicFilteringValuesSourceEnumerator( SplitEnumeratorContext context, + TerminatingLogic terminatingLogic, List allSplits, List dynamicFilteringFields) { this.context = context; this.allSplits = allSplits; + this.terminatingLogic = terminatingLogic; this.dynamicFilteringFields = dynamicFilteringFields; } @@ -68,8 +73,15 @@ public void handleSplitRequest(int subtaskId, @Nullable String requesterHostname throw new IllegalStateException("DynamicFilteringEvent has not receive"); } if (remainingSplits.isEmpty()) { - context.signalNoMoreSplits(subtaskId); - LOG.info("No more splits available for subtask {}", subtaskId); + if (terminatingLogic == TerminatingLogic.INFINITE) { + context.assignSplit( + new ValuesSourcePartitionSplit( + Collections.emptyMap(), TerminatingLogic.INFINITE), + subtaskId); + } else { + context.signalNoMoreSplits(subtaskId); + LOG.info("No more splits available for subtask {}", subtaskId); + } } else { ValuesSourcePartitionSplit split = remainingSplits.remove(0); LOG.debug("Assigned split to subtask {} : {}", subtaskId, split); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/enumerator/ValuesSourceEnumerator.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/enumerator/ValuesSourceEnumerator.java index 4e6f2d287c85f..8d60627198ecf 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/enumerator/ValuesSourceEnumerator.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/enumerator/ValuesSourceEnumerator.java @@ -20,6 +20,7 @@ import org.apache.flink.api.connector.source.SplitEnumerator; import org.apache.flink.api.connector.source.SplitEnumeratorContext; +import org.apache.flink.connector.source.TerminatingLogic; import org.apache.flink.connector.source.ValuesSource; import org.apache.flink.connector.source.split.ValuesSourceSplit; @@ -38,12 +39,15 @@ public class ValuesSourceEnumerator implements SplitEnumerator context; private final Queue remainingSplits; + private final TerminatingLogic terminatingLogic; public ValuesSourceEnumerator( SplitEnumeratorContext context, - List remainingSplits) { + List remainingSplits, + TerminatingLogic terminatingLogic) { this.context = context; this.remainingSplits = new ArrayDeque<>(remainingSplits); + this.terminatingLogic = terminatingLogic; } @Override @@ -54,6 +58,8 @@ public void handleSplitRequest(int subtaskId, @Nullable String requesterHostname final ValuesSourceSplit nextSplit = remainingSplits.poll(); if (nextSplit != null) { context.assignSplit(nextSplit, subtaskId); + } else if (terminatingLogic == TerminatingLogic.INFINITE) { + context.assignSplit(new ValuesSourceSplit(-1, TerminatingLogic.INFINITE), subtaskId); } else { context.signalNoMoreSplits(subtaskId); } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourcePartitionSplit.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourcePartitionSplit.java index a8eab5d624134..f580a92cb3d31 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourcePartitionSplit.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourcePartitionSplit.java @@ -20,6 +20,7 @@ import org.apache.flink.api.connector.source.SourceSplit; import org.apache.flink.connector.source.DynamicFilteringValuesSource; +import org.apache.flink.connector.source.TerminatingLogic; import java.util.Map; @@ -27,15 +28,30 @@ public class ValuesSourcePartitionSplit implements SourceSplit { private final Map partition; + private final TerminatingLogic terminatingLogic; public ValuesSourcePartitionSplit(Map partition) { + this(partition, TerminatingLogic.FINITE); + } + + public ValuesSourcePartitionSplit( + Map partition, TerminatingLogic terminatingLogic) { this.partition = partition; + this.terminatingLogic = terminatingLogic; } public Map getPartition() { return partition; } + public TerminatingLogic getTerminatingLogic() { + return terminatingLogic; + } + + public boolean isInfinite() { + return terminatingLogic == TerminatingLogic.INFINITE; + } + @Override public String splitId() { return partition.toString(); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourcePartitionSplitSerializer.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourcePartitionSplitSerializer.java index 416c5268e889a..e79d10a503ee6 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourcePartitionSplitSerializer.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourcePartitionSplitSerializer.java @@ -19,6 +19,7 @@ package org.apache.flink.connector.source.split; import org.apache.flink.connector.source.DynamicFilteringValuesSource; +import org.apache.flink.connector.source.TerminatingLogic; import org.apache.flink.core.io.SimpleVersionedSerializer; import org.apache.flink.util.Preconditions; @@ -50,6 +51,7 @@ public byte[] serialize(ValuesSourcePartitionSplit split) throws IOException { out.writeUTF(entry.getKey()); out.writeUTF(entry.getValue()); } + TerminatingLogic.writeTo(out, split.getTerminatingLogic()); out.flush(); return baos.toByteArray(); } @@ -68,7 +70,9 @@ public ValuesSourcePartitionSplit deserialize(int version, byte[] serialized) String value = in.readUTF(); partition.put(key, value); } - ValuesSourcePartitionSplit split = new ValuesSourcePartitionSplit(partition); + final TerminatingLogic terminatingLogic = TerminatingLogic.readFrom(in); + ValuesSourcePartitionSplit split = + new ValuesSourcePartitionSplit(partition, terminatingLogic); Preconditions.checkArgument(split.splitId().equals(splitId)); return split; } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourceSplit.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourceSplit.java index 87d4b9bce9e1b..31f6b431e4d6a 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourceSplit.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourceSplit.java @@ -19,15 +19,30 @@ package org.apache.flink.connector.source.split; import org.apache.flink.api.connector.source.SourceSplit; +import org.apache.flink.connector.source.TerminatingLogic; import org.apache.flink.connector.source.ValuesSource; /** The split of the {@link ValuesSource}. */ public class ValuesSourceSplit implements SourceSplit { private final int index; + private final TerminatingLogic terminatingLogic; public ValuesSourceSplit(int index) { + this(index, TerminatingLogic.FINITE); + } + + public ValuesSourceSplit(int index, TerminatingLogic terminatingLogic) { this.index = index; + this.terminatingLogic = terminatingLogic; + } + + public TerminatingLogic getTerminatingLogic() { + return terminatingLogic; + } + + public boolean isInfinite() { + return terminatingLogic == TerminatingLogic.INFINITE; } public int getIndex() { diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourceSplitSerializer.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourceSplitSerializer.java index 697162e18b49a..a52f4595497c9 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourceSplitSerializer.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/connector/source/split/ValuesSourceSplitSerializer.java @@ -18,6 +18,7 @@ package org.apache.flink.connector.source.split; +import org.apache.flink.connector.source.TerminatingLogic; import org.apache.flink.connector.source.ValuesSource; import org.apache.flink.core.io.SimpleVersionedSerializer; @@ -39,6 +40,7 @@ public byte[] serialize(ValuesSourceSplit split) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(baos)) { out.writeInt(split.getIndex()); + TerminatingLogic.writeTo(out, split.getTerminatingLogic()); out.flush(); return baos.toByteArray(); } @@ -49,7 +51,8 @@ public ValuesSourceSplit deserialize(int version, byte[] serialized) throws IOEx try (ByteArrayInputStream bais = new ByteArrayInputStream(serialized); DataInputStream in = new DataInputStream(bais)) { int index = in.readInt(); - return new ValuesSourceSplit(index); + final TerminatingLogic terminatingLogic = TerminatingLogic.readFrom(in); + return new ValuesSourceSplit(index, terminatingLogic); } } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/api/batch/sql/validation/MatchRecognizeValidationTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/api/batch/sql/validation/MatchRecognizeValidationTest.java index cb40481db8d46..c0ca9fbf975c7 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/api/batch/sql/validation/MatchRecognizeValidationTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/api/batch/sql/validation/MatchRecognizeValidationTest.java @@ -26,40 +26,40 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctions.PythonScalarFunction; import org.apache.flink.table.planner.utils.TableTestBase; import org.apache.flink.table.planner.utils.TableTestUtil; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameter; +import org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameters; import org.apache.calcite.sql.SqlMatchRecognize; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; import java.util.Arrays; import java.util.Collection; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + /** Validation test for {@link SqlMatchRecognize}. */ -@RunWith(Parameterized.class) -public class MatchRecognizeValidationTest extends TableTestBase { +@ExtendWith(ParameterizedTestExtension.class) +class MatchRecognizeValidationTest extends TableTestBase { private static final String STREAM = "stream"; private static final String BATCH = "batch"; - @Parameterized.Parameter public String mode; + @Parameter private String mode; - @Parameterized.Parameters(name = "mode = {0}") - public static Collection parameters() { + @Parameters(name = "mode = {0}") + private static Collection parameters() { return Arrays.asList(STREAM, BATCH); } - @Rule public ExpectedException expectedException = ExpectedException.none(); - private TableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = STREAM.equals(mode) ? streamTestUtil(TableConfig.getDefault()) @@ -86,32 +86,31 @@ public void setup() { + ")"); } - @After - public void after() { + @AfterEach + void after() { util.getTableEnv().executeSql("DROP TABLE Ticker"); util.getTableEnv().executeSql("DROP TABLE MyTable"); } /** Function 'MATCH_ROWTIME()' can only be used in MATCH_RECOGNIZE. */ - @Test(expected = ValidationException.class) - public void testMatchRowTimeInSelect() { + @TestTemplate + void testMatchRowTimeInSelect() { String sql = "SELECT MATCH_ROWTIME() FROM MyTable"; - util.verifyExplain(sql); + assertThatExceptionOfType(ValidationException.class) + .isThrownBy(() -> util.verifyExplain(sql)); } /** Function 'MATCH_PROCTIME()' can only be used in MATCH_RECOGNIZE. */ - @Test(expected = ValidationException.class) - public void testMatchProcTimeInSelect() { + @TestTemplate + void testMatchProcTimeInSelect() { String sql = "SELECT MATCH_PROCTIME() FROM MyTable"; - util.verifyExplain(sql); + assertThatExceptionOfType(ValidationException.class) + .isThrownBy(() -> util.verifyExplain(sql)); } - @Test - public void testSortProcessingTimeDesc() { + @TestTemplate + void testSortProcessingTimeDesc() { if (STREAM.equals(mode)) { - expectedException.expect(TableException.class); - expectedException.expectMessage( - "Primary sort order of a streaming table must be ascending on time."); String sqlQuery = "SELECT *\n" + "FROM Ticker\n" @@ -123,16 +122,16 @@ public void testSortProcessingTimeDesc() { + " DEFINE\n" + " A AS A.symbol = 'a'\n" + ") AS T"; - tEnv.executeSql(sqlQuery); + assertThatExceptionOfType(TableException.class) + .isThrownBy(() -> tEnv.executeSql(sqlQuery)) + .withMessageContaining( + "Primary sort order of a streaming table must be ascending on time."); } } - @Test - public void testSortProcessingTimeSecondaryField() { + @TestTemplate + void testSortProcessingTimeSecondaryField() { if (STREAM.equals(mode)) { - expectedException.expect(TableException.class); - expectedException.expectMessage( - "You must specify either rowtime or proctime for order by as the first one."); String sqlQuery = "SELECT *\n" + "FROM Ticker\n" @@ -144,16 +143,16 @@ public void testSortProcessingTimeSecondaryField() { + " DEFINE\n" + " A AS A.symbol = 'a'\n" + ") AS T"; - tEnv.executeSql(sqlQuery); + assertThatExceptionOfType(TableException.class) + .isThrownBy(() -> tEnv.executeSql(sqlQuery)) + .withMessageContaining( + "You must specify either rowtime or proctime for order by as the first one."); } } - @Test - public void testSortNoOrder() { + @TestTemplate + void testSortNoOrder() { if (STREAM.equals(mode)) { - expectedException.expect(TableException.class); - expectedException.expectMessage( - "You must specify either rowtime or proctime for order by."); String sqlQuery = "SELECT *\n" + "FROM Ticker\n" @@ -164,16 +163,16 @@ public void testSortNoOrder() { + " DEFINE\n" + " A AS A.symbol = 'a'\n" + ") AS T"; - tEnv.executeSql(sqlQuery); + assertThatExceptionOfType(TableException.class) + .isThrownBy(() -> tEnv.executeSql(sqlQuery)) + .withMessageContaining( + "You must specify either rowtime or proctime for order by."); } } - @Test - public void testUpdatesInUpstreamOperatorNotSupported() { + @TestTemplate + void testUpdatesInUpstreamOperatorNotSupported() { if (STREAM.equals(mode)) { - expectedException.expect(TableException.class); - expectedException.expectMessage( - "Match Recognize doesn't support consuming update changes which is produced by node GroupAggregate("); String sqlQuery = "SELECT *\n" + "FROM (SELECT DISTINCT * FROM Ticker)\n" @@ -186,14 +185,15 @@ public void testUpdatesInUpstreamOperatorNotSupported() { + " DEFINE\n" + " A AS A.symbol = 'a'\n" + ") AS T"; - tEnv.executeSql(sqlQuery); + assertThatExceptionOfType(TableException.class) + .isThrownBy(() -> tEnv.executeSql(sqlQuery)) + .withMessageContaining( + "Match Recognize doesn't support consuming update changes which is produced by node GroupAggregate("); } } - @Test - public void testAggregatesOnMultiplePatternVariablesNotSupported() { - expectedException.expect(ValidationException.class); - expectedException.expectMessage("SQL validation failed."); + @TestTemplate + void testAggregatesOnMultiplePatternVariablesNotSupported() { String sqlQuery = "SELECT *\n" + "FROM Ticker\n" @@ -205,13 +205,13 @@ public void testAggregatesOnMultiplePatternVariablesNotSupported() { + " DEFINE\n" + " A AS A.symbol = 'a'\n" + ") AS T"; - tEnv.executeSql(sqlQuery); + assertThatExceptionOfType(ValidationException.class) + .isThrownBy(() -> tEnv.executeSql(sqlQuery)) + .withMessageContaining("SQL validation failed."); } - @Test - public void testAggregatesOnMultiplePatternVariablesNotSupportedInUDAGs() { - expectedException.expect(ValidationException.class); - expectedException.expectMessage("Aggregation must be applied to a single pattern variable"); + @TestTemplate + void testAggregatesOnMultiplePatternVariablesNotSupportedInUDAGs() { util.addTemporarySystemFunction("weightedAvg", new WeightedAvg()); String sqlQuery = "SELECT *\n" @@ -224,13 +224,13 @@ public void testAggregatesOnMultiplePatternVariablesNotSupportedInUDAGs() { + " DEFINE\n" + " A AS A.symbol = 'a'\n" + ") AS T"; - tEnv.executeSql(sqlQuery); + assertThatExceptionOfType(ValidationException.class) + .isThrownBy(() -> tEnv.executeSql(sqlQuery)) + .withMessageContaining("Aggregation must be applied to a single pattern variable"); } - @Test - public void testValidatingAmbiguousColumns() { - expectedException.expect(ValidationException.class); - expectedException.expectMessage("Columns ambiguously defined: {symbol, price}"); + @TestTemplate + void testValidatingAmbiguousColumns() { String sqlQuery = "SELECT *\n" + "FROM Ticker\n" @@ -244,7 +244,9 @@ public void testValidatingAmbiguousColumns() { + " DEFINE\n" + " A AS A.symbol = 'a'\n" + ") AS T"; - tEnv.executeSql(sqlQuery); + assertThatExceptionOfType(ValidationException.class) + .isThrownBy(() -> tEnv.executeSql(sqlQuery)) + .withMessageContaining("Columns ambiguously defined: {symbol, price}"); } // *************************************************************************************** @@ -253,11 +255,8 @@ public void testValidatingAmbiguousColumns() { // *************************************************************************************** /** Python Function can not be used in MATCH_RECOGNIZE for now. */ - @Test - public void testMatchPythonFunction() { - expectedException.expect(TableException.class); - expectedException.expectMessage( - "Python Function can not be used in MATCH_RECOGNIZE for now."); + @TestTemplate + void testMatchPythonFunction() { util.addTemporarySystemFunction("pyFunc", new PythonScalarFunction("pyFunc")); String sql = "SELECT T.aa as ta\n" @@ -272,13 +271,14 @@ public void testMatchPythonFunction() { + " A AS a = 1,\n" + " B AS b = 'b'\n" + ") AS T"; - util.verifyExplain(sql); + assertThatExceptionOfType(TableException.class) + .isThrownBy(() -> tEnv.executeSql(sql)) + .withMessageContaining( + "Python Function can not be used in MATCH_RECOGNIZE for now."); } - @Test - public void testAllRowsPerMatch() { - expectedException.expect(TableException.class); - expectedException.expectMessage("All rows per match mode is not supported yet."); + @TestTemplate + void testAllRowsPerMatch() { String sqlQuery = "SELECT *\n" + "FROM Ticker\n" @@ -291,15 +291,13 @@ public void testAllRowsPerMatch() { + " DEFINE\n" + " A AS A.symbol = 'a'\n" + ") AS T"; - tEnv.executeSql(sqlQuery); + assertThatExceptionOfType(TableException.class) + .isThrownBy(() -> tEnv.executeSql(sqlQuery)) + .withMessageContaining("All rows per match mode is not supported yet."); } - @Test - public void testGreedyQuantifierAtTheEndIsNotSupported() { - expectedException.expect(TableException.class); - expectedException.expectMessage( - "Greedy quantifiers are not allowed as the last element of a " - + "Pattern yet. Finish your pattern with either a simple variable or reluctant quantifier."); + @TestTemplate + void testGreedyQuantifierAtTheEndIsNotSupported() { String sqlQuery = "SELECT *\n" + "FROM Ticker\n" @@ -311,15 +309,15 @@ public void testGreedyQuantifierAtTheEndIsNotSupported() { + " DEFINE\n" + " A AS A.symbol = 'a'\n" + ") AS T"; - tEnv.executeSql(sqlQuery); + assertThatExceptionOfType(TableException.class) + .isThrownBy(() -> tEnv.executeSql(sqlQuery)) + .withMessageContaining( + "Greedy quantifiers are not allowed as the last element of a " + + "Pattern yet. Finish your pattern with either a simple variable or reluctant quantifier."); } - @Test - public void testPatternsProducingEmptyMatchesAreNotSupported() { - expectedException.expect(TableException.class); - expectedException.expectMessage( - "Patterns that can produce empty matches are not supported. " - + "There must be at least one non-optional state."); + @TestTemplate + void testPatternsProducingEmptyMatchesAreNotSupported() { String sqlQuery = "SELECT *\n" + "FROM Ticker\n" @@ -331,13 +329,15 @@ public void testPatternsProducingEmptyMatchesAreNotSupported() { + " DEFINE\n" + " A AS A.symbol = 'a'\n" + ") AS T"; - tEnv.executeSql(sqlQuery); + assertThatExceptionOfType(TableException.class) + .isThrownBy(() -> tEnv.executeSql(sqlQuery)) + .withMessageContaining( + "Patterns that can produce empty matches are not supported. " + + "There must be at least one non-optional state."); } - @Test - public void testDistinctAggregationsNotSupported() { - expectedException.expect(ValidationException.class); - expectedException.expectMessage("SQL validation failed."); + @TestTemplate + void testDistinctAggregationsNotSupported() { String sqlQuery = "SELECT *\n" + "FROM Ticker\n" @@ -349,6 +349,8 @@ public void testDistinctAggregationsNotSupported() { + " DEFINE\n" + " A AS A.symbol = 'a'\n" + ") AS T"; - tEnv.executeSql(sqlQuery); + assertThatExceptionOfType(ValidationException.class) + .isThrownBy(() -> tEnv.executeSql(sqlQuery)) + .withMessageContaining("SQL validation failed."); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearJoinHintWithCapitalizeJoinHintShuttleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearJoinHintWithCapitalizeJoinHintShuttleTest.java index 210cc122c7d23..86b0157502ad6 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearJoinHintWithCapitalizeJoinHintShuttleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearJoinHintWithCapitalizeJoinHintShuttleTest.java @@ -31,13 +31,13 @@ import org.apache.calcite.rel.hint.RelHint; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.type.SqlTypeName; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.Collections; /** Tests clearing lookup join hint case-insensitive in stream. */ -public class ClearJoinHintWithCapitalizeJoinHintShuttleTest +class ClearJoinHintWithCapitalizeJoinHintShuttleTest extends ClearJoinHintWithInvalidPropagationShuttleTestBase { @Override @@ -50,8 +50,8 @@ boolean isBatchMode() { return false; } - @Before - public void before() throws Exception { + @BeforeEach + void before() throws Exception { super.before(); util.tableEnv() @@ -72,7 +72,7 @@ public void before() throws Exception { } @Test - public void testClearCaseInsensitiveLookupHint() { + void testClearCaseInsensitiveLookupHint() { // SELECT /*+ lookUp('table'='lookup', 'retry-predicate'='lookup_miss', // 'retry-strategy'='fixed_delay', 'fixed-delay'='155 ms', 'max-attempts'='10', // 'async'='true', 'output-mode'='allow_unordered','capacity'='1000', 'time-out'='300 s') @@ -130,7 +130,7 @@ public void testClearCaseInsensitiveLookupHint() { } @Override - public void verifyRelPlan(RelNode node) { + protected void verifyRelPlan(RelNode node) { String plan = buildRelPlanWithQueryBlockAlias(node); util.assertEqualsOrExpand("beforePropagatingHints", plan, true); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearJoinHintWithInvalidPropagationShuttleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearJoinHintWithInvalidPropagationShuttleTest.java index 6801c602539d2..d4ebef36706cd 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearJoinHintWithInvalidPropagationShuttleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearJoinHintWithInvalidPropagationShuttleTest.java @@ -26,10 +26,10 @@ import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.JoinRelType; import org.apache.calcite.rel.hint.RelHint; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** Tests clearing join hint with invalid propagation in batch. */ -public class ClearJoinHintWithInvalidPropagationShuttleTest +class ClearJoinHintWithInvalidPropagationShuttleTest extends ClearJoinHintWithInvalidPropagationShuttleTestBase { @Override TableTestUtil getTableTestUtil() { @@ -42,7 +42,7 @@ boolean isBatchMode() { } @Test - public void testNoNeedToClearJoinHint() { + void testNoNeedToClearJoinHint() { // SELECT /*+ BROADCAST(t1)*/t1.a FROM t1 JOIN t2 ON t1.a = t2.a RelHint joinHintInView = RelHint.builder(JoinStrategy.BROADCAST.getJoinHintName()).hintOption("t1").build(); @@ -60,7 +60,7 @@ public void testNoNeedToClearJoinHint() { } @Test - public void testClearJoinHintWithInvalidPropagationToViewWhileViewHasJoinHints() { + void testClearJoinHintWithInvalidPropagationToViewWhileViewHasJoinHints() { // SELECT /*+ BROADCAST(t3)*/t4.a FROM ( // SELECT /*+ BROADCAST(t1)*/t1.a FROM t1 JOIN t2 ON t1.a = t2.a // ) t4 JOIN t3 ON t4.a = t3.a @@ -91,7 +91,7 @@ public void testClearJoinHintWithInvalidPropagationToViewWhileViewHasJoinHints() } @Test - public void testClearJoinHintWithInvalidPropagationToViewWhileViewHasNoJoinHints() { + void testClearJoinHintWithInvalidPropagationToViewWhileViewHasNoJoinHints() { // SELECT /*+ BROADCAST(t3)*/t4.a FROM ( // SELECT t1.a FROM t1 JOIN t2 ON t1.a = t2.a // ) t4 JOIN t3 ON t4.a = t3.a @@ -119,7 +119,7 @@ public void testClearJoinHintWithInvalidPropagationToViewWhileViewHasNoJoinHints } @Test - public void testClearJoinHintWithoutPropagatingToView() { + void testClearJoinHintWithoutPropagatingToView() { // SELECT /*+ BROADCAST(t1)*/t4.a FROM ( // SELECT t1.a FROM t1 JOIN t2 ON t1.a = t2.a // ) t4 JOIN t3 ON t4.a = t3.a diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearJoinHintWithInvalidPropagationShuttleTestBase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearJoinHintWithInvalidPropagationShuttleTestBase.java index 105f4e42a38d3..745becc26ff81 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearJoinHintWithInvalidPropagationShuttleTestBase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearJoinHintWithInvalidPropagationShuttleTestBase.java @@ -34,12 +34,12 @@ import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.rel.RelNode; import org.apache.calcite.sql.SqlExplainLevel; -import org.junit.Before; +import org.junit.jupiter.api.BeforeEach; import java.util.Collections; /** A base class for testing clearing join hint with invalid propagation. */ -public abstract class ClearJoinHintWithInvalidPropagationShuttleTestBase extends TableTestBase { +abstract class ClearJoinHintWithInvalidPropagationShuttleTestBase extends TableTestBase { protected final TableTestUtil util = getTableTestUtil(); @@ -67,8 +67,8 @@ public abstract class ClearJoinHintWithInvalidPropagationShuttleTestBase extends .build(); protected final FlinkRelBuilder builder = plannerMocks.getPlannerContext().createRelBuilder(); - @Before - public void before() throws Exception { + @BeforeEach + void before() throws Exception { util.tableEnv().registerCatalog("testCatalog", catalog); util.tableEnv().executeSql("use catalog testCatalog"); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearLookupJoinHintWithInvalidPropagationShuttleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearLookupJoinHintWithInvalidPropagationShuttleTest.java index 13c8f7e6eb54a..0e89a4aa2b285 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearLookupJoinHintWithInvalidPropagationShuttleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/alias/ClearLookupJoinHintWithInvalidPropagationShuttleTest.java @@ -38,13 +38,13 @@ import org.apache.calcite.sql.fun.SqlCollectionTableOperator; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.validate.SqlModality; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.Collections; /** Tests clearing lookup join hint with invalid propagation in stream. */ -public class ClearLookupJoinHintWithInvalidPropagationShuttleTest +class ClearLookupJoinHintWithInvalidPropagationShuttleTest extends ClearJoinHintWithInvalidPropagationShuttleTestBase { @Override TableTestUtil getTableTestUtil() { @@ -56,8 +56,8 @@ boolean isBatchMode() { return false; } - @Before - public void before() throws Exception { + @BeforeEach + void before() throws Exception { super.before(); util.tableEnv() @@ -84,7 +84,7 @@ public void before() throws Exception { } @Test - public void testNoNeedToClearLookupHint() { + void testNoNeedToClearLookupHint() { // SELECT /*+ LOOKUP('table'='lookup', 'retry-predicate'='lookup_miss', // 'retry-strategy'='fixed_delay', 'fixed-delay'='155 ms', 'max-attempts'='10', // 'async'='true', 'output-mode'='allow_unordered','capacity'='1000', 'time-out'='300 s') @@ -129,7 +129,7 @@ public void testNoNeedToClearLookupHint() { } @Test - public void testClearLookupHintWithInvalidPropagationToSubQuery() { + void testClearLookupHintWithInvalidPropagationToSubQuery() { // SELECT /*+ LOOKUP('table'='src', 'retry-predicate'='lookup_miss', // 'retry-strategy'='fixed_delay', 'fixed-delay'='155 ms', 'max-attempts'='10', // 'async'='true', 'output-mode'='allow_unordered','capacity'='1000', 'time-out'='300 s') @@ -187,7 +187,7 @@ public void testClearLookupHintWithInvalidPropagationToSubQuery() { } @Test - public void testNoNeedToClearLookupHintWhileJoinWithUnnest() { + void testNoNeedToClearLookupHintWhileJoinWithUnnest() { // SELECT /*+ LOOKUP('table'='d', 'retry-predicate'='lookup_miss', // 'retry-strategy'='fixed_delay', 'fixed-delay'='155 ms', 'max-attempts'='10', // 'async'='true', 'output-mode'='allow_unordered','capacity'='1000', 'time-out'='300 s') @@ -224,7 +224,7 @@ public void testNoNeedToClearLookupHintWhileJoinWithUnnest() { } @Test - public void testNoNeedToClearLookupHintWhileJoinWithUDTF() { + void testNoNeedToClearLookupHintWhileJoinWithUDTF() { // SELECT /*+ LOOKUP('table'='d', 'retry-predicate'='lookup_miss', // 'retry-strategy'='fixed_delay', 'fixed-delay'='155 ms', 'max-attempts'='10', // 'async'='true', 'output-mode'='allow_unordered','capacity'='1000', 'time-out'='300 s') diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/analyze/GroupAggregationAnalyzerTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/analyze/GroupAggregationAnalyzerTest.java index 6dc02bbc75839..1c48e72300028 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/analyze/GroupAggregationAnalyzerTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/analyze/GroupAggregationAnalyzerTest.java @@ -28,11 +28,13 @@ import org.apache.flink.table.planner.utils.PlanKind; import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameter; +import org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameters; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; import java.time.Duration; @@ -41,21 +43,21 @@ import static scala.runtime.BoxedUnit.UNIT; /** Test for {@link GroupAggregationAnalyzer}. */ -@RunWith(Parameterized.class) -public class GroupAggregationAnalyzerTest extends TableTestBase { +@ExtendWith(ParameterizedTestExtension.class) +class GroupAggregationAnalyzerTest extends TableTestBase { private final StreamTableTestUtil util = streamTestUtil(TableConfig.getDefault()); - @Parameterized.Parameter public boolean isMiniBatchEnabled; + @Parameter private boolean isMiniBatchEnabled; - @Parameterized.Parameter(1) - public AggregatePhaseStrategy strategy; + @Parameter(1) + private AggregatePhaseStrategy strategy; - @Parameterized.Parameter(2) - public long miniBatchLatency; + @Parameter(2) + private long miniBatchLatency; - @Parameterized.Parameter(3) - public long miniBatchSize; + @Parameter(3) + private long miniBatchSize; private final String query = "SELECT\n" @@ -66,8 +68,8 @@ public class GroupAggregationAnalyzerTest extends TableTestBase { + " MAX(c) FILTER (WHERE a > 1) AS max_c\n" + "FROM MyTable"; - @Before - public void before() { + @BeforeEach + void before() { util.getTableEnv() .getConfig() .set(ExecutionConfigOptions.TABLE_EXEC_MINIBATCH_ENABLED, isMiniBatchEnabled) @@ -99,8 +101,8 @@ public void before() { + " 'sink-insert-only' = 'false')"); } - @Test - public void testSelect() { + @TestTemplate + void testSelect() { util.doVerifyPlan( query, new ExplainDetail[] {ExplainDetail.PLAN_ADVICE}, @@ -109,8 +111,8 @@ public void testSelect() { false); } - @Test - public void testInsertInto() { + @TestTemplate + void testInsertInto() { util.doVerifyPlanInsert( String.format("INSERT INTO MySink\n%s", query), new ExplainDetail[] {ExplainDetail.PLAN_ADVICE}, @@ -118,8 +120,8 @@ public void testInsertInto() { new Enumeration.Value[] {PlanKind.OPT_REL_WITH_ADVICE()}); } - @Test - public void testStatementSet() { + @TestTemplate + void testStatementSet() { StatementSet stmtSet = util.getTableEnv().createStatementSet(); util.getTableEnv().executeSql("CREATE TABLE MySink2 LIKE MySink"); util.getTableEnv() @@ -151,8 +153,8 @@ public void testStatementSet() { false); } - @Test - public void testSubplanReuse() { + @TestTemplate + void testSubplanReuse() { util.doVerifyPlan( "WITH r AS (SELECT c, SUM(a) a, SUM(b) b FROM MyTable GROUP BY c)\n" + "SELECT * FROM r r1, r r2 WHERE r1.a = CAST(r2.b AS BIGINT) AND r2.a > 1", @@ -162,8 +164,8 @@ public void testSubplanReuse() { false); } - @Test - public void testUserDefinedAggCalls() { + @TestTemplate + void testUserDefinedAggCalls() { StatementSet stmtSet = util.getTableEnv().createStatementSet(); util.addTemporarySystemFunction( "weightedAvg", JavaUserDefinedAggFunctions.WeightedAvgWithMerge.class); @@ -199,9 +201,9 @@ public void testUserDefinedAggCalls() { false); } - @Parameterized.Parameters( + @Parameters( name = "isMiniBatchEnabled={0}, strategy={1}, miniBatchLatency={2}, miniBatchSize={3}") - public static Object[][] data() { + private static Object[][] data() { return new Object[][] { new Object[] {true, AggregatePhaseStrategy.ONE_PHASE, 10L, 5L}, new Object[] {true, AggregatePhaseStrategy.AUTO, 10L, 5L}, diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/analyze/NonDeterministicUpdateAnalyzerTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/analyze/NonDeterministicUpdateAnalyzerTest.java index 7ee9cc1270069..5c2ac6316f161 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/analyze/NonDeterministicUpdateAnalyzerTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/analyze/NonDeterministicUpdateAnalyzerTest.java @@ -26,20 +26,20 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import scala.Enumeration; import static scala.runtime.BoxedUnit.UNIT; /** Test for {@link NonDeterministicUpdateAnalyzer}. */ -public class NonDeterministicUpdateAnalyzerTest extends TableTestBase { +class NonDeterministicUpdateAnalyzerTest extends TableTestBase { private final StreamTableTestUtil util = streamTestUtil(TableConfig.getDefault()); - @Before - public void before() { + @BeforeEach + void before() { util.getTableEnv() .executeSql( "create temporary table cdc (\n" @@ -104,7 +104,7 @@ public void before() { } @Test - public void testCdcWithMetaRenameSinkWithCompositePk() { + void testCdcWithMetaRenameSinkWithCompositePk() { // from NonDeterministicDagTest#testCdcWithMetaRenameSinkWithCompositePk util.getTableEnv() .executeSql( @@ -142,7 +142,7 @@ public void testCdcWithMetaRenameSinkWithCompositePk() { } @Test - public void testSourceWithComputedColumnSinkWithPk() { + void testSourceWithComputedColumnSinkWithPk() { // from NonDeterministicDagTest#testSourceWithComputedColumnSinkWithPk util.getTableEnv() .executeSql( @@ -168,7 +168,7 @@ public void testSourceWithComputedColumnSinkWithPk() { } @Test - public void testCdcJoinDimWithPkNonDeterministicLocalCondition() { + void testCdcJoinDimWithPkNonDeterministicLocalCondition() { // from NonDeterministicDagTest#testCdcJoinDimWithPkNonDeterministicLocalCondition util.doVerifyPlanInsert( "insert into sink_without_pk\n" @@ -183,7 +183,7 @@ public void testCdcJoinDimWithPkNonDeterministicLocalCondition() { } @Test - public void testCdcWithMetaSinkWithPk() { + void testCdcWithMetaSinkWithPk() { // from NonDeterministicDagTest#testCdcWithMetaSinkWithPk util.doVerifyPlanInsert( "insert into sink_with_pk\n" + "select a, metadata_3, c\n" + "from cdc_with_meta", @@ -193,7 +193,7 @@ public void testCdcWithMetaSinkWithPk() { } @Test - public void testGroupByNonDeterministicFuncWithCdcSource() { + void testGroupByNonDeterministicFuncWithCdcSource() { // from NonDeterministicDagTest#testGroupByNonDeterministicFuncWithCdcSource util.doVerifyPlanInsert( "insert into sink_with_pk\n" @@ -209,7 +209,7 @@ public void testGroupByNonDeterministicFuncWithCdcSource() { } @Test - public void testMultiSinkOnJoinedView() { + void testMultiSinkOnJoinedView() { // from NonDeterministicDagTest#testMultiSinkOnJoinedView util.getTableEnv() .executeSql( @@ -293,7 +293,7 @@ public void testMultiSinkOnJoinedView() { } @Test - public void testCdcJoinDimWithPkOutputNoPkSinkWithoutPk() { + void testCdcJoinDimWithPkOutputNoPkSinkWithoutPk() { // from NonDeterministicDagTest#testCdcJoinDimWithPkOutputNoPkSinkWithoutPk util.doVerifyPlanInsert( "insert into sink_without_pk\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/catalog/JavaCatalogTableTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/catalog/JavaCatalogTableTest.java index 1bec0f771eb2d..fbb5d8e89f559 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/catalog/JavaCatalogTableTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/catalog/JavaCatalogTableTest.java @@ -30,10 +30,12 @@ import org.apache.flink.table.catalog.ObjectPath; import org.apache.flink.table.planner.utils.TableTestBase; import org.apache.flink.table.planner.utils.TableTestUtil; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameter; +import org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameters; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; import java.util.Arrays; import java.util.Collection; @@ -49,14 +51,14 @@ /** * Tests for resolving types of computed columns (including time attributes) of tables from catalog. */ -@RunWith(Parameterized.class) -public class JavaCatalogTableTest extends TableTestBase { - @Parameterized.Parameters(name = "streamingMode = {0}") - public static Collection parameters() { +@ExtendWith(ParameterizedTestExtension.class) +class JavaCatalogTableTest extends TableTestBase { + @Parameters(name = "streamingMode = {0}") + private static Collection parameters() { return Arrays.asList(true, false); } - @Parameterized.Parameter public boolean isStreamingMode; + @Parameter private boolean isStreamingMode; private TableTestUtil getTestUtil() { if (isStreamingMode) { @@ -66,8 +68,8 @@ private TableTestUtil getTestUtil() { } } - @Test - public void testResolvingSchemaOfCustomCatalogTableSql() throws Exception { + @TestTemplate + void testResolvingSchemaOfCustomCatalogTableSql() throws Exception { TableTestUtil testUtil = getTestUtil(); TableEnvironment tableEnvironment = testUtil.getTableEnv(); GenericInMemoryCatalog genericInMemoryCatalog = new GenericInMemoryCatalog("in-memory"); @@ -83,8 +85,8 @@ public void testResolvingSchemaOfCustomCatalogTableSql() throws Exception { "SELECT COUNT(*) FROM testTable2 GROUP BY TUMBLE(rowtime, INTERVAL '10' MINUTE)"); } - @Test - public void testResolvingSchemaOfCustomCatalogTableTableApi() throws Exception { + @TestTemplate + void testResolvingSchemaOfCustomCatalogTableTableApi() throws Exception { TableTestUtil testUtil = getTestUtil(); TableEnvironment tableEnvironment = testUtil.getTableEnv(); GenericInMemoryCatalog genericInMemoryCatalog = new GenericInMemoryCatalog("in-memory"); @@ -103,8 +105,8 @@ public void testResolvingSchemaOfCustomCatalogTableTableApi() throws Exception { testUtil.verifyExecPlan(table); } - @Test - public void testResolvingProctimeOfCustomTableSql() throws Exception { + @TestTemplate + void testResolvingProctimeOfCustomTableSql() throws Exception { if (!isStreamingMode) { // proctime not supported in batch return; @@ -123,8 +125,8 @@ public void testResolvingProctimeOfCustomTableSql() throws Exception { + "GROUP BY TUMBLE(proctime, INTERVAL '10' MINUTE)"); } - @Test - public void testResolvingProctimeOfCustomTableTableApi() throws Exception { + @TestTemplate + void testResolvingProctimeOfCustomTableTableApi() throws Exception { if (!isStreamingMode) { // proctime not supported in batch return; diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/factories/TestValuesRuntimeFunctions.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/factories/TestValuesRuntimeFunctions.java index a381d573c41d5..fa79a9fed1f39 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/factories/TestValuesRuntimeFunctions.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/factories/TestValuesRuntimeFunctions.java @@ -25,7 +25,6 @@ import org.apache.flink.api.common.io.RichOutputFormat; import org.apache.flink.api.common.state.ListState; import org.apache.flink.api.common.state.ListStateDescriptor; -import org.apache.flink.api.common.typeinfo.Types; import org.apache.flink.api.common.typeutils.TypeSerializer; import org.apache.flink.configuration.Configuration; import org.apache.flink.core.memory.DataInputView; @@ -36,6 +35,7 @@ import org.apache.flink.streaming.api.checkpoint.CheckpointedFunction; import org.apache.flink.streaming.api.functions.sink.RichSinkFunction; import org.apache.flink.streaming.api.functions.source.SourceFunction; +import org.apache.flink.table.connector.RuntimeConverter; import org.apache.flink.table.connector.sink.DynamicTableSink.DataStructureConverter; import org.apache.flink.table.connector.source.LookupTableSource; import org.apache.flink.table.data.GenericRowData; @@ -46,13 +46,14 @@ import org.apache.flink.table.functions.LookupFunction; import org.apache.flink.table.runtime.generated.GeneratedProjection; import org.apache.flink.table.runtime.generated.Projection; +import org.apache.flink.table.runtime.typeutils.ExternalSerializer; import org.apache.flink.table.runtime.typeutils.InternalSerializers; +import org.apache.flink.table.types.DataType; import org.apache.flink.table.types.logical.RowType; import org.apache.flink.test.util.SuccessException; import org.apache.flink.types.Row; import org.apache.flink.types.RowKind; import org.apache.flink.types.RowUtils; -import org.apache.flink.util.StringUtils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -70,6 +71,8 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; import static org.apache.flink.table.planner.factories.TestValuesTableFactory.RESOURCE_COUNTER; import static org.apache.flink.util.Preconditions.checkArgument; @@ -82,29 +85,44 @@ final class TestValuesRuntimeFunctions { static final Object LOCK = TestValuesTableFactory.class; // [table_name, [task_id, List[value]]] - private static final Map>> globalRawResult = new HashMap<>(); + private static final Map>> globalRawResult = new HashMap<>(); // [table_name, [task_id, Map[key, value]]] - private static final Map>> globalUpsertResult = + private static final Map>> globalUpsertResult = new HashMap<>(); // [table_name, [task_id, List[value]]] - private static final Map>> globalRetractResult = - new HashMap<>(); + private static final Map>> globalRetractResult = new HashMap<>(); // [table_name, [watermark]] private static final Map> watermarkHistory = new HashMap<>(); - static List getRawResults(String tableName) { - List result = new ArrayList<>(); + // [table_name, [List[observer]] + private static final Map>>> + localRawResultsObservers = new HashMap<>(); + + static List getRawResultsAsStrings(String tableName) { + return getRawResults(tableName).stream() + .map(TestValuesRuntimeFunctions::rowToString) + .collect(Collectors.toList()); + } + + static List getRawResults(String tableName) { synchronized (LOCK) { if (globalRawResult.containsKey(tableName)) { - globalRawResult.get(tableName).values().forEach(result::addAll); + return globalRawResult.get(tableName).values().stream() + .flatMap(List::stream) + .collect(Collectors.toList()); } } - return result; + return Collections.emptyList(); } /** Returns raw results if there was only one table with results, throws otherwise. */ - static List getOnlyRawResults() { - List result = new ArrayList<>(); + static List getOnlyRawResultsAsStrings() { + return getOnlyRawResults().stream() + .map(TestValuesRuntimeFunctions::rowToString) + .collect(Collectors.toList()); + } + + static List getOnlyRawResults() { synchronized (LOCK) { if (globalRawResult.size() != 1) { throw new IllegalStateException( @@ -112,9 +130,10 @@ static List getOnlyRawResults() { + globalRawResult.size()); } - globalRawResult.values().iterator().next().values().forEach(result::addAll); + return globalRawResult.values().iterator().next().values().stream() + .flatMap(List::stream) + .collect(Collectors.toList()); } - return result; } static List getWatermarks(String tableName) { @@ -127,23 +146,30 @@ static List getWatermarks(String tableName) { } } - static List getResults(String tableName) { - List result = new ArrayList<>(); + static List getResultsAsStrings(String tableName) { + return getResults(tableName).stream().map(Row::toString).collect(Collectors.toList()); + } + + static List getResults(String tableName) { synchronized (LOCK) { if (globalUpsertResult.containsKey(tableName)) { - globalUpsertResult - .get(tableName) - .values() - .forEach(map -> result.addAll(map.values())); + return globalUpsertResult.get(tableName).values().stream() + .flatMap(map -> map.values().stream()) + .collect(Collectors.toList()); } else if (globalRetractResult.containsKey(tableName)) { - globalRetractResult.get(tableName).values().forEach(result::addAll); + return globalRetractResult.get(tableName).values().stream() + .flatMap(Collection::stream) + .collect(Collectors.toList()); } else if (globalRawResult.containsKey(tableName)) { - getRawResults(tableName).stream() - .map(s -> s.substring(3, s.length() - 1)) // removes the +I(...) wrapper - .forEach(result::add); + return getRawResults(tableName); } } - return result; + return Collections.emptyList(); + } + + static void registerLocalRawResultsObserver( + String tableName, BiConsumer> observer) { + localRawResultsObservers.computeIfAbsent(tableName, n -> new ArrayList<>()).add(observer); } static void clearResults() { @@ -155,6 +181,14 @@ static void clearResults() { } } + private static String rowToString(Row row) { + if (RowUtils.USE_LEGACY_TO_STRING) { + return String.format("%s(%s)", row.getKind().shortString(), row); + } else { + return row.toString(); + } + } + // ------------------------------------------------------------------------------------------ // Source Function implementations // ------------------------------------------------------------------------------------------ @@ -178,7 +212,7 @@ public static class FromElementSourceFunctionWithWatermark implements SourceFunc private volatile boolean isRunning = true; - private String tableName; + private final String tableName; public FromElementSourceFunctionWithWatermark( String tableName, @@ -283,22 +317,29 @@ private abstract static class AbstractExactlyOnceSink extends RichSinkFunction rawResultState; + protected transient List localRawResult; - protected transient ListState rawResultState; - protected transient List localRawResult; - - protected AbstractExactlyOnceSink(String tableName) { + protected AbstractExactlyOnceSink( + String tableName, DataType consumedDataType, DataStructureConverter converter) { this.tableName = tableName; + this.consumedDataType = consumedDataType; + this.converter = converter; } @Override public void initializeState(FunctionInitializationContext context) throws Exception { this.rawResultState = context.getOperatorStateStore() - .getListState(new ListStateDescriptor<>("sink-results", Types.STRING)); + .getListState( + new ListStateDescriptor<>( + "sink-results", + ExternalSerializer.of(consumedDataType))); this.localRawResult = new ArrayList<>(); if (context.isRestored()) { - for (String value : rawResultState.get()) { + for (Row value : rawResultState.get()) { localRawResult.add(value); } } @@ -316,27 +357,36 @@ public void snapshotState(FunctionSnapshotContext context) throws Exception { rawResultState.update(localRawResult); } } + + protected void addLocalRawResult(Row row) { + localRawResult.add(row); + Optional.ofNullable(localRawResultsObservers.get(tableName)) + .orElse(Collections.emptyList()) + .forEach( + c -> + c.accept( + getRuntimeContext().getIndexOfThisSubtask(), + localRawResult)); + } } static class AppendingSinkFunction extends AbstractExactlyOnceSink { private static final long serialVersionUID = 1L; - private final DataStructureConverter converter; private final int rowtimeIndex; protected AppendingSinkFunction( - String tableName, DataStructureConverter converter, int rowtimeIndex) { - super(tableName); - this.converter = converter; + String tableName, + DataType consumedDataType, + DataStructureConverter converter, + int rowtimeIndex) { + super(tableName, consumedDataType, converter); this.rowtimeIndex = rowtimeIndex; } @Override public void invoke(RowData value, Context context) throws Exception { - RowKind kind = value.getRowKind(); if (value.getRowKind() == RowKind.INSERT) { - Row row = (Row) converter.toExternal(value); - assertThat(row).isNotNull(); if (rowtimeIndex >= 0) { // currently, rowtime attribute always uses 3 precision TimestampData rowtime = value.getTimestamp(rowtimeIndex, 3); @@ -347,7 +397,7 @@ public void invoke(RowData value, Context context) throws Exception { } } synchronized (LOCK) { - localRawResult.add(kind.shortString() + "(" + row.toString() + ")"); + addLocalRawResult((Row) converter.toExternal(value)); } } else { throw new RuntimeException( @@ -362,25 +412,24 @@ public void invoke(RowData value, Context context) throws Exception { */ static class KeyedUpsertingSinkFunction extends AbstractExactlyOnceSink { private static final long serialVersionUID = 1L; - private final DataStructureConverter converter; private final int[] keyIndices; private final int[] targetColumnIndices; private final int expectedSize; private final int totalColumns; // [key, value] map result - private transient Map localUpsertResult; + private transient Map localUpsertResult; private transient int receivedNum; protected KeyedUpsertingSinkFunction( String tableName, + DataType consumedDataType, DataStructureConverter converter, int[] keyIndices, int[] targetColumnIndices, int expectedSize, int totalColumns) { - super(tableName); - this.converter = converter; + super(tableName, consumedDataType, converter); this.keyIndices = keyIndices; this.targetColumnIndices = targetColumnIndices; this.expectedSize = expectedSize; @@ -408,29 +457,27 @@ public void invoke(RowData value, Context context) throws Exception { assertThat(row).isNotNull(); synchronized (LOCK) { - if (RowUtils.USE_LEGACY_TO_STRING) { - localRawResult.add(kind.shortString() + "(" + row + ")"); - } else { - localRawResult.add(row.toString()); - } + addLocalRawResult(row); - row.setKind(RowKind.INSERT); Row key = Row.project(row, keyIndices); + key.setKind(RowKind.INSERT); + + final Row upsertRow = Row.copy(row); + upsertRow.setKind(RowKind.INSERT); if (kind == RowKind.INSERT || kind == RowKind.UPDATE_AFTER) { if (targetColumnIndices.length > 0) { // perform partial insert - localUpsertResult.put( - key.toString(), - updateRowValue( - localUpsertResult.get(key.toString()), - row, - targetColumnIndices)); + localUpsertResult.compute( + key, + (entryKey, currentValue) -> + updateRowValue( + currentValue, upsertRow, targetColumnIndices)); } else { - localUpsertResult.put(key.toString(), row.toString()); + localUpsertResult.put(key, upsertRow); } } else { - String oldValue = localUpsertResult.remove(key.toString()); + Row oldValue = localUpsertResult.remove(key); if (oldValue == null) { throw new RuntimeException( "Tried to delete a value that wasn't inserted first. " @@ -446,27 +493,17 @@ public void invoke(RowData value, Context context) throws Exception { } } - private String updateRowValue(String old, Row newRow, int[] targetColumnIndices) { - if (StringUtils.isNullOrWhitespaceOnly(old)) { + private Row updateRowValue(Row old, Row newRow, int[] targetColumnIndices) { + if (old == null) { // no old value, just return current - return newRow.toString(); + return newRow; } else { - String[] oldCols = - org.apache.commons.lang3.StringUtils.splitByWholeSeparatorPreserveAllTokens( - old, ", "); - assert oldCols.length == totalColumns; + assert old.getArity() == totalColumns; // exist old value, simply simulate an update - for (int i = 0; i < targetColumnIndices.length; i++) { - int idx = targetColumnIndices[i]; - if (idx == 0) { - oldCols[idx] = String.format("+I[%s", newRow.getField(idx)); - } else if (idx == totalColumns - 1) { - oldCols[idx] = String.format("%s]", newRow.getField(idx)); - } else { - oldCols[idx] = (String) newRow.getField(idx); - } + for (int idx : targetColumnIndices) { + old.setField(idx, newRow.getField(idx)); } - return String.join(", ", oldCols); + return old; } } } @@ -474,14 +511,12 @@ private String updateRowValue(String old, Row newRow, int[] targetColumnIndices) static class RetractingSinkFunction extends AbstractExactlyOnceSink { private static final long serialVersionUID = 1L; - private final DataStructureConverter converter; - - protected transient ListState retractResultState; - protected transient List localRetractResult; + protected transient ListState retractResultState; + protected transient List localRetractResult; - protected RetractingSinkFunction(String tableName, DataStructureConverter converter) { - super(tableName); - this.converter = converter; + protected RetractingSinkFunction( + String tableName, DataType consumedDataType, DataStructureConverter converter) { + super(tableName, consumedDataType, converter); } @Override @@ -491,11 +526,12 @@ public void initializeState(FunctionInitializationContext context) throws Except context.getOperatorStateStore() .getListState( new ListStateDescriptor<>( - "sink-retract-results", Types.STRING)); + "sink-retract-results", + ExternalSerializer.of(consumedDataType))); this.localRetractResult = new ArrayList<>(); if (context.isRestored()) { - for (String value : retractResultState.get()) { + for (Row value : retractResultState.get()) { localRetractResult.add(value); } } @@ -523,13 +559,13 @@ public void invoke(RowData value, Context context) throws Exception { Row row = (Row) converter.toExternal(value); assertThat(row).isNotNull(); synchronized (LOCK) { - localRawResult.add(kind.shortString() + "(" + row.toString() + ")"); + addLocalRawResult(row); + final Row retractRow = Row.copy(row); + retractRow.setKind(RowKind.INSERT); if (kind == RowKind.INSERT || kind == RowKind.UPDATE_AFTER) { - row.setKind(RowKind.INSERT); - localRetractResult.add(row.toString()); + localRetractResult.add(retractRow); } else { - row.setKind(RowKind.INSERT); - boolean contains = localRetractResult.remove(row.toString()); + boolean contains = localRetractResult.remove(retractRow); if (!contains) { throw new RuntimeException( "Tried to retract a value that wasn't inserted first. " @@ -546,7 +582,7 @@ static class AppendingOutputFormat extends RichOutputFormat { private final String tableName; private final DataStructureConverter converter; - protected transient List localRawResult; + protected transient List localRawResult; protected AppendingOutputFormat(String tableName, DataStructureConverter converter) { this.tableName = tableName; @@ -570,12 +606,18 @@ public void open(int taskNumber, int numTasks) throws IOException { @Override public void writeRecord(RowData value) throws IOException { - RowKind kind = value.getRowKind(); if (value.getRowKind() == RowKind.INSERT) { Row row = (Row) converter.toExternal(value); assertThat(row).isNotNull(); synchronized (LOCK) { - localRawResult.add(kind.shortString() + "(" + row.toString() + ")"); + localRawResult.add(row); + Optional.ofNullable(localRawResultsObservers.get(tableName)) + .orElse(Collections.emptyList()) + .forEach( + c -> + c.accept( + getRuntimeContext().getIndexOfThisSubtask(), + localRawResult)); } } else { throw new RuntimeException( @@ -631,11 +673,11 @@ protected TestValuesLookupFunction( public void open(FunctionContext context) throws Exception { RESOURCE_COUNTER.incrementAndGet(); isOpenCalled = true; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if (projectable) { - projection = - generatedProjection.newInstance( - Thread.currentThread().getContextClassLoader()); + projection = generatedProjection.newInstance(classLoader); } + converter.open(RuntimeConverter.Context.create(classLoader)); rowSerializer = InternalSerializers.create(producedRowType); indexDataByKey(); } @@ -725,11 +767,11 @@ protected AsyncTestValueLookupFunction( @Override public void open(FunctionContext context) throws Exception { RESOURCE_COUNTER.incrementAndGet(); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if (projectable) { - projection = - generatedProjection.newInstance( - Thread.currentThread().getContextClassLoader()); + projection = generatedProjection.newInstance(classLoader); } + converter.open(RuntimeConverter.Context.create(classLoader)); rowSerializer = InternalSerializers.create(producedRowType); isOpenCalled = true; // generate unordered result for async lookup diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/factories/TestValuesTableFactory.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/factories/TestValuesTableFactory.java index 8c5f34445c97e..8511e30ce3cb6 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/factories/TestValuesTableFactory.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/factories/TestValuesTableFactory.java @@ -24,11 +24,13 @@ import org.apache.flink.api.common.io.OutputFormat; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.api.common.typeutils.TypeSerializer; +import org.apache.flink.api.connector.source.Boundedness; import org.apache.flink.api.java.io.CollectionInputFormat; import org.apache.flink.configuration.ConfigOption; import org.apache.flink.configuration.ConfigOptions; import org.apache.flink.configuration.Configuration; import org.apache.flink.connector.source.DynamicFilteringValuesSource; +import org.apache.flink.connector.source.TerminatingLogic; import org.apache.flink.connector.source.ValuesSource; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.datastream.DataStreamSink; @@ -146,6 +148,7 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiConsumer; import java.util.function.Function; import java.util.stream.Collectors; @@ -224,7 +227,16 @@ public static String registerRowData(Seq data) { * * @param tableName the table name of the registered table sink. */ - public static List getRawResults(String tableName) { + public static List getRawResultsAsStrings(String tableName) { + return TestValuesRuntimeFunctions.getRawResultsAsStrings(tableName); + } + + /** + * Returns received raw results of the registered table sink. + * + * @param tableName the table name of the registered table sink. + */ + public static List getRawResults(String tableName) { return TestValuesRuntimeFunctions.getRawResults(tableName); } @@ -234,8 +246,8 @@ public static List getRawResults(String tableName) { * *

The raw results are encoded with {@link RowKind}. */ - public static List getOnlyRawResults() { - return TestValuesRuntimeFunctions.getOnlyRawResults(); + public static List getOnlyRawResultsAsStrings() { + return TestValuesRuntimeFunctions.getOnlyRawResultsAsStrings(); } /** @@ -243,10 +255,31 @@ public static List getOnlyRawResults() { * * @param tableName the table name of the registered table sink. */ - public static List getResults(String tableName) { + public static List getResultsAsStrings(String tableName) { + return TestValuesRuntimeFunctions.getResultsAsStrings(tableName); + } + + /** + * Returns materialized (final) results of the registered table sink. + * + * @param tableName the table name of the registered table sink. + */ + public static List getResults(String tableName) { return TestValuesRuntimeFunctions.getResults(tableName); } + /** + * Registers an observer for a table that gets notified of each incoming raw data for every + * subtask. It gets all rows seen so far, by a given task. + * + * @param tableName the table name of the registered table sink. + * @param observer the observer to be notified + */ + public static void registerLocalRawResultsObserver( + String tableName, BiConsumer> observer) { + TestValuesRuntimeFunctions.registerLocalRawResultsObserver(tableName, observer); + } + public static List getWatermarkOutput(String tableName) { return TestValuesRuntimeFunctions.getWatermarks(tableName); } @@ -295,6 +328,15 @@ private static RowKind parseRowKind(String rowKindShortString) { private static final ConfigOption BOUNDED = ConfigOptions.key("bounded").booleanType().defaultValue(false); + private static final ConfigOption TERMINATING = + ConfigOptions.key("terminating") + .booleanType() + .defaultValue(true) + .withDescription( + "Declares the behaviour of sources after all data has been" + + " produced. It is separate from 'bounded', because even if a source" + + " is unbounded it can stop producing records and shutdown."); + private static final ConfigOption CHANGELOG_MODE = ConfigOptions.key("changelog-mode") .stringType() @@ -439,6 +481,7 @@ public DynamicTableSource createDynamicTableSource(Context context) { ChangelogMode changelogMode = parseChangelogMode(helper.getOptions().get(CHANGELOG_MODE)); String runtimeSource = helper.getOptions().get(RUNTIME_SOURCE); boolean isBounded = helper.getOptions().get(BOUNDED); + boolean isFinite = helper.getOptions().get(TERMINATING); String dataId = helper.getOptions().get(DATA_ID); String sourceClass = helper.getOptions().get(TABLE_SOURCE_CLASS); boolean isAsync = helper.getOptions().get(ASYNC_ENABLED); @@ -477,6 +520,16 @@ public DynamicTableSource createDynamicTableSource(Context context) { convertToMetadataMap( helper.getOptions().get(READABLE_METADATA), context.getClassLoader()); + if (!isFinite && isBounded) { + throw new IllegalArgumentException( + "Source can not be bounded and infinite at the same time."); + } + + TerminatingLogic terminating = + isFinite ? TerminatingLogic.FINITE : TerminatingLogic.INFINITE; + Boundedness boundedness = + isBounded ? Boundedness.BOUNDED : Boundedness.CONTINUOUS_UNBOUNDED; + if (sourceClass.equals("DEFAULT")) { if (internalData) { return new TestValuesScanTableSourceWithInternalData( @@ -503,7 +556,8 @@ public DynamicTableSource createDynamicTableSource(Context context) { return new TestValuesScanTableSourceWithoutProjectionPushDown( producedDataType, changelogMode, - isBounded, + boundedness, + terminating, runtimeSource, failingSource, partition2Rows, @@ -524,6 +578,7 @@ public DynamicTableSource createDynamicTableSource(Context context) { return new TestValuesScanTableSourceWithWatermarkPushDown( producedDataType, changelogMode, + terminating, runtimeSource, failingSource, partition2Rows, @@ -542,7 +597,8 @@ public DynamicTableSource createDynamicTableSource(Context context) { return new TestValuesScanTableSource( producedDataType, changelogMode, - isBounded, + boundedness, + terminating, runtimeSource, failingSource, partition2Rows, @@ -562,7 +618,8 @@ public DynamicTableSource createDynamicTableSource(Context context) { context.getCatalogTable().getResolvedSchema().toPhysicalRowDataType(), producedDataType, changelogMode, - isBounded, + boundedness, + terminating, runtimeSource, failingSource, partition2Rows, @@ -661,6 +718,7 @@ public Set> optionalOptions() { DATA_ID, CHANGELOG_MODE, BOUNDED, + TERMINATING, RUNTIME_SOURCE, TABLE_SOURCE_CLASS, FAILING_SOURCE, @@ -837,7 +895,8 @@ private static class TestValuesScanTableSourceWithoutProjectionPushDown protected DataType producedDataType; protected final ChangelogMode changelogMode; - protected final boolean bounded; + protected final Boundedness boundedness; + protected final TerminatingLogic terminating; protected final String runtimeSource; protected final boolean failingSource; protected Map, Collection> data; @@ -860,7 +919,8 @@ private static class TestValuesScanTableSourceWithoutProjectionPushDown private TestValuesScanTableSourceWithoutProjectionPushDown( DataType producedDataType, ChangelogMode changelogMode, - boolean bounded, + Boundedness boundedness, + TerminatingLogic terminating, String runtimeSource, boolean failingSource, Map, Collection> data, @@ -876,7 +936,8 @@ private TestValuesScanTableSourceWithoutProjectionPushDown( @Nullable int[] projectedMetadataFields) { this.producedDataType = producedDataType; this.changelogMode = changelogMode; - this.bounded = bounded; + this.boundedness = boundedness; + this.terminating = terminating; this.runtimeSource = runtimeSource; this.failingSource = failingSource; this.data = data; @@ -912,6 +973,9 @@ public ScanRuntimeProvider getScanRuntimeProvider(ScanContext runtimeProviderCon switch (runtimeSource) { case "SourceFunction": try { + checkArgument( + terminating == TerminatingLogic.FINITE, + "Values Source doesn't support infinite SourceFunction."); Collection values = convertToRowData(converter); final SourceFunction sourceFunction; if (failingSource) { @@ -921,7 +985,8 @@ public ScanRuntimeProvider getScanRuntimeProvider(ScanContext runtimeProviderCon } else { sourceFunction = new FromElementsFunction<>(serializer, values); } - return SourceFunctionProvider.of(sourceFunction, bounded); + return SourceFunctionProvider.of( + sourceFunction, boundedness == Boundedness.BOUNDED); } catch (IOException e) { throw new TableException("Fail to init source function", e); } @@ -929,12 +994,18 @@ public ScanRuntimeProvider getScanRuntimeProvider(ScanContext runtimeProviderCon checkArgument( !failingSource, "Values InputFormat Source doesn't support as failing source."); + checkArgument( + terminating == TerminatingLogic.FINITE, + "Values Source doesn't support infinite InputFormat."); Collection values = convertToRowData(converter); return InputFormatProvider.of(new CollectionInputFormat<>(values, serializer)); case "DataStream": checkArgument( !failingSource, "Values DataStream Source doesn't support as failing source."); + checkArgument( + terminating == TerminatingLogic.FINITE, + "Values Source doesn't support infinite DataStream."); try { Collection values2 = convertToRowData(converter); FromElementsFunction function = @@ -954,7 +1025,7 @@ public DataStream produceDataStream( @Override public boolean isBounded() { - return bounded; + return boundedness == Boundedness.BOUNDED; } }; } catch (IOException e) { @@ -966,13 +1037,18 @@ public boolean isBounded() { if (acceptedPartitionFilterFields == null || acceptedPartitionFilterFields.isEmpty()) { Collection values2 = convertToRowData(converter); - return SourceProvider.of(new ValuesSource(values2, serializer)); + return SourceProvider.of( + new ValuesSource(terminating, boundedness, values2, serializer)); } else { Map, Collection> partitionValues = convertToPartitionedRowData(converter); DynamicFilteringValuesSource source = new DynamicFilteringValuesSource( - partitionValues, serializer, acceptedPartitionFilterFields); + terminating, + boundedness, + partitionValues, + serializer, + acceptedPartitionFilterFields); return SourceProvider.of(source); } default: @@ -1023,7 +1099,8 @@ public DynamicTableSource copy() { return new TestValuesScanTableSourceWithoutProjectionPushDown( producedDataType, changelogMode, - bounded, + boundedness, + terminating, runtimeSource, failingSource, data, @@ -1367,7 +1444,8 @@ private static class TestValuesScanTableSource private TestValuesScanTableSource( DataType producedDataType, ChangelogMode changelogMode, - boolean bounded, + Boundedness boundedness, + TerminatingLogic terminating, String runtimeSource, boolean failingSource, Map, Collection> data, @@ -1384,7 +1462,8 @@ private TestValuesScanTableSource( super( producedDataType, changelogMode, - bounded, + boundedness, + terminating, runtimeSource, failingSource, data, @@ -1405,7 +1484,8 @@ public DynamicTableSource copy() { return new TestValuesScanTableSource( producedDataType, changelogMode, - bounded, + boundedness, + terminating, runtimeSource, failingSource, data, @@ -1446,6 +1526,7 @@ private static class TestValuesScanTableSourceWithWatermarkPushDown private TestValuesScanTableSourceWithWatermarkPushDown( DataType producedDataType, ChangelogMode changelogMode, + TerminatingLogic terminating, String runtimeSource, boolean failingSource, Map, Collection> data, @@ -1463,7 +1544,8 @@ private TestValuesScanTableSourceWithWatermarkPushDown( super( producedDataType, changelogMode, - false, + Boundedness.CONTINUOUS_UNBOUNDED, + terminating, runtimeSource, failingSource, data, @@ -1516,6 +1598,7 @@ public DynamicTableSource copy() { new TestValuesScanTableSourceWithWatermarkPushDown( producedDataType, changelogMode, + terminating, runtimeSource, failingSource, data, @@ -1556,7 +1639,8 @@ private TestValuesScanLookupTableSource( DataType originType, DataType producedDataType, ChangelogMode changelogMode, - boolean bounded, + Boundedness boundedness, + TerminatingLogic terminating, String runtimeSource, boolean failingSource, Map, Collection> data, @@ -1578,7 +1662,8 @@ private TestValuesScanLookupTableSource( super( producedDataType, changelogMode, - bounded, + boundedness, + terminating, runtimeSource, failingSource, data, @@ -1762,7 +1847,8 @@ public DynamicTableSource copy() { originType, producedDataType, changelogMode, - bounded, + boundedness, + terminating, runtimeSource, failingSource, data, @@ -1936,7 +2022,7 @@ public Optional getParallelism() { @Override public SinkFunction createSinkFunction() { return new AppendingSinkFunction( - tableName, converter, rowtimeIndex); + tableName, consumedDataType, converter, rowtimeIndex); } }; case "OutputFormat": @@ -1960,7 +2046,10 @@ public DataStreamSink consumeDataStream( DataStreamSink sink = dataStream.addSink( new AppendingSinkFunction( - tableName, converter, rowtimeIndex)); + tableName, + consumedDataType, + converter, + rowtimeIndex)); providerContext.generateUid("sink-function").ifPresent(sink::uid); return sink; } @@ -1989,6 +2078,7 @@ public Optional getParallelism() { sinkFunction = new KeyedUpsertingSinkFunction( tableName, + consumedDataType, converter, primaryKeyIndices, Arrays.stream(targetColumns).mapToInt(a -> a[0]).toArray(), @@ -2000,7 +2090,8 @@ public Optional getParallelism() { "Retracting Sink doesn't support '" + SINK_EXPECTED_MESSAGES_NUM.key() + "' yet."); - sinkFunction = new RetractingSinkFunction(tableName, converter); + sinkFunction = + new RetractingSinkFunction(tableName, consumedDataType, converter); } return SinkFunctionProvider.of(sinkFunction, this.parallelism); } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CollectionFunctionsITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CollectionFunctionsITCase.java index 61b8128d3f748..a758d7cdf3b4b 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CollectionFunctionsITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CollectionFunctionsITCase.java @@ -975,11 +975,16 @@ private Stream arrayJoinTestCases() { DataTypes.STRING().nullable()) .testSqlValidationError( "ARRAY_JOIN(f0)", - "No match found for function " - + "signature ARRAY_JOIN()") + "No match found for function signature ARRAY_JOIN().\n" + + "Supported signatures are:\n" + + "ARRAY_JOIN(ARRAY, )\n" + + "ARRAY_JOIN(ARRAY, , )") .testSqlValidationError( "ARRAY_JOIN()", - "No match found for function signature ARRAY_JOIN()") + "No match found for function signature ARRAY_JOIN().\n" + + "Supported signatures are:\n" + + "ARRAY_JOIN(ARRAY, )\n" + + "ARRAY_JOIN(ARRAY, , )") .testSqlValidationError( "ARRAY_JOIN(f5, '+')", "Invalid input arguments. Expected signatures are:\n" @@ -1190,7 +1195,16 @@ private Stream arraySliceTestCases() { + "ARRAY_SLICE(, , )") .testSqlValidationError( "ARRAY_SLICE()", - " No match found for function signature ARRAY_SLICE()") + "No match found for function signature ARRAY_SLICE().\n" + + "Supported signatures are:\n" + + "ARRAY_SLICE(, , )\n" + + "ARRAY_SLICE(, )") + .testSqlValidationError( + "ARRAY_SLICE(1)", + "No match found for function signature ARRAY_SLICE().\n" + + "Supported signatures are:\n" + + "ARRAY_SLICE(, , )\n" + + "ARRAY_SLICE(, )") .testSqlValidationError("ARRAY_SLICE(null)", "Illegal use of 'NULL'")); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/CalcMergeTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/CalcMergeTest.java index 4a0e196f1cbbe..e9d25da76b351 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/CalcMergeTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/CalcMergeTest.java @@ -23,7 +23,7 @@ import org.apache.flink.table.planner.utils.TableTestUtil; /** Plan test for calc merge. */ -public class CalcMergeTest extends CalcMergeTestBase { +class CalcMergeTest extends CalcMergeTestBase { @Override protected boolean isBatchMode() { return true; diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/DynamicFilteringTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/DynamicFilteringTest.java index 682c19fbf2b7b..7d8fde5746465 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/DynamicFilteringTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/DynamicFilteringTest.java @@ -26,23 +26,24 @@ import org.apache.flink.table.planner.utils.BatchTableTestUtil; import org.apache.flink.table.planner.utils.JavaScalaConversionUtil; import org.apache.flink.table.planner.utils.TableTestBase; +import org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameters; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; import java.util.Arrays; import java.util.Collection; import java.util.Collections; /** Plan test for dynamic filtering. */ -@RunWith(Parameterized.class) -public class DynamicFilteringTest extends TableTestBase { +@ExtendWith(ParameterizedTestExtension.class) +class DynamicFilteringTest extends TableTestBase { // Notes that the here name is used to load the correct plan. - @Parameterized.Parameters(name = "mode = {0}") - public static Collection data() { + @Parameters(name = "mode = {0}") + private static Collection data() { return Arrays.asList( new Object[][] { {BatchShuffleMode.ALL_EXCHANGES_BLOCKING}, @@ -52,14 +53,14 @@ public static Collection data() { private final BatchShuffleMode batchShuffleMode; - public DynamicFilteringTest(BatchShuffleMode batchShuffleMode) { + DynamicFilteringTest(BatchShuffleMode batchShuffleMode) { this.batchShuffleMode = batchShuffleMode; } private BatchTableTestUtil util; - @Before - public void before() { + @BeforeEach + void before() { util = batchTestUtil(TableConfig.getDefault()); util.tableEnv() .getConfig() @@ -117,8 +118,8 @@ public void before() { + ")"); } - @Test - public void testLegacySource() { + @TestTemplate + void testLegacySource() { util.tableEnv() .executeSql( "CREATE TABLE legacy_source (\n" @@ -142,8 +143,8 @@ public void testLegacySource() { Collections.singletonList(ExplainDetail.JSON_EXECUTION_PLAN))); } - @Test - public void testSimpleDynamicFiltering() { + @TestTemplate + void testSimpleDynamicFiltering() { // the execution plan contains 'Placeholder-Filter' operator util.verifyExplain( "SELECT * FROM fact1, dim WHERE p1 = p AND x > 10", @@ -151,8 +152,8 @@ public void testSimpleDynamicFiltering() { Collections.singletonList(ExplainDetail.JSON_EXECUTION_PLAN))); } - @Test - public void testDynamicFilteringWithMultipleInput() { + @TestTemplate + void testDynamicFilteringWithMultipleInput() { // the execution plan does not contain 'Placeholder-Filter' operator util.verifyExplain( "SELECT * FROM fact1, dim, fact2 WHERE p1 = p and p1 = p2 AND x > 10", @@ -160,8 +161,8 @@ public void testDynamicFilteringWithMultipleInput() { Collections.singletonList(ExplainDetail.JSON_EXECUTION_PLAN))); } - @Test - public void testDuplicateFactTables() { + @TestTemplate + void testDuplicateFactTables() { // the fact tables can not be reused util.verifyExplain( "SELECT * FROM (SELECT * FROM fact1, dim WHERE p1 = p AND x > 10) t1 JOIN fact1 t2 ON t1.y = t2.b1", @@ -169,8 +170,8 @@ public void testDuplicateFactTables() { Collections.singletonList(ExplainDetail.JSON_EXECUTION_PLAN))); } - @Test - public void testReuseDimSide() { + @TestTemplate + void testReuseDimSide() { // dynamic filtering collector will be reused for both fact tables util.verifyExplain( "SELECT * FROM fact1, dim WHERE p1 = p AND x > 10 " @@ -180,8 +181,8 @@ public void testReuseDimSide() { Collections.singletonList(ExplainDetail.JSON_EXECUTION_PLAN))); } - @Test - public void testDynamicFilteringWithStaticPartitionPruning() { + @TestTemplate + void testDynamicFilteringWithStaticPartitionPruning() { util.verifyExplain( "SELECT * FROM fact1, dim WHERE p1 = p AND x > 10 and p1 > 1", JavaScalaConversionUtil.toScala( diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/DynamicFunctionPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/DynamicFunctionPlanTest.java index a765f94c881f9..2b2e64b417188 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/DynamicFunctionPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/DynamicFunctionPlanTest.java @@ -23,7 +23,7 @@ import org.apache.flink.table.planner.utils.TableTestUtil; /** Plan test for queries contain dynamic functions in batch. */ -public class DynamicFunctionPlanTest extends DynamicFunctionPlanTestBase { +class DynamicFunctionPlanTest extends DynamicFunctionPlanTestBase { @Override protected TableTestUtil getTableTestUtil() { return batchTestUtil(TableConfig.getDefault()); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/ForwardHashExchangeTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/ForwardHashExchangeTest.java index 33fe021ac3e2f..eaf2202a11fb1 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/ForwardHashExchangeTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/ForwardHashExchangeTest.java @@ -23,16 +23,16 @@ import org.apache.flink.table.planner.utils.BatchTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Tests for ForwardHashExchangeProcessor. */ -public class ForwardHashExchangeTest extends TableTestBase { +class ForwardHashExchangeTest extends TableTestBase { private BatchTableTestUtil util; - @Before - public void before() { + @BeforeEach + void before() { util = batchTestUtil(TableConfig.getDefault()); util.tableEnv() @@ -77,13 +77,13 @@ public void before() { } @Test - public void testRankWithHashShuffle() { + void testRankWithHashShuffle() { util.verifyExecPlan( "SELECT * FROM (SELECT a, b, RANK() OVER(PARTITION BY a ORDER BY b) rk FROM T) WHERE rk <= 10"); } @Test - public void testSortAggregateWithHashShuffle() { + void testSortAggregateWithHashShuffle() { util.tableEnv() .getConfig() .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashAgg"); @@ -91,7 +91,7 @@ public void testSortAggregateWithHashShuffle() { } @Test - public void testOverAggOnHashAggWithHashShuffle() { + void testOverAggOnHashAggWithHashShuffle() { util.tableEnv() .getConfig() .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "SortAgg"); @@ -106,7 +106,7 @@ public void testOverAggOnHashAggWithHashShuffle() { } @Test - public void testOverAggOnHashAggWithGlobalShuffle() { + void testOverAggOnHashAggWithGlobalShuffle() { util.tableEnv() .getConfig() .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "SortAgg"); @@ -114,7 +114,7 @@ public void testOverAggOnHashAggWithGlobalShuffle() { } @Test - public void testOverAggOnSortAggWithHashShuffle() { + void testOverAggOnSortAggWithHashShuffle() { util.tableEnv() .getConfig() .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashAgg"); @@ -129,7 +129,7 @@ public void testOverAggOnSortAggWithHashShuffle() { } @Test - public void testOverAggOnSortAggWithGlobalShuffle() { + void testOverAggOnSortAggWithGlobalShuffle() { util.tableEnv() .getConfig() .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashAgg"); @@ -137,7 +137,7 @@ public void testOverAggOnSortAggWithGlobalShuffle() { } @Test - public void testHashAggOnHashJoinWithHashShuffle() { + void testHashAggOnHashJoinWithHashShuffle() { util.tableEnv() .getConfig() .set( @@ -149,7 +149,7 @@ public void testHashAggOnHashJoinWithHashShuffle() { } @Test - public void testOnePhaseSortAggOnSortMergeJoinWithHashShuffle() { + void testOnePhaseSortAggOnSortMergeJoinWithHashShuffle() { util.tableEnv() .getConfig() .set( @@ -164,7 +164,7 @@ public void testOnePhaseSortAggOnSortMergeJoinWithHashShuffle() { } @Test - public void testTwoPhaseSortAggOnSortMergeJoinWithHashShuffle() { + void testTwoPhaseSortAggOnSortMergeJoinWithHashShuffle() { util.tableEnv() .getConfig() .set( @@ -179,7 +179,7 @@ public void testTwoPhaseSortAggOnSortMergeJoinWithHashShuffle() { } @Test - public void testAutoPhaseSortAggOnSortMergeJoinWithHashShuffle() { + void testAutoPhaseSortAggOnSortMergeJoinWithHashShuffle() { util.tableEnv() .getConfig() .set( @@ -194,7 +194,7 @@ public void testAutoPhaseSortAggOnSortMergeJoinWithHashShuffle() { } @Test - public void testHashAggOnNestedLoopJoinWithGlobalShuffle() { + void testHashAggOnNestedLoopJoinWithGlobalShuffle() { util.tableEnv() .getConfig() .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "SortAgg"); @@ -208,7 +208,7 @@ public void testHashAggOnNestedLoopJoinWithGlobalShuffle() { } @Test - public void testSortAggOnNestedLoopJoinWithGlobalShuffle() { + void testSortAggOnNestedLoopJoinWithGlobalShuffle() { util.tableEnv() .getConfig() .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashAgg"); @@ -222,7 +222,7 @@ public void testSortAggOnNestedLoopJoinWithGlobalShuffle() { } @Test - public void testRankOnHashAggWithHashShuffle() { + void testRankOnHashAggWithHashShuffle() { util.tableEnv() .getConfig() .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "SortAgg"); @@ -235,7 +235,7 @@ public void testRankOnHashAggWithHashShuffle() { } @Test - public void testRankOnHashAggWithGlobalShuffle() { + void testRankOnHashAggWithGlobalShuffle() { util.tableEnv() .getConfig() .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "SortAgg"); @@ -248,7 +248,7 @@ public void testRankOnHashAggWithGlobalShuffle() { } @Test - public void testRankOnOnePhaseSortAggWithHashShuffle() { + void testRankOnOnePhaseSortAggWithHashShuffle() { util.tableEnv() .getConfig() .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashAgg"); @@ -264,7 +264,7 @@ public void testRankOnOnePhaseSortAggWithHashShuffle() { } @Test - public void testRankOnTwoPhaseSortAggWithHashShuffle() { + void testRankOnTwoPhaseSortAggWithHashShuffle() { util.tableEnv() .getConfig() .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashAgg"); @@ -280,7 +280,7 @@ public void testRankOnTwoPhaseSortAggWithHashShuffle() { } @Test - public void testRankOnOnePhaseSortAggWithGlobalShuffle() { + void testRankOnOnePhaseSortAggWithGlobalShuffle() { util.tableEnv() .getConfig() .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashAgg"); @@ -296,7 +296,7 @@ public void testRankOnOnePhaseSortAggWithGlobalShuffle() { } @Test - public void testRankOnTwoPhaseSortAggWithGlobalShuffle() { + void testRankOnTwoPhaseSortAggWithGlobalShuffle() { util.tableEnv() .getConfig() .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashAgg"); @@ -312,7 +312,7 @@ public void testRankOnTwoPhaseSortAggWithGlobalShuffle() { } @Test - public void testHashJoinWithMultipleInputDisabled() { + void testHashJoinWithMultipleInputDisabled() { util.tableEnv() .getConfig() .set( @@ -328,7 +328,7 @@ public void testHashJoinWithMultipleInputDisabled() { } @Test - public void testSortJoinWithMultipleInputDisabled() { + void testSortJoinWithMultipleInputDisabled() { util.tableEnv() .getConfig() .set( @@ -344,7 +344,7 @@ public void testSortJoinWithMultipleInputDisabled() { } @Test - public void testMultipleInputs() { + void testMultipleInputs() { util.getTableEnv() .getConfig() .set(OptimizerConfigOptions.TABLE_OPTIMIZER_JOIN_REORDER_ENABLED, false) diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/MatchRecognizeTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/MatchRecognizeTest.java index eca0bb0cfcfb2..b5e42f7819e61 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/MatchRecognizeTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/MatchRecognizeTest.java @@ -22,17 +22,17 @@ import org.apache.flink.table.planner.utils.TableTestBase; import org.apache.calcite.sql.SqlMatchRecognize; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Tests for {@link SqlMatchRecognize}. */ -public class MatchRecognizeTest extends TableTestBase { +class MatchRecognizeTest extends TableTestBase { private BatchTableTestUtil util; - @Before - public void before() { + @BeforeEach + void before() { util = batchTestUtil(TableConfig.getDefault()); util.getTableEnv() .executeSql( @@ -47,13 +47,13 @@ public void before() { + ")"); } - @After - public void after() { + @AfterEach + void after() { util.getTableEnv().executeSql("DROP TABLE Ticker"); } @Test - public void testCascadeMatch() { + void testCascadeMatch() { String sqlQuery = "SELECT *\n" + "FROM (\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/RowLevelDeleteTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/RowLevelDeleteTest.java index 7bc295ce6fd03..874de69f3a8c8 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/RowLevelDeleteTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/RowLevelDeleteTest.java @@ -25,11 +25,12 @@ import org.apache.flink.table.planner.utils.BatchTableTestUtil; import org.apache.flink.table.planner.utils.JavaScalaConversionUtil; import org.apache.flink.table.planner.utils.TableTestBase; +import org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameters; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; import java.util.Arrays; import java.util.Collection; @@ -38,8 +39,8 @@ import scala.collection.Seq; /** Test for row-level delete. */ -@RunWith(Parameterized.class) -public class RowLevelDeleteTest extends TableTestBase { +@ExtendWith(ParameterizedTestExtension.class) +class RowLevelDeleteTest extends TableTestBase { private final SupportsRowLevelDelete.RowLevelDeleteMode deleteMode; private final Seq explainDetails = @@ -48,19 +49,19 @@ public class RowLevelDeleteTest extends TableTestBase { private BatchTableTestUtil util; - @Parameterized.Parameters(name = "deleteMode = {0}") - public static Collection data() { + @Parameters(name = "deleteMode = {0}") + private static Collection data() { return Arrays.asList( SupportsRowLevelDelete.RowLevelDeleteMode.DELETED_ROWS, SupportsRowLevelDelete.RowLevelDeleteMode.REMAINING_ROWS); } - public RowLevelDeleteTest(SupportsRowLevelDelete.RowLevelDeleteMode deleteMode) { + RowLevelDeleteTest(SupportsRowLevelDelete.RowLevelDeleteMode deleteMode) { this.deleteMode = deleteMode; } - @Before - public void before() { + @BeforeEach + void before() { util = batchTestUtil(TableConfig.getDefault()); util.tableEnv() .getConfig() @@ -68,27 +69,27 @@ public void before() { .set(ExecutionConfigOptions.TABLE_EXEC_RESOURCE_DEFAULT_PARALLELISM, 12); } - @Test - public void testDeleteWithoutFilter() { + @TestTemplate + void testDeleteWithoutFilter() { createTableForDelete(); util.verifyExplainInsert("DELETE FROM t", explainDetails); } - @Test - public void testDeleteWithFilter() { + @TestTemplate + void testDeleteWithFilter() { createTableForDelete(); util.verifyExplainInsert("DELETE FROM t where a = 1 and b = '123'", explainDetails); } - @Test - public void testDeleteWithSubQuery() { + @TestTemplate + void testDeleteWithSubQuery() { createTableForDelete(); util.verifyExplainInsert( "DELETE FROM t where b = '123' and a = (select count(*) from t)", explainDetails); } - @Test - public void testDeleteWithCustomColumns() { + @TestTemplate + void testDeleteWithCustomColumns() { util.tableEnv() .executeSql( String.format( @@ -102,8 +103,8 @@ public void testDeleteWithCustomColumns() { util.verifyExplainInsert("DELETE FROM t where b = '123'", explainDetails); } - @Test - public void testDeleteWithMetaColumns() { + @TestTemplate + void testDeleteWithMetaColumns() { util.tableEnv() .executeSql( String.format( diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/RowLevelUpdateTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/RowLevelUpdateTest.java index 22a09cd3159ce..57f22238d0540 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/RowLevelUpdateTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/RowLevelUpdateTest.java @@ -26,11 +26,12 @@ import org.apache.flink.table.planner.utils.BatchTableTestUtil; import org.apache.flink.table.planner.utils.JavaScalaConversionUtil; import org.apache.flink.table.planner.utils.TableTestBase; +import org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameters; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; import java.util.Arrays; import java.util.Collection; @@ -38,9 +39,11 @@ import scala.collection.Seq; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + /** Test for row-level update. */ -@RunWith(Parameterized.class) -public class RowLevelUpdateTest extends TableTestBase { +@ExtendWith(ParameterizedTestExtension.class) +class RowLevelUpdateTest extends TableTestBase { private final Seq explainDetails = JavaScalaConversionUtil.toScala( @@ -49,19 +52,19 @@ public class RowLevelUpdateTest extends TableTestBase { private BatchTableTestUtil util; - @Parameterized.Parameters(name = "updateMode = {0}") - public static Collection data() { + @Parameters(name = "updateMode = {0}") + private static Collection data() { return Arrays.asList( SupportsRowLevelUpdate.RowLevelUpdateMode.UPDATED_ROWS, SupportsRowLevelUpdate.RowLevelUpdateMode.ALL_ROWS); } - public RowLevelUpdateTest(SupportsRowLevelUpdate.RowLevelUpdateMode updateMode) { + RowLevelUpdateTest(SupportsRowLevelUpdate.RowLevelUpdateMode updateMode) { this.updateMode = updateMode; } - @Before - public void before() { + @BeforeEach + void before() { util = batchTestUtil(TableConfig.getDefault()); util.tableEnv() .getConfig() @@ -69,21 +72,21 @@ public void before() { .set(ExecutionConfigOptions.TABLE_EXEC_RESOURCE_DEFAULT_PARALLELISM, 12); } - @Test - public void testUpdateWithoutFilter() { + @TestTemplate + void testUpdateWithoutFilter() { createTableForUpdate(); util.verifyExplainInsert("UPDATE t SET b = 'n1', a = char_length(b) * a ", explainDetails); } - @Test - public void testUpdateWithFilter() { + @TestTemplate + void testUpdateWithFilter() { createTableForUpdate(); util.verifyExplainInsert( "UPDATE t SET b = 'v2' WHERE a = 123 AND b = 'v1'", explainDetails); } - @Test - public void testUpdateWithSubQuery() { + @TestTemplate + void testUpdateWithSubQuery() { createTableForUpdate(); util.tableEnv() .executeSql( @@ -95,8 +98,8 @@ public void testUpdateWithSubQuery() { "UPDATE t SET b = 'v2' WHERE a = (SELECT count(*) FROM t1)", explainDetails); } - @Test - public void testUpdateAllColsWithOnlyRequireUpdatedCols() { + @TestTemplate + void testUpdateAllColsWithOnlyRequireUpdatedCols() { util.tableEnv() .executeSql( String.format( @@ -110,8 +113,8 @@ public void testUpdateAllColsWithOnlyRequireUpdatedCols() { "UPDATE t SET b = 'v2', a = 123, c = c + 1 WHERE c > 123", explainDetails); } - @Test - public void testUpdatePartColsWithOnlyRequireUpdatedCols() { + @TestTemplate + void testUpdatePartColsWithOnlyRequireUpdatedCols() { util.tableEnv() .executeSql( String.format( @@ -125,8 +128,8 @@ public void testUpdatePartColsWithOnlyRequireUpdatedCols() { "UPDATE t SET b = 'v2', a = 123, c = c + 1 WHERE c > 123", explainDetails); } - @Test - public void testUpdateWithCustomColumns() { + @TestTemplate + void testUpdateWithCustomColumns() { util.tableEnv() .executeSql( String.format( @@ -140,8 +143,8 @@ public void testUpdateWithCustomColumns() { util.verifyExplainInsert("UPDATE t SET b = 'v2' WHERE b = '123'", explainDetails); } - @Test - public void testUpdateWithMetaColumns() { + @TestTemplate + void testUpdateWithMetaColumns() { util.tableEnv() .executeSql( String.format( @@ -155,8 +158,8 @@ public void testUpdateWithMetaColumns() { util.verifyExplainInsert("UPDATE t SET b = 'v2' WHERE b = '123'", explainDetails); } - @Test(expected = SqlParserException.class) - public void testUpdateWithCompositeType() { + @TestTemplate + void testUpdateWithCompositeType() { util.tableEnv() .executeSql( String.format( @@ -169,8 +172,13 @@ public void testUpdateWithCompositeType() { + "'update-mode' = '%s'" + ") ", updateMode)); - util.verifyExplainInsert( - "UPDATE t SET b.b1 = 'v2', c.c1 = 1000 WHERE b = '123'", explainDetails); + + assertThatExceptionOfType(SqlParserException.class) + .isThrownBy( + () -> + util.verifyExplainInsert( + "UPDATE t SET b.b1 = 'v2', c.c1 = 1000 WHERE b = '123'", + explainDetails)); } private void createTableForUpdate() { diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/TimeTravelTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/TimeTravelTest.java index 92b0a16bbe6ef..89acd62c5d672 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/TimeTravelTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/batch/sql/TimeTravelTest.java @@ -28,20 +28,20 @@ import org.apache.flink.table.planner.utils.DateTimeTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.time.ZoneId; import static org.assertj.core.api.Assertions.assertThatThrownBy; /** Plan tests for time travel. */ -public class TimeTravelTest extends TableTestBase { +class TimeTravelTest extends TableTestBase { private BatchTableTestUtil util; - @Before - public void before() { + @BeforeEach + void before() { util = batchTestUtil(TableConfig.getDefault()); String catalogName = "TimeTravelCatalog"; TestTimeTravelCatalog catalog = @@ -53,25 +53,25 @@ public void before() { } @Test - public void testTimeTravel() { + void testTimeTravel() { util.verifyExecPlan( "SELECT * FROM t1 FOR SYSTEM_TIME AS OF TIMESTAMP '2023-01-01 02:00:00'"); } @Test - public void testTimeTravelWithAsExpression() { + void testTimeTravelWithAsExpression() { util.verifyExecPlan( "SELECT * FROM t1 FOR SYSTEM_TIME AS OF TIMESTAMP '2023-01-01 02:00:00' AS t2"); } @Test - public void testTimeTravelWithSimpleExpression() { + void testTimeTravelWithSimpleExpression() { util.verifyExecPlan( "SELECT * FROM t1 FOR SYSTEM_TIME AS OF TIMESTAMP '2023-01-01 00:00:00'+INTERVAL '60' DAY"); } @Test - public void testTimeTravelWithDifferentTimezone() { + void testTimeTravelWithDifferentTimezone() { util.tableEnv().getConfig().setLocalTimeZone(ZoneId.of("Asia/Shanghai")); util.verifyExecPlan( @@ -85,7 +85,7 @@ public void testTimeTravelWithDifferentTimezone() { } @Test - public void testTimeTravelOneTableMultiTimes() { + void testTimeTravelOneTableMultiTimes() { util.verifyExecPlan( "SELECT\n" + " f1\n" @@ -99,7 +99,7 @@ public void testTimeTravelOneTableMultiTimes() { } @Test - public void testTimeTravelWithLookupJoin() { + void testTimeTravelWithLookupJoin() { util.verifyExecPlan( "SELECT\n" + " l.f2,\n" @@ -116,13 +116,13 @@ public void testTimeTravelWithLookupJoin() { } @Test - public void testTimeTravelWithHints() { + void testTimeTravelWithHints() { util.verifyExecPlan( "SELECT * FROM t1 /*+ options('bounded'='true') */ FOR SYSTEM_TIME AS OF TIMESTAMP '2023-01-01 02:00:00' AS t2"); } @Test - public void testTimeTravelWithUnsupportedExpression() { + void testTimeTravelWithUnsupportedExpression() { assertThatThrownBy( () -> util.tableEnv() @@ -149,7 +149,7 @@ public void testTimeTravelWithUnsupportedExpression() { } @Test - public void testTimeTravelWithIdentifierSnapshot() { + void testTimeTravelWithIdentifierSnapshot() { util.tableEnv() .executeSql( "CREATE TABLE\n" @@ -173,7 +173,7 @@ public void testTimeTravelWithIdentifierSnapshot() { } @Test - public void testTimeTravelWithView() { + void testTimeTravelWithView() { util.tableEnv().executeSql("CREATE VIEW tb_view AS SELECT * FROM t1"); assertThatThrownBy( diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/common/CalcMergeTestBase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/common/CalcMergeTestBase.java index 1f8cf1caad262..728ef1edfdb91 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/common/CalcMergeTestBase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/common/CalcMergeTestBase.java @@ -22,8 +22,8 @@ import org.apache.flink.table.planner.utils.TableTestBase; import org.apache.flink.table.planner.utils.TableTestUtil; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** * Base plan test for calc merge, the difference between FlinkCalcMergeRuleTest is this test @@ -37,8 +37,8 @@ public abstract class CalcMergeTestBase extends TableTestBase { protected abstract TableTestUtil getTableTestUtil(); - @Before - public void setup() { + @BeforeEach + void setup() { util = getTableTestUtil(); util.tableEnv() @@ -57,59 +57,59 @@ public void setup() { } @Test - public void testCalcMergeWithSameDigest() { + void testCalcMergeWithSameDigest() { util.verifyExecPlan("SELECT a, b FROM (SELECT * FROM MyTable WHERE a = b) t WHERE b = a"); } @Test - public void testCalcMergeWithNonDeterministicExpr1() { + void testCalcMergeWithNonDeterministicExpr1() { util.verifyExecPlan( "SELECT a, a1 FROM (SELECT a, random_udf(a) AS a1 FROM MyTable) t WHERE a1 > 10"); } @Test - public void testCalcMergeWithNonDeterministicExpr2() { + void testCalcMergeWithNonDeterministicExpr2() { util.verifyExecPlan( "SELECT random_udf(a1) as a2 FROM (SELECT random_udf(a) as" + " a1, b FROM MyTable) t WHERE b > 10"); } @Test - public void testCalcMergeWithTopMultiNonDeterministicExpr() { + void testCalcMergeWithTopMultiNonDeterministicExpr() { util.verifyExecPlan( "SELECT random_udf(a1) as a2, random_udf(a1) as a3 FROM" + " (SELECT random_udf(a) as a1, b FROM MyTable) t WHERE b > 10"); } @Test - public void testCalcMergeTopFilterHasNonDeterministicExpr() { + void testCalcMergeTopFilterHasNonDeterministicExpr() { util.verifyExecPlan( "SELECT a, c FROM" + " (SELECT a, random_udf(b) as b1, c FROM MyTable) t WHERE b1 > 10"); } @Test - public void testCalcMergeWithBottomMultiNonDeterministicExpr() { + void testCalcMergeWithBottomMultiNonDeterministicExpr() { util.verifyExecPlan( "SELECT a1, b2 FROM" + " (SELECT random_udf(a) as a1, random_udf(b) as b2, c FROM MyTable) t WHERE c > 10"); } @Test - public void testCalcMergeWithBottomMultiNonDeterministicInConditionExpr() { + void testCalcMergeWithBottomMultiNonDeterministicInConditionExpr() { util.verifyExecPlan( "SELECT c FROM" + " (SELECT random_udf(a) as a1, random_udf(b) as b2, c FROM MyTable) t WHERE a1 > b2"); } @Test - public void testCalcMergeWithoutInnerNonDeterministicExpr() { + void testCalcMergeWithoutInnerNonDeterministicExpr() { util.verifyExecPlan( "SELECT a, c FROM (SELECT a, random_udf(a) as a1, c FROM MyTable) t WHERE c > 10"); } @Test - public void testCalcMergeWithNonDeterministicNestedExpr() { + void testCalcMergeWithNonDeterministicNestedExpr() { util.verifyExecPlan( "SELECT a, a1 FROM (SELECT a, substr(cast(random_udf(a) as varchar), 1, 2) AS a1 FROM MyTable) t WHERE a1 > '10'"); } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/common/DynamicFunctionPlanTestBase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/common/DynamicFunctionPlanTestBase.java index 22140d384a67e..78b414954c239 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/common/DynamicFunctionPlanTestBase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/common/DynamicFunctionPlanTestBase.java @@ -21,8 +21,8 @@ import org.apache.flink.table.planner.utils.TableTestBase; import org.apache.flink.table.planner.utils.TableTestUtil; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Plan test for queries contain dynamic functions. */ public abstract class DynamicFunctionPlanTestBase extends TableTestBase { @@ -33,8 +33,8 @@ public abstract class DynamicFunctionPlanTestBase extends TableTestBase { protected abstract TableTestUtil getTableTestUtil(); - @Before - public void setup() { + @BeforeEach + void setup() { util = getTableTestUtil(); util.tableEnv() @@ -56,7 +56,7 @@ public void setup() { } @Test - public void testAggregateReduceConstants() { + void testAggregateReduceConstants() { util.verifyExecPlan( "SELECT\n" + " cat, gmt_date, SUM(cnt), count(*)\n" @@ -66,7 +66,7 @@ public void testAggregateReduceConstants() { } @Test - public void testAggregateReduceConstants2() { + void testAggregateReduceConstants2() { // current RelMdPredicates only look at columns that are projected without any function // applied, so 'SUBSTR(CAST(LOCALTIME AS VARCHAR), 1, 2)' will never be inferred as constant util.verifyExecPlan( @@ -78,7 +78,7 @@ public void testAggregateReduceConstants2() { } @Test - public void testAggregateReduceConstants3() { + void testAggregateReduceConstants3() { util.verifyExecPlan( "SELECT\n" + " gmt_date, ts, cat, SUBSTR(CAST(ts AS VARCHAR), 1, 2), SUM(cnt)\n" @@ -89,7 +89,7 @@ public void testAggregateReduceConstants3() { } @Test - public void testCalcMerge() { + void testCalcMerge() { util.verifyExecPlan( "SELECT * FROM ( \n" + " SELECT *, SUBSTR(CAST(LOCALTIME AS VARCHAR), 1, 2) hh\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/BroadcastJoinHintTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/BroadcastJoinHintTest.java index 1b93904339a42..23fd75908aea1 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/BroadcastJoinHintTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/BroadcastJoinHintTest.java @@ -21,7 +21,7 @@ import org.apache.flink.table.planner.hint.JoinStrategy; /** Test for Broadcast join hint. */ -public class BroadcastJoinHintTest extends JoinHintTestBase { +class BroadcastJoinHintTest extends JoinHintTestBase { @Override protected String getTestSingleJoinHint() { diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/JoinHintTestBase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/JoinHintTestBase.java index 829581d482322..eada3bb388173 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/JoinHintTestBase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/JoinHintTestBase.java @@ -36,14 +36,15 @@ import org.apache.calcite.rel.RelNode; import org.apache.calcite.sql.SqlExplainLevel; import org.apache.logging.log4j.util.Strings; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.List; import java.util.stream.Collectors; import scala.Enumeration; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static scala.runtime.BoxedUnit.UNIT; /** @@ -62,8 +63,8 @@ public abstract class JoinHintTestBase extends TableTestBase { .map(JoinStrategy::getJoinHintName) .collect(Collectors.toList()); - @Before - public void before() { + @BeforeEach + void before() { util = batchTestUtil(TableConfig.getDefault()); util.tableEnv() .executeSql( @@ -130,21 +131,21 @@ protected List getOtherJoinHints() { } @Test - public void testSimpleJoinHintWithLeftSideAsBuildSide() { + void testSimpleJoinHintWithLeftSideAsBuildSide() { String sql = "select /*+ %s(T1) */* from T1 join T2 on T1.a1 = T2.a2"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); } @Test - public void testSimpleJoinHintWithRightSideAsBuildSide() { + void testSimpleJoinHintWithRightSideAsBuildSide() { String sql = "select /*+ %s(T2) */* from T1 join T2 on T1.a1 = T2.a2"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); } @Test - public void testJoinHintWithMultiJoinAndFirstSideAsBuildSide1() { + void testJoinHintWithMultiJoinAndFirstSideAsBuildSide1() { // the T1 will be the build side in first join String sql = "select /*+ %s(T1, T2) */* from T1, T2, T3 where T1.a1 = T2.a2 and T1.b1 = T3.b3"; @@ -153,7 +154,7 @@ public void testJoinHintWithMultiJoinAndFirstSideAsBuildSide1() { } @Test - public void testJoinHintWithMultiJoinAndFirstSideAsBuildSide2() { + void testJoinHintWithMultiJoinAndFirstSideAsBuildSide2() { String sql = "select /*+ %s(T1, T2) */* from T1, T2, T3 where T1.a1 = T2.a2 and T2.b2 = T3.b3"; @@ -161,7 +162,7 @@ public void testJoinHintWithMultiJoinAndFirstSideAsBuildSide2() { } @Test - public void testJoinHintWithMultiJoinAndSecondThirdSideAsBuildSides1() { + void testJoinHintWithMultiJoinAndSecondThirdSideAsBuildSides1() { String sql = "select /*+ %s(T2, T3) */* from T1, T2, T3 where T1.a1 = T2.a2 and T1.b1 = T3.b3"; @@ -169,7 +170,7 @@ public void testJoinHintWithMultiJoinAndSecondThirdSideAsBuildSides1() { } @Test - public void testJoinHintWithMultiJoinAndSecondThirdSideAsBuildSides2() { + void testJoinHintWithMultiJoinAndSecondThirdSideAsBuildSides2() { String sql = "select /*+ %s(T2, T3) */* from T1, T2, T3 where T1.a1 = T2.a2 and T2.b2 = T3.b3"; @@ -177,7 +178,7 @@ public void testJoinHintWithMultiJoinAndSecondThirdSideAsBuildSides2() { } @Test - public void testJoinHintWithMultiJoinAndFirstThirdSideAsBuildSides() { + void testJoinHintWithMultiJoinAndFirstThirdSideAsBuildSides() { String sql = "select /*+ %s(T1, T3) */* from T1, T2, T3 where T1.a1 = T2.a2 and T2.b2 = T3.b3"; @@ -185,95 +186,90 @@ public void testJoinHintWithMultiJoinAndFirstThirdSideAsBuildSides() { } @Test - public void testJoinHintWithUnknownTable() { - thrown().expect(ValidationException.class); - thrown().expectMessage( - String.format( - "The options of following hints cannot match the name of input tables or views: \n`%s` in `%s`", - "T99", getTestSingleJoinHint())); + void testJoinHintWithUnknownTable() { String sql = "select /*+ %s(T99) */* from T1 join T2 on T1.a1 = T2.a2"; - verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); + assertThatThrownBy(() -> verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint()))) + .isInstanceOf(ValidationException.class) + .hasMessageContaining( + "The options of following hints cannot match the name of input tables or views: \n`%s` in `%s`", + "T99", getTestSingleJoinHint()); } @Test - public void testJoinHintWithUnknownTableNameMixedWithValidTableNames1() { - thrown().expect(ValidationException.class); - thrown().expectMessage( - String.format( - "The options of following hints cannot match the name of input tables or views: \n`%s` in `%s`", - "T99", getTestSingleJoinHint())); + void testJoinHintWithUnknownTableNameMixedWithValidTableNames1() { String sql = "select /*+ %s(T1, T99) */* from T1 join T2 on T1.a1 = T2.a2"; - verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); + assertThatThrownBy(() -> verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint()))) + .isInstanceOf(ValidationException.class) + .hasMessageContaining( + "The options of following hints cannot match the name of input tables or views: \n`%s` in `%s`", + "T99", getTestSingleJoinHint()); } @Test - public void testJoinHintWithUnknownTableNameMixedWithValidTableNames2() { - thrown().expect(ValidationException.class); - thrown().expectMessage( - String.format( - "The options of following hints cannot match the name of input tables or views: \n`%s` in `%s`", - "T99", getTestSingleJoinHint())); + void testJoinHintWithUnknownTableNameMixedWithValidTableNames2() { String sql = "select /*+ %s(T1, T99, T2) */* from T1 join T2 on T1.a1 = T2.a2"; - verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); + assertThatThrownBy(() -> verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint()))) + .isInstanceOf(ValidationException.class) + .hasMessageContaining( + "The options of following hints cannot match the name of input tables or views: \n`%s` in `%s`", + "T99", getTestSingleJoinHint()); } @Test - public void testJoinHintWithMultiUnknownTableNamesMixedWithValidTableNames() { - thrown().expect(ValidationException.class); - thrown().expectMessage( - String.format( - "The options of following hints cannot match the name of input tables or views: \n`%s` in `%s`", - "T98, T99", getTestSingleJoinHint())); + void testJoinHintWithMultiUnknownTableNamesMixedWithValidTableNames() { String sql = "select /*+ %s(T1, T99, T98) */* from T1 join T2 on T1.a1 = T2.a2"; - verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); + assertThatThrownBy(() -> verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint()))) + .isInstanceOf(ValidationException.class) + .hasMessageContaining( + "The options of following hints cannot match the name of input tables or views: \n`%s` in `%s`", + "T98, T99", getTestSingleJoinHint()); } @Test - public void testJoinHintWithView() { + void testJoinHintWithView() { String sql = "select /*+ %s(V4) */* from T1 join V4 on T1.a1 = V4.a4"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); } @Test - public void testJoinHintWithUnknownView() { - thrown().expect(ValidationException.class); - thrown().expectMessage( - String.format( - "The options of following hints cannot match the name of input tables or views: \n`%s` in `%s`", - "V99", getTestSingleJoinHint())); + void testJoinHintWithUnknownView() { String sql = "select /*+ %s(V99) */* from T1 join V4 on T1.a1 = V4.a4"; - verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); + assertThatThrownBy(() -> verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint()))) + .isInstanceOf(ValidationException.class) + .hasMessageContaining( + "The options of following hints cannot match the name of input tables or views: \n`%s` in `%s`", + "V99", getTestSingleJoinHint()); } @Test - public void testJoinHintWithEquiPred() { + void testJoinHintWithEquiPred() { String sql = "select /*+ %s(T1) */* from T1, T2 where T1.a1 = T2.a2"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); } @Test - public void testJoinHintWithEquiPredAndFilter() { + void testJoinHintWithEquiPredAndFilter() { String sql = "select /*+ %s(T1) */* from T1, T2 where T1.a1 = T2.a2 and T1.a1 > 1"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); } @Test - public void testJoinHintWithEquiAndLocalPred() { + void testJoinHintWithEquiAndLocalPred() { String sql = "select /*+ %s(T1) */* from T1 inner join T2 on T1.a1 = T2.a2 and T1.a1 < 1"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); } @Test - public void testJoinHintWithEquiAndNonEquiPred() { + void testJoinHintWithEquiAndNonEquiPred() { String sql = "select /*+ %s(T1) */* from T1 inner join T2 on T1.b1 = T2.b2 and T1.a1 < 1 and T1.a1 < T2.a2"; @@ -281,56 +277,56 @@ public void testJoinHintWithEquiAndNonEquiPred() { } @Test - public void testJoinHintWithoutJoinPred() { + void testJoinHintWithoutJoinPred() { String sql = "select /*+ %s(T1) */* from T1, T2"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); } @Test - public void testJoinHintWithNonEquiPred() { + void testJoinHintWithNonEquiPred() { String sql = "select /*+ %s(T1) */* from T1 inner join T2 on T1.a1 > T2.a2"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); } @Test - public void testJoinHintWithLeftJoinAndLeftSideAsBuildSide() { + void testJoinHintWithLeftJoinAndLeftSideAsBuildSide() { String sql = "select /*+ %s(T1) */* from T1 left join T2 on T1.a1 = T2.a2"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); } @Test - public void testJoinHintWithLeftJoinAndRightSideAsBuildSide() { + void testJoinHintWithLeftJoinAndRightSideAsBuildSide() { String sql = "select /*+ %s(T2) */* from T1 left join T2 on T1.a1 = T2.a2"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); } @Test - public void testJoinHintWithRightJoinAndLeftSideAsBuildSide() { + void testJoinHintWithRightJoinAndLeftSideAsBuildSide() { String sql = "select /*+ %s(T1) */* from T1 right join T2 on T1.a1 = T2.a2"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); } @Test - public void testJoinHintWithRightJoinAndRightSideAsBuildSide() { + void testJoinHintWithRightJoinAndRightSideAsBuildSide() { String sql = "select /*+ %s(T2) */* from T1 right join T2 on T1.a1 = T2.a2"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); } @Test - public void testJoinHintWithFullJoinAndLeftSideAsBuildSide() { + void testJoinHintWithFullJoinAndLeftSideAsBuildSide() { String sql = "select /*+ %s(T1) */* from T1 full join T2 on T1.a1 = T2.a2"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); } @Test - public void testJoinHintWithFullJoinAndRightSideAsBuildSide() { + void testJoinHintWithFullJoinAndRightSideAsBuildSide() { String sql = "select /*+ %s(T2) */* from T1 full join T2 on T1.a1 = T2.a2"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); @@ -339,7 +335,7 @@ public void testJoinHintWithFullJoinAndRightSideAsBuildSide() { // TODO currently join hint is not supported on SEMI join, it will use default join strategy by // planner @Test - public void testJoinHintWithSemiJoinAndLeftSideAsBuildSide() { + void testJoinHintWithSemiJoinAndLeftSideAsBuildSide() { String sql = "select /*+ %s(T1) */* from T1 where a1 in (select a2 from T2)"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); @@ -348,7 +344,7 @@ public void testJoinHintWithSemiJoinAndLeftSideAsBuildSide() { // TODO currently join hint is not supported on SEMI join, it will use default join strategy by // planner @Test - public void testJoinHintWithSemiJoinAndRightSideAsBuildSide() { + void testJoinHintWithSemiJoinAndRightSideAsBuildSide() { String sql = "select /*+ %s(T2) */* from T1 where a1 in (select a2 from T2)"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); @@ -357,7 +353,7 @@ public void testJoinHintWithSemiJoinAndRightSideAsBuildSide() { // TODO currently join hint is not supported on ANTI join, it will use default join strategy by // planner @Test - public void testJoinHintWithAntiJoinAndLeftSideAsBuildSide() { + void testJoinHintWithAntiJoinAndLeftSideAsBuildSide() { String sql = "select /*+ %s(T1) */* from T1 where a1 not in (select a2 from T2)"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); @@ -366,14 +362,14 @@ public void testJoinHintWithAntiJoinAndLeftSideAsBuildSide() { // TODO currently join hint is not supported on ANTI join, it will use default join strategy by // planner @Test - public void testJoinHintWithAntiJoinAndRightSideAsBuildSide() { + void testJoinHintWithAntiJoinAndRightSideAsBuildSide() { String sql = "select /*+ %s(T2) */* from T1 where a1 not in (select a2 from T2)"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); } @Test - public void testJoinHintWithMultiArgsAndLeftSideFirst() { + void testJoinHintWithMultiArgsAndLeftSideFirst() { // the first arg will be chosen as the build side String sql = "select /*+ %s(T1, T2) */* from T1 right join T2 on T1.a1 = T2.a2"; @@ -381,7 +377,7 @@ public void testJoinHintWithMultiArgsAndLeftSideFirst() { } @Test - public void testJoinHintWithMultiArgsAndRightSideFirst() { + void testJoinHintWithMultiArgsAndRightSideFirst() { // the first arg will be chosen as the build side String sql = "select /*+ %s(T2, T1) */* from T1 right join T2 on T1.a1 = T2.a2"; @@ -389,7 +385,7 @@ public void testJoinHintWithMultiArgsAndRightSideFirst() { } @Test - public void testMultiJoinHints() { + void testMultiJoinHints() { // the first join hint will be chosen String sql = "select /*+ %s(T1), %s */* from T1 join T2 on T1.a1 = T2.a2"; @@ -404,7 +400,7 @@ public void testMultiJoinHints() { } @Test - public void testMultiJoinHintsWithTheFirstOneIsInvalid() { + void testMultiJoinHintsWithTheFirstOneIsInvalid() { // the first join hint is invalid because it is not equi join except NEST_LOOP String sql = "select /*+ %s(T1), NEST_LOOP(T1) */* from T1 join T2 on T1.a1 > T2.a2"; @@ -412,7 +408,7 @@ public void testMultiJoinHintsWithTheFirstOneIsInvalid() { } @Test - public void testJoinHintWithoutAffectingJoinInViewWhileArgsCanBeFoundInOuterJoin() { + void testJoinHintWithoutAffectingJoinInViewWhileArgsCanBeFoundInOuterJoin() { // the join in V2 will use the planner's default join strategy, // and the join between T1 and V5 will use the tested join hint String sql = "select /*+ %s(T1)*/T1.* from T1 join V5 on T1.a1 = V5.a1"; @@ -421,7 +417,7 @@ public void testJoinHintWithoutAffectingJoinInViewWhileArgsCanBeFoundInOuterJoin } @Test - public void testJoinHintWithoutAffectingJoinInViewWhileOuterQueryIsNotJoin() { + void testJoinHintWithoutAffectingJoinInViewWhileOuterQueryIsNotJoin() { // the join in V2 will use the planner's default join strategy, // and the join between T1 and V5 will use the tested join hint String sql = "select /*+ %s(T1)*/* from V5"; @@ -430,7 +426,7 @@ public void testJoinHintWithoutAffectingJoinInViewWhileOuterQueryIsNotJoin() { } @Test - public void testJoinHintWithoutAffectingJoinInViewWhileRootOfViewIsFilter() { + void testJoinHintWithoutAffectingJoinInViewWhileRootOfViewIsFilter() { // the join in V2 will use the planner's default join strategy, // and the join between T1 and V2 will use the tested join hint util.tableEnv() @@ -443,7 +439,7 @@ public void testJoinHintWithoutAffectingJoinInViewWhileRootOfViewIsFilter() { } @Test - public void testJoinHintWithSimpleSumInSelectList() { + void testJoinHintWithSimpleSumInSelectList() { String sql = "select /*+ %s(T1)*/T1.b1, sum(T1.a1) from T1 join T2 on T1.b1 = T2.b2 group by T1.b1"; @@ -451,7 +447,7 @@ public void testJoinHintWithSimpleSumInSelectList() { } @Test - public void testJoinHintWithCastInSelectList() { + void testJoinHintWithCastInSelectList() { String sql = "select /*+ %s(T1)*/T1.b1, cast(T1.a1 as int) from T1 join T2 on T1.b1 = T2.b2"; @@ -459,7 +455,7 @@ public void testJoinHintWithCastInSelectList() { } @Test - public void testJoinHintWithoutAffectingJoinInSubQueryWhileArgsCanBeFoundInOuterJoin() { + void testJoinHintWithoutAffectingJoinInSubQueryWhileArgsCanBeFoundInOuterJoin() { // the join in sub-query will use the planner's default join strategy, // and the join outside will use the tested join hint String sql = @@ -469,14 +465,14 @@ public void testJoinHintWithoutAffectingJoinInSubQueryWhileArgsCanBeFoundInOuter } @Test - public void testJoinHintWithoutAffectingJoinInSubQueryWhileOuterQueryIsNotJoin() { + void testJoinHintWithoutAffectingJoinInSubQueryWhileOuterQueryIsNotJoin() { String sql = "select /*+ %s(T1)*/* from (select T1.* from T1 join T2 on T1.a1 = T2.a2)"; verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint())); } @Test - public void testJoinHintWithoutAffectingJoinInSubQueryWhileRootOfSubQueryIsFilter() { + void testJoinHintWithoutAffectingJoinInSubQueryWhileRootOfSubQueryIsFilter() { String sql = "select /*+ %s(T1)*/* from (select T1.* from T1 join T2 on T1.a1 = T2.a2 where T1.b1 = 'abc')"; @@ -484,7 +480,7 @@ public void testJoinHintWithoutAffectingJoinInSubQueryWhileRootOfSubQueryIsFilte } @Test - public void testJoinHintWithoutAffectingJoinInSubQueryWhileContainsSumInQueryBlock() { + void testJoinHintWithoutAffectingJoinInSubQueryWhileContainsSumInQueryBlock() { String sql = "select /*+ %s(T1)*/T4.a1, (select count(*) from T1 join T3 on T1.a1 = T3.a3) as cnt from (select T1.* from T1 join T2 on T1.a1 = T2.a2 where T1.b1 = 'abc') T4"; @@ -492,7 +488,7 @@ public void testJoinHintWithoutAffectingJoinInSubQueryWhileContainsSumInQueryBlo } @Test - public void testJoinHintWithoutAffectingJoinInSubQueryWhileContainsUnionAndJoinInSelectList() { + void testJoinHintWithoutAffectingJoinInSubQueryWhileContainsUnionAndJoinInSelectList() { String sql = "select /*+ %s(T1)*/T4.a1, (select count(*) from T1 join ((select T1.a1 as a3 from T1) union (select a3 from T3)) T3 on T1.a1 = T3.a3 where T3.a3 = 1) as cnt from (select T1.* from T1 join T2 on T1.a1 = T2.a2) T4"; @@ -500,7 +496,7 @@ public void testJoinHintWithoutAffectingJoinInSubQueryWhileContainsUnionAndJoinI } @Test - public void testJoinHintWithoutAffectingJoinInSubQueryWhileContainsUnionAndJoinInSelectFrom() { + void testJoinHintWithoutAffectingJoinInSubQueryWhileContainsUnionAndJoinInSelectFrom() { String sql = "select /*+ %s(T1)*/T4.a1 from (select T1.* from T1 join ((select T1.a1 as a2 from T1) union (select a2 from T2)) T2 on T1.a1 = T2.a2) T4"; @@ -508,7 +504,7 @@ public void testJoinHintWithoutAffectingJoinInSubQueryWhileContainsUnionAndJoinI } @Test - public void testJoinHintWithTableAlias() { + void testJoinHintWithTableAlias() { // the join in sub-query will use the planner's default join strategy, // and the join between T1 and alias V2 will use the tested join hint String sql = @@ -518,7 +514,7 @@ public void testJoinHintWithTableAlias() { } @Test - public void testJoinHintsWithMultiSameJoinHintsAndSingleArg() { + void testJoinHintsWithMultiSameJoinHintsAndSingleArg() { // the first join hint will be chosen and T1 will be chosen as the build side String sql = "select /*+ %s(T1), %s(T2) */* from T1 join T2 on T1.a1 = T2.a2"; @@ -526,7 +522,7 @@ public void testJoinHintsWithMultiSameJoinHintsAndSingleArg() { } @Test - public void testJoinHintsWithDuplicatedArgs() { + void testJoinHintsWithDuplicatedArgs() { // T1 will be chosen as the build side String sql = "select /*+ %s(T1, T1) */* from T1 join T2 on T1.a1 = T2.a2"; @@ -534,7 +530,7 @@ public void testJoinHintsWithDuplicatedArgs() { } @Test - public void testJoinHintsWithMultiSameJoinHintsAndMultiArgs() { + void testJoinHintsWithMultiSameJoinHintsAndMultiArgs() { // the first join hint will be chosen and T1 will be chosen as the build side String sql = "select /*+ %s(T1, T2), %s(T2, T1) */* from T1 join T2 on T1.a1 = T2.a2"; @@ -542,16 +538,22 @@ public void testJoinHintsWithMultiSameJoinHintsAndMultiArgs() { } @Test - public void testJoinHintsWithMultiHintsThrowException() { - thrown().expect(SqlParserException.class); - thrown().expectMessage("SQL parse failed."); + void testJoinHintsWithMultiHintsThrowException() { String sql = "select /*+ %s(T1) */ /*+ %s(T2) */ * from T1 join T2 on T1.a1 = T2.a2"; - verifyRelPlanByCustom(String.format(sql, getTestSingleJoinHint(), getTestSingleJoinHint())); + assertThatThrownBy( + () -> + verifyRelPlanByCustom( + String.format( + sql, + getTestSingleJoinHint(), + getTestSingleJoinHint()))) + .isInstanceOf(SqlParserException.class) + .hasMessageContaining("SQL parse failed."); } @Test - public void testJoinHintWithDisabledOperator() { + void testJoinHintWithDisabledOperator() { util.tableEnv() .getConfig() .set( @@ -564,7 +566,7 @@ public void testJoinHintWithDisabledOperator() { } @Test - public void testJoinHintsWithUnion() { + void testJoinHintsWithUnion() { // there are two query blocks and join hints are independent String sql = "select /*+ %s(T1) */* from T1 join T2 on T1.a1 = T2.a2 union select /*+ %s(T3) */* from T3 join T1 on T3.a3 = T1.a1"; @@ -573,7 +575,7 @@ public void testJoinHintsWithUnion() { } @Test - public void testJoinHintsWithFilter() { + void testJoinHintsWithFilter() { // there are two query blocks and join hints are independent String sql = "select /*+ %s(T1) */* from T1 join T2 on T1.a1 = T2.a2 where T1.a1 > 5"; @@ -581,7 +583,7 @@ public void testJoinHintsWithFilter() { } @Test - public void testJoinHintsWithCalc() { + void testJoinHintsWithCalc() { // there are two query blocks and join hints are independent String sql = "select /*+ %s(T1) */a1 + 1, a1 * 10 from T1 join T2 on T1.a1 = T2.a2"; @@ -589,7 +591,7 @@ public void testJoinHintsWithCalc() { } @Test - public void testJoinHintInView() { + void testJoinHintInView() { // the build side in view is left util.tableEnv() .executeSql( @@ -604,7 +606,7 @@ public void testJoinHintInView() { } @Test - public void testJoinHintInMultiLevelView() { + void testJoinHintInMultiLevelView() { // the inside view keeps multi alias // the build side in this view is left util.tableEnv() @@ -627,7 +629,7 @@ public void testJoinHintInMultiLevelView() { } @Test - public void testJoinHintsOnSameViewWithoutReusingView() { + void testJoinHintsOnSameViewWithoutReusingView() { // the build side in this view is left util.tableEnv() .executeSql( @@ -671,7 +673,7 @@ public void testJoinHintsOnSameViewWithoutReusingView() { } @Test - public void testJoinHintsOnSameViewWithReusingView() { + void testJoinHintsOnSameViewWithReusingView() { util.tableEnv() .getConfig() .set( @@ -722,7 +724,7 @@ public void testJoinHintsOnSameViewWithReusingView() { } @Test - public void testJoinHintsOnSameViewWithoutReusingViewBecauseDifferentJoinHints() { + void testJoinHintsOnSameViewWithoutReusingViewBecauseDifferentJoinHints() { util.tableEnv() .getConfig() .set( @@ -781,7 +783,7 @@ public void testJoinHintsOnSameViewWithoutReusingViewBecauseDifferentJoinHints() } @Test - public void testJoinHintWithSubStringViewName1() { + void testJoinHintWithSubStringViewName1() { util.tableEnv() .executeSql( String.format( @@ -802,7 +804,7 @@ public void testJoinHintWithSubStringViewName1() { } @Test - public void testJoinHintWithSubStringViewName2() { + void testJoinHintWithSubStringViewName2() { util.tableEnv() .executeSql( String.format( @@ -823,14 +825,14 @@ public void testJoinHintWithSubStringViewName2() { } @Test - public void testJoinHintWithoutCaseSensitive() { + void testJoinHintWithoutCaseSensitive() { String sql = "select /*+ %s(T1) */* from T1 join T2 on T1.a1 = T2.a2"; verifyRelPlanByCustom(String.format(sql, buildCaseSensitiveStr(getTestSingleJoinHint()))); } @Test - public void testJoinHintWithJoinHintInSubQuery() { + void testJoinHintWithJoinHintInSubQuery() { String sql = "select * from T1 WHERE a1 IN (select /*+ %s(T2) */ a2 from T2 join T3 on T2.a2 = T3.a3)"; @@ -838,7 +840,7 @@ public void testJoinHintWithJoinHintInSubQuery() { } @Test - public void testJoinHintWithJoinHintInCorrelateAndWithFilter() { + void testJoinHintWithJoinHintInCorrelateAndWithFilter() { String sql = "select * from T1 WHERE a1 IN (select /*+ %s(T2) */ a2 from T2 join T3 on T2.a2 = T3.a3 where T1.a1 = T2.a2)"; @@ -846,7 +848,7 @@ public void testJoinHintWithJoinHintInCorrelateAndWithFilter() { } @Test - public void testJoinHintWithJoinHintInCorrelateAndWithProject() { + void testJoinHintWithJoinHintInCorrelateAndWithProject() { String sql = "select * from T1 WHERE a1 IN (select /*+ %s(T2) */ a2 + T1.a1 from T2 join T3 on T2.a2 = T3.a3)"; @@ -854,7 +856,7 @@ public void testJoinHintWithJoinHintInCorrelateAndWithProject() { } @Test - public void testJoinHintWithJoinHintInCorrelateAndWithAgg() { + void testJoinHintWithJoinHintInCorrelateAndWithAgg() { String sql = "select * from T1 WHERE a1 IN (select /*+ %s(T2) */ count(T2.a2) from T2 join T1 on T2.a2 = T1.a1 group by T1.a1)"; @@ -862,7 +864,7 @@ public void testJoinHintWithJoinHintInCorrelateAndWithAgg() { } @Test - public void testJoinHintWithJoinHintInCorrelateAndWithSortLimit() { + void testJoinHintWithJoinHintInCorrelateAndWithSortLimit() { String sql = "select * from T1 WHERE a1 IN (select /*+ %s(T2) */ T2.a2 from T2 join T1 on T2.a2 = T1.a1 order by T1.a1 limit 10)"; diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/NestLoopJoinHintTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/NestLoopJoinHintTest.java index 1daf888052e02..2e70b6abe5fce 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/NestLoopJoinHintTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/NestLoopJoinHintTest.java @@ -21,7 +21,7 @@ import org.apache.flink.table.planner.hint.JoinStrategy; /** Test for Nest Loop join hint. */ -public class NestLoopJoinHintTest extends JoinHintTestBase { +class NestLoopJoinHintTest extends JoinHintTestBase { @Override protected String getTestSingleJoinHint() { diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/ShuffleHashJoinHintTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/ShuffleHashJoinHintTest.java index cc9f2be89e212..42e512a7866e9 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/ShuffleHashJoinHintTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/ShuffleHashJoinHintTest.java @@ -21,7 +21,7 @@ import org.apache.flink.table.planner.hint.JoinStrategy; /** Test for Shuffle Hash join hint. */ -public class ShuffleHashJoinHintTest extends JoinHintTestBase { +class ShuffleHashJoinHintTest extends JoinHintTestBase { @Override protected String getTestSingleJoinHint() { diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/ShuffleMergeJoinHintTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/ShuffleMergeJoinHintTest.java index deaa6c97b881a..a28be93a79eaf 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/ShuffleMergeJoinHintTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/hints/batch/ShuffleMergeJoinHintTest.java @@ -21,7 +21,7 @@ import org.apache.flink.table.planner.hint.JoinStrategy; /** Test for Shuffle Merge join hint. */ -public class ShuffleMergeJoinHintTest extends JoinHintTestBase { +class ShuffleMergeJoinHintTest extends JoinHintTestBase { @Override protected String getTestSingleJoinHint() { diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/UnsupportedNodesInPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/UnsupportedNodesInPlanTest.java index 6f1a0adce9de5..39540efb0efea 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/UnsupportedNodesInPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/UnsupportedNodesInPlanTest.java @@ -23,15 +23,15 @@ import org.apache.flink.table.api.TableEnvironment; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThatThrownBy; /** Tests for deserialising invalid {@link org.apache.flink.table.api.CompiledPlan}. */ -public class UnsupportedNodesInPlanTest extends TableTestBase { +class UnsupportedNodesInPlanTest extends TableTestBase { @Test - public void testInvalidType() { + void testInvalidType() { final TableEnvironment tEnv = TableEnvironment.create(EnvironmentSettings.inStreamingMode()); assertThatThrownBy( diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/operator/BatchOperatorNameTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/operator/BatchOperatorNameTest.java index 80c8baa66e0d2..bd0bf014d922b 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/operator/BatchOperatorNameTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/operator/BatchOperatorNameTest.java @@ -30,20 +30,20 @@ import org.apache.flink.table.types.logical.LogicalType; import org.apache.flink.types.Row; -import org.junit.Test; +import org.junit.jupiter.api.TestTemplate; import java.util.Optional; /** Tests for verifying name and description of batch sql operator. */ -public class BatchOperatorNameTest extends OperatorNameTestBase { +class BatchOperatorNameTest extends OperatorNameTestBase { @Override protected TableTestUtil getTableTestUtil() { return batchTestUtil(TableConfig.getDefault()); } - @Test - public void testBoundedStreamScan() { + @TestTemplate + void testBoundedStreamScan() { final DataStream dataStream = util.getStreamEnv().fromElements(1, 2, 3, 4, 5); TableTestUtil.createTemporaryView( tEnv, @@ -56,23 +56,23 @@ public void testBoundedStreamScan() { } /** Verify Expand, HashAggregate. */ - @Test - public void testHashAggregate() { + @TestTemplate + void testHashAggregate() { createTestSource(); verifyQuery("SELECT a, " + "count(distinct b) as b " + "FROM MyTable GROUP BY a"); } /** Verify Sort, SortAggregate. */ - @Test - public void testSortAggregate() { + @TestTemplate + void testSortAggregate() { tEnv.getConfig().set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashAgg"); createTestSource(); verifyQuery("SELECT a, " + "count(distinct b) as b " + "FROM MyTable GROUP BY a"); } /** Verify SortWindowAggregate. */ - @Test - public void testSortWindowAggregate() { + @TestTemplate + void testSortWindowAggregate() { createSourceWithTimeAttribute(); verifyQuery( "SELECT\n" @@ -84,14 +84,14 @@ public void testSortWindowAggregate() { } /** Verify HashJoin. */ - @Test - public void testHashJoin() { + @TestTemplate + void testHashJoin() { testJoinInternal(); } /** Verify NestedLoopJoin. */ - @Test - public void testNestedLoopJoin() { + @TestTemplate + void testNestedLoopJoin() { tEnv.getConfig() .set( ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, @@ -100,8 +100,8 @@ public void testNestedLoopJoin() { } /** Verify SortMergeJoin. */ - @Test - public void testSortMergeJoin() { + @TestTemplate + void testSortMergeJoin() { tEnv.getConfig() .set( ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, @@ -110,8 +110,8 @@ public void testSortMergeJoin() { } /** Verify MultiInput. */ - @Test - public void testMultiInput() { + @TestTemplate + void testMultiInput() { createTestSource("A"); createTestSource("B"); createTestSource("C"); @@ -119,21 +119,21 @@ public void testMultiInput() { } /** Verify Limit. */ - @Test - public void testLimit() { + @TestTemplate + void testLimit() { createTestSource(); verifyQuery("select * from MyTable limit 10"); } /** Verify SortLimit. */ - @Test - public void testSortLimit() { + @TestTemplate + void testSortLimit() { createTestSource(); verifyQuery("select * from MyTable order by a limit 10"); } - @Test - public void testLegacySourceSink() { + @TestTemplate + void testLegacySourceSink() { TableSchema schema = TestLegacyFilterableTableSource.defaultSchema(); TestLegacyFilterableTableSource.createTemporaryTable( tEnv, @@ -153,8 +153,8 @@ public void testLegacySourceSink() { verifyInsert("insert into MySink select * from MySource"); } - @Test - public void testMatch() { + @TestTemplate + void testMatch() { createSourceWithTimeAttribute(); String sql = "SELECT T.aid, T.bid, T.cid\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/operator/OperatorNameTestBase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/operator/OperatorNameTestBase.java index 82c3fbfa0558a..991d42c88b0f3 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/operator/OperatorNameTestBase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/operator/OperatorNameTestBase.java @@ -25,30 +25,32 @@ import org.apache.flink.table.planner.utils.TableFunc1; import org.apache.flink.table.planner.utils.TableTestBase; import org.apache.flink.table.planner.utils.TableTestUtil; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameter; +import org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameters; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; import java.util.Arrays; import java.util.Collections; import java.util.List; /** Base class for verifying name and description of SQL operators. */ -@RunWith(Parameterized.class) -public abstract class OperatorNameTestBase extends TableTestBase { - @Parameterized.Parameter public boolean isNameSimplifyEnabled; +@ExtendWith(ParameterizedTestExtension.class) +abstract class OperatorNameTestBase extends TableTestBase { + @Parameter private boolean isNameSimplifyEnabled; protected TableTestUtil util; protected TableEnvironment tEnv; - @Parameterized.Parameters(name = "isNameSimplifyEnabled={0}") - public static List testData() { + @Parameters(name = "isNameSimplifyEnabled={0}") + private static List testData() { return Arrays.asList(true, false); } - @Before - public void setup() { + @BeforeEach + void setup() { util = getTableTestUtil(); util.getStreamEnv().setParallelism(2); tEnv = util.getTableEnv(); @@ -73,16 +75,16 @@ protected void verifyInsert(String statement) { } /** Verify Correlate and Calc. */ - @Test - public void testCorrelate() { + @TestTemplate + void testCorrelate() { createTestSource(); util.addTemporarySystemFunction("func1", new TableFunc1()); verifyQuery("SELECT s FROM MyTable, LATERAL TABLE(func1(c)) AS T(s)"); } /** Verify LookUpJoin. */ - @Test - public void testLookupJoin() { + @TestTemplate + void testLookupJoin() { createSourceWithTimeAttribute(); String srcTableB = "CREATE TABLE LookupTable (\n" @@ -99,8 +101,8 @@ public void testLookupJoin() { } /** Verify GroupWindowAggregate. */ - @Test - public void testGroupWindowAggregate() { + @TestTemplate + void testGroupWindowAggregate() { createSourceWithTimeAttribute(); verifyQuery( "SELECT\n" @@ -112,8 +114,8 @@ public void testGroupWindowAggregate() { } /** Verify OverAggregate. */ - @Test - public void testOverAggregate() { + @TestTemplate + void testOverAggregate() { createSourceWithTimeAttribute(); String sql = "SELECT b,\n" @@ -124,8 +126,8 @@ public void testOverAggregate() { } /** Verify Rank. */ - @Test - public void testRank() { + @TestTemplate + void testRank() { createTestSource(); String sql = "SELECT a, row_num\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/operator/StreamOperatorNameTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/operator/StreamOperatorNameTest.java index c49e5ab31bea2..6adc3f69f92a8 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/operator/StreamOperatorNameTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/operator/StreamOperatorNameTest.java @@ -32,8 +32,8 @@ import org.apache.flink.table.types.logical.LogicalType; import org.apache.flink.types.Row; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; import java.util.Optional; @@ -41,7 +41,7 @@ import static org.apache.flink.table.api.Expressions.call; /** Tests for verifying name and description of stream sql operator name. */ -public class StreamOperatorNameTest extends OperatorNameTestBase { +class StreamOperatorNameTest extends OperatorNameTestBase { private StreamTableTestUtil util; @@ -50,15 +50,15 @@ protected TableTestUtil getTableTestUtil() { return streamTestUtil(TableConfig.getDefault()); } - @Before - public void setup() { + @BeforeEach + void setup() { super.setup(); util = (StreamTableTestUtil) super.util; } /** Verify DropUpdateBefore. */ - @Test - public void testDropUpdateBefore() throws Exception { + @TestTemplate + void testDropUpdateBefore() { util.getStreamEnv().setParallelism(2); @@ -92,8 +92,8 @@ public void testDropUpdateBefore() throws Exception { } /** Verify ChangelogNormalize and SinkMaterialize. */ - @Test - public void testChangelogNormalize() throws Exception { + @TestTemplate + void testChangelogNormalize() throws Exception { util.getStreamEnv().setParallelism(2); @@ -127,8 +127,8 @@ public void testChangelogNormalize() throws Exception { } /** Verify Deduplicate. */ - @Test - public void testDeduplicate() { + @TestTemplate + void testDeduplicate() { createSourceWithTimeAttribute(); verifyQuery( "SELECT a, b, c FROM " @@ -142,8 +142,8 @@ public void testDeduplicate() { * Verify Expand, MiniBatchAssigner, LocalGroupAggregate, GlobalGroupAggregate, * IncrementalAggregate. */ - @Test - public void testIncrementalAggregate() { + @TestTemplate + void testIncrementalAggregate() { util.enableMiniBatch(); tEnv.getConfig() .set(OptimizerConfigOptions.TABLE_OPTIMIZER_DISTINCT_AGG_SPLIT_ENABLED, true); @@ -152,14 +152,14 @@ public void testIncrementalAggregate() { } /** Verify GroupAggregate. */ - @Test - public void testGroupAggregate() { + @TestTemplate + void testGroupAggregate() { testGroupAggregateInternal(); } /** Verify RowConversion, TableGroupAggregate. */ - @Test - public void testTableGroupAggregate() { + @TestTemplate + void testTableGroupAggregate() { final DataStream dataStream = util.getStreamEnv().fromElements(1, 2, 3, 4, 5); TableTestUtil.createTemporaryView( tEnv, @@ -178,8 +178,8 @@ public void testTableGroupAggregate() { } /** Verify IntervalJoin. */ - @Test - public void testIntervalJoin() { + @TestTemplate + void testIntervalJoin() { createSourceWithTimeAttribute("A"); createSourceWithTimeAttribute("B"); verifyQuery( @@ -189,8 +189,8 @@ public void testIntervalJoin() { } /** Verify IntervalJoin. */ - @Test - public void testIntervalJoinNegativeWindow() { + @TestTemplate + void testIntervalJoinNegativeWindow() { createSourceWithTimeAttribute("A"); createSourceWithTimeAttribute("B"); verifyQuery( @@ -200,13 +200,13 @@ public void testIntervalJoinNegativeWindow() { } /** Verify Join. */ - @Test - public void testJoin() { + @TestTemplate + void testJoin() { testJoinInternal(); } - @Test - public void testMatch() { + @TestTemplate + void testMatch() { createSourceWithTimeAttribute(); String sql = "SELECT T.aid, T.bid, T.cid\n" @@ -225,8 +225,8 @@ public void testMatch() { verifyQuery(sql); } - @Test - public void testTemporalJoin() { + @TestTemplate + void testTemporalJoin() { tEnv.executeSql( "CREATE TABLE Orders (\n" + " amount INT,\n" @@ -257,21 +257,21 @@ public void testTemporalJoin() { + "WHERE o.currency = r.currency "); } - @Test - public void testTemporalSortOnProcTime() { + @TestTemplate + void testTemporalSortOnProcTime() { createSourceWithTimeAttribute(); verifyQuery("SELECT a FROM MyTable order by proctime, c"); } - @Test - public void testTemporalSortOnEventTime() { + @TestTemplate + void testTemporalSortOnEventTime() { createSourceWithTimeAttribute(); verifyQuery("SELECT a FROM MyTable order by rowtime, c"); } /** Verify WindowJoin, WindowRank, WindowAggregate, WindowDeduplicate. */ - @Test - public void testWindowAggregate() { + @TestTemplate + void testWindowAggregate() { tEnv.getConfig() .set(OptimizerConfigOptions.TABLE_OPTIMIZER_AGG_PHASE_STRATEGY, "ONE_PHASE"); createSourceWithTimeAttribute(); @@ -288,8 +288,8 @@ public void testWindowAggregate() { } /** Verify LocalWindowAggregate, GlobalWindowAggregate. */ - @Test - public void testLocalGlobalWindowAggregate() { + @TestTemplate + void testLocalGlobalWindowAggregate() { createSourceWithTimeAttribute(); verifyQuery( "SELECT\n" @@ -304,8 +304,8 @@ public void testLocalGlobalWindowAggregate() { } /** Verify WindowJoin. */ - @Test - public void testWindowJoin() { + @TestTemplate + void testWindowJoin() { createSourceWithTimeAttribute("MyTable"); createSourceWithTimeAttribute("MyTable2"); verifyQuery( @@ -342,8 +342,8 @@ public void testWindowJoin() { } /** Verify WindowTableFunction and WindowRank. */ - @Test - public void testWindowRank() { + @TestTemplate + void testWindowRank() { createSourceWithTimeAttribute(); verifyQuery( "select\n" @@ -361,8 +361,8 @@ public void testWindowRank() { } /** Verify WindowDeduplicate. */ - @Test - public void testWindowDeduplicate() { + @TestTemplate + void testWindowDeduplicate() { createSourceWithTimeAttribute(); verifyQuery( "select\n" @@ -380,8 +380,8 @@ public void testWindowDeduplicate() { } /** Verify LegacySource and LegacySink. */ - @Test - public void testLegacySourceSink() { + @TestTemplate + void testLegacySourceSink() { TableSchema schema = TestLegacyFilterableTableSource.defaultSchema(); TestLegacyFilterableTableSource.createTemporaryTable( tEnv, diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/processor/MultipleInputNodeCreationProcessorTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/processor/MultipleInputNodeCreationProcessorTest.java index 213982425cef3..31b7ebf7273ce 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/processor/MultipleInputNodeCreationProcessorTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/processor/MultipleInputNodeCreationProcessorTest.java @@ -32,9 +32,10 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; import org.apache.flink.table.planner.utils.TableTestUtil; +import org.apache.flink.testutils.junit.utils.TempDirUtils; import org.apache.flink.util.FileUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; import java.io.IOException; @@ -42,13 +43,13 @@ import static org.assertj.core.api.Assertions.assertThat; /** Tests for {@link MultipleInputNodeCreationProcessor}. */ -public class MultipleInputNodeCreationProcessorTest extends TableTestBase { +class MultipleInputNodeCreationProcessorTest extends TableTestBase { private final BatchTableTestUtil batchUtil = batchTestUtil(TableConfig.getDefault()); private final StreamTableTestUtil streamUtil = streamTestUtil(TableConfig.getDefault()); @Test - public void testIsChainableDataStreamSource() { + void testIsChainableDataStreamSource() { createChainableStream(batchUtil); assertChainableSource("chainableStream", batchUtil, true); createChainableStream(streamUtil); @@ -56,7 +57,7 @@ public void testIsChainableDataStreamSource() { } @Test - public void testNonChainableDataStreamSource() { + void testNonChainableDataStreamSource() { createNonChainableStream(batchUtil); assertChainableSource("nonChainableStream", batchUtil, false); createNonChainableStream(streamUtil); @@ -64,7 +65,7 @@ public void testNonChainableDataStreamSource() { } @Test - public void testIsChainableTableSource() throws IOException { + void testIsChainableTableSource() throws IOException { createTestFileSource(batchUtil.tableEnv(), "fileSource1", "Source"); assertChainableSource("fileSource1", batchUtil, true); createTestFileSource(streamUtil.tableEnv(), "fileSource1", "Source"); @@ -77,7 +78,7 @@ public void testIsChainableTableSource() throws IOException { } @Test - public void testNonChainableTableSource() throws IOException { + void testNonChainableTableSource() throws IOException { createTestValueSource(batchUtil.tableEnv(), "valueSource1", "DataStream"); assertChainableSource("valueSource1", batchUtil, false); createTestValueSource(streamUtil.tableEnv(), "valueSource1", "DataStream"); @@ -135,7 +136,7 @@ private void createNonChainableStream(TableTestUtil util) { private void createTestFileSource(TableEnvironment tEnv, String name, String runtimeSource) throws IOException { - File file = tempFolder().newFile(); + File file = TempDirUtils.newFile(tempFolder()); file.delete(); file.createNewFile(); FileUtils.writeFileUtf8(file, "1\n2\n3\n"); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/CalcJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/CalcJsonPlanTest.java index 0eea911954f62..7509fc282b383 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/CalcJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/CalcJsonPlanTest.java @@ -28,17 +28,17 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for calc. */ -public class CalcJsonPlanTest extends TableTestBase { +class CalcJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -55,7 +55,7 @@ public void setup() { } @Test - public void testSimpleProject() { + void testSimpleProject() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" @@ -68,7 +68,7 @@ public void testSimpleProject() { } @Test - public void testSimpleFilter() { + void testSimpleFilter() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" @@ -83,7 +83,7 @@ public void testSimpleFilter() { } @Test - public void testComplexCalc() { + void testComplexCalc() { tEnv.createTemporaryFunction("udf1", new JavaFunc0()); tEnv.createTemporaryFunction("udf2", JavaFunc1.class); tEnv.createTemporarySystemFunction("udf3", new JavaFunc2()); @@ -116,7 +116,7 @@ public void testComplexCalc() { } @Test - public void testSarg() { + void testSarg() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint\n" @@ -130,7 +130,7 @@ public void testSarg() { } @Test - public void testProjectPushDown() { + void testProjectPushDown() { // ensure PartitionPushDownSpec was added to exec plan String sinkTableDdl = "CREATE TABLE MySink (\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ChangelogSourceJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ChangelogSourceJsonPlanTest.java index 1ec1567d134b5..0e35bc11bca3e 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ChangelogSourceJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ChangelogSourceJsonPlanTest.java @@ -24,24 +24,24 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for changelog source, including upsert source. */ -public class ChangelogSourceJsonPlanTest extends TableTestBase { +class ChangelogSourceJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); tEnv.getConfig().set(ExecutionConfigOptions.TABLE_EXEC_SOURCE_CDC_EVENTS_DUPLICATE, true); } @Test - public void testChangelogSource() { + void testChangelogSource() { String srcTableDdl = "CREATE TABLE MyTable (\n" + " a bigint,\n" @@ -68,7 +68,7 @@ public void testChangelogSource() { } @Test - public void testUpsertSource() { + void testUpsertSource() { String srcTableDdl = "CREATE TABLE MyTable (\n" + " a bigint,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/CorrelateJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/CorrelateJsonPlanTest.java index 8d1ebe80727a1..d7672a03e03a5 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/CorrelateJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/CorrelateJsonPlanTest.java @@ -24,17 +24,17 @@ import org.apache.flink.table.planner.utils.TableFunc1; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for correlate. */ -public class CorrelateJsonPlanTest extends TableTestBase { +class CorrelateJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -51,7 +51,7 @@ public void setup() { } @Test - public void testCrossJoin() { + void testCrossJoin() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a varchar,\n" @@ -68,8 +68,8 @@ public void testCrossJoin() { } @Test - @Ignore("the case is ignored because of FLINK-21870") - public void testRegisterByClass() { + @Disabled("the case is ignored because of FLINK-21870") + void testRegisterByClass() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a varchar,\n" @@ -86,7 +86,7 @@ public void testRegisterByClass() { } @Test - public void testCrossJoinOverrideParameters() { + void testCrossJoinOverrideParameters() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a varchar,\n" @@ -103,7 +103,7 @@ public void testCrossJoinOverrideParameters() { } @Test - public void testLeftOuterJoinWithLiteralTrue() { + void testLeftOuterJoinWithLiteralTrue() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a varchar,\n" @@ -120,7 +120,7 @@ public void testLeftOuterJoinWithLiteralTrue() { } @Test - public void testJoinWithFilter() { + void testJoinWithFilter() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a varchar,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/DeduplicationJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/DeduplicationJsonPlanTest.java index 10543540f5880..cccd49cffc049 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/DeduplicationJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/DeduplicationJsonPlanTest.java @@ -23,23 +23,23 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for deduplicate. */ -public class DeduplicationJsonPlanTest extends TableTestBase { +class DeduplicationJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); } @Test - public void testDeduplication() { + void testDeduplication() { String srcTableDdl = "CREATE TABLE srcValuesTable (\n" + " order_id bigint,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ExpandJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ExpandJsonPlanTest.java index b55cf03fb9f47..9d568e6417e9d 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ExpandJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ExpandJsonPlanTest.java @@ -26,17 +26,17 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for expand. */ -public class ExpandJsonPlanTest extends TableTestBase { +class ExpandJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); tEnv.getConfig() @@ -58,7 +58,7 @@ public void setup() { } @Test - public void testExpand() { + void testExpand() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/GroupAggregateJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/GroupAggregateJsonPlanTest.java index 27b7296488853..e655a3527ea4f 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/GroupAggregateJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/GroupAggregateJsonPlanTest.java @@ -27,32 +27,34 @@ import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.WeightedAvg; import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameter; +import org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameters; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; import java.time.Duration; import java.util.Arrays; import java.util.List; /** Test json serialization/deserialization for group aggregate. */ -@RunWith(Parameterized.class) -public class GroupAggregateJsonPlanTest extends TableTestBase { +@ExtendWith(ParameterizedTestExtension.class) +class GroupAggregateJsonPlanTest extends TableTestBase { - @Parameterized.Parameter public boolean isMiniBatchEnabled; + @Parameter private boolean isMiniBatchEnabled; private StreamTableTestUtil util; private TableEnvironment tEnv; - @Parameterized.Parameters(name = "isMiniBatchEnabled={0}") - public static List testData() { + @Parameters(name = "isMiniBatchEnabled={0}") + private static List testData() { return Arrays.asList(true, false); } - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); if (isMiniBatchEnabled) { @@ -78,8 +80,8 @@ public void setup() { tEnv.executeSql(srcTableDdl); } - @Test - public void testSimpleAggCallsWithGroupBy() { + @TestTemplate + void testSimpleAggCallsWithGroupBy() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b bigint,\n" @@ -99,8 +101,8 @@ public void testSimpleAggCallsWithGroupBy() { + "from MyTable group by b"); } - @Test - public void testSimpleAggWithoutGroupBy() { + @TestTemplate + void testSimpleAggWithoutGroupBy() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " avg_a double,\n" @@ -123,8 +125,8 @@ public void testSimpleAggWithoutGroupBy() { + "from MyTable"); } - @Test - public void testDistinctAggCalls() { + @TestTemplate + void testDistinctAggCalls() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " d bigint,\n" @@ -150,8 +152,8 @@ public void testDistinctAggCalls() { + "from MyTable group by d"); } - @Test - public void testUserDefinedAggCalls() { + @TestTemplate + void testUserDefinedAggCalls() { tEnv.createTemporaryFunction("my_sum1", new VarSum1AggFunction()); tEnv.createFunction("my_avg", WeightedAvg.class); tEnv.createTemporarySystemFunction("my_sum2", VarSum2AggFunction.class); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/GroupWindowAggregateJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/GroupWindowAggregateJsonPlanTest.java index 12313ff234e1d..d7341829a7375 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/GroupWindowAggregateJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/GroupWindowAggregateJsonPlanTest.java @@ -24,16 +24,16 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for group window aggregate. */ -public class GroupWindowAggregateJsonPlanTest extends TableTestBase { +class GroupWindowAggregateJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -51,7 +51,7 @@ public void setup() { } @Test - public void testEventTimeTumbleWindow() { + void testEventTimeTumbleWindow() { tEnv.createFunction( "concat_distinct_agg", JavaUserDefinedAggFunctions.ConcatDistinctAggFunction.class); String sinkTableDdl = @@ -80,7 +80,7 @@ public void testEventTimeTumbleWindow() { } @Test - public void testProcTimeTumbleWindow() { + void testProcTimeTumbleWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -99,7 +99,7 @@ public void testProcTimeTumbleWindow() { } @Test - public void testEventTimeHopWindow() { + void testEventTimeHopWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -118,7 +118,7 @@ public void testEventTimeHopWindow() { } @Test - public void testProcTimeHopWindow() { + void testProcTimeHopWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -135,7 +135,7 @@ public void testProcTimeHopWindow() { } @Test - public void testEventTimeSessionWindow() { + void testEventTimeSessionWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -154,7 +154,7 @@ public void testEventTimeSessionWindow() { } @Test - public void testProcTimeSessionWindow() { + void testProcTimeSessionWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/IncrementalAggregateJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/IncrementalAggregateJsonPlanTest.java index cc210a6dff4e5..26dcc04f30346 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/IncrementalAggregateJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/IncrementalAggregateJsonPlanTest.java @@ -27,19 +27,19 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.time.Duration; /** Test json serialization/deserialization for incremental aggregate. */ -public class IncrementalAggregateJsonPlanTest extends TableTestBase { +class IncrementalAggregateJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); tEnv.getConfig() @@ -67,7 +67,7 @@ public void setup() { } @Test - public void testIncrementalAggregate() { + void testIncrementalAggregate() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" @@ -84,7 +84,7 @@ public void testIncrementalAggregate() { } @Test - public void testIncrementalAggregateWithSumCountDistinctAndRetraction() { + void testIncrementalAggregateWithSumCountDistinctAndRetraction() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b bigint,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/IntervalJoinJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/IntervalJoinJsonPlanTest.java index d5325f133fa33..71a6f52ea0b0d 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/IntervalJoinJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/IntervalJoinJsonPlanTest.java @@ -23,17 +23,17 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for IntervalJoin. */ -public class IntervalJoinJsonPlanTest extends TableTestBase { +class IntervalJoinJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -64,7 +64,7 @@ public void setup() { } @Test - public void testProcessingTimeInnerJoinWithOnClause() { + void testProcessingTimeInnerJoinWithOnClause() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a int,\n" @@ -81,7 +81,7 @@ public void testProcessingTimeInnerJoinWithOnClause() { } @Test - public void testRowTimeInnerJoinWithOnClause() { + void testRowTimeInnerJoinWithOnClause() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a int,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/JoinJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/JoinJsonPlanTest.java index c0154949333e2..dd2771ea7cf8c 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/JoinJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/JoinJsonPlanTest.java @@ -23,17 +23,17 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for join. */ -public class JoinJsonPlanTest extends TableTestBase { +class JoinJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -76,7 +76,7 @@ public void setup() { } @Test - public void testInnerJoin() { + void testInnerJoin() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a1 int,\n" @@ -89,7 +89,7 @@ public void testInnerJoin() { } @Test - public void testInnerJoinWithEqualPk() { + void testInnerJoinWithEqualPk() { String query1 = "SELECT SUM(a2) AS a2, a1 FROM A GROUP BY a1"; String query2 = "SELECT SUM(b2) AS b2, b1 FROM B GROUP BY b1"; String query = @@ -107,7 +107,7 @@ public void testInnerJoinWithEqualPk() { } @Test - public void testInnerJoinWithPk() { + void testInnerJoinWithPk() { String query1 = "SELECT SUM(a2) AS a2, a1 FROM A GROUP BY a1"; String query2 = "SELECT SUM(b2) AS b2, b1 FROM B GROUP BY b1"; String query = @@ -128,7 +128,7 @@ public void testInnerJoinWithPk() { } @Test - public void testLeftJoinNonEqui() { + void testLeftJoinNonEqui() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a1 int,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/LimitJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/LimitJsonPlanTest.java deleted file mode 100644 index 062edcaff15d7..0000000000000 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/LimitJsonPlanTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.flink.table.planner.plan.nodes.exec.stream; - -import org.apache.flink.table.api.TableConfig; -import org.apache.flink.table.api.TableEnvironment; -import org.apache.flink.table.planner.utils.StreamTableTestUtil; -import org.apache.flink.table.planner.utils.TableTestBase; - -import org.junit.Before; -import org.junit.Test; - -/** Test json serialization for sort limit. */ -public class LimitJsonPlanTest extends TableTestBase { - - private StreamTableTestUtil util; - private TableEnvironment tEnv; - - @Before - public void setup() { - util = streamTestUtil(TableConfig.getDefault()); - tEnv = util.getTableEnv(); - - String srcTableDdl = - "CREATE TABLE MyTable (\n" - + " a bigint,\n" - + " b int not null,\n" - + " c varchar,\n" - + " d timestamp(3)\n" - + ") with (\n" - + " 'connector' = 'values',\n" - + " 'bounded' = 'false')"; - tEnv.executeSql(srcTableDdl); - } - - @Test - public void testLimit() { - String sinkTableDdl = - "CREATE TABLE MySink (\n" - + " a bigint,\n" - + " b bigint\n" - + ") with (\n" - + " 'connector' = 'values',\n" - + " 'sink-insert-only' = 'false',\n" - + " 'table-sink-class' = 'DEFAULT')"; - tEnv.executeSql(sinkTableDdl); - String sql = "insert into MySink SELECT a, a from MyTable limit 10"; - util.verifyJsonPlan(sql); - } -} diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/LimitRestoreTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/LimitRestoreTest.java new file mode 100644 index 0000000000000..9d6180064ffea --- /dev/null +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/LimitRestoreTest.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.planner.plan.nodes.exec.stream; + +import org.apache.flink.table.planner.plan.nodes.exec.testutils.RestoreTestBase; +import org.apache.flink.table.test.program.TableTestProgram; + +import java.util.Arrays; +import java.util.List; + +/** Restore tests for {@link StreamExecLimit}. */ +public class LimitRestoreTest extends RestoreTestBase { + + public LimitRestoreTest() { + super(StreamExecLimit.class); + } + + @Override + public List programs() { + return Arrays.asList(LimitTestPrograms.LIMIT); + } +} diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/LimitTestPrograms.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/LimitTestPrograms.java new file mode 100644 index 0000000000000..0fc8a44695f3d --- /dev/null +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/LimitTestPrograms.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.planner.plan.nodes.exec.stream; + +import org.apache.flink.table.test.program.SinkTestStep; +import org.apache.flink.table.test.program.SourceTestStep; +import org.apache.flink.table.test.program.TableTestProgram; +import org.apache.flink.types.Row; + +/** + * {@link TableTestProgram} definitions for testing {@link + * org.apache.flink.table.planner.plan.nodes.exec.stream.StreamExecLimit}. + */ +public class LimitTestPrograms { + + static final Row[] DATA1 = + new Row[] { + Row.of(2, "a", 6), + Row.of(4, "b", 8), + Row.of(6, "c", 10), + Row.of(1, "a", 5), + Row.of(3, "b", 7), + Row.of(5, "c", 9) + }; + + static final Row[] DATA2 = new Row[] {Row.of(8, "d", 3), Row.of(7, "e", 2)}; + static final TableTestProgram LIMIT = + TableTestProgram.of("limit", "validates limit node") + .setupTableSource( + SourceTestStep.newBuilder("source_t") + .addSchema("a INT", "b VARCHAR", "c INT") + .producedBeforeRestore(DATA1) + .producedAfterRestore(DATA2) + .build()) + .setupTableSink( + SinkTestStep.newBuilder("sink_t") + .addSchema("a INT", "b VARCHAR", "c BIGINT") + .consumedBeforeRestore( + "+I[2, a, 6]", "+I[4, b, 8]", "+I[6, c, 10]") + .consumedAfterRestore(new String[] {}) + .build()) + .runSql("INSERT INTO sink_t SELECT * from source_t LIMIT 3") + .build(); +} diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/LookupJoinJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/LookupJoinJsonPlanTest.java index 1cdfca1c3db28..6aa3a3a75c7f1 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/LookupJoinJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/LookupJoinJsonPlanTest.java @@ -29,8 +29,8 @@ import org.apache.flink.table.planner.utils.TableTestBase; import org.apache.flink.types.Row; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -40,13 +40,13 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; /** Test json serialization/deserialization for LookupJoin. */ -public class LookupJoinJsonPlanTest extends TableTestBase { +class LookupJoinJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -97,7 +97,7 @@ public void setup() { } @Test - public void testJoinTemporalTable() { + void testJoinTemporalTable() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a int,\n" @@ -118,7 +118,7 @@ public void testJoinTemporalTable() { } @Test - public void testJoinTemporalTableWithProjectionPushDown() { + void testJoinTemporalTableWithProjectionPushDown() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a int,\n" @@ -140,7 +140,7 @@ public void testJoinTemporalTableWithProjectionPushDown() { } @Test - public void testLegacyTableSourceException() { + void testLegacyTableSourceException() { TableSchema tableSchema = TableSchema.builder() .field("id", Types.INT) @@ -182,7 +182,7 @@ public void testLegacyTableSourceException() { } @Test - public void testAggAndLeftJoinWithTryResolveMode() { + void testAggAndLeftJoinWithTryResolveMode() { tEnv.getConfig() .set( OptimizerConfigOptions.TABLE_OPTIMIZER_NONDETERMINISTIC_UPDATE_STRATEGY, @@ -197,7 +197,7 @@ public void testAggAndLeftJoinWithTryResolveMode() { } @Test - public void testJoinTemporalTableWithAsyncHint() { + void testJoinTemporalTableWithAsyncHint() { // LookupTable has sync func only, just verify the hint has take effect util.verifyJsonPlan( "INSERT INTO MySink1 SELECT " @@ -207,7 +207,7 @@ public void testJoinTemporalTableWithAsyncHint() { } @Test - public void testJoinTemporalTableWithAsyncHint2() { + void testJoinTemporalTableWithAsyncHint2() { // LookupTable has sync func only, just verify the hint has take effect util.verifyJsonPlan( "INSERT INTO MySink1 SELECT " @@ -217,7 +217,7 @@ public void testJoinTemporalTableWithAsyncHint2() { } @Test - public void testJoinTemporalTableWithRetryHint() { + void testJoinTemporalTableWithRetryHint() { util.verifyJsonPlan( "INSERT INTO MySink1 SELECT " + "/*+ LOOKUP('table'='D', 'retry-predicate'='lookup_miss', 'retry-strategy'='fixed_delay', 'fixed-delay'='10s', 'max-attempts'='3') */ * " @@ -226,7 +226,7 @@ public void testJoinTemporalTableWithRetryHint() { } @Test - public void testJoinTemporalTableWithAsyncRetryHint() { + void testJoinTemporalTableWithAsyncRetryHint() { // LookupTable has sync func only, just verify the hint has take effect util.verifyJsonPlan( "INSERT INTO MySink1 SELECT " @@ -236,7 +236,7 @@ public void testJoinTemporalTableWithAsyncRetryHint() { } @Test - public void testJoinTemporalTableWithAsyncRetryHint2() { + void testJoinTemporalTableWithAsyncRetryHint2() { // LookupTable has sync func only, just verify the hint has take effect util.verifyJsonPlan( "INSERT INTO MySink1 SELECT " @@ -246,7 +246,7 @@ public void testJoinTemporalTableWithAsyncRetryHint2() { } @Test - public void testLeftJoinTemporalTableWithPreFilter() { + void testLeftJoinTemporalTableWithPreFilter() { util.verifyJsonPlan( "INSERT INTO MySink1 SELECT * " + "FROM MyTable AS T LEFT JOIN LookupTable " @@ -254,7 +254,7 @@ public void testLeftJoinTemporalTableWithPreFilter() { } @Test - public void testLeftJoinTemporalTableWithPostFilter() { + void testLeftJoinTemporalTableWithPostFilter() { util.verifyJsonPlan( "INSERT INTO MySink1 SELECT * " + "FROM MyTable AS T LEFT JOIN LookupTable " @@ -263,7 +263,7 @@ public void testLeftJoinTemporalTableWithPostFilter() { } @Test - public void testLeftJoinTemporalTableWithMultiJoinConditions() { + void testLeftJoinTemporalTableWithMultiJoinConditions() { util.verifyJsonPlan( "INSERT INTO MySink1 SELECT * " + "FROM MyTable AS T LEFT JOIN LookupTable " diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/MatchRecognizeJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/MatchRecognizeJsonPlanTest.java index e0ef65b447beb..c31f300ca4e5d 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/MatchRecognizeJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/MatchRecognizeJsonPlanTest.java @@ -23,22 +23,22 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization for match recognize. */ -public class MatchRecognizeJsonPlanTest extends TableTestBase { +class MatchRecognizeJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); } @Test - public void testMatch() { + void testMatch() { String srcTableDdl = "CREATE TABLE MyTable (\n" + " id bigint,\n" @@ -78,22 +78,22 @@ public void testMatch() { } @Test - public void testSkipToLast() { + void testSkipToLast() { doTestAfterMatch("AFTER MATCH SKIP TO LAST B"); } @Test - public void testSkipToFirst() { + void testSkipToFirst() { doTestAfterMatch("AFTER MATCH SKIP TO FIRST B"); } @Test - public void testSkipPastLastRow() { + void testSkipPastLastRow() { doTestAfterMatch("AFTER MATCH SKIP PAST LAST ROW"); } @Test - public void testSkipToNextRow() { + void testSkipToNextRow() { doTestAfterMatch("AFTER MATCH SKIP TO NEXT ROW"); } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/OverAggregateJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/OverAggregateJsonPlanTest.java index 6607dca9a3443..c01fa4121768e 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/OverAggregateJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/OverAggregateJsonPlanTest.java @@ -23,16 +23,16 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization for over aggregate. */ -public class OverAggregateJsonPlanTest extends TableTestBase { +class OverAggregateJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); String srcTableDdl = @@ -50,7 +50,7 @@ public void setup() { } @Test - public void testProctimeBoundedDistinctWithNonDistinctPartitionedRowOver() { + void testProctimeBoundedDistinctWithNonDistinctPartitionedRowOver() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a varchar,\n" @@ -78,7 +78,7 @@ public void testProctimeBoundedDistinctWithNonDistinctPartitionedRowOver() { } @Test - public void testProctimeBoundedDistinctPartitionedRowOver() { + void testProctimeBoundedDistinctPartitionedRowOver() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" @@ -100,7 +100,7 @@ public void testProctimeBoundedDistinctPartitionedRowOver() { } @Test - public void testProcTimeBoundedPartitionedRangeOver() { + void testProcTimeBoundedPartitionedRangeOver() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" @@ -119,7 +119,7 @@ public void testProcTimeBoundedPartitionedRangeOver() { } @Test - public void testProcTimeBoundedNonPartitionedRangeOver() { + void testProcTimeBoundedNonPartitionedRangeOver() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" @@ -138,7 +138,7 @@ public void testProcTimeBoundedNonPartitionedRangeOver() { } @Test - public void testProcTimeUnboundedPartitionedRangeOver() { + void testProcTimeUnboundedPartitionedRangeOver() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" @@ -158,7 +158,7 @@ public void testProcTimeUnboundedPartitionedRangeOver() { } @Test - public void testRowTimeBoundedPartitionedRowsOver() { + void testRowTimeBoundedPartitionedRowsOver() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" @@ -177,7 +177,7 @@ public void testRowTimeBoundedPartitionedRowsOver() { } @Test - public void testProcTimeBoundedPartitionedRowsOverWithBuiltinProctime() { + void testProcTimeBoundedPartitionedRowsOverWithBuiltinProctime() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonCalcJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonCalcJsonPlanTest.java index 07a367d6d24d0..dbe3c840cb075 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonCalcJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonCalcJsonPlanTest.java @@ -25,17 +25,17 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for calc. */ -public class PythonCalcJsonPlanTest extends TableTestBase { +class PythonCalcJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -52,7 +52,7 @@ public void setup() { } @Test - public void testPythonCalc() { + void testPythonCalc() { tEnv.createTemporaryFunction("pyFunc", new PythonScalarFunction("pyFunc")); String sinkTableDdl = "CREATE TABLE MySink (\n" @@ -66,7 +66,7 @@ public void testPythonCalc() { } @Test - public void testPythonFunctionInWhereClause() { + void testPythonFunctionInWhereClause() { tEnv.createTemporaryFunction("pyFunc", new BooleanPythonScalarFunction("pyFunc")); String sinkTableDdl = "CREATE TABLE MySink (\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonCorrelateJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonCorrelateJsonPlanTest.java index 456df0dd0ed1d..c2bc2009b4601 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonCorrelateJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonCorrelateJsonPlanTest.java @@ -25,16 +25,16 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for correlate. */ -public class PythonCorrelateJsonPlanTest extends TableTestBase { +class PythonCorrelateJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { TableConfig tableConfig = TableConfig.getDefault(); util = streamTestUtil(tableConfig); tEnv = util.getTableEnv(); @@ -54,7 +54,7 @@ public void setup() { } @Test - public void testPythonTableFunction() { + void testPythonTableFunction() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a int,\n" @@ -71,7 +71,7 @@ public void testPythonTableFunction() { } @Test - public void testJoinWithFilter() { + void testJoinWithFilter() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a int,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonGroupAggregateJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonGroupAggregateJsonPlanTest.java index 5b50a2597cb3e..8f2e72d491604 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonGroupAggregateJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonGroupAggregateJsonPlanTest.java @@ -24,17 +24,17 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for group aggregate. */ -public class PythonGroupAggregateJsonPlanTest extends TableTestBase { +class PythonGroupAggregateJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -52,7 +52,7 @@ public void setup() { } @Test - public void tesPythonAggCallsWithGroupBy() { + void tesPythonAggCallsWithGroupBy() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonGroupWindowAggregateJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonGroupWindowAggregateJsonPlanTest.java index afcd4a86fe1a6..0b1058b15299a 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonGroupWindowAggregateJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonGroupWindowAggregateJsonPlanTest.java @@ -24,16 +24,16 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for group window aggregate. */ -public class PythonGroupWindowAggregateJsonPlanTest extends TableTestBase { +class PythonGroupWindowAggregateJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -52,7 +52,7 @@ public void setup() { } @Test - public void testEventTimeTumbleWindow() { + void testEventTimeTumbleWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -73,7 +73,7 @@ public void testEventTimeTumbleWindow() { } @Test - public void testProcTimeTumbleWindow() { + void testProcTimeTumbleWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -92,7 +92,7 @@ public void testProcTimeTumbleWindow() { } @Test - public void testEventTimeHopWindow() { + void testEventTimeHopWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -109,7 +109,7 @@ public void testEventTimeHopWindow() { } @Test - public void testProcTimeHopWindow() { + void testProcTimeHopWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -126,7 +126,7 @@ public void testProcTimeHopWindow() { } @Test - public void testEventTimeSessionWindow() { + void testEventTimeSessionWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -143,7 +143,7 @@ public void testEventTimeSessionWindow() { } @Test - public void testProcTimeSessionWindow() { + void testProcTimeSessionWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonOverAggregateJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonOverAggregateJsonPlanTest.java index ec5f4e3f96768..cf91be90074e7 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonOverAggregateJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/PythonOverAggregateJsonPlanTest.java @@ -24,16 +24,16 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization for over aggregate. */ -public class PythonOverAggregateJsonPlanTest extends TableTestBase { +class PythonOverAggregateJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); String srcTableDdl = @@ -52,7 +52,7 @@ public void setup() { } @Test - public void testProcTimeBoundedPartitionedRangeOver() { + void testProcTimeBoundedPartitionedRangeOver() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" @@ -71,7 +71,7 @@ public void testProcTimeBoundedPartitionedRangeOver() { } @Test - public void testProcTimeBoundedNonPartitionedRangeOver() { + void testProcTimeBoundedNonPartitionedRangeOver() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" @@ -90,7 +90,7 @@ public void testProcTimeBoundedNonPartitionedRangeOver() { } @Test - public void testProcTimeUnboundedPartitionedRangeOver() { + void testProcTimeUnboundedPartitionedRangeOver() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" @@ -108,7 +108,7 @@ public void testProcTimeUnboundedPartitionedRangeOver() { } @Test - public void testRowTimeBoundedPartitionedRowsOver() { + void testRowTimeBoundedPartitionedRowsOver() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" @@ -127,7 +127,7 @@ public void testRowTimeBoundedPartitionedRowsOver() { } @Test - public void testProcTimeBoundedPartitionedRowsOverWithBuiltinProctime() { + void testProcTimeBoundedPartitionedRowsOverWithBuiltinProctime() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/RankJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/RankJsonPlanTest.java index ab3e1411b7ddb..ba4bdd7c6f1aa 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/RankJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/RankJsonPlanTest.java @@ -23,17 +23,17 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization for rank. */ -public class RankJsonPlanTest extends TableTestBase { +class RankJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -50,7 +50,7 @@ public void setup() { } @Test - public void testRank() { + void testRank() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/SortJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/SortJsonPlanTest.java index a23264cc2b753..91c62f9fe3364 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/SortJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/SortJsonPlanTest.java @@ -23,17 +23,17 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization for sort limit. */ -public class SortJsonPlanTest extends TableTestBase { +class SortJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -50,7 +50,7 @@ public void setup() { } @Test - public void testSort() { + void testSort() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/SortLimitJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/SortLimitJsonPlanTest.java index 089f4d676f446..374b309696211 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/SortLimitJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/SortLimitJsonPlanTest.java @@ -23,17 +23,17 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization for sort limit. */ -public class SortLimitJsonPlanTest extends TableTestBase { +class SortLimitJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -50,7 +50,7 @@ public void setup() { } @Test - public void testSortLimit() { + void testSortLimit() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TableSinkJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TableSinkJsonPlanTest.java index 1536d3e499a29..3c321b5c59ae3 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TableSinkJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TableSinkJsonPlanTest.java @@ -24,17 +24,17 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for table sink. */ -public class TableSinkJsonPlanTest extends TableTestBase { +class TableSinkJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -50,7 +50,7 @@ public void setup() { } @Test - public void testOverwrite() { + void testOverwrite() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" @@ -65,7 +65,7 @@ public void testOverwrite() { } @Test - public void testPartitioning() { + void testPartitioning() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" @@ -80,7 +80,7 @@ public void testPartitioning() { } @Test - public void testWritingMetadata() { + void testWritingMetadata() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" @@ -94,7 +94,7 @@ public void testWritingMetadata() { } @Test - public void testCdcWithNonDeterministicFuncSinkWithDifferentPk() { + void testCdcWithNonDeterministicFuncSinkWithDifferentPk() { tEnv.createTemporaryFunction( "ndFunc", new JavaUserDefinedScalarFunctions.NonDeterministicUdf()); @@ -129,7 +129,7 @@ public void testCdcWithNonDeterministicFuncSinkWithDifferentPk() { } @Test - public void testPartialInsert() { + void testPartialInsert() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TableSourceJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TableSourceJsonPlanTest.java index 390e0901ecafc..fef94f021e06e 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TableSourceJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TableSourceJsonPlanTest.java @@ -24,17 +24,17 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for table source. */ -public class TableSourceJsonPlanTest extends TableTestBase { +class TableSourceJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -60,7 +60,7 @@ public void setup() { } @Test - public void testProjectPushDown() { + void testProjectPushDown() { String sinkTableDdl = "CREATE TABLE sink (\n" + " a bigint,\n" @@ -73,7 +73,7 @@ public void testProjectPushDown() { } @Test - public void testReadingMetadata() { + void testReadingMetadata() { String srcTableDdl = "CREATE TABLE MyTable2 (\n" + " a bigint,\n" @@ -98,7 +98,7 @@ public void testReadingMetadata() { } @Test - public void testFilterPushDown() { + void testFilterPushDown() { String srcTableDdl = "CREATE TABLE src (\n" + " a bigint,\n" @@ -113,12 +113,12 @@ public void testFilterPushDown() { } @Test - public void testLimitPushDown() { + void testLimitPushDown() { util.verifyJsonPlan("insert into MySink select * from MyTable limit 3"); } @Test - public void testPartitionPushDown() { + void testPartitionPushDown() { String srcTableDdl = "CREATE TABLE PartitionTable (\n" + " a bigint,\n" @@ -134,7 +134,7 @@ public void testPartitionPushDown() { } @Test - public void testWatermarkPushDown() { + void testWatermarkPushDown() { String srcTableDdl = "CREATE TABLE WatermarkTable (\n" + " a bigint,\n" @@ -160,7 +160,7 @@ public void testWatermarkPushDown() { } @Test - public void testReuseSourceWithoutProjectionPushDown() { + void testReuseSourceWithoutProjectionPushDown() { tEnv.executeSql( "CREATE TEMPORARY TABLE src (\n" + " x varchar,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TemporalJoinJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TemporalJoinJsonPlanTest.java index 5d51c8757a44a..da3e6eaebae1d 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TemporalJoinJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TemporalJoinJsonPlanTest.java @@ -24,19 +24,19 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static org.apache.flink.table.api.Expressions.$; /** Test json serialization/deserialization for TemporalJoin. */ -public class TemporalJoinJsonPlanTest extends TableTestBase { +class TemporalJoinJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -66,7 +66,7 @@ public void setup() { } @Test - public void testJoinTemporalFunction() { + void testJoinTemporalFunction() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a int\n" @@ -83,7 +83,7 @@ public void testJoinTemporalFunction() { } @Test - public void testTemporalTableJoin() { + void testTemporalTableJoin() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a int\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TemporalSortJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TemporalSortJsonPlanTest.java index 3ba476dc5c591..8ebd6a47e5971 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TemporalSortJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/TemporalSortJsonPlanTest.java @@ -23,16 +23,16 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for temporal sort. */ -public class TemporalSortJsonPlanTest extends TableTestBase { +class TemporalSortJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); TableEnvironment tEnv = util.getTableEnv(); @@ -57,12 +57,12 @@ public void setup() { } @Test - public void testSortProcessingTime() { + void testSortProcessingTime() { util.verifyJsonPlan("insert into MySink SELECT a FROM MyTable order by proctime, c"); } @Test - public void testSortRowTime() { + void testSortRowTime() { util.verifyJsonPlan("insert into MySink SELECT a FROM MyTable order by rowtime, c"); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/UnionJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/UnionJsonPlanTest.java index 7bad10a85d664..bbb1b041c2314 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/UnionJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/UnionJsonPlanTest.java @@ -23,16 +23,16 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for union. */ -public class UnionJsonPlanTest extends TableTestBase { +class UnionJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -58,7 +58,7 @@ public void setup() { } @Test - public void testUnion() { + void testUnion() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " a bigint,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ValuesJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ValuesJsonPlanTest.java index b42a3a24ff464..23eae28bc66cc 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ValuesJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/ValuesJsonPlanTest.java @@ -23,17 +23,17 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for values. */ -public class ValuesJsonPlanTest extends TableTestBase { +class ValuesJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); String sinkTableDdl = @@ -49,7 +49,7 @@ public void setup() { } @Test - public void testValues() { + void testValues() { util.verifyJsonPlan( "INSERT INTO MySink SELECT * FROM (VALUES (1, 2, 'Hi'), (3, 4, 'Hello'))"); } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WatermarkAssignerJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WatermarkAssignerJsonPlanTest.java index 8552862427080..a8690eec56f2a 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WatermarkAssignerJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WatermarkAssignerJsonPlanTest.java @@ -23,23 +23,23 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for watermark assigner. */ -public class WatermarkAssignerJsonPlanTest extends TableTestBase { +class WatermarkAssignerJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); } @Test - public void testWatermarkAssigner() { + void testWatermarkAssigner() { String srcTableDdl = "CREATE TABLE WatermarkTable (\n" + " a bigint,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WindowAggregateJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WindowAggregateJsonPlanTest.java index 7f6672f3fd0fa..c6a2662c813cd 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WindowAggregateJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WindowAggregateJsonPlanTest.java @@ -25,17 +25,17 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for window aggregate. */ -public class WindowAggregateJsonPlanTest extends TableTestBase { +class WindowAggregateJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -53,7 +53,7 @@ public void setup() { } @Test - public void testEventTimeTumbleWindow() { + void testEventTimeTumbleWindow() { tEnv.createFunction("concat_distinct_agg", ConcatDistinctAggFunction.class); String sinkTableDdl = "CREATE TABLE MySink (\n" @@ -82,7 +82,7 @@ public void testEventTimeTumbleWindow() { } @Test - public void testEventTimeTumbleWindowWithOffset() { + void testEventTimeTumbleWindowWithOffset() { tEnv.createFunction("concat_distinct_agg", ConcatDistinctAggFunction.class); String sinkTableDdl = "CREATE TABLE MySink (\n" @@ -115,7 +115,7 @@ public void testEventTimeTumbleWindowWithOffset() { } @Test - public void testProcTimeTumbleWindow() { + void testProcTimeTumbleWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -135,7 +135,7 @@ public void testProcTimeTumbleWindow() { } @Test - public void testEventTimeHopWindow() { + void testEventTimeHopWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -155,7 +155,7 @@ public void testEventTimeHopWindow() { } @Test - public void testEventTimeHopWindowWithOffset() { + void testEventTimeHopWindowWithOffset() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -180,7 +180,7 @@ public void testEventTimeHopWindowWithOffset() { } @Test - public void testProcTimeHopWindow() { + void testProcTimeHopWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -198,7 +198,7 @@ public void testProcTimeHopWindow() { } @Test - public void testEventTimeCumulateWindow() { + void testEventTimeCumulateWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -224,7 +224,7 @@ public void testEventTimeCumulateWindow() { } @Test - public void testEventTimeCumulateWindowWithOffset() { + void testEventTimeCumulateWindowWithOffset() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -251,7 +251,7 @@ public void testEventTimeCumulateWindowWithOffset() { } @Test - public void testProcTimeCumulateWindow() { + void testProcTimeCumulateWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " b BIGINT,\n" @@ -273,7 +273,7 @@ public void testProcTimeCumulateWindow() { } @Test - public void testDistinctSplitEnabled() { + void testDistinctSplitEnabled() { tEnv.getConfig() .set(OptimizerConfigOptions.TABLE_OPTIMIZER_DISTINCT_AGG_SPLIT_ENABLED, true); String sinkTableDdl = diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WindowJoinJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WindowJoinJsonPlanTest.java index 58a36f01df5ba..5d69beb7acea1 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WindowJoinJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WindowJoinJsonPlanTest.java @@ -23,17 +23,17 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for window join. */ -public class WindowJoinJsonPlanTest extends TableTestBase { +class WindowJoinJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -63,7 +63,7 @@ public void setup() { } @Test - public void testEventTimeTumbleWindow() { + void testEventTimeTumbleWindow() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " l_a INT,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WindowTableFunctionJsonPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WindowTableFunctionJsonPlanTest.java index 87da0b2e2e200..2b9ec7ba1e475 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WindowTableFunctionJsonPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/stream/WindowTableFunctionJsonPlanTest.java @@ -23,17 +23,17 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test json serialization/deserialization for window table function. */ -public class WindowTableFunctionJsonPlanTest extends TableTestBase { +class WindowTableFunctionJsonPlanTest extends TableTestBase { private StreamTableTestUtil util; private TableEnvironment tEnv; - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -63,7 +63,7 @@ public void setup() { } @Test - public void testIndividualWindowTVF() { + void testIndividualWindowTVF() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " window_start TIMESTAMP(3),\n" @@ -85,7 +85,7 @@ public void testIndividualWindowTVF() { } @Test - public void testIndividualWindowTVFProcessingTime() { + void testIndividualWindowTVFProcessingTime() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " window_start TIMESTAMP(3),\n" @@ -107,7 +107,7 @@ public void testIndividualWindowTVFProcessingTime() { } @Test - public void testFollowedByWindowJoin() { + void testFollowedByWindowJoin() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " window_start TIMESTAMP(3) NOT NULL,\n" @@ -155,7 +155,7 @@ public void testFollowedByWindowJoin() { } @Test - public void testFollowedByWindowRank() { + void testFollowedByWindowRank() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " window_start TIMESTAMP(3),\n" @@ -182,7 +182,7 @@ public void testFollowedByWindowRank() { } @Test - public void testFollowedByWindowDeduplicate() { + void testFollowedByWindowDeduplicate() { String sinkTableDdl = "CREATE TABLE MySink (\n" + " window_start TIMESTAMP(3),\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/testutils/CalcRestoreTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/testutils/CalcRestoreTest.java new file mode 100644 index 0000000000000..2b408bfcd675d --- /dev/null +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/testutils/CalcRestoreTest.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.planner.plan.nodes.exec.testutils; + +import org.apache.flink.table.planner.plan.nodes.exec.stream.StreamExecCalc; +import org.apache.flink.table.test.program.TableTestProgram; + +import java.util.Arrays; +import java.util.List; + +/** Restore tests for {@link StreamExecCalc}. */ +public class CalcRestoreTest extends RestoreTestBase { + + public CalcRestoreTest() { + super(StreamExecCalc.class); + } + + @Override + public List programs() { + return Arrays.asList( + CalcTestPrograms.SIMPLE_CALC, + CalcTestPrograms.CALC_FILTER, + CalcTestPrograms.CALC_FILTER_PUSHDOWN, + CalcTestPrograms.CALC_PROJECT_PUSHDOWN, + CalcTestPrograms.CALC_SARG, + CalcTestPrograms.CALC_UDF_SIMPLE, + CalcTestPrograms.CALC_UDF_COMPLEX); + } +} diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/testutils/CalcTestPrograms.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/testutils/CalcTestPrograms.java new file mode 100644 index 0000000000000..51d23f3183cd9 --- /dev/null +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/testutils/CalcTestPrograms.java @@ -0,0 +1,212 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.planner.plan.nodes.exec.testutils; + +import org.apache.flink.table.planner.plan.nodes.exec.stream.StreamExecCalc; +import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctions.JavaFunc0; +import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctions.JavaFunc1; +import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctions.JavaFunc2; +import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctions.JavaFunc5; +import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctions.UdfWithOpen; +import org.apache.flink.table.test.program.SinkTestStep; +import org.apache.flink.table.test.program.SourceTestStep; +import org.apache.flink.table.test.program.TableTestProgram; +import org.apache.flink.types.Row; + +import java.time.LocalDateTime; + +/** {@link TableTestProgram} definitions for testing {@link StreamExecCalc}. */ +public class CalcTestPrograms { + + static final TableTestProgram SIMPLE_CALC = + TableTestProgram.of("calc-simple", "validates basic calc node") + .setupTableSource( + SourceTestStep.newBuilder("t") + .addSchema("a BIGINT", "b DOUBLE") + .producedBeforeRestore(Row.of(420L, 42.0)) + .producedAfterRestore(Row.of(421L, 42.1)) + .build()) + .setupTableSink( + SinkTestStep.newBuilder("sink_t") + .addSchema("a BIGINT", "b DOUBLE") + .consumedBeforeRestore(Row.of(421L, 42.0)) + .consumedAfterRestore(Row.of(422L, 42.1)) + .build()) + .runSql("INSERT INTO sink_t SELECT a + 1, b FROM t") + .build(); + + static final TableTestProgram CALC_PROJECT_PUSHDOWN = + TableTestProgram.of( + "calc-project-pushdown", "validates calc node with project pushdown") + .setupTableSource( + SourceTestStep.newBuilder("source_t") + .addSchema("a BIGINT", "b DOUBLE") + .addOption("filterable-fields", "a") + .producedBeforeRestore(Row.of(421L, 42.1)) + .producedAfterRestore(Row.of(421L, 42.1)) + .build()) + .setupTableSink( + SinkTestStep.newBuilder("sink_t") + .addSchema("a BIGINT", "a1 VARCHAR") + .consumedBeforeRestore(Row.of(421L, "421")) + .consumedAfterRestore(Row.of(421L, "421")) + .build()) + .runSql( + "INSERT INTO sink_t SELECT a, CAST(a AS VARCHAR) FROM source_t WHERE a > CAST(1 AS BIGINT)") + .build(); + + static final TableTestProgram CALC_FILTER = + TableTestProgram.of("calc-filter", "validates calc node with filter") + .setupTableSource( + SourceTestStep.newBuilder("source_t") + .addSchema("a BIGINT", "b INT", "c DOUBLE", "d VARCHAR") + .producedBeforeRestore(Row.of(420L, 1, 42.0, "hello")) + .producedAfterRestore(Row.of(420L, 1, 42.0, "hello")) + .build()) + .setupTableSink( + SinkTestStep.newBuilder("sink_t") + .addSchema("a BIGINT", "b INT", "c DOUBLE", "d VARCHAR") + .consumedBeforeRestore(Row.of(420L, 1, 42.0, "hello")) + .consumedAfterRestore(Row.of(420L, 1, 42.0, "hello")) + .build()) + .runSql("INSERT INTO sink_t SELECT * FROM source_t WHERE b > 0") + .build(); + + static final TableTestProgram CALC_FILTER_PUSHDOWN = + TableTestProgram.of("calc-filter-pushdown", "validates calc node with filter pushdown") + .setupTableSource( + SourceTestStep.newBuilder("source_t") + .addSchema("a BIGINT", "b DOUBLE") + .addOption("filterable-fields", "a") + .producedBeforeRestore(Row.of(421L, 42.1)) + .producedAfterRestore(Row.of(421L, 42.1)) + .build()) + .setupTableSink( + SinkTestStep.newBuilder("sink_t") + .addSchema("a BIGINT", "b DOUBLE") + .consumedBeforeRestore(Row.of(421L, 42.1)) + .consumedAfterRestore(Row.of(421L, 42.1)) + .build()) + .runSql( + "INSERT INTO sink_t SELECT a, b FROM source_t WHERE a > CAST(420 AS BIGINT)") + .build(); + + static final TableTestProgram CALC_SARG = + TableTestProgram.of("calc-sarg", "validates calc node with Sarg") + .setupTableSource( + SourceTestStep.newBuilder("source_t") + .addSchema("a INT") + .addOption("filterable-fields", "a") + .producedBeforeRestore(Row.of(1)) + .producedAfterRestore(Row.of(1)) + .build()) + .setupTableSink( + SinkTestStep.newBuilder("sink_t") + .addSchema("a INT") + .consumedBeforeRestore(Row.of(1)) + .consumedAfterRestore(Row.of(1)) + .build()) + .runSql( + "INSERT INTO sink_t SELECT a FROM source_t WHERE a = 1 or a = 2 or a is null") + .build(); + + static final TableTestProgram CALC_UDF_SIMPLE = + TableTestProgram.of("calc-udf-simple", "validates calc node with simple UDF") + .setupTemporaryCatalogFunction("udf1", JavaFunc0.class) + .setupTableSource( + SourceTestStep.newBuilder("source_t") + .addSchema("a INT") + .producedBeforeRestore(Row.of(5)) + .producedAfterRestore(Row.of(5)) + .build()) + .setupTableSink( + SinkTestStep.newBuilder("sink_t") + .addSchema("a INT", "a1 BIGINT") + .consumedBeforeRestore(Row.of(5, 6L)) + .consumedAfterRestore(Row.of(5, 6L)) + .build()) + .runSql("INSERT INTO sink_t SELECT a, udf1(a) FROM source_t") + .build(); + + static final TableTestProgram CALC_UDF_COMPLEX = + TableTestProgram.of("calc-udf-complex", "validates calc node with complex UDFs") + .setupTemporaryCatalogFunction("udf1", JavaFunc0.class) + .setupTemporaryCatalogFunction("udf2", JavaFunc1.class) + .setupTemporarySystemFunction("udf3", JavaFunc2.class) + .setupTemporarySystemFunction("udf4", UdfWithOpen.class) + .setupCatalogFunction("udf5", JavaFunc5.class) + .setupTableSource( + SourceTestStep.newBuilder("source_t") + .addSchema( + "a BIGINT, b INT NOT NULL, c VARCHAR, d TIMESTAMP(3)") + .producedBeforeRestore( + Row.of( + 5L, + 11, + "hello world", + LocalDateTime.of(2023, 12, 16, 1, 1, 1, 123))) + .producedAfterRestore( + Row.of( + 5L, + 11, + "hello world", + LocalDateTime.of(2023, 12, 16, 1, 1, 1, 123))) + .build()) + .setupTableSink( + SinkTestStep.newBuilder("sink_t") + .addSchema( + "a BIGINT", + "a1 VARCHAR", + "b INT NOT NULL", + "b1 VARCHAR", + "c1 VARCHAR", + "c2 VARCHAR", + "d1 TIMESTAMP(3)") + .consumedBeforeRestore( + Row.of( + 5L, + "5", + 11, + "11 and 11 and 1702688461000", + "hello world11", + "$hello", + LocalDateTime.of(2023, 12, 16, 01, 01, 00, 0))) + .consumedAfterRestore( + Row.of( + 5L, + "5", + 11, + "11 and 11 and 1702688461000", + "hello world11", + "$hello", + LocalDateTime.of(2023, 12, 16, 01, 01, 00, 0))) + .build()) + .runSql( + "INSERT INTO sink_t SELECT " + + "a, " + + "cast(a as VARCHAR) as a1, " + + "b, " + + "udf2(b, b, d) as b1, " + + "udf3(c, b) as c1, " + + "udf4(substring(c, 1, 5)) as c2, " + + "udf5(d, 1000) as d1 " + + "from source_t where " + + "(udf1(a) > 0 or (a * b) < 100) and b > 10") + .build(); +} diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/testutils/RestoreTestBase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/testutils/RestoreTestBase.java new file mode 100644 index 0000000000000..f5c370668e22a --- /dev/null +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/testutils/RestoreTestBase.java @@ -0,0 +1,252 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.planner.plan.nodes.exec.testutils; + +import org.apache.flink.api.common.JobStatus; +import org.apache.flink.core.execution.JobClient; +import org.apache.flink.core.execution.SavepointFormatType; +import org.apache.flink.runtime.jobgraph.RestoreMode; +import org.apache.flink.runtime.jobgraph.SavepointRestoreSettings; +import org.apache.flink.runtime.testutils.CommonTestUtils; +import org.apache.flink.table.api.CompiledPlan; +import org.apache.flink.table.api.EnvironmentSettings; +import org.apache.flink.table.api.PlanReference; +import org.apache.flink.table.api.TableEnvironment; +import org.apache.flink.table.api.TableResult; +import org.apache.flink.table.api.config.TableConfigOptions; +import org.apache.flink.table.planner.factories.TestValuesTableFactory; +import org.apache.flink.table.planner.plan.nodes.exec.ExecNode; +import org.apache.flink.table.planner.plan.nodes.exec.ExecNodeMetadata; +import org.apache.flink.table.planner.plan.utils.ExecNodeMetadataUtil; +import org.apache.flink.table.test.program.SinkTestStep; +import org.apache.flink.table.test.program.SourceTestStep; +import org.apache.flink.table.test.program.SqlTestStep; +import org.apache.flink.table.test.program.TableTestProgram; +import org.apache.flink.table.test.program.TableTestProgramRunner; +import org.apache.flink.table.test.program.TestStep.TestKind; +import org.apache.flink.test.junit5.MiniClusterExtension; + +import org.apache.commons.collections.CollectionUtils; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Base class for implementing restore tests for {@link ExecNode}.You can generate json compiled + * plan and a savepoint for the latest node version by running {@link + * RestoreTestBase#generateTestSetupFiles(TableTestProgram)} which is disabled by default. + * + *

Note: The test base uses {@link TableConfigOptions.CatalogPlanCompilation#SCHEMA} + * because it needs to adjust source and sink properties before and after the restore. Therefore, + * the test base can not be used for testing storing table options in the compiled plan. + */ +@ExtendWith(MiniClusterExtension.class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public abstract class RestoreTestBase implements TableTestProgramRunner { + + private final Class execNodeUnderTest; + + protected RestoreTestBase(Class execNodeUnderTest) { + this.execNodeUnderTest = execNodeUnderTest; + } + + @Override + public EnumSet supportedSetupSteps() { + return EnumSet.of( + TestKind.FUNCTION, + TestKind.SOURCE_WITH_RESTORE_DATA, + TestKind.SINK_WITH_RESTORE_DATA); + } + + @Override + public EnumSet supportedRunSteps() { + return EnumSet.of(TestKind.SQL); + } + + private @TempDir Path tmpDir; + + private List getAllMetadata() { + return ExecNodeMetadataUtil.extractMetadataFromAnnotation(execNodeUnderTest); + } + + private ExecNodeMetadata getLatestMetadata() { + return ExecNodeMetadataUtil.latestAnnotation(execNodeUnderTest); + } + + private Stream createSpecs() { + return getAllMetadata().stream() + .flatMap( + metadata -> + supportedPrograms().stream().map(p -> Arguments.of(p, metadata))); + } + + /** + * Execute this test to generate test files. Remember to be using the correct branch when + * generating the test files. + */ + @Disabled + @ParameterizedTest + @MethodSource("supportedPrograms") + public void generateTestSetupFiles(TableTestProgram program) throws Exception { + final TableEnvironment tEnv = + TableEnvironment.create(EnvironmentSettings.inStreamingMode()); + tEnv.getConfig() + .set( + TableConfigOptions.PLAN_COMPILE_CATALOG_OBJECTS, + TableConfigOptions.CatalogPlanCompilation.SCHEMA); + for (SourceTestStep sourceTestStep : program.getSetupSourceTestSteps()) { + final String id = TestValuesTableFactory.registerData(sourceTestStep.dataBeforeRestore); + final Map options = new HashMap<>(); + options.put("connector", "values"); + options.put("data-id", id); + options.put("terminating", "false"); + options.put("disable-lookup", "true"); + options.put("runtime-source", "NewSource"); + sourceTestStep.apply(tEnv, options); + } + + final List> futures = new ArrayList<>(); + for (SinkTestStep sinkTestStep : program.getSetupSinkTestSteps()) { + final CompletableFuture future = new CompletableFuture<>(); + futures.add(future); + final String tableName = sinkTestStep.name; + TestValuesTableFactory.registerLocalRawResultsObserver( + tableName, + (integer, strings) -> { + final boolean shouldTakeSavepoint = + CollectionUtils.isEqualCollection( + TestValuesTableFactory.getRawResultsAsStrings(tableName), + sinkTestStep.getExpectedBeforeRestoreAsStrings()); + if (shouldTakeSavepoint) { + future.complete(null); + } + }); + final Map options = new HashMap<>(); + options.put("connector", "values"); + options.put("disable-lookup", "true"); + options.put("sink-insert-only", "false"); + sinkTestStep.apply(tEnv, options); + } + + program.getSetupFunctionTestSteps().forEach(s -> s.apply(tEnv)); + + final SqlTestStep sqlTestStep = program.getRunSqlTestStep(); + + final CompiledPlan compiledPlan = tEnv.compilePlanSql(sqlTestStep.sql); + compiledPlan.writeToFile(getPlanPath(program, getLatestMetadata())); + + final TableResult tableResult = compiledPlan.execute(); + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(); + final JobClient jobClient = tableResult.getJobClient().get(); + final String savepoint = + jobClient + .stopWithSavepoint(false, tmpDir.toString(), SavepointFormatType.DEFAULT) + .get(); + CommonTestUtils.waitForJobStatus(jobClient, Collections.singletonList(JobStatus.FINISHED)); + final Path savepointPath = Paths.get(new URI(savepoint)); + final Path savepointDirPath = getSavepointPath(program, getLatestMetadata()); + Files.createDirectories(savepointDirPath); + Files.move(savepointPath, savepointDirPath, StandardCopyOption.ATOMIC_MOVE); + } + + @ParameterizedTest + @MethodSource("createSpecs") + void testRestore(TableTestProgram program, ExecNodeMetadata metadata) throws Exception { + final EnvironmentSettings settings = EnvironmentSettings.inStreamingMode(); + final SavepointRestoreSettings restoreSettings = + SavepointRestoreSettings.forPath( + getSavepointPath(program, metadata).toString(), + false, + RestoreMode.NO_CLAIM); + SavepointRestoreSettings.toConfiguration(restoreSettings, settings.getConfiguration()); + final TableEnvironment tEnv = TableEnvironment.create(settings); + tEnv.getConfig() + .set( + TableConfigOptions.PLAN_RESTORE_CATALOG_OBJECTS, + TableConfigOptions.CatalogPlanRestore.IDENTIFIER); + for (SourceTestStep sourceTestStep : program.getSetupSourceTestSteps()) { + final String id = TestValuesTableFactory.registerData(sourceTestStep.dataAfterRestore); + final Map options = new HashMap<>(); + options.put("connector", "values"); + options.put("data-id", id); + options.put("disable-lookup", "true"); + options.put("runtime-source", "NewSource"); + sourceTestStep.apply(tEnv, options); + } + + for (SinkTestStep sinkTestStep : program.getSetupSinkTestSteps()) { + final Map options = new HashMap<>(); + options.put("connector", "values"); + options.put("disable-lookup", "true"); + options.put("sink-insert-only", "false"); + sinkTestStep.apply(tEnv, options); + } + + program.getSetupFunctionTestSteps().forEach(s -> s.apply(tEnv)); + + final CompiledPlan compiledPlan = + tEnv.loadPlan(PlanReference.fromFile(getPlanPath(program, metadata))); + compiledPlan.execute().await(); + for (SinkTestStep sinkTestStep : program.getSetupSinkTestSteps()) { + assertThat(TestValuesTableFactory.getRawResultsAsStrings(sinkTestStep.name)) + .containsExactlyInAnyOrder( + Stream.concat( + sinkTestStep.getExpectedBeforeRestoreAsStrings() + .stream(), + sinkTestStep.getExpectedAfterRestoreAsStrings() + .stream()) + .toArray(String[]::new)); + } + } + + private Path getPlanPath(TableTestProgram program, ExecNodeMetadata metadata) { + return Paths.get( + getTestResourceDirectory(program, metadata) + "/plan/" + program.id + ".json"); + } + + private Path getSavepointPath(TableTestProgram program, ExecNodeMetadata metadata) { + return Paths.get(getTestResourceDirectory(program, metadata) + "/savepoint/"); + } + + private String getTestResourceDirectory(TableTestProgram program, ExecNodeMetadata metadata) { + return String.format( + "%s/src/test/resources/restore-tests/%s_%d/%s", + System.getProperty("user.dir"), metadata.name(), metadata.version(), program.id); + } +} diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/ClearQueryBlockAliasResolverTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/ClearQueryBlockAliasResolverTest.java index 3fd2b8131e19f..873604a3b0b9f 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/ClearQueryBlockAliasResolverTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/ClearQueryBlockAliasResolverTest.java @@ -33,7 +33,7 @@ import java.util.stream.Collectors; /** A test class for {@link ClearQueryBlockAliasResolver}. */ -public class ClearQueryBlockAliasResolverTest extends JoinHintTestBase { +class ClearQueryBlockAliasResolverTest extends JoinHintTestBase { // use any join hint for test @Override diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/ScanReuseTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/ScanReuseTest.java index 7016e15d15ec7..82b9d851ac89d 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/ScanReuseTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/ScanReuseTest.java @@ -21,37 +21,38 @@ import org.apache.flink.table.api.TableConfig; import org.apache.flink.table.planner.utils.TableTestBase; import org.apache.flink.table.planner.utils.TableTestUtil; +import org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameters; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; import java.util.Arrays; import java.util.Collection; -import static org.junit.Assume.assumeTrue; +import static org.assertj.core.api.Assumptions.assumeThat; /** Test push project into source with sub plan reuse. */ -@RunWith(Parameterized.class) -public class ScanReuseTest extends TableTestBase { +@ExtendWith(ParameterizedTestExtension.class) +class ScanReuseTest extends TableTestBase { private final boolean isStreaming; private final TableTestUtil util; - public ScanReuseTest(boolean isStreaming) { + ScanReuseTest(boolean isStreaming) { this.isStreaming = isStreaming; TableConfig config = TableConfig.getDefault(); this.util = isStreaming ? streamTestUtil(config) : batchTestUtil(config); } - @Parameterized.Parameters(name = "isStreaming: {0}") - public static Collection parameters() { + @Parameters(name = "isStreaming: {0}") + private static Collection parameters() { return Arrays.asList(true, false); } - @Before - public void before() { + @BeforeEach + void before() { String table = isStreaming ? "CREATE TABLE MyTable (\n" @@ -89,29 +90,29 @@ public void before() { util.tableEnv().executeSql(table); } - @Test - public void testProject() { + @TestTemplate + void testProject() { String sqlQuery = "SELECT T1.a, T1.c, T2.c FROM MyTable T1, MyTable T2 WHERE T1.a = T2.a"; util.verifyExecPlan(sqlQuery); } - @Test - public void testProject1() { + @TestTemplate + void testProject1() { // One side projection String sqlQuery = "SELECT T1.a, T1.b, T1.c, T2.c FROM MyTable T1, MyTable T2 WHERE T1.a = T2.a"; util.verifyExecPlan(sqlQuery); } - @Test - public void testProject2() { + @TestTemplate + void testProject2() { // Two side projection String sqlQuery = "SELECT T1.a, T1.b, T2.c FROM MyTable T1, MyTable T2 WHERE T1.a = T2.a"; util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectNested1() { + @TestTemplate + void testProjectNested1() { String sqlQuery = "SELECT T1.a, T1.i, T2.j FROM" + " (SELECT a, nested.i as i FROM MyTable) T1," @@ -119,8 +120,8 @@ public void testProjectNested1() { util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectNested2() { + @TestTemplate + void testProjectNested2() { String sqlQuery = "SELECT T1.a, T1.i, T2.i FROM" + " (SELECT a, nested.i as i FROM MyTable) T1," @@ -128,8 +129,8 @@ public void testProjectNested2() { util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectNestedWithWholeField() { + @TestTemplate + void testProjectNestedWithWholeField() { String sqlQuery = "SELECT * FROM" + " (SELECT a, nested.i FROM MyTable) T1," @@ -137,16 +138,16 @@ public void testProjectNestedWithWholeField() { util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectWithExpr() { + @TestTemplate + void testProjectWithExpr() { String sqlQuery = "SELECT T1.a, T1.b, T2.c FROM" + " (SELECT a, b + 1 as b FROM MyTable) T1, MyTable T2 WHERE T1.a = T2.a"; util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectWithFilter() { + @TestTemplate + void testProjectWithFilter() { String sqlQuery = "SELECT T1.a, T1.b, T2.c FROM" + " (SELECT * FROM MyTable WHERE b = 2) T1," @@ -154,8 +155,8 @@ public void testProjectWithFilter() { util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectWithMeta1() { + @TestTemplate + void testProjectWithMeta1() { // One side meta String sqlQuery = "SELECT T1.a, T1.b, T1.metadata_1, T1.metadata_2, T2.c, T2.metadata_2" @@ -163,8 +164,8 @@ public void testProjectWithMeta1() { util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectWithMeta2() { + @TestTemplate + void testProjectWithMeta2() { // One side meta String sqlQuery = "SELECT T1.a, T1.b, T1.metadata_1, T2.c, T2.metadata_2" @@ -172,8 +173,8 @@ public void testProjectWithMeta2() { util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectWithMeta3() { + @TestTemplate + void testProjectWithMeta3() { // meta projection String sqlQuery = "SELECT T1.a, T1.b, T1.metadata_1, T2.c, T2.metadata_1" @@ -181,16 +182,16 @@ public void testProjectWithMeta3() { util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectWithMetaAndCompute() { + @TestTemplate + void testProjectWithMetaAndCompute() { String sqlQuery = "SELECT T1.a, T1.b, T1.metadata_1, T1.compute_metadata, T2.c, T2.metadata_2" + " FROM MyTable T1, MyTable T2 WHERE T1.a = T2.a"; util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectWithHints() { + @TestTemplate + void testProjectWithHints() { String sqlQuery = "SELECT T1.a, T1.c, T2.c FROM" + " MyTable /*+ OPTIONS('source.num-element-to-skip'='1') */ T1," @@ -199,8 +200,8 @@ public void testProjectWithHints() { util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectReuseWithHints() { + @TestTemplate + void testProjectReuseWithHints() { String sqlQuery = "SELECT T1.a, T1.c, T2.c FROM" + " MyTable /*+ OPTIONS('source.num-element-to-skip'='1') */ T1," @@ -209,8 +210,8 @@ public void testProjectReuseWithHints() { util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectWithDifferentHints() { + @TestTemplate + void testProjectWithDifferentHints() { String sqlQuery = "SELECT T1.a, T1.c, T2.c FROM" + " MyTable /*+ OPTIONS('source.num-element-to-skip'='1') */ T1," @@ -219,8 +220,8 @@ public void testProjectWithDifferentHints() { util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectWithFilterPushDown() { + @TestTemplate + void testProjectWithFilterPushDown() { String sqlQuery = "SELECT T1.a, T1.c, T2.c FROM" + " (SELECT * FROM" @@ -231,8 +232,8 @@ public void testProjectWithFilterPushDown() { util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectReuseWithFilterPushDown() { + @TestTemplate + void testProjectReuseWithFilterPushDown() { String sqlQuery = "SELECT T1.a, T1.c, T2.c FROM" + " (SELECT * FROM" @@ -243,8 +244,8 @@ public void testProjectReuseWithFilterPushDown() { util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectReuseWithWatermark() { + @TestTemplate + void testProjectReuseWithWatermark() { if (isStreaming) { String ddl = "CREATE TABLE W_T (\n" @@ -271,8 +272,8 @@ public void testProjectReuseWithWatermark() { } } - @Test - public void testProjectWithLimitPushDown() { + @TestTemplate + void testProjectWithLimitPushDown() { String sqlQuery = "SELECT T1.a, T1.c, T2.c FROM" + " (SELECT * FROM" @@ -283,8 +284,8 @@ public void testProjectWithLimitPushDown() { util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectReuseWithLimitPushDown() { + @TestTemplate + void testProjectReuseWithLimitPushDown() { String sqlQuery = "SELECT T1.a, T1.c, T2.c FROM" + " (SELECT * FROM" @@ -295,8 +296,8 @@ public void testProjectReuseWithLimitPushDown() { util.verifyExecPlan(sqlQuery); } - @Test - public void testProjectWithPartitionPushDown() { + @TestTemplate + void testProjectWithPartitionPushDown() { if (!isStreaming) { String sqlQuery = "SELECT T1.a, T1.c, T2.c FROM" @@ -311,8 +312,8 @@ public void testProjectWithPartitionPushDown() { } } - @Test - public void testProjectReuseWithPartitionPushDown() { + @TestTemplate + void testProjectReuseWithPartitionPushDown() { if (!isStreaming) { String sqlQuery = "SELECT T1.a, T1.c, T2.c FROM" @@ -327,9 +328,9 @@ public void testProjectReuseWithPartitionPushDown() { } } - @Test - public void testReuseWithReadMetadataAndWatermarkPushDown1() { - assumeTrue(isStreaming); + @TestTemplate + void testReuseWithReadMetadataAndWatermarkPushDown1() { + assumeThat(isStreaming).isTrue(); String ddl = "CREATE TABLE MyTable1 (\n" + " metadata_0 int METADATA VIRTUAL,\n" @@ -364,9 +365,9 @@ public void testReuseWithReadMetadataAndWatermarkPushDown1() { util.verifyExecPlan(sqlQuery); } - @Test - public void testReuseWithReadMetadataAndWatermarkPushDown2() { - assumeTrue(isStreaming); + @TestTemplate + void testReuseWithReadMetadataAndWatermarkPushDown2() { + assumeThat(isStreaming).isTrue(); String ddl = "CREATE TABLE MyTable1 (\n" + " metadata_0 int METADATA VIRTUAL,\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/program/DynamicPartitionPruningProgramTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/program/DynamicPartitionPruningProgramTest.java index dd776667a155d..9eae51af4120f 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/program/DynamicPartitionPruningProgramTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/program/DynamicPartitionPruningProgramTest.java @@ -27,20 +27,20 @@ import org.apache.flink.table.planner.utils.BatchTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** * Tests for rules that extend {@link FlinkDynamicPartitionPruningProgram} to create {@link * org.apache.flink.table.planner.plan.nodes.physical.batch.BatchPhysicalDynamicFilteringTableSourceScan}. */ -public class DynamicPartitionPruningProgramTest extends TableTestBase { +class DynamicPartitionPruningProgramTest extends TableTestBase { private final BatchTableTestUtil util = batchTestUtil(TableConfig.getDefault()); private final TestValuesCatalog catalog = new TestValuesCatalog("testCatalog", "test_database", true); - @Before - public void setup() { + @BeforeEach + void setup() { catalog.open(); util.tableEnv().registerCatalog("testCatalog", catalog); util.tableEnv().useCatalog("testCatalog"); @@ -82,7 +82,7 @@ public void setup() { } @Test - public void testDimTableFilteringFieldsNotInJoinKey() { + void testDimTableFilteringFieldsNotInJoinKey() { // fact_part.id not in dynamic-filtering-fields, so dynamic partition pruning will not // succeed. String query = @@ -91,7 +91,7 @@ public void testDimTableFilteringFieldsNotInJoinKey() { } @Test - public void testDimTableWithoutFilter() { + void testDimTableWithoutFilter() { // If dim side without filters, dynamic partition pruning will not succeed. String query = "Select * from dim, fact_part where fact_part.fact_date_sk = dim.dim_date_sk" @@ -100,7 +100,7 @@ public void testDimTableWithoutFilter() { } @Test - public void testDimTableWithUnsuitableFilter() { + void testDimTableWithUnsuitableFilter() { // For filters in dim table side, they need to filter enough partitions. Like NOT NULL will // not succeed for dynamic partition pruning. String query = @@ -109,7 +109,7 @@ public void testDimTableWithUnsuitableFilter() { } @Test - public void testFactTableIsNotPartitionTable() { + void testFactTableIsNotPartitionTable() { // non-partition fact table. Dynamic partition pruning will not succeed if fact side is not // partition table. util.tableEnv() @@ -134,7 +134,7 @@ public void testFactTableIsNotPartitionTable() { } @Test - public void testFactTableIsLegacySource() { + void testFactTableIsLegacySource() { util.tableEnv() .executeSql( "CREATE TABLE legacy_source (\n" @@ -158,7 +158,7 @@ public void testFactTableIsLegacySource() { } @Test - public void testDimTableWithFilterPushDown() { + void testDimTableWithFilterPushDown() { // Even though have filter push down, dynamic partition pruning will succeed. String query = "Select * from fact_part join (Select * from dim) t1" @@ -167,7 +167,7 @@ public void testDimTableWithFilterPushDown() { } @Test - public void testJoinKeyIsDynamicFilterFieldNotPartitionKey() { + void testJoinKeyIsDynamicFilterFieldNotPartitionKey() { // Not only partition key, but also dynamic filtering field in join key will succeed in // dynamic partition pruning. String query = @@ -176,7 +176,7 @@ public void testJoinKeyIsDynamicFilterFieldNotPartitionKey() { } @Test - public void testDynamicFilteringFactInRightRule() throws TableNotExistException { + void testDynamicFilteringFactInRightRule() throws TableNotExistException { // Base rule. CatalogTableStatistics tableStatistics = new CatalogTableStatistics(1, 1, 1, 1); catalog.alterTableStatistics( @@ -187,7 +187,7 @@ public void testDynamicFilteringFactInRightRule() throws TableNotExistException } @Test - public void testDynamicFilteringFactInLeftRule() throws TableNotExistException { + void testDynamicFilteringFactInLeftRule() throws TableNotExistException { // Base rule. CatalogTableStatistics tableStatistics = new CatalogTableStatistics(1, 1, 1, 1); catalog.alterTableStatistics( @@ -198,7 +198,7 @@ public void testDynamicFilteringFactInLeftRule() throws TableNotExistException { } @Test - public void testDynamicFilteringFactInRightWithExchangeRule() { + void testDynamicFilteringFactInRightWithExchangeRule() { // Base rule. String query = "Select * from dim, fact_part where fact_part.fact_date_sk = dim.dim_date_sk and dim.price < 500"; @@ -206,7 +206,7 @@ public void testDynamicFilteringFactInRightWithExchangeRule() { } @Test - public void testDynamicFilteringFactInLeftWithExchangeRule() { + void testDynamicFilteringFactInLeftWithExchangeRule() { // Base rule. String query = "Select * from fact_part, dim where fact_part.fact_date_sk = dim.dim_date_sk and dim.price < 500"; @@ -214,7 +214,7 @@ public void testDynamicFilteringFactInLeftWithExchangeRule() { } @Test - public void testDynamicFilteringFactInRightWithCalcRule() throws TableNotExistException { + void testDynamicFilteringFactInRightWithCalcRule() throws TableNotExistException { // Base rule. CatalogTableStatistics tableStatistics = new CatalogTableStatistics(1, 1, 1, 1); catalog.alterTableStatistics( @@ -226,7 +226,7 @@ public void testDynamicFilteringFactInRightWithCalcRule() throws TableNotExistEx } @Test - public void testDynamicFilteringFactInLeftWithCalcRule() throws TableNotExistException { + void testDynamicFilteringFactInLeftWithCalcRule() throws TableNotExistException { // Base rule. CatalogTableStatistics tableStatistics = new CatalogTableStatistics(1, 1, 1, 1); catalog.alterTableStatistics( @@ -238,7 +238,7 @@ public void testDynamicFilteringFactInLeftWithCalcRule() throws TableNotExistExc } @Test - public void testDynamicFilteringFactInRightWithExchangeAndCalcRule() { + void testDynamicFilteringFactInRightWithExchangeAndCalcRule() { // Base rule. String query = "Select * from dim, fact_part where fact_part.fact_date_sk = dim.dim_date_sk" @@ -247,7 +247,7 @@ public void testDynamicFilteringFactInRightWithExchangeAndCalcRule() { } @Test - public void testDynamicFilteringFactInLeftWithExchangeAndCalcRule() { + void testDynamicFilteringFactInLeftWithExchangeAndCalcRule() { // Base rule. String query = "Select * from fact_part, dim where fact_part.fact_date_sk = dim.dim_date_sk" @@ -256,7 +256,7 @@ public void testDynamicFilteringFactInLeftWithExchangeAndCalcRule() { } @Test - public void testComplexCalcInFactSide() { + void testComplexCalcInFactSide() { // Although the partition key is converted, Dynamic Partition pruning can be successfully // applied. String query = @@ -266,7 +266,7 @@ public void testComplexCalcInFactSide() { } @Test - public void testPartitionKeysIsComputeColumnsInFactSide() { + void testPartitionKeysIsComputeColumnsInFactSide() { // Dynamic filtering will not succeed for this query. String query = "Select * from dim join (select fact_date_sk + 1 as fact_date_sk1, price + 1 as price1 from fact_part) t1" @@ -275,7 +275,7 @@ public void testPartitionKeysIsComputeColumnsInFactSide() { } @Test - public void testPartitionKeysOrderIsChangedInFactSide() { + void testPartitionKeysOrderIsChangedInFactSide() { // Dynamic filtering will succeed for this query. String query = "Select * from dim join (select fact_date_sk, id, name, amount, price from fact_part) t1" @@ -284,7 +284,7 @@ public void testPartitionKeysOrderIsChangedInFactSide() { } @Test - public void testPartitionKeysNameIsChangedInFactSide() { + void testPartitionKeysNameIsChangedInFactSide() { // Dynamic filtering will succeed for this query. String query = "Select * from dim join (select id, name, amount, price, fact_date_sk as fact_date_sk1 from fact_part) t1" @@ -293,8 +293,7 @@ public void testPartitionKeysNameIsChangedInFactSide() { } @Test - public void testDynamicFilteringFieldIsComputeColumnsInFactSide() - throws TableNotExistException { + void testDynamicFilteringFieldIsComputeColumnsInFactSide() throws TableNotExistException { CatalogTableStatistics tableStatistics = new CatalogTableStatistics(1, 1, 1, 1); catalog.alterTableStatistics( new ObjectPath("test_database", "dim"), tableStatistics, false); @@ -306,7 +305,7 @@ public void testDynamicFilteringFieldIsComputeColumnsInFactSide() } @Test - public void testLeftOuterJoinWithFactInLeft() { + void testLeftOuterJoinWithFactInLeft() { // left outer join with fact in left will not succeed. Because if fact in left, filtering // condition is useless. String query = @@ -316,7 +315,7 @@ public void testLeftOuterJoinWithFactInLeft() { } @Test - public void testLeftOutJoinWithFactInRight() { + void testLeftOutJoinWithFactInRight() { // left outer join with fact in right will succeed. String query = "Select * from dim left outer join fact_part on fact_part.fact_date_sk = dim.dim_date_sk" @@ -325,7 +324,7 @@ public void testLeftOutJoinWithFactInRight() { } @Test - public void testSemiJoin() { + void testSemiJoin() { // Now dynamic partition pruning support semi join, this query will succeed. String query = "Select * from fact_part where fact_part.fact_date_sk in" @@ -334,7 +333,7 @@ public void testSemiJoin() { } @Test - public void testFullOuterJoin() { + void testFullOuterJoin() { // Now dynamic partition pruning don't support full outer join. String query = "Select * from fact_part full outer join" @@ -343,7 +342,7 @@ public void testFullOuterJoin() { } @Test - public void testAntiJoin() { + void testAntiJoin() { // Now dynamic partition prune don't support anti join. String query = "Select * from fact_part where not exists" @@ -352,7 +351,7 @@ public void testAntiJoin() { } @Test - public void testMultiJoin() { + void testMultiJoin() { // Another table. util.tableEnv() .executeSql( @@ -372,7 +371,7 @@ public void testMultiJoin() { } @Test - public void testComplexDimSideWithJoinInDimSide() { + void testComplexDimSideWithJoinInDimSide() { // TODO, Dpp will not success with complex dim side. util.tableEnv() .executeSql( @@ -405,7 +404,7 @@ public void testComplexDimSideWithJoinInDimSide() { } @Test - public void testComplexDimSideWithAggInDimSide() { + void testComplexDimSideWithAggInDimSide() { // Dim side contains agg will not succeed in this version, it will improve later. util.tableEnv() .executeSql( @@ -427,7 +426,7 @@ public void testComplexDimSideWithAggInDimSide() { } @Test - public void testDppWithoutJoinReorder() { + void testDppWithoutJoinReorder() { // Dpp will success String ddl = "CREATE TABLE test_database.item (\n" @@ -453,7 +452,7 @@ public void testDppWithoutJoinReorder() { } @Test - public void testDppWithSubQuery() { + void testDppWithSubQuery() { // Dpp will success String ddl = "CREATE TABLE test_database.item (\n" @@ -478,7 +477,7 @@ public void testDppWithSubQuery() { } @Test - public void testDppWithUnionInFactSide() { + void testDppWithUnionInFactSide() { // Dpp will success. String ddl = "CREATE TABLE test_database.item (\n" @@ -501,7 +500,7 @@ public void testDppWithUnionInFactSide() { } @Test - public void testDppWithAggInFactSideAndJoinKeyInGrouping() { + void testDppWithAggInFactSideAndJoinKeyInGrouping() { // Dpp will success String ddl = "CREATE TABLE test_database.item (\n" @@ -522,7 +521,7 @@ public void testDppWithAggInFactSideAndJoinKeyInGrouping() { } @Test - public void testDppWithAggInFactSideAndJoinKeyInGroupFunction() { + void testDppWithAggInFactSideAndJoinKeyInGroupFunction() { // Dpp will not success because join key in group function. String ddl = "CREATE TABLE test_database.item (\n" @@ -544,7 +543,7 @@ public void testDppWithAggInFactSideAndJoinKeyInGroupFunction() { } @Test - public void testDppWithAggInFactSideWithAggPushDownEnable() { + void testDppWithAggInFactSideWithAggPushDownEnable() { // Dpp will not success while fact side source support agg push down and source agg push // down enabled is true. String ddl = @@ -566,7 +565,7 @@ public void testDppWithAggInFactSideWithAggPushDownEnable() { } @Test - public void testDppWithAggInFactSideWithAggPushDownDisable() { + void testDppWithAggInFactSideWithAggPushDownDisable() { // Dpp will success while fact side source support agg push down but source agg push down // enabled is false. TableConfig tableConfig = util.tableEnv().getConfig(); @@ -593,7 +592,7 @@ public void testDppWithAggInFactSideWithAggPushDownDisable() { } @Test - public void testDPPWithFactSideJoinKeyChanged() { + void testDPPWithFactSideJoinKeyChanged() { // If partition keys changed in fact side. DPP factor will not success. String ddl = "CREATE TABLE test_database.item (\n" @@ -615,7 +614,7 @@ public void testDPPWithFactSideJoinKeyChanged() { } @Test - public void testDPPWithDimSideJoinKeyChanged() { + void testDPPWithDimSideJoinKeyChanged() { // Although partition keys changed in dim side. DPP will success. String ddl = "CREATE TABLE test_database.item (\n" @@ -637,7 +636,7 @@ public void testDPPWithDimSideJoinKeyChanged() { } @Test - public void testDPPWithJoinKeysNotIncludePartitionKeys() { + void testDPPWithJoinKeysNotIncludePartitionKeys() { // If join keys of partition table join with dim table not include partition keys, dpp will // not success. String ddl = @@ -661,7 +660,7 @@ public void testDPPWithJoinKeysNotIncludePartitionKeys() { } @Test - public void testDppFactSideCannotReuseWithSameCommonSource() { + void testDppFactSideCannotReuseWithSameCommonSource() { String query = "SELECT * FROM(\n" + " Select fact_part.id, fact_part.price, fact_part.amount from fact_part join (Select * from dim) t1" @@ -671,7 +670,7 @@ public void testDppFactSideCannotReuseWithSameCommonSource() { } @Test - public void testDimSideReuseAfterProjectionPushdown() { + void testDimSideReuseAfterProjectionPushdown() { util.tableEnv() .executeSql( "CREATE TABLE fact_part2 (\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/program/FlinkRuntimeFilterProgramTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/program/FlinkRuntimeFilterProgramTest.java index 217ca5577c307..8d01b4cace0ad 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/program/FlinkRuntimeFilterProgramTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/optimize/program/FlinkRuntimeFilterProgramTest.java @@ -31,8 +31,8 @@ import org.apache.flink.table.planner.utils.BatchTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.HashMap; @@ -48,8 +48,8 @@ public class FlinkRuntimeFilterProgramTest extends TableTestBase { private final TestValuesCatalog catalog = new TestValuesCatalog("testCatalog", "test_database", true); - @Before - public void setup() { + @BeforeEach + void setup() { catalog.open(); util.tableEnv().registerCatalog("testCatalog", catalog); util.tableEnv().useCatalog("testCatalog"); @@ -91,7 +91,7 @@ public void setup() { } @Test - public void testSimpleInnerJoin() throws Exception { + void testSimpleInnerJoin() throws Exception { // runtime filter will succeed setupSuitableTableStatistics(); String query = "select * from fact, dim where fact.amount = dim.amount and dim.price < 500"; @@ -99,7 +99,7 @@ public void testSimpleInnerJoin() throws Exception { } @Test - public void testSemiJoin() throws Exception { + void testSemiJoin() throws Exception { // runtime filter will succeed setupSuitableTableStatistics(); util.getTableEnv() @@ -111,7 +111,7 @@ public void testSemiJoin() throws Exception { } @Test - public void testLeftOuterJoinWithLeftBuild() throws Exception { + void testLeftOuterJoinWithLeftBuild() throws Exception { // runtime filter will succeed setupSuitableTableStatistics(); String query = @@ -120,7 +120,7 @@ public void testLeftOuterJoinWithLeftBuild() throws Exception { } @Test - public void testLeftOuterJoinWithRightBuild() throws Exception { + void testLeftOuterJoinWithRightBuild() throws Exception { // runtime filter will not succeed setupSuitableTableStatistics(); String query = @@ -129,7 +129,7 @@ public void testLeftOuterJoinWithRightBuild() throws Exception { } @Test - public void testFullOuterJoin() throws Exception { + void testFullOuterJoin() throws Exception { // runtime filter will not succeed setupSuitableTableStatistics(); String query = @@ -138,7 +138,7 @@ public void testFullOuterJoin() throws Exception { } @Test - public void testAntiJoin() throws Exception { + void testAntiJoin() throws Exception { // runtime filter will not succeed setupSuitableTableStatistics(); String query = @@ -147,7 +147,7 @@ public void testAntiJoin() throws Exception { } @Test - public void testNestedLoopJoin() throws Exception { + void testNestedLoopJoin() throws Exception { // runtime filter will not succeed setupTableRowCount("dim", 1L); setupTableRowCount("fact", SUITABLE_FACT_ROW_COUNT); @@ -156,7 +156,7 @@ public void testNestedLoopJoin() throws Exception { } @Test - public void testProbeSideIsTooSmall() throws Exception { + void testProbeSideIsTooSmall() throws Exception { // runtime filter will not succeed setupTableRowCount("dim", SUITABLE_DIM_ROW_COUNT); // fact is 7.5 GB < 10 GB @@ -166,7 +166,7 @@ public void testProbeSideIsTooSmall() throws Exception { } @Test - public void testBuildSideIsTooLarge() throws Exception { + void testBuildSideIsTooLarge() throws Exception { // runtime filter will not succeed // dim is 48 MB > 6MB setupTableRowCount("dim", 1024L * 1024L); @@ -176,7 +176,7 @@ public void testBuildSideIsTooLarge() throws Exception { } @Test - public void testFilterRatioIsTooSmall() throws Exception { + void testFilterRatioIsTooSmall() throws Exception { // runtime filter will not succeed setupSuitableTableStatistics(); setupTableColumnNdv("dim", "amount", 768L); @@ -186,7 +186,7 @@ public void testFilterRatioIsTooSmall() throws Exception { } @Test - public void testBuildSideIsJoinWithoutExchange() throws Exception { + void testBuildSideIsJoinWithoutExchange() throws Exception { // runtime filter will succeed setupSuitableTableStatistics(); util.tableEnv() @@ -209,7 +209,7 @@ public void testBuildSideIsJoinWithoutExchange() throws Exception { } @Test - public void testBuildSideIsJoinWithTwoAggInputs() throws Exception { + void testBuildSideIsJoinWithTwoAggInputs() throws Exception { // runtime filter will succeed setupSuitableTableStatistics(); util.tableEnv() @@ -241,7 +241,7 @@ public void testBuildSideIsJoinWithTwoAggInputs() throws Exception { } @Test - public void testBuildSideIsLeftJoinWithoutExchange() throws Exception { + void testBuildSideIsLeftJoinWithoutExchange() throws Exception { // runtime filter will not succeed, because the original build side is left join(without // exchange), so we can only push builder to it's left input, but the left input is too // large to as builder. @@ -267,7 +267,7 @@ public void testBuildSideIsLeftJoinWithoutExchange() throws Exception { } @Test - public void testBuildSideIsAggWithoutExchange() throws Exception { + void testBuildSideIsAggWithoutExchange() throws Exception { // runtime filter will succeed // The following two config are used to let the build side is a direct Agg (without // Exchange) @@ -288,7 +288,7 @@ public void testBuildSideIsAggWithoutExchange() throws Exception { } @Test - public void testBuildSideIsCalcWithoutExchange() throws Exception { + void testBuildSideIsCalcWithoutExchange() throws Exception { // runtime filter will succeed // The following two config are used to let the build side is a direct Calc (without // Exchange) @@ -308,7 +308,7 @@ public void testBuildSideIsCalcWithoutExchange() throws Exception { } @Test - public void testCannotInjectMoreThanOneRuntimeFilterInSamePlace() throws Exception { + void testCannotInjectMoreThanOneRuntimeFilterInSamePlace() throws Exception { setupSuitableTableStatistics(); util.tableEnv() .executeSql( @@ -330,7 +330,7 @@ public void testCannotInjectMoreThanOneRuntimeFilterInSamePlace() throws Excepti } @Test - public void testPushDownProbeSideWithCalc() throws Exception { + void testPushDownProbeSideWithCalc() throws Exception { setupSuitableTableStatistics(); String query = "select * from dim, fact where dim.amount = fact.amount and dim.price < 500 and fact.price > 600"; @@ -338,7 +338,7 @@ public void testPushDownProbeSideWithCalc() throws Exception { } @Test - public void testCannotPushDownProbeSideWithCalc() throws Exception { + void testCannotPushDownProbeSideWithCalc() throws Exception { setupSuitableTableStatistics(); String query = "select * from dim inner join (select fact_date_sk, RAND(10) as random from fact) " @@ -347,7 +347,7 @@ public void testCannotPushDownProbeSideWithCalc() throws Exception { } @Test - public void testPushDownProbeSideToAllInputsOfJoin() throws Exception { + void testPushDownProbeSideToAllInputsOfJoin() throws Exception { setupSuitableTableStatistics(); util.tableEnv() .executeSql( @@ -369,7 +369,7 @@ public void testPushDownProbeSideToAllInputsOfJoin() throws Exception { } @Test - public void testPushDownProbeSideToOneInputOfJoin() throws Exception { + void testPushDownProbeSideToOneInputOfJoin() throws Exception { setupSuitableTableStatistics(); util.tableEnv() .executeSql( @@ -391,7 +391,7 @@ public void testPushDownProbeSideToOneInputOfJoin() throws Exception { } @Test - public void testCannotPushDownProbeSideWithJoin() throws Exception { + void testCannotPushDownProbeSideWithJoin() throws Exception { setupSuitableTableStatistics(); util.tableEnv() .executeSql( @@ -413,7 +413,7 @@ public void testCannotPushDownProbeSideWithJoin() throws Exception { } @Test - public void testPushDownProbeSideWithAgg() throws Exception { + void testPushDownProbeSideWithAgg() throws Exception { setupTableRowCount("dim", SUITABLE_DIM_ROW_COUNT); setupTableRowCount("fact", 1024L * SUITABLE_FACT_ROW_COUNT); util.getTableEnv() @@ -427,7 +427,7 @@ public void testPushDownProbeSideWithAgg() throws Exception { } @Test - public void testCannotPushDownProbeSideWithAgg() throws Exception { + void testCannotPushDownProbeSideWithAgg() throws Exception { setupTableRowCount("dim", SUITABLE_DIM_ROW_COUNT); setupTableRowCount("fact", 1024L * SUITABLE_FACT_ROW_COUNT); util.getTableEnv() @@ -441,7 +441,7 @@ public void testCannotPushDownProbeSideWithAgg() throws Exception { } @Test - public void testPushDownProbeSideWithUnion() throws Exception { + void testPushDownProbeSideWithUnion() throws Exception { // probe side will be pushed down to union. setupSuitableTableStatistics(); String query = @@ -453,7 +453,7 @@ public void testPushDownProbeSideWithUnion() throws Exception { } @Test - public void testDoesNotApplyRuntimeFilterAndDPPOnSameKey() throws Exception { + void testDoesNotApplyRuntimeFilterAndDPPOnSameKey() throws Exception { // runtime filter will not success, because already applied DPP on the key setupTableRowCount("dim", SUITABLE_DIM_ROW_COUNT); createPartitionedFactTable(SUITABLE_FACT_ROW_COUNT); @@ -463,7 +463,7 @@ public void testDoesNotApplyRuntimeFilterAndDPPOnSameKey() throws Exception { } @Test - public void testProbeSideIsTableSourceWithoutExchange() throws Exception { + void testProbeSideIsTableSourceWithoutExchange() throws Exception { // runtime filter will not succeed, because probe side is a direct table source setupSuitableTableStatistics(); String query = "select * from fact, dim where fact.amount = dim.amount and dim.price = 500"; diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/FlinkFilterJoinRuleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/FlinkFilterJoinRuleTest.java index 4ec5f93f6cca9..7eb96ba0193c5 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/FlinkFilterJoinRuleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/FlinkFilterJoinRuleTest.java @@ -31,16 +31,16 @@ import org.apache.calcite.plan.hep.HepMatchOrder; import org.apache.calcite.rel.rules.CoreRules; import org.apache.calcite.tools.RuleSets; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test for {@link FlinkFilterJoinRule}. */ -public class FlinkFilterJoinRuleTest extends TableTestBase { +class FlinkFilterJoinRuleTest extends TableTestBase { private BatchTableTestUtil util; - @Before - public void setup() { + @BeforeEach + void setup() { util = batchTestUtil(TableConfig.getDefault()); util.buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE()); CalciteConfig calciteConfig = @@ -102,256 +102,256 @@ public void setup() { } @Test - public void testFilterPushDownLeftSemi1() { + void testFilterPushDownLeftSemi1() { util.verifyRelPlan( "SELECT * FROM (SELECT * FROM leftT WHERE a IN (SELECT c FROM rightT)) T WHERE T.b > 2"); } @Test - public void testFilterPushDownLeftSemi2() { + void testFilterPushDownLeftSemi2() { util.verifyRelPlan( "SELECT * FROM (SELECT * FROM leftT WHERE EXISTS (SELECT * FROM rightT)) T WHERE T.b > 2"); } @Test - public void testFilterPushDownLeftSemi3() { + void testFilterPushDownLeftSemi3() { util.verifyRelPlan( "SELECT * FROM (SELECT * FROM leftT WHERE EXISTS (SELECT * FROM rightT WHERE a = c)) T WHERE T.b > 2"); } @Test - public void testJoinConditionPushDownLeftSemi1() { + void testJoinConditionPushDownLeftSemi1() { util.verifyRelPlan("SELECT * FROM leftT WHERE a IN (SELECT c FROM rightT WHERE b > 2)"); } @Test - public void testJoinConditionPushDownLeftSemi2() { + void testJoinConditionPushDownLeftSemi2() { util.verifyRelPlan("SELECT * FROM leftT WHERE EXISTS (SELECT * FROM rightT WHERE b > 2)"); } @Test - public void testJoinConditionPushDownLeftSemi3() { + void testJoinConditionPushDownLeftSemi3() { util.verifyRelPlan( "SELECT * FROM leftT WHERE EXISTS (SELECT * FROM rightT WHERE a = c AND b > 2)"); } @Test - public void testFilterPushDownLeftAnti1() { + void testFilterPushDownLeftAnti1() { util.verifyRelPlan( "SELECT * FROM (SELECT * FROM leftT WHERE a NOT IN (SELECT c FROM rightT WHERE c < 3)) T WHERE T.b > 2"); } @Test - public void testFilterPushDownLeftAnti2() { + void testFilterPushDownLeftAnti2() { util.verifyRelPlan( "SELECT * FROM (SELECT * FROM leftT WHERE NOT EXISTS (SELECT * FROM rightT where c > 10)) T WHERE T.b > 2"); } @Test - public void testFilterPushDownLeftAnti3() { + void testFilterPushDownLeftAnti3() { util.verifyRelPlan( "SELECT * FROM (SELECT * FROM leftT WHERE a NOT IN (SELECT c FROM rightT WHERE b = d AND c < 3)) T WHERE T.b > 2"); } @Test - public void testFilterPushDownLeftAnti4() { + void testFilterPushDownLeftAnti4() { util.verifyRelPlan( "SELECT * FROM (SELECT * FROM leftT WHERE NOT EXISTS (SELECT * FROM rightT WHERE a = c)) T WHERE T.b > 2"); } @Test - public void testJoinConditionPushDownLeftAnti1() { + void testJoinConditionPushDownLeftAnti1() { util.verifyRelPlan("SELECT * FROM leftT WHERE a NOT IN (SELECT c FROM rightT WHERE b > 2)"); } @Test - public void testJoinConditionPushDownLeftAnti2() { + void testJoinConditionPushDownLeftAnti2() { util.verifyRelPlan( "SELECT * FROM leftT WHERE NOT EXISTS (SELECT * FROM rightT WHERE b > 2)"); } @Test - public void testJoinConditionPushDownLeftAnti3() { + void testJoinConditionPushDownLeftAnti3() { util.verifyRelPlan( "SELECT * FROM leftT WHERE a NOT IN (SELECT c FROM rightT WHERE b = d AND b > 1)"); } @Test - public void testJoinConditionPushDownLeftAnti4() { + void testJoinConditionPushDownLeftAnti4() { util.verifyRelPlan( "SELECT * FROM leftT WHERE NOT EXISTS (SELECT * FROM rightT WHERE a = c AND b > 2)"); } @Test - public void testInnerJoinWithAllFilterFromBothSide() { + void testInnerJoinWithAllFilterFromBothSide() { // can not be pushed down util.verifyRelPlan("SELECT * FROM MyTable1 JOIN MyTable2 ON a1 = a2 WHERE a1 = a2 + 2"); } @Test - public void testInnerJoinWithAllFilterInONClause() { + void testInnerJoinWithAllFilterInONClause() { util.verifyRelPlan( "SELECT * FROM MyTable1 JOIN MyTable2 ON a1 = a2 AND b1 = b2 AND a1 = 2 AND b2 > 10"); } @Test - public void testInnerJoinWithSomeFiltersFromLeftSide() { + void testInnerJoinWithSomeFiltersFromLeftSide() { util.verifyRelPlan("SELECT * FROM MyTable1 JOIN MyTable2 ON a1 = a2 WHERE a1 = 2"); } @Test - public void testInnerJoinWithSomeFiltersFromRightSide() { + void testInnerJoinWithSomeFiltersFromRightSide() { util.verifyRelPlan("SELECT * FROM MyTable1 JOIN MyTable2 ON a1 = a2 WHERE a2 = 2"); } @Test - public void testInnerJoinWithSomeFiltersFromLeftRightSide() { + void testInnerJoinWithSomeFiltersFromLeftRightSide() { util.verifyRelPlan( "SELECT * FROM MyTable1 JOIN MyTable2 ON a1 = a2 AND b1 = b2 AND c1 = c2 WHERE a2 = 2 AND b2 > 10 AND c1 IS NOT NULL"); } @Test - public void testInnerJoinWithAllFiltersFromWhere() { + void testInnerJoinWithAllFiltersFromWhere() { util.verifyRelPlan( "SELECT * FROM MyTable2, MyTable1 WHERE b1 = b2 AND c1 = c2 AND a2 = 2 AND b2 > 10 AND COALESCE(c1, c2) <> '' "); } @Test - public void testInnerJoinWithNullFilter() { + void testInnerJoinWithNullFilter() { util.verifyRelPlan( "SELECT * FROM MyTable1 INNER JOIN MyTable2 ON a1 = a2 WHERE a2 IS NULL"); } @Test - public void testInnerJoinWithNullFilter2() { + void testInnerJoinWithNullFilter2() { util.verifyRelPlan( "SELECT * FROM MyTable1 INNER JOIN MyTable2 ON a1 = a2 WHERE a2 IS NULL AND a1 < 10"); } @Test - public void testInnerJoinWithFilter1() { + void testInnerJoinWithFilter1() { util.verifyRelPlan("SELECT * FROM MyTable1 INNER JOIN MyTable2 ON a1 = a2 WHERE a2 < 1"); } @Test - public void testInnerJoinWithFilter2() { + void testInnerJoinWithFilter2() { util.verifyRelPlan("SELECT * FROM MyTable1 INNER JOIN MyTable2 ON a1 = a2 WHERE a2 <> 1"); } @Test - public void testInnerJoinWithFilter3() { + void testInnerJoinWithFilter3() { util.verifyRelPlan("SELECT * FROM MyTable1 INNER JOIN MyTable2 ON a1 = a2 WHERE a2 > 1"); } @Test - public void testInnerJoinWithFilter4() { + void testInnerJoinWithFilter4() { util.verifyRelPlan("SELECT * FROM MyTable1 INNER JOIN MyTable2 ON a1 = a2 WHERE a2 >= 1"); } @Test - public void testInnerJoinWithFilter5() { + void testInnerJoinWithFilter5() { util.verifyRelPlan("SELECT * FROM MyTable1 INNER JOIN MyTable2 ON a1 = a2 WHERE a2 <= 1"); } @Test - public void testInnerJoinWithFilter6() { + void testInnerJoinWithFilter6() { util.verifyRelPlan("SELECT * FROM MyTable1 INNER JOIN MyTable2 ON a1 = a2 WHERE a2 = null"); } @Test - public void testLeftJoinWithSomeFiltersFromLeftSide() { + void testLeftJoinWithSomeFiltersFromLeftSide() { util.verifyRelPlan("SELECT * FROM MyTable1 LEFT JOIN MyTable2 ON a1 = a2 WHERE a1 = 2"); } @Test - public void testLeftJoinWithAllFilterInONClause() { + void testLeftJoinWithAllFilterInONClause() { util.verifyRelPlan("SELECT * FROM MyTable1 LEFT JOIN MyTable2 ON a1 = a2 AND a2 = 2"); } @Test - public void testLeftJoinWithSomeFiltersFromLeftRightSide() { + void testLeftJoinWithSomeFiltersFromLeftRightSide() { // will be converted to inner join util.verifyRelPlan( "SELECT * FROM MyTable1 LEFT JOIN MyTable2 ON a1 = a2 AND b1 = b2 AND c1 = c2 WHERE a2 = 2 AND b2 > 10 AND c1 IS NOT NULL"); } @Test - public void testLeftJoinWithAllFiltersFromWhere() { + void testLeftJoinWithAllFiltersFromWhere() { // will be converted to inner join util.verifyRelPlan( "SELECT * FROM MyTable1 LEFT JOIN MyTable2 ON true WHERE b1 = b2 AND c1 = c2 AND a2 = 2 AND b2 > 10 AND COALESCE(c1, c2) <> '' "); } @Test - public void testLeftJoinWithNullFilterInRightSide() { + void testLeftJoinWithNullFilterInRightSide() { // Even if there is a filter 'a2 IS NULL', the 'a1 IS NULL' cannot be generated for left // join and this filter cannot be pushed down to both MyTable1 and MyTable2. util.verifyRelPlan("SELECT * FROM MyTable1 LEFT JOIN MyTable2 ON a1 = a2 WHERE a2 IS NULL"); } @Test - public void testLeftJoinWithNullFilterInRightSide2() { + void testLeftJoinWithNullFilterInRightSide2() { // 'a2 IS NULL' cannot infer that 'a1 IS NULL'. util.verifyRelPlan( "SELECT * FROM MyTable1 LEFT JOIN MyTable2 ON a1 = a2 WHERE a2 IS NULL AND a1 < 10"); } @Test - public void testLeftJoinWithFilter1() { + void testLeftJoinWithFilter1() { util.verifyRelPlan("SELECT * FROM MyTable1 LEFT JOIN MyTable2 ON a1 = a2 WHERE a2 < 1"); } @Test - public void testLeftJoinWithFilter2() { + void testLeftJoinWithFilter2() { util.verifyRelPlan("SELECT * FROM MyTable1 LEFT JOIN MyTable2 ON a1 = a2 WHERE a2 <> 1"); } @Test - public void testLeftJoinWithFilter3() { + void testLeftJoinWithFilter3() { util.verifyRelPlan("SELECT * FROM MyTable1 LEFT JOIN MyTable2 ON a1 = a2 WHERE a2 > 1"); } @Test - public void testLeftJoinWithFilter4() { + void testLeftJoinWithFilter4() { util.verifyRelPlan("SELECT * FROM MyTable1 LEFT JOIN MyTable2 ON a1 = a2 WHERE a2 >= 1"); } @Test - public void testLeftJoinWithFilter5() { + void testLeftJoinWithFilter5() { util.verifyRelPlan("SELECT * FROM MyTable1 LEFT JOIN MyTable2 ON a1 = a2 WHERE a2 <= 1"); } @Test - public void testLeftJoinWithFilter6() { + void testLeftJoinWithFilter6() { util.verifyRelPlan("SELECT * FROM MyTable1 LEFT JOIN MyTable2 ON a1 = a2 WHERE a2 = null"); } @Test - public void testRightJoinWithAllFilterInONClause() { + void testRightJoinWithAllFilterInONClause() { util.verifyRelPlan("SELECT * FROM MyTable1 RIGHT JOIN MyTable2 ON a1 = a2 AND a1 = 2"); } @Test - public void testRightJoinWithSomeFiltersFromRightSide() { + void testRightJoinWithSomeFiltersFromRightSide() { util.verifyRelPlan("SELECT * FROM MyTable1 RIGHT JOIN MyTable2 ON a1 = a2 WHERE a2 = 2"); } @Test - public void testRightJoinWithSomeFiltersFromLeftRightSide() { + void testRightJoinWithSomeFiltersFromLeftRightSide() { // will be converted to inner join util.verifyRelPlan( "SELECT * FROM MyTable1 RIGHT JOIN MyTable2 ON a1 = a2 AND b1 = b2 AND c1 = c2 WHERE a2 = 2 AND b2 > 10 AND c1 IS NOT NULL"); } @Test - public void testRightJoinWithAllFiltersFromWhere() { + void testRightJoinWithAllFiltersFromWhere() { // will be converted to inner join util.verifyRelPlan( "SELECT * FROM MyTable1 RIGHT JOIN MyTable2 ON true WHERE b1 = b2 AND c1 = c2 AND a2 = 2 AND b2 > 10 AND COALESCE(c1, c2) <> '' "); } @Test - public void testRightJoinWithNullFilterInLeftSide() { + void testRightJoinWithNullFilterInLeftSide() { // Even if there is a filter 'a1 IS NULL', the 'a2 IS NULL' cannot be generated for right // join and this filter cannot be pushed down to both MyTable1 and MyTable2. util.verifyRelPlan( @@ -359,7 +359,7 @@ public void testRightJoinWithNullFilterInLeftSide() { } @Test - public void testRightJoinWithNullFilterInRightSide2() { + void testRightJoinWithNullFilterInRightSide2() { // 'a1 IS NULL' cannot infer that 'a2 IS NULL'. However, 'a2 < 10' can infer that 'a1 < 10', // and both of them can be pushed down. util.verifyRelPlan( @@ -367,47 +367,47 @@ public void testRightJoinWithNullFilterInRightSide2() { } @Test - public void testFullJoinWithAllFilterInONClause() { + void testFullJoinWithAllFilterInONClause() { util.verifyRelPlan("SELECT * FROM MyTable1 FULL JOIN MyTable2 ON a1 = a2 AND a1 = 2"); } @Test - public void testFullJoinWithSomeFiltersFromLeftSide() { + void testFullJoinWithSomeFiltersFromLeftSide() { // will be converted to inner join util.verifyRelPlan( "SELECT * FROM MyTable1 FULL JOIN MyTable2 ON a1 = a2 AND b1 = b2 WHERE a1 = 2"); } @Test - public void testFullJoinWithSomeFiltersFromRightSide() { + void testFullJoinWithSomeFiltersFromRightSide() { // will be converted to inner join util.verifyRelPlan( "SELECT * FROM MyTable1 FULL JOIN MyTable2 ON a1 = a2 AND b1 = b2 WHERE a2 = 2"); } @Test - public void testFullJoinWithSomeFiltersFromLeftRightSide() { + void testFullJoinWithSomeFiltersFromLeftRightSide() { // will be converted to inner join util.verifyRelPlan( "SELECT * FROM MyTable1 FULL JOIN MyTable2 ON a1 = a2 AND b1 = b2 AND c1 = c2 WHERE a2 = 2 AND b2 > 10 AND c1 IS NOT NULL"); } @Test - public void testFullJoinWithAllFiltersFromWhere() { + void testFullJoinWithAllFiltersFromWhere() { // will be converted to inner join util.verifyRelPlan( "SELECT * FROM MyTable2, MyTable1 WHERE b1 = b2 AND c1 = c2 AND a2 = 2 AND b2 > 10 AND COALESCE(c1, c2) <> '' "); } @Test - public void testSemiJoin() { + void testSemiJoin() { // TODO can not be pushed down now, support it later util.verifyRelPlan( "SELECT * FROM MyTable1 WHERE (a1, b1, c1) IN (SELECT a2, b2, c2 FROM MyTable2 WHERE a2 = 2 AND b2 > 10) AND c1 IS NOT NULL"); } @Test - public void testAntiJoin() { + void testAntiJoin() { // can not be pushed down util.verifyRelPlan( "SELECT * FROM MyTable1 WHERE (a1, b1, c1) NOT IN (select a2, b2, c2 FROM MyTable2 WHERE a2 = 2 AND b2 > 10) AND c1 IS NOT NULL"); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/ProjectSnapshotTransposeRuleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/ProjectSnapshotTransposeRuleTest.java index 7850bda83bd1e..7d0c3374e63ba 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/ProjectSnapshotTransposeRuleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/ProjectSnapshotTransposeRuleTest.java @@ -26,33 +26,35 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; import org.apache.flink.table.planner.utils.TableTestUtil; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameter; +import org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameters; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; import java.util.Arrays; import java.util.Collection; /** Test rule {@link ProjectSnapshotTransposeRule}. */ -@RunWith(Parameterized.class) -public class ProjectSnapshotTransposeRuleTest extends TableTestBase { +@ExtendWith(ParameterizedTestExtension.class) +class ProjectSnapshotTransposeRuleTest extends TableTestBase { private static final String STREAM = "stream"; private static final String BATCH = "batch"; - @Parameterized.Parameter public String mode; + @Parameter private String mode; - @Parameterized.Parameters(name = "mode = {0}") - public static Collection parameters() { + @Parameters(name = "mode = {0}") + private static Collection parameters() { return Arrays.asList(STREAM, BATCH); } private TableTestUtil util; - @Before - public void setup() { + @BeforeEach + void setup() { boolean isStreaming = STREAM.equals(mode); if (isStreaming) { util = streamTestUtil(TableConfig.getDefault()); @@ -90,8 +92,8 @@ public void setup() { tEnv.executeSql(lookup); } - @Test - public void testJoinTemporalTableWithProjectionPushDown() { + @TestTemplate + void testJoinTemporalTableWithProjectionPushDown() { String sql = "SELECT T.*, D.id\n" + "FROM MyTable AS T\n" @@ -101,8 +103,8 @@ public void testJoinTemporalTableWithProjectionPushDown() { util.verifyRelPlan(sql); } - @Test - public void testJoinTemporalTableNotProjectable() { + @TestTemplate + void testJoinTemporalTableNotProjectable() { String sql = "SELECT T.*, D.*\n" + "FROM MyTable AS T\n" @@ -112,8 +114,8 @@ public void testJoinTemporalTableNotProjectable() { util.verifyRelPlan(sql); } - @Test - public void testJoinTemporalTableWithReorderedProject() { + @TestTemplate + void testJoinTemporalTableWithReorderedProject() { String sql = "SELECT T.*, D.age, D.name, D.id\n" + "FROM MyTable AS T\n" @@ -123,8 +125,8 @@ public void testJoinTemporalTableWithReorderedProject() { util.verifyRelPlan(sql); } - @Test - public void testJoinTemporalTableWithProjectAndFilter() { + @TestTemplate + void testJoinTemporalTableWithProjectAndFilter() { String sql = "SELECT T.*, D.id\n" + "FROM MyTable AS T\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/ProjectWatermarkAssignerTransposeRuleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/ProjectWatermarkAssignerTransposeRuleTest.java index 4da8871104e4d..d8968d95ed4ed 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/ProjectWatermarkAssignerTransposeRuleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/ProjectWatermarkAssignerTransposeRuleTest.java @@ -29,15 +29,15 @@ import org.apache.calcite.plan.hep.HepMatchOrder; import org.apache.calcite.tools.RuleSets; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test for {@link ProjectWatermarkAssignerTransposeRule}. */ -public class ProjectWatermarkAssignerTransposeRuleTest extends TableTestBase { +class ProjectWatermarkAssignerTransposeRuleTest extends TableTestBase { private final StreamTableTestUtil util = streamTestUtil(TableConfig.getDefault()); - @Before - public void setup() { + @BeforeEach + void setup() { FlinkChainedProgram program = new FlinkChainedProgram<>(); program.addLast( @@ -106,57 +106,57 @@ public void setup() { } @Test - public void simpleTranspose() { + void simpleTranspose() { util.verifyRelPlan("SELECT a, c FROM SimpleTable"); } @Test - public void transposeWithReorder() { + void transposeWithReorder() { util.verifyRelPlan("SELECT b, a FROM SimpleTable"); } @Test - public void transposeWithNestedField() { + void transposeWithNestedField() { util.verifyRelPlan("SELECT b, d.d1, d.d2 FROM SimpleTable"); } @Test - public void complicatedTranspose() { + void complicatedTranspose() { util.verifyRelPlan("SELECT d.d1, d.d2 + b FROM SimpleTable"); } @Test - public void transposeExcludeRowTime() { + void transposeExcludeRowTime() { util.verifyRelPlan("SELECT SECOND(c) FROM SimpleTable"); } @Test - public void transposeWithIncludeComputedRowTime() { + void transposeWithIncludeComputedRowTime() { util.verifyRelPlan("SELECT a, b, d FROM VirtualTable"); } @Test - public void transposeWithExcludeComputedRowTime() { + void transposeWithExcludeComputedRowTime() { util.verifyRelPlan("SELECT a, b FROM VirtualTable"); } @Test - public void transposeWithExcludeComputedRowTime2() { + void transposeWithExcludeComputedRowTime2() { util.verifyRelPlan("SELECT a, b, SECOND(d) FROM VirtualTable"); } @Test - public void transposeWithExcludeComputedRowTime3() { + void transposeWithExcludeComputedRowTime3() { util.verifyRelPlan("SELECT a, SECOND(d) FROM NestedTable"); } @Test - public void transposeWithDuplicateColumns() { + void transposeWithDuplicateColumns() { util.verifyRelPlan("SELECT a, b, b as e FROM VirtualTable"); } @Test - public void transposeWithWatermarkWithMultipleInput() { + void transposeWithWatermarkWithMultipleInput() { util.verifyRelPlan("SELECT a FROM UdfTable"); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushFilterInCalcIntoTableSourceRuleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushFilterInCalcIntoTableSourceRuleTest.java index 9875443137623..1d929b751aea5 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushFilterInCalcIntoTableSourceRuleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushFilterInCalcIntoTableSourceRuleTest.java @@ -34,15 +34,14 @@ import org.apache.calcite.plan.hep.HepMatchOrder; import org.apache.calcite.rel.rules.CoreRules; import org.apache.calcite.tools.RuleSets; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test for {@link PushFilterInCalcIntoTableSourceRuleTest}. */ -public class PushFilterInCalcIntoTableSourceRuleTest - extends PushFilterIntoTableSourceScanRuleTestBase { +class PushFilterInCalcIntoTableSourceRuleTest extends PushFilterIntoTableSourceScanRuleTestBase { - @Before - public void setup() { + @BeforeEach + void setup() { util = streamTestUtil(TableConfig.getDefault()); FlinkChainedProgram program = new FlinkChainedProgram<>(); @@ -112,12 +111,12 @@ public void setup() { } @Test - public void testFailureToPushFilterIntoSourceWithoutWatermarkPushdown() { + void testFailureToPushFilterIntoSourceWithoutWatermarkPushdown() { util.verifyRelPlan("SELECT * FROM WithWatermark WHERE LOWER(name) = 'foo'"); } @Test - public void testLowerUpperPushdown() { + void testLowerUpperPushdown() { String ddl = "CREATE TABLE MTable (\n" + " a STRING,\n" @@ -132,7 +131,7 @@ public void testLowerUpperPushdown() { } @Test - public void testWithInterval() { + void testWithInterval() { String ddl = "CREATE TABLE MTable (\n" + "a TIMESTAMP(3),\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushFilterIntoTableSourceScanRuleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushFilterIntoTableSourceScanRuleTest.java index 47e03c3beb409..f4969a6fe887b 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushFilterIntoTableSourceScanRuleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushFilterIntoTableSourceScanRuleTest.java @@ -30,15 +30,14 @@ import org.apache.calcite.plan.hep.HepMatchOrder; import org.apache.calcite.rel.rules.CoreRules; import org.apache.calcite.tools.RuleSets; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** Test for {@link PushFilterIntoTableSourceScanRule}. */ -public class PushFilterIntoTableSourceScanRuleTest - extends PushFilterIntoTableSourceScanRuleTestBase { +class PushFilterIntoTableSourceScanRuleTest extends PushFilterIntoTableSourceScanRuleTestBase { - @Before - public void setup() { + @BeforeEach + void setup() { util = batchTestUtil(TableConfig.getDefault()); ((BatchTableTestUtil) util).buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE()); CalciteConfig calciteConfig = @@ -122,7 +121,7 @@ public void setup() { } @Test - public void testLowerUpperPushdown() { + void testLowerUpperPushdown() { String ddl = "CREATE TABLE MTable (\n" + " a STRING,\n" @@ -137,7 +136,7 @@ public void testLowerUpperPushdown() { } @Test - public void testWithInterval() { + void testWithInterval() { String ddl = "CREATE TABLE MTable (\n" + "a TIMESTAMP(3),\n" @@ -153,31 +152,31 @@ public void testWithInterval() { } @Test - public void testBasicNestedFilter() { + void testBasicNestedFilter() { util.verifyRelPlan("SELECT * FROM NestedTable WHERE deepNested.nested1.`value` > 2"); } @Test - public void testNestedFilterWithDotInTheName() { + void testNestedFilterWithDotInTheName() { util.verifyRelPlan( "SELECT id FROM NestedTable WHERE `deepNestedWith.`.nested.`.value` > 5"); } @Test - public void testNestedFilterWithBacktickInTheName() { + void testNestedFilterWithBacktickInTheName() { util.verifyRelPlan( "SELECT id FROM NestedTable WHERE `deepNestedWith.`.nested.```name` = 'foo'"); } @Test - public void testNestedFilterOnMapKey() { + void testNestedFilterOnMapKey() { util.verifyRelPlan( "SELECT * FROM NestedItemTable WHERE" + " `Result`.`Mid`.data_map['item'].`value` = 3"); } @Test - public void testNestedFilterOnArrayField() { + void testNestedFilterOnArrayField() { util.verifyRelPlan( "SELECT * FROM NestedItemTable WHERE `Result`.`Mid`.data_arr[2].`value` = 3"); } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushFilterIntoTableSourceScanRuleTestBase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushFilterIntoTableSourceScanRuleTestBase.java index 80629167fd1ee..435474926b65b 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushFilterIntoTableSourceScanRuleTestBase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushFilterIntoTableSourceScanRuleTestBase.java @@ -22,106 +22,106 @@ import org.apache.flink.table.planner.utils.TableTestBase; import org.apache.flink.table.planner.utils.TableTestUtil; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** Test base for testing rule which pushes filter into table source. */ -public abstract class PushFilterIntoTableSourceScanRuleTestBase extends TableTestBase { +abstract class PushFilterIntoTableSourceScanRuleTestBase extends TableTestBase { protected TableTestUtil util; @Test - public void testCanPushDown() { + void testCanPushDown() { util.verifyRelPlan("SELECT * FROM MyTable WHERE amount > 2"); } @Test - public void testCanPushDownWithCastConstant() { + void testCanPushDownWithCastConstant() { util.verifyRelPlan("SELECT * FROM MyTable WHERE amount > cast(1.1 as int)"); } @Test - public void testCanPushDownWithVirtualColumn() { + void testCanPushDownWithVirtualColumn() { util.verifyRelPlan("SELECT * FROM VirtualTable WHERE amount > 2"); } @Test - public void testCannotPushDown() { + void testCannotPushDown() { // TestFilterableTableSource only accept predicates with `amount` util.verifyRelPlan("SELECT * FROM MyTable WHERE price > 10"); } @Test - public void testCannotPushDownWithCastConstant() { + void testCannotPushDownWithCastConstant() { // TestFilterableTableSource only accept predicates with `amount` util.verifyRelPlan("SELECT * FROM MyTable WHERE price > cast(10.1 as int)"); } @Test - public void testCannotPushDownWithVirtualColumn() { + void testCannotPushDownWithVirtualColumn() { // TestFilterableTableSource only accept predicates with `amount` util.verifyRelPlan("SELECT * FROM VirtualTable WHERE price > 10"); } @Test - public void testPartialPushDown() { + void testPartialPushDown() { util.verifyRelPlan("SELECT * FROM MyTable WHERE amount > 2 AND price > 10"); } @Test - public void testPartialPushDownWithVirtualColumn() { + void testPartialPushDownWithVirtualColumn() { util.verifyRelPlan("SELECT * FROM VirtualTable WHERE amount > 2 AND price > 10"); } @Test - public void testFullyPushDown() { + void testFullyPushDown() { util.verifyRelPlan("SELECT * FROM MyTable WHERE amount > 2 AND amount < 10"); } @Test - public void testFullyPushDownWithVirtualColumn() { + void testFullyPushDownWithVirtualColumn() { util.verifyRelPlan("SELECT * FROM VirtualTable WHERE amount > 2 AND amount < 10"); } @Test - public void testPartialPushDown2() { + void testPartialPushDown2() { util.verifyRelPlan("SELECT * FROM MyTable WHERE amount > 2 OR price > 10"); } @Test - public void testPartialPushDown2WithVirtualColumn() { + void testPartialPushDown2WithVirtualColumn() { util.verifyRelPlan("SELECT * FROM VirtualTable WHERE amount > 2 OR price > 10"); } @Test - public void testCannotPushDown3() { + void testCannotPushDown3() { util.verifyRelPlan("SELECT * FROM MyTable WHERE amount > 2 OR amount < 10"); } @Test - public void testCannotPushDown3WithVirtualColumn() { + void testCannotPushDown3WithVirtualColumn() { util.verifyRelPlan("SELECT * FROM VirtualTable WHERE amount > 2 OR amount < 10"); } @Test - public void testUnconvertedExpression() { + void testUnconvertedExpression() { util.verifyRelPlan( "SELECT * FROM MyTable WHERE\n" + " amount > 2 AND id < 100 AND CAST(amount AS BIGINT) > 10"); } @Test - public void testWithUdf() { + void testWithUdf() { util.addFunction("myUdf", Func1$.MODULE$); util.verifyRelPlan("SELECT * FROM MyTable WHERE amount > 2 AND myUdf(amount) < 32"); } @Test - public void testLowerUpperPushdown() { + void testLowerUpperPushdown() { util.verifyRelPlan("SELECT * FROM MTable WHERE LOWER(a) = 'foo' AND UPPER(b) = 'bar'"); } @Test - public void testWithInterval() { + void testWithInterval() { util.verifyRelPlan( "SELECT * FROM MTable\n" + "WHERE TIMESTAMPADD(HOUR, 5, a) >= b\n" @@ -130,7 +130,7 @@ public void testWithInterval() { } @Test - public void testCannotPushDownIn() { + void testCannotPushDownIn() { // this test is to avoid filter push down rules throwing exceptions // when dealing with IN expressions, this is because Filter in calcite // requires its condition to be "flat" @@ -138,7 +138,7 @@ public void testCannotPushDownIn() { } @Test - public void testWithNullLiteral() { + void testWithNullLiteral() { util.verifyRelPlan( "WITH MyView AS (SELECT CASE\n" + " WHEN amount > 0 THEN name\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushLimitIntoTableSourceScanRuleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushLimitIntoTableSourceScanRuleTest.java index 3fb88c1cbfe6f..671457a670221 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushLimitIntoTableSourceScanRuleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushLimitIntoTableSourceScanRuleTest.java @@ -30,10 +30,12 @@ import org.apache.calcite.plan.hep.HepMatchOrder; import org.apache.calcite.rel.rules.CoreRules; import org.apache.calcite.tools.RuleSets; +import org.junit.jupiter.api.BeforeEach; /** Test for {@link PushLimitIntoTableSourceScanRule}. */ -public class PushLimitIntoTableSourceScanRuleTest - extends PushLimitIntoLegacyTableSourceScanRuleTest { +class PushLimitIntoTableSourceScanRuleTest extends PushLimitIntoLegacyTableSourceScanRuleTest { + + @BeforeEach @Override public void setup() { util().buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE()); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushPartitionIntoTableSourceScanRuleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushPartitionIntoTableSourceScanRuleTest.java index 355374f3d8d5c..613f6c75adf84 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushPartitionIntoTableSourceScanRuleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushPartitionIntoTableSourceScanRuleTest.java @@ -33,7 +33,8 @@ import org.apache.calcite.plan.hep.HepMatchOrder; import org.apache.calcite.rel.rules.CoreRules; import org.apache.calcite.tools.RuleSets; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; import java.util.Arrays; import java.util.HashMap; @@ -41,13 +42,13 @@ import java.util.Map; /** Test for {@link PushPartitionIntoTableSourceScanRule}. */ -public class PushPartitionIntoTableSourceScanRuleTest +class PushPartitionIntoTableSourceScanRuleTest extends PushPartitionIntoLegacyTableSourceScanRuleTest { - public PushPartitionIntoTableSourceScanRuleTest( - boolean sourceFetchPartitions, boolean useFilter) { + PushPartitionIntoTableSourceScanRuleTest(boolean sourceFetchPartitions, boolean useFilter) { super(sourceFetchPartitions, useFilter); } + @BeforeEach @Override public void setup() throws Exception { util().buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE()); @@ -147,8 +148,8 @@ public void setup() throws Exception { } } - @Test - public void testUnconvertedExpression() { + @TestTemplate + void testUnconvertedExpression() { String sql = "select * from MyTable where trim(part1) = 'A' and part2 > 1"; util().verifyRelPlan(sql); } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushProjectIntoTableSourceScanRuleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushProjectIntoTableSourceScanRuleTest.java index 9d16e67c7c513..1349cf2cf1c2c 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushProjectIntoTableSourceScanRuleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushProjectIntoTableSourceScanRuleTest.java @@ -31,14 +31,15 @@ import org.apache.flink.table.planner.plan.optimize.program.HEP_RULES_EXECUTION_TYPE; import org.apache.flink.table.planner.utils.TableConfigUtils; import org.apache.flink.table.types.DataType; -import org.apache.flink.testutils.junit.SharedObjects; +import org.apache.flink.testutils.junit.SharedObjectsExtension; import org.apache.flink.testutils.junit.SharedReference; import org.apache.calcite.plan.hep.HepMatchOrder; import org.apache.calcite.rel.rules.CoreRules; import org.apache.calcite.tools.RuleSets; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.Arrays; @@ -51,11 +52,12 @@ import static org.assertj.core.api.Assertions.assertThat; /** Test for {@link PushProjectIntoTableSourceScanRule}. */ -public class PushProjectIntoTableSourceScanRuleTest - extends PushProjectIntoLegacyTableSourceScanRuleTest { +class PushProjectIntoTableSourceScanRuleTest extends PushProjectIntoLegacyTableSourceScanRuleTest { - @Rule public final SharedObjects sharedObjects = SharedObjects.create(); + @RegisterExtension + private final SharedObjectsExtension sharedObjects = SharedObjectsExtension.create(); + @BeforeEach @Override public void setup() { util().buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE()); @@ -177,7 +179,7 @@ public void setup() { } @Test - public void testProjectWithMapType() { + void testProjectWithMapType() { String sqlQuery = "SELECT id, testMap['e']\n" + "FROM NestedTable"; util().verifyRelPlan(sqlQuery); } @@ -196,7 +198,7 @@ public void testNestedProject() { } @Test - public void testComplicatedNestedProject() { + void testComplicatedNestedProject() { String sqlQuery = "SELECT id," + " deepNested.nested1.name AS nestedName,\n" @@ -206,14 +208,14 @@ public void testComplicatedNestedProject() { } @Test - public void testProjectWithDuplicateMetadataKey() { + void testProjectWithDuplicateMetadataKey() { String sqlQuery = "SELECT id, metadata_3, metadata_1 FROM MetadataTable"; util().verifyRelPlan(sqlQuery); } @Test - public void testNestProjectWithMetadata() { + void testNestProjectWithMetadata() { String sqlQuery = "SELECT id," + " deepNested.nested1 AS nested1,\n" @@ -224,7 +226,7 @@ public void testNestProjectWithMetadata() { } @Test - public void testNestProjectWithUpsertSource() { + void testNestProjectWithUpsertSource() { String sqlQuery = "SELECT id," + " deepNested.nested1 AS nested1,\n" @@ -235,7 +237,7 @@ public void testNestProjectWithUpsertSource() { } @Test - public void testNestedProjectFieldAccessWithITEM() { + void testNestedProjectFieldAccessWithITEM() { util().verifyRelPlan( "SELECT " + "`Result`.`Mid`.data_arr[ID].`value`, " @@ -244,7 +246,7 @@ public void testNestedProjectFieldAccessWithITEM() { } @Test - public void testNestedProjectFieldAccessWithITEMWithConstantIndex() { + void testNestedProjectFieldAccessWithITEMWithConstantIndex() { util().verifyRelPlan( "SELECT " + "`Result`.`Mid`.data_arr[2].`value`, " @@ -253,7 +255,7 @@ public void testNestedProjectFieldAccessWithITEMWithConstantIndex() { } @Test - public void testNestedProjectFieldAccessWithITEMContainsTopLevelAccess() { + void testNestedProjectFieldAccessWithITEMContainsTopLevelAccess() { util().verifyRelPlan( "SELECT " + "`Result`.`Mid`.data_arr[2].`value`, " @@ -264,7 +266,7 @@ public void testNestedProjectFieldAccessWithITEMContainsTopLevelAccess() { } @Test - public void testProjectFieldAccessWithITEM() { + void testProjectFieldAccessWithITEM() { util().verifyRelPlan( "SELECT " + "`Result`.data_arr[ID].`value`, " @@ -276,7 +278,7 @@ public void testProjectFieldAccessWithITEM() { } @Test - public void testMetadataProjectionWithoutProjectionPushDownWhenSupported() { + void testMetadataProjectionWithoutProjectionPushDownWhenSupported() { final SharedReference> appliedKeys = sharedObjects.add(new ArrayList<>()); final TableDescriptor sourceDescriptor = TableFactoryHarness.newBuilder() @@ -290,7 +292,7 @@ public void testMetadataProjectionWithoutProjectionPushDownWhenSupported() { } @Test - public void testMetadataProjectionWithoutProjectionPushDownWhenNotSupported() { + void testMetadataProjectionWithoutProjectionPushDownWhenNotSupported() { final SharedReference> appliedKeys = sharedObjects.add(new ArrayList<>()); final TableDescriptor sourceDescriptor = TableFactoryHarness.newBuilder() @@ -304,7 +306,7 @@ public void testMetadataProjectionWithoutProjectionPushDownWhenNotSupported() { } @Test - public void testMetadataProjectionWithoutProjectionPushDownWhenSupportedAndNoneSelected() { + void testMetadataProjectionWithoutProjectionPushDownWhenSupportedAndNoneSelected() { final SharedReference> appliedKeys = sharedObjects.add(new ArrayList<>()); final TableDescriptor sourceDescriptor = TableFactoryHarness.newBuilder() @@ -320,7 +322,7 @@ public void testMetadataProjectionWithoutProjectionPushDownWhenSupportedAndNoneS } @Test - public void testMetadataProjectionWithoutProjectionPushDownWhenNotSupportedAndNoneSelected() { + void testMetadataProjectionWithoutProjectionPushDownWhenNotSupportedAndNoneSelected() { final SharedReference> appliedKeys = sharedObjects.add(new ArrayList<>()); final TableDescriptor sourceDescriptor = TableFactoryHarness.newBuilder() @@ -334,7 +336,7 @@ public void testMetadataProjectionWithoutProjectionPushDownWhenNotSupportedAndNo } @Test - public void testProjectionIncludingOnlyMetadata() { + void testProjectionIncludingOnlyMetadata() { replaceProgramWithProjectMergeRule(); final AtomicReference appliedProjectionDataType = new AtomicReference<>(null); @@ -374,7 +376,7 @@ private void replaceProgramWithProjectMergeRule() { } @Test - public void testProjectionWithMetadataAndPhysicalFields() { + void testProjectionWithMetadataAndPhysicalFields() { replaceProgramWithProjectMergeRule(); final AtomicReference appliedProjectionDataType = new AtomicReference<>(null); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushWatermarkIntoTableSourceScanRuleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushWatermarkIntoTableSourceScanRuleTest.java index 47a21ad0ae97f..6fc848f56b45a 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushWatermarkIntoTableSourceScanRuleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/PushWatermarkIntoTableSourceScanRuleTest.java @@ -37,8 +37,8 @@ import org.apache.calcite.plan.hep.HepMatchOrder; import org.apache.calcite.rel.rules.CoreRules; import org.apache.calcite.tools.RuleSets; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.time.Duration; @@ -48,10 +48,10 @@ * Test rule {@link PushWatermarkIntoTableSourceScanAcrossCalcRule} and {@link * PushWatermarkIntoTableSourceScanRule}. */ -public class PushWatermarkIntoTableSourceScanRuleTest extends TableTestBase { +class PushWatermarkIntoTableSourceScanRuleTest extends TableTestBase { private final StreamTableTestUtil util = streamTestUtil(TableConfig.getDefault()); - @Before + @BeforeEach public void setup() { FlinkChainedProgram program = new FlinkChainedProgram<>(); program.addLast( @@ -80,7 +80,7 @@ public void setup() { } @Test - public void testSimpleWatermark() { + void testSimpleWatermark() { String ddl = "CREATE TABLE MyTable(" + " a INT,\n" @@ -98,7 +98,7 @@ public void testSimpleWatermark() { } @Test - public void testWatermarkOnComputedColumn() { + void testWatermarkOnComputedColumn() { String ddl = "CREATE TABLE MyTable(" + " a INT,\n" @@ -117,7 +117,7 @@ public void testWatermarkOnComputedColumn() { } @Test - public void testWatermarkOnComputedColumnWithQuery() { + void testWatermarkOnComputedColumnWithQuery() { String ddl = "CREATE TABLE MyTable(" + " a INT,\n" @@ -137,7 +137,7 @@ public void testWatermarkOnComputedColumnWithQuery() { } @Test - public void testWatermarkOnComputedColumnWithMultipleInputs() { + void testWatermarkOnComputedColumnWithMultipleInputs() { String ddl = "CREATE TABLE MyTable(" + " a STRING,\n" @@ -155,7 +155,7 @@ public void testWatermarkOnComputedColumnWithMultipleInputs() { } @Test - public void testWatermarkOnRow() { + void testWatermarkOnRow() { String ddl = "CREATE TABLE MyTable(" + " a INT,\n" @@ -174,7 +174,7 @@ public void testWatermarkOnRow() { } @Test - public void testWatermarkOnNestedRow() { + void testWatermarkOnNestedRow() { String ddl = "CREATE TABLE MyTable(" + " a INT,\n" @@ -193,7 +193,7 @@ public void testWatermarkOnNestedRow() { } @Test - public void testWatermarkWithMultiInputUdf() { + void testWatermarkWithMultiInputUdf() { JavaFunc5.closeCalled = false; JavaFunc5.openCalled = false; util.addTemporarySystemFunction("func", new JavaFunc5()); @@ -216,7 +216,7 @@ public void testWatermarkWithMultiInputUdf() { } @Test - public void testWatermarkOnMetadata() { + void testWatermarkOnMetadata() { String ddl = "CREATE TABLE MyTable(" + " `a` INT,\n" @@ -237,7 +237,7 @@ public void testWatermarkOnMetadata() { } @Test - public void testWatermarkWithIdleSource() { + void testWatermarkWithIdleSource() { util.tableEnv().getConfig().set(TABLE_EXEC_SOURCE_IDLE_TIMEOUT, Duration.ofMillis(1000)); String ddl = "CREATE TABLE MyTable(" @@ -256,7 +256,7 @@ public void testWatermarkWithIdleSource() { } @Test - public void testWatermarkWithPythonFunctionInComputedColumn() { + void testWatermarkWithPythonFunctionInComputedColumn() { util.tableEnv() .createTemporaryFunction( "parse_ts", @@ -277,7 +277,7 @@ public void testWatermarkWithPythonFunctionInComputedColumn() { } @Test - public void testWatermarkEmitStrategyWithOptions() { + void testWatermarkEmitStrategyWithOptions() { String ddl = "CREATE TABLE MyTable(" + " a INT,\n" @@ -296,7 +296,7 @@ public void testWatermarkEmitStrategyWithOptions() { } @Test - public void testWatermarkEmitStrategyWithHint() { + void testWatermarkEmitStrategyWithHint() { String ddl = "CREATE TABLE MyTable(" + " a INT,\n" @@ -316,7 +316,7 @@ public void testWatermarkEmitStrategyWithHint() { } @Test - public void testWatermarkAlignmentWithOptions() { + void testWatermarkAlignmentWithOptions() { String ddl = "CREATE TABLE MyTable(" + " a INT,\n" @@ -336,7 +336,7 @@ public void testWatermarkAlignmentWithOptions() { } @Test - public void testWatermarkAlignmentWithHint() { + void testWatermarkAlignmentWithHint() { String ddl = "CREATE TABLE MyTable(" + " a INT,\n" @@ -357,7 +357,7 @@ public void testWatermarkAlignmentWithHint() { } @Test - public void testIdleSourceWithOptions() { + void testIdleSourceWithOptions() { util.tableEnv().getConfig().set(TABLE_EXEC_SOURCE_IDLE_TIMEOUT, Duration.ofMillis(1000)); String ddl = "CREATE TABLE MyTable(" @@ -377,7 +377,7 @@ public void testIdleSourceWithOptions() { } @Test - public void testIdleSourceWithHint() { + void testIdleSourceWithHint() { util.tableEnv().getConfig().set(TABLE_EXEC_SOURCE_IDLE_TIMEOUT, Duration.ofMillis(1000)); String ddl = "CREATE TABLE MyTable(" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/RemoveUnreachableCoalesceArgumentsRuleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/RemoveUnreachableCoalesceArgumentsRuleTest.java index 799ac38014c4e..3610b20d6f3a5 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/RemoveUnreachableCoalesceArgumentsRuleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/RemoveUnreachableCoalesceArgumentsRuleTest.java @@ -25,18 +25,18 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static org.apache.flink.table.api.DataTypes.STRING; /** Test rule {@link RemoveUnreachableCoalesceArgumentsRule}. */ -public class RemoveUnreachableCoalesceArgumentsRuleTest extends TableTestBase { +class RemoveUnreachableCoalesceArgumentsRuleTest extends TableTestBase { private StreamTableTestUtil util; - @Before - public void before() { + @BeforeEach + void before() { util = streamTestUtil(TableConfig.getDefault()); final TableDescriptor sourceDescriptor = @@ -54,38 +54,38 @@ public void before() { } @Test - public void testOnlyLastNonNull() { + void testOnlyLastNonNull() { util.verifyRelPlan("SELECT COALESCE(f0, f1) FROM T"); } @Test - public void testAllNullable() { + void testAllNullable() { util.verifyRelPlan("SELECT COALESCE(f0, f2) FROM T"); } @Test - public void testDropLastConstant() { + void testDropLastConstant() { util.verifyRelPlan("SELECT COALESCE(f0, f1, '-') FROM T"); } @Test - public void testDropCoalesce() { + void testDropCoalesce() { util.verifyRelPlan("SELECT COALESCE(f1, '-') FROM T"); } @Test - public void testFilterCoalesce() { + void testFilterCoalesce() { util.verifyRelPlan("SELECT * FROM T WHERE COALESCE(f0, f1, '-') = 'abc'"); } @Test - public void testJoinCoalesce() { + void testJoinCoalesce() { util.verifyRelPlan( "SELECT * FROM T t1 LEFT JOIN T t2 ON COALESCE(t1.f0, '-', t1.f2) = t2.f0"); } @Test - public void testMultipleCoalesces() { + void testMultipleCoalesces() { util.verifyRelPlan( "SELECT COALESCE(1),\n" + "COALESCE(1, 2),\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRuleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRuleTest.java index ba4d497fd9e5c..e512d3b26d545 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRuleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRuleTest.java @@ -21,33 +21,34 @@ import org.apache.flink.table.api.TableConfig; import org.apache.flink.table.planner.utils.TableTestBase; import org.apache.flink.table.planner.utils.TableTestUtil; +import org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameters; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; import java.util.Arrays; import java.util.Collection; /** Tests for {@link WrapJsonAggFunctionArgumentsRule}. */ -@RunWith(Parameterized.class) -public class WrapJsonAggFunctionArgumentsRuleTest extends TableTestBase { +@ExtendWith(ParameterizedTestExtension.class) +class WrapJsonAggFunctionArgumentsRuleTest extends TableTestBase { private final boolean batchMode; private TableTestUtil util; - @Parameterized.Parameters(name = "batchMode = {0}") - public static Collection data() { + @Parameters(name = "batchMode = {0}") + private static Collection data() { return Arrays.asList(true, false); } - public WrapJsonAggFunctionArgumentsRuleTest(boolean batchMode) { + WrapJsonAggFunctionArgumentsRuleTest(boolean batchMode) { this.batchMode = batchMode; } - @Before - public void setup() { + @BeforeEach + void setup() { if (batchMode) { util = batchTestUtil(TableConfig.getDefault()); } else { @@ -79,55 +80,55 @@ public void setup() { + "'\n)"); } - @Test - public void testJsonObjectAgg() { + @TestTemplate + void testJsonObjectAgg() { util.verifyRelPlan("SELECT JSON_OBJECTAGG(f1 VALUE f1) FROM T"); } - @Test - public void testJsonObjectAggInGroupWindow() { + @TestTemplate + void testJsonObjectAggInGroupWindow() { util.verifyRelPlan("SELECT f0, JSON_OBJECTAGG(f1 VALUE f0) FROM T GROUP BY f0"); } - @Test - public void testJsonArrayAgg() { + @TestTemplate + void testJsonArrayAgg() { util.verifyRelPlan("SELECT JSON_ARRAYAGG(f0) FROM T"); } - @Test - public void testJsonArrayAggInGroupWindow() { + @TestTemplate + void testJsonArrayAggInGroupWindow() { util.verifyRelPlan("SELECT f0, JSON_ARRAYAGG(f0) FROM T GROUP BY f0"); } - @Test - public void testJsonObjectAggWithOtherAggs() { + @TestTemplate + void testJsonObjectAggWithOtherAggs() { util.verifyRelPlan("SELECT COUNT(*), JSON_OBJECTAGG(f1 VALUE f1) FROM T"); } - @Test - public void testGroupJsonObjectAggWithOtherAggs() { + @TestTemplate + void testGroupJsonObjectAggWithOtherAggs() { util.verifyRelPlan( "SELECT f0, COUNT(*), JSON_OBJECTAGG(f1 VALUE f0), SUM(f2) FROM T GROUP BY f0"); } - @Test - public void testJsonArrayAggWithOtherAggs() { + @TestTemplate + void testJsonArrayAggWithOtherAggs() { util.verifyRelPlan("SELECT COUNT(*), JSON_ARRAYAGG(f0) FROM T"); } - @Test - public void testGroupJsonArrayAggInWithOtherAggs() { + @TestTemplate + void testGroupJsonArrayAggInWithOtherAggs() { util.verifyRelPlan("SELECT f0, COUNT(*), JSON_ARRAYAGG(f0), SUM(f2) FROM T GROUP BY f0"); } - @Test - public void testJsonArrayAggAndJsonObjectAggWithOtherAggs() { + @TestTemplate + void testJsonArrayAggAndJsonObjectAggWithOtherAggs() { util.verifyRelPlan( "SELECT MAX(f0), JSON_OBJECTAGG(f1 VALUE f0), JSON_ARRAYAGG(f1), JSON_ARRAYAGG(f0) FROM T"); } - @Test - public void testGroupJsonArrayAggAndJsonObjectAggWithOtherAggs() { + @TestTemplate + void testGroupJsonArrayAggAndJsonObjectAggWithOtherAggs() { util.verifyRelPlan( "SELECT f0, JSON_OBJECTAGG(f1 VALUE f2), JSON_ARRAYAGG(f1), JSON_ARRAYAGG(f2)," + " SUM(f2) FROM T GROUP BY f0"); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/physical/batch/PushLocalAggIntoTableSourceScanRuleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/physical/batch/PushLocalAggIntoTableSourceScanRuleTest.java index 6f20e6ef0bba6..01558291d809b 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/physical/batch/PushLocalAggIntoTableSourceScanRuleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/physical/batch/PushLocalAggIntoTableSourceScanRuleTest.java @@ -26,18 +26,18 @@ import org.apache.flink.table.planner.utils.TableTestBase; import org.apache.flink.table.runtime.functions.aggregate.CollectAggFunction; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** * Test for rules that extend {@link PushLocalAggIntoScanRuleBase} to push down local aggregates * into table source. */ -public class PushLocalAggIntoTableSourceScanRuleTest extends TableTestBase { +class PushLocalAggIntoTableSourceScanRuleTest extends TableTestBase { protected BatchTableTestUtil util = batchTestUtil(TableConfig.getDefault()); - @Before - public void setup() { + @BeforeEach + void setup() { TableConfig tableConfig = util.tableEnv().getConfig(); tableConfig.set( OptimizerConfigOptions.TABLE_OPTIMIZER_SOURCE_AGGREGATE_PUSHDOWN_ENABLED, true); @@ -109,7 +109,7 @@ public void setup() { } @Test - public void testCanPushDownLocalHashAggWithGroup() { + void testCanPushDownLocalHashAggWithGroup() { util.verifyRelPlan( "SELECT\n" + " sum(amount),\n" @@ -120,7 +120,7 @@ public void testCanPushDownLocalHashAggWithGroup() { } @Test - public void testDisablePushDownLocalAgg() { + void testDisablePushDownLocalAgg() { // disable push down local agg util.getTableEnv() .getConfig() @@ -145,7 +145,7 @@ public void testDisablePushDownLocalAgg() { } @Test - public void testCanPushDownLocalHashAggWithoutGroup() { + void testCanPushDownLocalHashAggWithoutGroup() { util.verifyRelPlan( "SELECT\n" + " min(id),\n" @@ -157,7 +157,7 @@ public void testCanPushDownLocalHashAggWithoutGroup() { } @Test - public void testCanPushDownLocalSortAggWithoutSort() { + void testCanPushDownLocalSortAggWithoutSort() { // enable sort agg util.getTableEnv() .getConfig() @@ -179,7 +179,7 @@ public void testCanPushDownLocalSortAggWithoutSort() { } @Test - public void testCanPushDownLocalSortAggWithSort() { + void testCanPushDownLocalSortAggWithSort() { // enable sort agg util.getTableEnv() .getConfig() @@ -200,7 +200,7 @@ public void testCanPushDownLocalSortAggWithSort() { } @Test - public void testCanPushDownLocalAggAfterFilterPushDown() { + void testCanPushDownLocalAggAfterFilterPushDown() { util.verifyRelPlan( "SELECT\n" @@ -213,7 +213,7 @@ public void testCanPushDownLocalAggAfterFilterPushDown() { } @Test - public void testCanPushDownLocalAggWithMetadata() { + void testCanPushDownLocalAggWithMetadata() { util.verifyRelPlan( "SELECT\n" + " sum(amount),\n" @@ -226,7 +226,7 @@ public void testCanPushDownLocalAggWithMetadata() { } @Test - public void testCanPushDownLocalAggWithPartition() { + void testCanPushDownLocalAggWithPartition() { util.verifyRelPlan( "SELECT\n" + " sum(amount),\n" @@ -238,7 +238,7 @@ public void testCanPushDownLocalAggWithPartition() { } @Test - public void testCanPushDownLocalAggWithoutProjectionPushDown() { + void testCanPushDownLocalAggWithoutProjectionPushDown() { util.verifyRelPlan( "SELECT\n" + " sum(amount),\n" @@ -250,7 +250,7 @@ public void testCanPushDownLocalAggWithoutProjectionPushDown() { } @Test - public void testCanPushDownLocalAggWithAuxGrouping() { + void testCanPushDownLocalAggWithAuxGrouping() { // enable two-phase aggregate, otherwise there is no local aggregate util.getTableEnv() .getConfig() @@ -264,7 +264,7 @@ public void testCanPushDownLocalAggWithAuxGrouping() { } @Test - public void testCannotPushDownLocalAggAfterLimitPushDown() { + void testCannotPushDownLocalAggAfterLimitPushDown() { util.verifyRelPlan( "SELECT\n" @@ -281,7 +281,7 @@ public void testCannotPushDownLocalAggAfterLimitPushDown() { } @Test - public void testCannotPushDownLocalAggWithUDAF() { + void testCannotPushDownLocalAggWithUDAF() { // add udf util.addTemporarySystemFunction( "udaf_collect", new CollectAggFunction<>(DataTypes.BIGINT().getLogicalType())); @@ -296,7 +296,7 @@ public void testCannotPushDownLocalAggWithUDAF() { } @Test - public void testCannotPushDownLocalAggWithUnsupportedDataTypes() { + void testCannotPushDownLocalAggWithUnsupportedDataTypes() { util.verifyRelPlan( "SELECT\n" + " max(name),\n" @@ -306,7 +306,7 @@ public void testCannotPushDownLocalAggWithUnsupportedDataTypes() { } @Test - public void testCannotPushDownWithColumnExpression() { + void testCannotPushDownWithColumnExpression() { util.verifyRelPlan( "SELECT\n" + " min(amount + price),\n" @@ -319,7 +319,7 @@ public void testCannotPushDownWithColumnExpression() { } @Test - public void testCannotPushDownWithUnsupportedAggFunction() { + void testCannotPushDownWithUnsupportedAggFunction() { util.verifyRelPlan( "SELECT\n" + " min(id),\n" @@ -332,7 +332,7 @@ public void testCannotPushDownWithUnsupportedAggFunction() { } @Test - public void testCannotPushDownWithWindowAggFunction() { + void testCannotPushDownWithWindowAggFunction() { util.verifyRelPlan( "SELECT\n" + " id,\n" @@ -343,7 +343,7 @@ public void testCannotPushDownWithWindowAggFunction() { } @Test - public void testCannotPushDownWithArgFilter() { + void testCannotPushDownWithArgFilter() { util.verifyRelPlan( "SELECT\n" + " min(id),\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/physical/stream/PushCalcPastChangelogNormalizeRuleTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/physical/stream/PushCalcPastChangelogNormalizeRuleTest.java index 4e0b2009e7d3d..b27dda1ded20b 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/physical/stream/PushCalcPastChangelogNormalizeRuleTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/physical/stream/PushCalcPastChangelogNormalizeRuleTest.java @@ -26,25 +26,25 @@ import org.apache.flink.table.planner.utils.StreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static org.apache.flink.table.api.DataTypes.BIGINT; import static org.apache.flink.table.api.DataTypes.INT; import static org.apache.flink.table.api.DataTypes.STRING; /** Tests for {@link PushCalcPastChangelogNormalizeRule}. */ -public class PushCalcPastChangelogNormalizeRuleTest extends TableTestBase { +class PushCalcPastChangelogNormalizeRuleTest extends TableTestBase { private StreamTableTestUtil util; - @Before - public void before() { + @BeforeEach + void before() { util = streamTestUtil(TableConfig.getDefault()); } @Test - public void testWithSinglePrimaryKeyFilter() { + void testWithSinglePrimaryKeyFilter() { final TableDescriptor sourceDescriptor = TableFactoryHarness.newBuilder() .schema( @@ -61,7 +61,7 @@ public void testWithSinglePrimaryKeyFilter() { } @Test - public void testWithMultipleFilters() { + void testWithMultipleFilters() { final TableDescriptor sourceDescriptor = TableFactoryHarness.newBuilder() .schema( @@ -82,7 +82,7 @@ public void testWithMultipleFilters() { } @Test - public void testWithMultiplePrimaryKeyColumns() { + void testWithMultiplePrimaryKeyColumns() { final TableDescriptor sourceDescriptor = TableFactoryHarness.newBuilder() .schema( @@ -100,7 +100,7 @@ public void testWithMultiplePrimaryKeyColumns() { } @Test - public void testOnlyProjection() { + void testOnlyProjection() { final TableDescriptor sourceDescriptor = TableFactoryHarness.newBuilder() .schema( @@ -118,7 +118,7 @@ public void testOnlyProjection() { } @Test - public void testFilterAndProjection() { + void testFilterAndProjection() { final TableDescriptor sourceDescriptor = TableFactoryHarness.newBuilder() .schema( @@ -141,7 +141,7 @@ public void testFilterAndProjection() { } @Test - public void testPartialPrimaryKeyFilterAndProjection() { + void testPartialPrimaryKeyFilterAndProjection() { final TableDescriptor sourceDescriptor = TableFactoryHarness.newBuilder() .schema( diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/CalcMergeTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/CalcMergeTest.java index 9da719f03b19f..74efb225979d3 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/CalcMergeTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/CalcMergeTest.java @@ -23,7 +23,7 @@ import org.apache.flink.table.planner.utils.TableTestUtil; /** Plan test for calc merge. */ -public class CalcMergeTest extends CalcMergeTestBase { +class CalcMergeTest extends CalcMergeTestBase { @Override protected boolean isBatchMode() { return false; diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/ColumnExpansionTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/ColumnExpansionTest.java index 290b981583e92..331cc13f36c03 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/ColumnExpansionTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/ColumnExpansionTest.java @@ -260,6 +260,31 @@ public void testExplicitTableWithinTableFunction() { "agg"); } + @Test + public void testExplicitTableWithinTableFunctionWithInsertIntoNamedColumns() { + tableEnv.getConfig() + .set( + TABLE_COLUMN_EXPANSION_STRATEGY, + Collections.singletonList(EXCLUDE_DEFAULT_VIRTUAL_METADATA_COLUMNS)); + + tableEnv.executeSql( + "CREATE TABLE sink (\n" + + " a STRING,\n" + + " c BIGINT\n" + + ") WITH (\n" + + " 'connector' = 'values'," + + " 'sink-insert-only' = 'false'" + + ")"); + + // Test case for FLINK-33327, we can not assert column names of an INSERT INTO query. Make + // sure the query can be planned. + tableEnv.explainSql( + "INSERT INTO sink(a, c) " + + "SELECT t3_s, COUNT(t3_i) FROM " + + " TABLE(TUMBLE(TABLE t3, DESCRIPTOR(t3_m_virtual), INTERVAL '1' MINUTE)) " + + "GROUP BY t3_s;"); + } + private void assertColumnNames(String sql, String... columnNames) { assertThat(tableEnv.sqlQuery(sql).getResolvedSchema().getColumnNames()) .containsExactly(columnNames); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/DynamicFunctionPlanTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/DynamicFunctionPlanTest.java index adb4523e78dd4..2a00d2f033bb5 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/DynamicFunctionPlanTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/DynamicFunctionPlanTest.java @@ -23,7 +23,7 @@ import org.apache.flink.table.planner.utils.TableTestUtil; /** Plan test for queries contain dynamic functions in streaming. */ -public class DynamicFunctionPlanTest extends DynamicFunctionPlanTestBase { +class DynamicFunctionPlanTest extends DynamicFunctionPlanTestBase { @Override protected TableTestUtil getTableTestUtil() { return streamTestUtil(TableConfig.getDefault()); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/InsertIntoValuesTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/InsertIntoValuesTest.java index 34c5286a3c399..eb032a1c0efdd 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/InsertIntoValuesTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/InsertIntoValuesTest.java @@ -21,14 +21,14 @@ import org.apache.flink.table.planner.utils.JavaStreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** Plan test for INSERT INTO. */ -public class InsertIntoValuesTest extends TableTestBase { +class InsertIntoValuesTest extends TableTestBase { private final JavaStreamTableTestUtil util = javaStreamTestUtil(); @Test - public void testTypeInferenceWithNestedTypes() { + void testTypeInferenceWithNestedTypes() { util.tableEnv() .executeSql( "CREATE TABLE t1 (" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/LegacyTableFactoryTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/LegacyTableFactoryTest.java index 2984e74086dd1..73d849826cb22 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/LegacyTableFactoryTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/LegacyTableFactoryTest.java @@ -22,25 +22,25 @@ import org.apache.flink.table.planner.utils.JavaStreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** Tests for usages of {@link LegacyTableFactory}. */ -public class LegacyTableFactoryTest extends TableTestBase { +class LegacyTableFactoryTest extends TableTestBase { private final JavaStreamTableTestUtil util; - public LegacyTableFactoryTest() { + LegacyTableFactoryTest() { util = javaStreamTestUtil(); util.tableEnv().executeSql("CREATE TABLE T (a INT) WITH ('type'='legacy')"); } @Test - public void testSelect() { + void testSelect() { util.verifyExecPlan("SELECT * FROM T"); } @Test - public void testInsert() { + void testInsert() { util.verifyExecPlanInsert("INSERT INTO T VALUES (1)"); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/NullTypeTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/NullTypeTest.java index 9aa303dcadba1..61f161bb2b26b 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/NullTypeTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/sql/NullTypeTest.java @@ -25,71 +25,82 @@ import org.apache.flink.table.planner.utils.JavaStreamTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** Tests for usages of {@link DataTypes#NULL()}. */ -public class NullTypeTest extends TableTestBase { +class NullTypeTest extends TableTestBase { private final JavaStreamTableTestUtil util = javaStreamTestUtil(); @Test - public void testValues() { - expectedException().expect(ValidationException.class); - expectedException().expectMessage("Illegal use of 'NULL'"); - util.verifyExecPlan("SELECT * FROM (VALUES (1, NULL), (2, NULL)) AS T(a, b)"); + void testValues() { + assertThatThrownBy( + () -> + util.verifyExecPlan( + "SELECT * FROM (VALUES (1, NULL), (2, NULL)) AS T(a, b)")) + .hasMessageContaining("Illegal use of 'NULL'") + .isInstanceOf(ValidationException.class); } @Test - public void testValuesWithoutTypeCoercion() { + void testValuesWithoutTypeCoercion() { // should work if we enable type coercion, works already in Table API - expectedException().expect(ValidationException.class); - expectedException().expectMessage("Illegal use of 'NULL'"); - util.verifyExecPlan("SELECT * FROM (VALUES (1, NULL), (2, 1)) AS T(a, b)"); + assertThatThrownBy( + () -> + util.verifyExecPlan( + "SELECT * FROM (VALUES (1, NULL), (2, 1)) AS T(a, b)")) + .hasMessageContaining("Illegal use of 'NULL'") + .isInstanceOf(ValidationException.class); } @Test - public void testSetOperationWithoutTypeCoercion() { + void testSetOperationWithoutTypeCoercion() { // we might want to support type coercion here - expectedException().expect(ValidationException.class); - expectedException().expectMessage("Parameters must be of the same type"); - util.verifyExecPlan("SELECT ARRAY[1,2] IN (ARRAY[1], ARRAY[1,2], ARRAY[NULL, NULL, NULL])"); + assertThatThrownBy( + () -> + util.verifyExecPlan( + "SELECT ARRAY[1,2] IN (ARRAY[1], ARRAY[1,2], ARRAY[NULL, NULL, NULL])")) + .hasMessageContaining("Parameters must be of the same type") + .isInstanceOf(ValidationException.class); } @Test - public void testBuiltInFunction() { - expectedException().expect(ValidationException.class); - expectedException().expectMessage("Illegal use of 'NULL'"); - util.verifyExecPlan("SELECT ABS(NULL)"); + void testBuiltInFunction() { + assertThatThrownBy(() -> util.verifyExecPlan("SELECT ABS(NULL)")) + .hasMessageContaining("Illegal use of 'NULL'") + .isInstanceOf(ValidationException.class); } @Test - public void testArrayConstructor() { - expectedException().expect(ValidationException.class); - expectedException().expectMessage("Parameters must be of the same type"); - util.verifyExecPlan("SELECT ARRAY[NULL]"); + void testArrayConstructor() { + assertThatThrownBy(() -> util.verifyExecPlan("SELECT ARRAY[NULL]")) + .hasMessageContaining("Parameters must be of the same type") + .isInstanceOf(ValidationException.class); } @Test - public void testMapConstructor() { - expectedException().expect(ValidationException.class); - expectedException().expectMessage("Parameters must be of the same type"); - util.verifyExecPlan("SELECT MAP[NULL, NULL]"); + void testMapConstructor() { + assertThatThrownBy(() -> util.verifyExecPlan("SELECT MAP[NULL, NULL]")) + .hasMessageContaining("Parameters must be of the same type") + .isInstanceOf(ValidationException.class); } @Test - public void testFunctionReturningNull() { - expectedException().expect(ValidationException.class); - expectedException().expectMessage("SQL validation failed. Invalid function call"); + void testFunctionReturningNull() { util.addTemporarySystemFunction("NullTypeFunction", NullTypeFunction.class); - util.verifyExecPlan("SELECT NullTypeFunction(12)"); + assertThatThrownBy(() -> util.verifyExecPlan("SELECT NullTypeFunction(12)")) + .hasMessageContaining("SQL validation failed. Invalid function call") + .isInstanceOf(ValidationException.class); } @Test - public void testNestedNull() { - expectedException().expect(ValidationException.class); - expectedException().expectMessage("SQL validation failed. Invalid function call"); + void testNestedNull() { util.addTemporarySystemFunction("NestedNullTypeFunction", NestedNullTypeFunction.class); - util.verifyExecPlan("SELECT NestedNullTypeFunction(12)"); + assertThatThrownBy(() -> util.verifyExecPlan("SELECT NestedNullTypeFunction(12)")) + .hasMessageContaining("SQL validation failed. Invalid function call") + .isInstanceOf(ValidationException.class); } // -------------------------------------------------------------------------------------------- diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/table/ValuesTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/table/ValuesTest.java index 7c4e1a8d276f8..667683089a6c5 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/table/ValuesTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/stream/table/ValuesTest.java @@ -26,7 +26,7 @@ import org.apache.flink.table.types.DataType; import org.apache.flink.types.Row; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.LocalTime; import java.util.Arrays; @@ -38,12 +38,13 @@ import static org.apache.flink.table.api.Expressions.nullOf; import static org.apache.flink.table.api.Expressions.pi; import static org.apache.flink.table.api.Expressions.row; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** Tests for {@link org.apache.flink.table.api.TableEnvironment#fromValues}. */ -public class ValuesTest extends TableTestBase { +class ValuesTest extends TableTestBase { @Test - public void testValuesAllEqualTypes() { + void testValuesAllEqualTypes() { JavaStreamTableTestUtil util = javaStreamTestUtil(); Table t = util.getTableEnv() @@ -56,14 +57,14 @@ public void testValuesAllEqualTypes() { } @Test - public void testValuesFromLiterals() { + void testValuesFromLiterals() { JavaStreamTableTestUtil util = javaStreamTestUtil(); Table t = util.getTableEnv().fromValues(1, 3.1f, 99L, null); util.verifyExecPlan(t); } @Test - public void testValuesFromRowExpression() { + void testValuesFromRowExpression() { JavaStreamTableTestUtil util = javaStreamTestUtil(); Table t = util.getTableEnv() @@ -84,7 +85,7 @@ public void testValuesFromRowExpression() { } @Test - public void testValuesFromRowObject() { + void testValuesFromRowObject() { JavaStreamTableTestUtil util = javaStreamTestUtil(); Table t = util.getTableEnv() @@ -98,7 +99,7 @@ public void testValuesFromRowObject() { } @Test - public void testValuesFromMixedObjectsAndExpressions() { + void testValuesFromMixedObjectsAndExpressions() { JavaStreamTableTestUtil util = javaStreamTestUtil(); Table t = util.getTableEnv() @@ -112,7 +113,7 @@ public void testValuesFromMixedObjectsAndExpressions() { } @Test - public void testValuesFromRowObjectInCollection() { + void testValuesFromRowObjectInCollection() { JavaStreamTableTestUtil util = javaStreamTestUtil(); List data = Arrays.asList( @@ -132,7 +133,7 @@ public void testValuesFromRowObjectInCollection() { } @Test - public void testValuesFromNestedRowObject() { + void testValuesFromNestedRowObject() { JavaStreamTableTestUtil util = javaStreamTestUtil(); Table t = util.getTableEnv() @@ -143,7 +144,7 @@ public void testValuesFromNestedRowObject() { } @Test - public void testValuesOverrideSchema() { + void testValuesOverrideSchema() { JavaStreamTableTestUtil util = javaStreamTestUtil(); Table t = util.getTableEnv() @@ -157,7 +158,7 @@ public void testValuesOverrideSchema() { } @Test - public void testValuesOverrideNullability() { + void testValuesOverrideNullability() { JavaStreamTableTestUtil util = javaStreamTestUtil(); Table t = util.getTableEnv() @@ -171,7 +172,7 @@ public void testValuesOverrideNullability() { } @Test - public void testValuesWithComplexNesting() { + void testValuesWithComplexNesting() { JavaStreamTableTestUtil util = javaStreamTestUtil(); Table t = util.getTableEnv() @@ -210,39 +211,54 @@ public void testValuesWithComplexNesting() { } @Test - public void testNoCommonType() { - thrown().expect(ValidationException.class); - thrown().expectMessage( - "Types in fromValues(...) must have a common super type. Could not" - + " find a common type for all rows at column 1."); + void testNoCommonType() { JavaStreamTableTestUtil util = javaStreamTestUtil(); - util.getTableEnv() - .fromValues(row("ABC", 1L), row("ABC", lit(LocalTime.of(0, 0, 0))), row("ABC", 2)); + + assertThatThrownBy( + () -> + util.getTableEnv() + .fromValues( + row("ABC", 1L), + row("ABC", lit(LocalTime.of(0, 0, 0))), + row("ABC", 2))) + .hasMessageContaining( + "Types in fromValues(...) must have a common super type. Could not" + + " find a common type for all rows at column 1.") + .isInstanceOf(ValidationException.class); } @Test - public void testCannotCast() { - thrown().expect(ValidationException.class); - thrown().expectMessage( - "Could not cast the value of the 0 column: [ 4 ] of a row: [ 4 ]" - + " to the requested type: BINARY(3)"); + void testCannotCast() { JavaStreamTableTestUtil util = javaStreamTestUtil(); - util.getTableEnv() - .fromValues(DataTypes.ROW(DataTypes.FIELD("f1", DataTypes.BINARY(3))), row(4)); + + assertThatThrownBy( + () -> + util.getTableEnv() + .fromValues( + DataTypes.ROW( + DataTypes.FIELD("f1", DataTypes.BINARY(3))), + row(4))) + .hasMessageContaining( + "Could not cast the value of the 0 column: [ 4 ] of a row: [ 4 ]" + + " to the requested type: BINARY(3)") + .isInstanceOf(ValidationException.class); } @Test - public void testWrongRowTypeLength() { - thrown().expect(ValidationException.class); - thrown().expectMessage( - "All rows in a fromValues(...) clause must have the same fields number. Row [4] has a different" - + " length than the expected size: 2."); + void testWrongRowTypeLength() { JavaStreamTableTestUtil util = javaStreamTestUtil(); - util.getTableEnv() - .fromValues( - DataTypes.ROW( - DataTypes.FIELD("f1", DataTypes.BINARY(3)), - DataTypes.FIELD("f2", DataTypes.STRING())), - row(4)); + + assertThatThrownBy( + () -> + util.getTableEnv() + .fromValues( + DataTypes.ROW( + DataTypes.FIELD("f1", DataTypes.BINARY(3)), + DataTypes.FIELD("f2", DataTypes.STRING())), + row(4))) + .hasMessageContaining( + "All rows in a fromValues(...) clause must have the same fields number. Row [4] has a different" + + " length than the expected size: 2.") + .isInstanceOf(ValidationException.class); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/ParallelismSettingTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/ParallelismSettingTest.java index e5115a4d3e858..844a0fbec333f 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/ParallelismSettingTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/ParallelismSettingTest.java @@ -25,8 +25,8 @@ import org.apache.flink.table.planner.utils.BatchTableTestUtil; import org.apache.flink.table.planner.utils.TableTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.List; @@ -38,8 +38,8 @@ public class ParallelismSettingTest extends TableTestBase { private BatchTableTestUtil util; - @Before - public void before() { + @BeforeEach + void before() { util = batchTestUtil(TableConfig.getDefault()); util.getTableEnv() .getConfig() @@ -58,7 +58,7 @@ public void before() { } @Test - public void testParallelismSettingAfterSingletonShuffleRemove() { + void testParallelismSettingAfterSingletonShuffleRemove() { util.getTableEnv() .executeSql( "CREATE TABLE MySink (\n" @@ -88,7 +88,7 @@ public void testParallelismSettingAfterSingletonShuffleRemove() { } @Test - public void testSortQuery() { + void testSortQuery() { util.getTableEnv() .executeSql( "CREATE TABLE MySink (\n" @@ -121,7 +121,7 @@ public void testSortQuery() { } @Test - public void testLimitQuery() { + void testLimitQuery() { util.getTableEnv() .executeSql( "CREATE TABLE MySink (\n" @@ -155,7 +155,7 @@ public void testLimitQuery() { } @Test - public void testSortLimitQuery() { + void testSortLimitQuery() { util.getTableEnv() .executeSql( "CREATE TABLE MySink (\n" @@ -190,7 +190,7 @@ public void testSortLimitQuery() { } @Test - public void testRankQuery() { + void testRankQuery() { util.getTableEnv() .executeSql( "CREATE TABLE MySink (\n" @@ -241,7 +241,7 @@ public void testRankQuery() { } @Test - public void testJoinQuery() { + void testJoinQuery() { util.tableEnv() .executeSql( "CREATE TABLE MyTable2 (\n" diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/sql/RTASITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/sql/RTASITCase.java index 3f35086e44394..ec077566ccf02 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/sql/RTASITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/sql/RTASITCase.java @@ -65,7 +65,7 @@ void testReplaceTableAS() throws Exception { .await(); // verify written rows - assertThat(TestValuesTableFactory.getResults("target").toString()) + assertThat(TestValuesTableFactory.getResultsAsStrings("target").toString()) .isEqualTo("[+I[1, 1, Hi], +I[2, 2, Hello], +I[3, 2, Hello world]]"); // verify the table after replacing @@ -96,7 +96,7 @@ void testCreateOrReplaceTableAS() throws Exception { .await(); // verify written rows - assertThat(TestValuesTableFactory.getResults("target").toString()) + assertThat(TestValuesTableFactory.getResultsAsStrings("target").toString()) .isEqualTo("[+I[1, Hi], +I[2, Hello], +I[3, Hello world]]"); // verify the table after replacing @@ -116,7 +116,7 @@ void testCreateOrReplaceTableASWithTableNotExist() throws Exception { .await(); // verify written rows - assertThat(TestValuesTableFactory.getResults("not_exist_target").toString()) + assertThat(TestValuesTableFactory.getResultsAsStrings("not_exist_target").toString()) .isEqualTo("[+I[1, Hi], +I[2, Hello], +I[3, Hello world]]"); // verify the table after replacing diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/sql/join/AdaptiveHashJoinITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/sql/join/AdaptiveHashJoinITCase.java index 2e7bd7e05e2c8..62670e2307993 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/sql/join/AdaptiveHashJoinITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/sql/join/AdaptiveHashJoinITCase.java @@ -168,7 +168,7 @@ public void testBuildRightStringKeyAdaptiveHashJoin() throws Exception { private void asserResult(String sinkTableName, int resultSize) { // Due to concern OOM and record value is same, here just assert result size - List result = TestValuesTableFactory.getResults(sinkTableName); + List result = TestValuesTableFactory.getResultsAsStrings(sinkTableName); assertThat(result.size()).isEqualTo(resultSize); } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ChangelogSourceJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ChangelogSourceJsonPlanITCase.java index 5ceb0bd040cea..a6b78346f7c42 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ChangelogSourceJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ChangelogSourceJsonPlanITCase.java @@ -52,7 +52,7 @@ void testChangelogSource() throws Exception { "+I[user1, Tom, tom123@gmail.com, 8.10, 16.20]", "+I[user3, Bailey, bailey@qq.com, 9.99, 19.98]", "+I[user4, Tina, tina@gmail.com, 11.30, 22.60]"); - assertResult(expected, TestValuesTableFactory.getResults("user_sink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("user_sink")); } @Test @@ -74,7 +74,7 @@ void testToUpsertSource() throws Exception { "+I[user1, Tom, tom123@gmail.com, 8.10, 16.20]", "+I[user3, Bailey, bailey@qq.com, 9.99, 19.98]", "+I[user4, Tina, tina@gmail.com, 11.30, 22.60]"); - assertResult(expected, TestValuesTableFactory.getResults("user_sink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("user_sink")); } // ------------------------------------------------------------------------------------------ diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ConfigureOperatorLevelStateTtlJsonITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ConfigureOperatorLevelStateTtlJsonITCase.java index fb879bf10c5fa..2f47d76f62ce8 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ConfigureOperatorLevelStateTtlJsonITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ConfigureOperatorLevelStateTtlJsonITCase.java @@ -113,7 +113,7 @@ void testDifferentStateTtlForDifferentOneInputOperator() throws Exception { "+I[Jerry, 1, 2, 99.9]", "+I[Olivia, 2, 4, 1100.0]", "+I[Michael, 1, 3, 599.9]"); - assertResult(expected, TestValuesTableFactory.getResults("OrdersStats")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("OrdersStats")); } @Test @@ -187,7 +187,7 @@ void testDifferentStateTtlForSameTwoInputStreamOperator() throws Exception { List expected = Arrays.asList( "+I[1, 1000002, TRUCK]", "+I[1, 1000004, RAIL]", "+I[1, 1000005, AIR]"); - assertResult(expected, TestValuesTableFactory.getResults("OrdersShipInfo")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("OrdersShipInfo")); } private static Map getProperties( diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/CorrelateJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/CorrelateJsonPlanITCase.java index a90c3cd86cbfa..a6e4fe6198acb 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/CorrelateJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/CorrelateJsonPlanITCase.java @@ -49,7 +49,7 @@ void testSystemFuncByObject() throws ExecutionException, InterruptedException { "insert into MySink SELECT a, v FROM MyTable, lateral table(STRING_SPLIT(a, ',')) as T(v)"; compileSqlAndExecutePlan(query).await(); List expected = Arrays.asList("+I[1,1,hi, 1]", "+I[1,1,hi, 1]", "+I[1,1,hi, hi]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -61,7 +61,7 @@ void testSystemFuncByClass() throws ExecutionException, InterruptedException { "insert into MySink SELECT a, v FROM MyTable, lateral table(STRING_SPLIT(a, ',')) as T(v)"; compileSqlAndExecutePlan(query).await(); List expected = Arrays.asList("+I[1,1,hi, 1]", "+I[1,1,hi, 1]", "+I[1,1,hi, hi]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -73,7 +73,7 @@ void testTemporaryFuncByObject() throws ExecutionException, InterruptedException "insert into MySink SELECT a, v FROM MyTable, lateral table(STRING_SPLIT(a, ',')) as T(v)"; compileSqlAndExecutePlan(query).await(); List expected = Arrays.asList("+I[1,1,hi, 1]", "+I[1,1,hi, 1]", "+I[1,1,hi, hi]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -85,7 +85,7 @@ void testTemporaryFuncByClass() throws ExecutionException, InterruptedException "insert into MySink SELECT a, v FROM MyTable, lateral table(STRING_SPLIT(a, ',')) as T(v)"; compileSqlAndExecutePlan(query).await(); List expected = Arrays.asList("+I[1,1,hi, 1]", "+I[1,1,hi, 1]", "+I[1,1,hi, hi]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -99,7 +99,7 @@ void testFilter() throws ExecutionException, InterruptedException { + "where try_cast(v as int) > 0"; compileSqlAndExecutePlan(query).await(); List expected = Arrays.asList("+I[1,1,hi, 1]", "+I[1,1,hi, 1]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -114,6 +114,6 @@ void testUnnest() throws ExecutionException, InterruptedException { "INSERT INTO MySink SELECT name, nested FROM MyNestedTable CROSS JOIN UNNEST(arr) AS t (nested)"; compileSqlAndExecutePlan(query).await(); List expected = Arrays.asList("+I[Bob, 1]", "+I[Bob, 2]", "+I[Bob, 3]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/DeduplicationJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/DeduplicationJsonPlanITCase.java index afb9f5e2d5c45..191ec3a6997f3 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/DeduplicationJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/DeduplicationJsonPlanITCase.java @@ -74,6 +74,6 @@ void testDeduplication() throws Exception { assertResult( Arrays.asList("+I[1, terry, pen, 1000]", "+I[4, bob, apple, 4000]"), - TestValuesTableFactory.getRawResults("MySink")); + TestValuesTableFactory.getRawResultsAsStrings("MySink")); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ExpandJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ExpandJsonPlanITCase.java index 1226dac39ca6c..80109626f7be7 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ExpandJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ExpandJsonPlanITCase.java @@ -58,7 +58,7 @@ void testExpand() throws Exception { + "from MyTable group by b") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult(Arrays.asList("+I[1, 1, Hi]", "+I[2, 2, Hello world]"), result); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/GroupAggregateJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/GroupAggregateJsonPlanITCase.java index 367253eea761b..9b2f9b01f7a6c 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/GroupAggregateJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/GroupAggregateJsonPlanITCase.java @@ -89,7 +89,7 @@ void testSimpleAggCallsWithGroupBy() throws Exception { + "from MyTable group by b") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult(Arrays.asList("+I[1, 1, null, Hi]", "+I[2, 2, 2.0, Hello]"), result); } @@ -124,7 +124,7 @@ void testDistinctAggCalls() throws Exception { + "from MyTable group by e") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult( Arrays.asList( "+I[1, 1, 4, 12, 32, 6.0, 5]", @@ -164,7 +164,7 @@ void testUserDefinedAggCallsWithoutMerge() throws Exception { + "from MyTable group by e") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult( Arrays.asList("+I[1, 77, 0, 1]", "+I[2, 120, 0, 2]", "+I[3, 58, 0, 3]"), result); } @@ -194,7 +194,7 @@ void testUserDefinedAggCallsWithMerge() throws Exception { + "from MyTable group by e") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult( Arrays.asList( "+I[1, 1, Hallo Welt wie|Hallo|GHI|EFG|DEF]", diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/GroupWindowAggregateJsonITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/GroupWindowAggregateJsonITCase.java index f461640b3e0d2..81efe95a642d4 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/GroupWindowAggregateJsonITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/GroupWindowAggregateJsonITCase.java @@ -81,7 +81,7 @@ void testEventTimeTumbleWindow() throws Exception { + "GROUP BY name, TUMBLE(rowtime, INTERVAL '5' SECOND)") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult( Arrays.asList( "+I[a, 2020-10-10T00:00, 2020-10-10T00:00:05, 4, 10, 2]", @@ -104,7 +104,7 @@ void testEventTimeHopWindow() throws Exception { + "GROUP BY name, HOP(rowtime, INTERVAL '5' SECOND, INTERVAL '10' SECOND)") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult( Arrays.asList( "+I[a, 1]", @@ -132,7 +132,7 @@ void testEventTimeSessionWindow() throws Exception { + "GROUP BY name, Session(rowtime, INTERVAL '3' SECOND)") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult( Arrays.asList( "+I[a, 1]", "+I[a, 4]", "+I[b, 1]", "+I[b, 1]", "+I[b, 2]", "+I[null, 1]"), diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/IncrementalAggregateJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/IncrementalAggregateJsonPlanITCase.java index 8888bca40349f..6f72a3930a8ef 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/IncrementalAggregateJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/IncrementalAggregateJsonPlanITCase.java @@ -72,7 +72,7 @@ void testIncrementalAggregate() throws IOException, ExecutionException, Interrup + "from MyTable group by b") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult(Arrays.asList("+I[1, 1]", "+I[2, 2]"), result); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/IntervalJoinJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/IntervalJoinJsonPlanITCase.java index 94a1e41b2acbc..d4569058eaac4 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/IntervalJoinJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/IntervalJoinJsonPlanITCase.java @@ -65,7 +65,7 @@ void testProcessTimeInnerJoin() throws Exception { "+I[1, HiHi, Hi6]", "+I[1, HiHi, Hi8]", "+I[2, HeHe, Hi5]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -112,6 +112,6 @@ void testRowTimeInnerJoin() throws Exception { "+I[1, HiHi, Hi2]", "+I[1, HiHi, Hi3]", "+I[2, HeHe, Hi5]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/JoinJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/JoinJsonPlanITCase.java index a722226e044f4..539cfc401e823 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/JoinJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/JoinJsonPlanITCase.java @@ -121,7 +121,7 @@ void testIsNullInnerJoinWithNullCond() throws Exception { "+I[1, HiHi, Hi8]", "+I[2, HeHe, Hi5]", "+I[null, HeHe, Hi9]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -132,7 +132,7 @@ void testJoin() throws Exception { List expected = Arrays.asList( "+I[Hello world, Hallo Welt]", "+I[Hello, Hallo Welt]", "+I[Hi, Hallo]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -141,7 +141,7 @@ void testInnerJoin() throws Exception { compileSqlAndExecutePlan("insert into MySink \n" + "SELECT a1, b1 FROM A JOIN B ON a1 = b1") .await(); List expected = Arrays.asList("+I[1, 1]", "+I[2, 2]", "+I[2, 2]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -152,7 +152,7 @@ void testJoinWithFilter() throws Exception { + "SELECT a3, b4 FROM A, B where a2 = b2 and a2 < 2") .await(); List expected = Arrays.asList("+I[Hi, Hallo]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -163,6 +163,6 @@ void testInnerJoinWithDuplicateKey() throws Exception { + "SELECT a1, b1, b3 FROM A JOIN B ON a1 = b1 AND a1 = b3") .await(); List expected = Arrays.asList("+I[2, 2, 2]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/LimitJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/LimitJsonPlanITCase.java deleted file mode 100644 index 6507481d6389b..0000000000000 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/LimitJsonPlanITCase.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.flink.table.planner.runtime.stream.jsonplan; - -import org.apache.flink.table.planner.factories.TestValuesTableFactory; -import org.apache.flink.table.planner.runtime.utils.TestData; -import org.apache.flink.table.planner.utils.JavaScalaConversionUtil; -import org.apache.flink.table.planner.utils.JsonPlanTestBase; - -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.ExecutionException; - -/** Test for limit JsonPlan ser/de. */ -class LimitJsonPlanITCase extends JsonPlanTestBase { - @Test - void testLimit() throws ExecutionException, InterruptedException, IOException { - createTestValuesSourceTable( - "MyTable", - JavaScalaConversionUtil.toJava(TestData.data1()), - "a int", - "b varchar", - "c int"); - createTestNonInsertOnlyValuesSinkTable("`result`", "a int", "b varchar", "c bigint"); - String sql = "insert into `result` select * from MyTable limit 3"; - compileSqlAndExecutePlan(sql).await(); - - List expected = Arrays.asList("+I[2, a, 6]", "+I[4, b, 8]", "+I[6, c, 10]"); - assertResult(expected, TestValuesTableFactory.getResults("result")); - } -} diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/LookupJoinJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/LookupJoinJsonPlanITCase.java index 71e76e0e2189d..9e4ac5fa35484 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/LookupJoinJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/LookupJoinJsonPlanITCase.java @@ -79,7 +79,7 @@ void testJoinLookupTable() throws Exception { "+I[1, 12, Julian, Julian]", "+I[2, 15, Hello, Jark]", "+I[3, 15, Fabian, Fabian]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -91,7 +91,7 @@ void testJoinLookupTableWithPushDown() throws Exception { .await(); List expected = Arrays.asList("+I[2, 15, Hello, Jark]", "+I[3, 15, Fabian, Fabian]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -108,6 +108,6 @@ void testLeftJoinLookupTableWithPreFilter() throws Exception { "+I[3, 15, Fabian, null]", "+I[8, 11, Hello world, null]", "+I[9, 12, Hello world!, null]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/MatchRecognizeJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/MatchRecognizeJsonPlanITCase.java index ca64b2126dfa3..a5cb80e3709d0 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/MatchRecognizeJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/MatchRecognizeJsonPlanITCase.java @@ -66,7 +66,7 @@ void testSimpleMatch() throws Exception { compileSqlAndExecutePlan(sql).await(); List expected = Collections.singletonList("+I[6, 7, 8]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -104,6 +104,6 @@ void testComplexMatch() throws Exception { compileSqlAndExecutePlan(sql).await(); List expected = Collections.singletonList("+I[19, 13, null]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/OverAggregateJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/OverAggregateJsonPlanITCase.java index 55fc7a0a8d2e8..c42c961936de3 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/OverAggregateJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/OverAggregateJsonPlanITCase.java @@ -75,7 +75,7 @@ void testProcTimeBoundedPartitionedRowsOver() "+I[5, 33, 10]", "+I[5, 46, 10]", "+I[5, 60, 10]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -120,7 +120,7 @@ void testProcTimeUnboundedNonPartitionedRangeOver() "+I[Hello, 4, null]", "+I[Hello, 5, null]", "+I[Hello, 6, null]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -197,6 +197,6 @@ void testRowTimeBoundedPartitionedRangeOver() "+I[Hello World, 18, 1, 1, 7]", "+I[Hello World, 8, 2, 2, 15]", "+I[Hello World, 20, 1, 1, 20]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/RankJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/RankJsonPlanITCase.java index 1b866271787cf..b6ce7733ebfc1 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/RankJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/RankJsonPlanITCase.java @@ -48,7 +48,7 @@ void testRank() throws ExecutionException, InterruptedException, IOException { compileSqlAndExecutePlan(sql).await(); List expected = Arrays.asList("+I[1, a, 1]", "+I[3, b, 1]", "+I[5, c, 1]"); - assertResult(expected, TestValuesTableFactory.getResults("result")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("result")); } @Test @@ -70,6 +70,6 @@ void testFirstN() throws ExecutionException, InterruptedException, IOException { List expected = Arrays.asList( "+I[book, 1, 1]", "+I[book, 2, 2]", "+I[fruit, 4, 1]", "+I[fruit, 3, 2]"); - assertResult(expected, TestValuesTableFactory.getResults("result1")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("result1")); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/SargJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/SargJsonPlanITCase.java index bb40b5ab18133..e1b2558e4fccb 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/SargJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/SargJsonPlanITCase.java @@ -41,6 +41,6 @@ void testSarg() throws ExecutionException, InterruptedException { + "FROM MyTable WHERE a = 1 OR a = 2 OR a IS NULL"; compileSqlAndExecutePlan(sql).await(); List expected = Arrays.asList("+I[1]", "+I[2]", "+I[null]"); - assertResult(expected, TestValuesTableFactory.getResults("result")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("result")); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/SortLimitJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/SortLimitJsonPlanITCase.java index 7473057ffc424..cd80e6a07d58b 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/SortLimitJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/SortLimitJsonPlanITCase.java @@ -45,6 +45,6 @@ void testSortLimit() throws ExecutionException, InterruptedException, IOExceptio compileSqlAndExecutePlan(sql).await(); List expected = Arrays.asList("+I[1, a, 5]", "+I[2, a, 6]", "+I[3, b, 7]"); - assertResult(expected, TestValuesTableFactory.getResults("result")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("result")); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/TableSinkJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/TableSinkJsonPlanITCase.java index 73577d0775b59..0ba61358fbfb5 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/TableSinkJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/TableSinkJsonPlanITCase.java @@ -68,7 +68,7 @@ void testWritingMetadata() throws Exception { compileSqlAndExecutePlan("insert into MySink select * from MyTable").await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult( Arrays.asList("+I[1, 1, hi]", "+I[2, 1, hello]", "+I[3, 2, hello world]"), result); } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/TemporalJoinJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/TemporalJoinJsonPlanITCase.java index 56ebcf559476a..ca1ed35f05004 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/TemporalJoinJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/TemporalJoinJsonPlanITCase.java @@ -89,7 +89,7 @@ void testJoinTemporalFunction() throws Exception { + "WHERE o.currency = r.currency ") .await(); List expected = Arrays.asList("+I[102]", "+I[228]", "+I[348]", "+I[50]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -102,6 +102,6 @@ void testTemporalTableJoin() throws Exception { + "ON o.currency = r.currency ") .await(); List expected = Arrays.asList("+I[102]", "+I[228]", "+I[348]", "+I[50]"); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/TemporalSortJsonITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/TemporalSortJsonITCase.java index a97a90bf00330..fe623816f1a9c 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/TemporalSortJsonITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/TemporalSortJsonITCase.java @@ -48,7 +48,7 @@ void testSortProcessingTime() throws Exception { assertResult( Arrays.asList("+I[1]", "+I[2]", "+I[3]"), - TestValuesTableFactory.getResults("MySink")); + TestValuesTableFactory.getResultsAsStrings("MySink")); } @Test @@ -78,7 +78,7 @@ void testSortRowTime() throws Exception { "insert into MySink SELECT `int` FROM MyTable order by rowtime, `double`") .await(); - assertThat(TestValuesTableFactory.getResults("MySink")) + assertThat(TestValuesTableFactory.getResultsAsStrings("MySink")) .isEqualTo( Arrays.asList( "+I[1]", "+I[2]", "+I[2]", "+I[5]", "+I[6]", "+I[3]", "+I[3]", diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/UnionJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/UnionJsonPlanITCase.java index 1d7096a851865..2145dd0179ae8 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/UnionJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/UnionJsonPlanITCase.java @@ -61,6 +61,6 @@ void testUnion() throws Exception { "+I[5, c, 9]", "+I[3, b, 7]" // a=3 need to be doubled ); - assertResult(expected, TestValuesTableFactory.getResults("MySink")); + assertResult(expected, TestValuesTableFactory.getResultsAsStrings("MySink")); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ValuesJsonPlanITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ValuesJsonPlanITCase.java index e71546fbec65d..420f895aed520 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ValuesJsonPlanITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/ValuesJsonPlanITCase.java @@ -36,7 +36,7 @@ void testValues() throws Exception { "INSERT INTO MySink SELECT * from (VALUES (1, 2, 'Hi'), (3, 4, 'Hello'))") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult(Arrays.asList("+I[1, 2, Hi]", "+I[3, 4, Hello]"), result); } } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowAggregateJsonITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowAggregateJsonITCase.java index dc07965637c7a..f636ee8bb55aa 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowAggregateJsonITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowAggregateJsonITCase.java @@ -103,7 +103,7 @@ void testEventTimeTumbleWindow() throws Exception { + "GROUP BY name, window_start, window_end") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult( Arrays.asList( "+I[a, 2020-10-10T00:00, 2020-10-10T00:00:05, 4, 10, 2]", @@ -127,7 +127,7 @@ void testEventTimeHopWindow() throws Exception { + "GROUP BY name, window_start, window_end") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult( Arrays.asList( "+I[a, 1]", @@ -160,7 +160,7 @@ void testEventTimeCumulateWindow() throws Exception { + "GROUP BY name, window_start, window_end") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult( Arrays.asList( "+I[a, 4]", @@ -200,7 +200,7 @@ void testDistinctSplitEnabled() throws Exception { + "GROUP BY name, window_start, window_end") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult( Arrays.asList( "+I[a, 5.0, 3]", diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowDeduplicateJsonITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowDeduplicateJsonITCase.java index 75eb714c9df6c..ef2cd5d58d904 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowDeduplicateJsonITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowDeduplicateJsonITCase.java @@ -96,7 +96,7 @@ void testEventTimeTumbleWindow() throws Exception { + "WHERE rownum <= 1") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult( Arrays.asList( "+I[2020-10-10 00:00:04, 5, 5.0, 5.0, 5.55, null, a, 2020-10-10 00:00:04.000, 2020-10-10T00:00, 2020-10-10T00:00:05, 2020-10-10T00:00:04.999]", diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowJoinJsonITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowJoinJsonITCase.java index c4e84d96b32f8..d0405ca07b11c 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowJoinJsonITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowJoinJsonITCase.java @@ -119,7 +119,7 @@ void testEventTimeTumbleWindow() throws Exception { + "ON L.window_start = R.window_start AND L.window_end = R.window_end AND L.name = R.name") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult( Arrays.asList( "+I[b, 2020-10-10T00:00:05, 2020-10-10T00:00:10, 2, 2]", diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowTableFunctionJsonITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowTableFunctionJsonITCase.java index 1ccf0e03da1ef..b816fdc189a53 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowTableFunctionJsonITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/jsonplan/WindowTableFunctionJsonITCase.java @@ -90,7 +90,7 @@ void testEventTimeTumbleWindow() throws Exception { + "FROM TABLE(TUMBLE(TABLE MyTable, DESCRIPTOR(rowtime), INTERVAL '5' SECOND))") .await(); - List result = TestValuesTableFactory.getResults("MySink"); + List result = TestValuesTableFactory.getResultsAsStrings("MySink"); assertResult( Arrays.asList( "+I[2020-10-10 00:00:01, 1, 1.0, 1.0, 1.11, Hi, a, 2020-10-10 00:00:01.000, 2020-10-10T00:00, 2020-10-10T00:00:05, 2020-10-10T00:00:04.999]", diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/sql/DataStreamJavaITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/sql/DataStreamJavaITCase.java index 71e05066d09b8..9827e37b85e24 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/sql/DataStreamJavaITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/sql/DataStreamJavaITCase.java @@ -735,10 +735,10 @@ public void testAttachAsDataStream() throws Exception { // submits all source-to-sink pipelines testResult(env.fromElements(3, 4, 5), 3, 4, 5); - assertThat(TestValuesTableFactory.getResults("OutputTable1")) + assertThat(TestValuesTableFactory.getResultsAsStrings("OutputTable1")) .containsExactlyInAnyOrder("+I[1, a]", "+I[2, b]"); - assertThat(TestValuesTableFactory.getResults("OutputTable2")) + assertThat(TestValuesTableFactory.getResultsAsStrings("OutputTable2")) .containsExactlyInAnyOrder("+I[1]", "+I[2]", "+I[3]"); } diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/sql/RTASITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/sql/RTASITCase.java index ba1b2b6513523..a673e87a0cda1 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/sql/RTASITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/sql/RTASITCase.java @@ -66,7 +66,7 @@ void testReplaceTableAS() throws Exception { .await(); // verify written rows - assertThat(TestValuesTableFactory.getResults("target").toString()) + assertThat(TestValuesTableFactory.getResultsAsStrings("target").toString()) .isEqualTo("[+I[1, 1, Hi], +I[2, 2, Hello], +I[3, 2, Hello world]]"); // verify the table after replacing @@ -97,7 +97,7 @@ void testCreateOrReplaceTableAS() throws Exception { .await(); // verify written rows - assertThat(TestValuesTableFactory.getResults("target").toString()) + assertThat(TestValuesTableFactory.getResultsAsStrings("target").toString()) .isEqualTo("[+I[1, Hi], +I[2, Hello], +I[3, Hello world]]"); // verify the table after replacing @@ -117,7 +117,7 @@ void testCreateOrReplaceTableASWithTableNotExist() throws Exception { .await(); // verify written rows - assertThat(TestValuesTableFactory.getResults("not_exist_target").toString()) + assertThat(TestValuesTableFactory.getResultsAsStrings("not_exist_target").toString()) .isEqualTo("[+I[1, Hi], +I[2, Hello], +I[3, Hello world]]"); // verify the table after replacing diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/utils/InternalConfigOptionsTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/utils/InternalConfigOptionsTest.java index 8d1c182d3eaf2..bb30acf1228a5 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/utils/InternalConfigOptionsTest.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/utils/InternalConfigOptionsTest.java @@ -24,12 +24,14 @@ import org.apache.flink.table.api.TableEnvironment; import org.apache.flink.table.planner.delegation.PlannerBase; import org.apache.flink.table.planner.plan.nodes.exec.ExecNodeGraph; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameter; +import org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension; +import org.apache.flink.testutils.junit.extensions.parameterized.Parameters; import org.apache.calcite.rel.RelNode; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; import java.util.Arrays; import java.util.Collection; @@ -40,21 +42,21 @@ import static org.assertj.core.api.Assertions.assertThat; /** Tests for {@link InternalConfigOptions}. */ -@RunWith(Parameterized.class) -public class InternalConfigOptionsTest extends TableTestBase { +@ExtendWith(ParameterizedTestExtension.class) +class InternalConfigOptionsTest extends TableTestBase { private TableEnvironment tEnv; private PlannerBase planner; - @Parameterized.Parameters(name = "plannerMode = {0}") - public static Collection parameters() { + @Parameters(name = "plannerMode = {0}") + private static Collection parameters() { return Arrays.asList("STREAMING", "BATCH"); } - @Parameterized.Parameter public String plannerMode; + @Parameter private String plannerMode; - @Before - public void setUp() { + @BeforeEach + void setUp() { if (plannerMode.equals("STREAMING")) { StreamTableTestUtil util = streamTestUtil(TableConfig.getDefault()); tEnv = util.getTableEnv(); @@ -66,8 +68,8 @@ public void setUp() { } } - @Test - public void testTranslateExecNodeGraphWithInternalTemporalConf() { + @TestTemplate + void testTranslateExecNodeGraphWithInternalTemporalConf() { Table table = tEnv.sqlQuery("SELECT LOCALTIME, LOCALTIMESTAMP, CURRENT_TIME, CURRENT_TIMESTAMP"); RelNode relNode = planner.optimize(TableTestUtil.toRelNode(table)); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/test/program/TableTestProgramRunnerTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/test/program/TableTestProgramRunnerTest.java new file mode 100644 index 0000000000000..a0d114c1178ec --- /dev/null +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/test/program/TableTestProgramRunnerTest.java @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.test.program; + +import org.apache.flink.table.api.EnvironmentSettings; +import org.apache.flink.table.api.TableEnvironment; +import org.apache.flink.table.api.config.TableConfigOptions; +import org.apache.flink.table.test.program.TestStep.TestKind; +import org.apache.flink.table.utils.UserDefinedFunctions; + +import org.junit.jupiter.api.Test; + +import java.time.ZoneId; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** Tests for {@link TableTestProgram} and {@link TableTestProgramRunner}. */ +public class TableTestProgramRunnerTest { + + private static final String ID = "id"; + private static final String DESCRIPTION = "description"; + + @Test + void testConfigStep() { + final TableTestProgram program = + TableTestProgram.of(ID, DESCRIPTION) + .setupConfig(TableConfigOptions.LOCAL_TIME_ZONE, "GMT+3") + .build(); + + assertThat(program.setupSteps).hasSize(1); + + final TableEnvironment env = TableEnvironment.create(EnvironmentSettings.inStreamingMode()); + program.getSetupConfigOptionTestSteps().forEach(s -> s.apply(env)); + assertThat(env.getConfig().getLocalTimeZone()).isEqualTo(ZoneId.of("GMT+3")); + } + + @Test + void testFunctionStep() { + final TableTestProgram program = + TableTestProgram.of(ID, DESCRIPTION) + .setupTemporarySystemFunction( + "tmp_sys", UserDefinedFunctions.ScalarUDF.class) + .setupTemporaryCatalogFunction( + "tmp_cat", UserDefinedFunctions.ScalarUDF.class) + .setupCatalogFunction("cat", UserDefinedFunctions.ScalarUDF.class) + .build(); + + assertThat(program.setupSteps).hasSize(3); + + final TableEnvironment env = TableEnvironment.create(EnvironmentSettings.inStreamingMode()); + program.getSetupFunctionTestSteps().forEach(s -> s.apply(env)); + + assertThat(env.listUserDefinedFunctions()).contains("tmp_sys", "tmp_cat", "cat"); + } + + @Test + void testSqlStep() { + final TableTestProgram program = + TableTestProgram.of(ID, DESCRIPTION) + .setupSql("CREATE TABLE MyTable1 (i INT) WITH ('connector' = 'datagen')") + .runSql("CREATE TABLE MyTable2 (i INT) WITH ('connector' = 'datagen')") + .build(); + + assertThat(program.setupSteps).hasSize(1); + assertThat(program.runSteps).hasSize(1); + + final TableEnvironment env = TableEnvironment.create(EnvironmentSettings.inStreamingMode()); + program.setupSteps.stream().map(SqlTestStep.class::cast).forEach(s -> s.apply(env)); + program.runSteps.stream().map(SqlTestStep.class::cast).forEach(s -> s.apply(env)); + + assertThat(env.listTables()).contains("MyTable1", "MyTable2"); + } + + @Test + @SuppressWarnings("resource") + void testTableStep() { + final TableTestProgram program = + TableTestProgram.of(ID, DESCRIPTION) + .setupTableSource( + SourceTestStep.newBuilder("MyTableSource") + .addSchema("i INT") + .addOption("connector", "datagen") + .build()) + .setupTableSink( + SinkTestStep.newBuilder("MyTableSink") + .addSchema("i INT") + .addOption("connector", "blackhole") + .build()) + .build(); + + assertThat(program.setupSteps).hasSize(2); + + final TableEnvironment env = TableEnvironment.create(EnvironmentSettings.inStreamingMode()); + program.getSetupSourceTestSteps() + .forEach(s -> s.apply(env, Collections.singletonMap("number-of-rows", "3"))); + program.getSetupSinkTestSteps().forEach(s -> s.apply(env)); + + assertThat(env.executeSql("SHOW CREATE TABLE MyTableSource").collect().next().getField(0)) + .isEqualTo( + "CREATE TABLE `default_catalog`.`default_database`.`MyTableSource` (\n" + + " `i` INT\n" + + ") WITH (\n" + + " 'connector' = 'datagen',\n" + + " 'number-of-rows' = '3'\n" + + ")\n"); + assertThat(env.executeSql("SHOW CREATE TABLE MyTableSink").collect().next().getField(0)) + .isEqualTo( + "CREATE TABLE `default_catalog`.`default_database`.`MyTableSink` (\n" + + " `i` INT\n" + + ") WITH (\n" + + " 'connector' = 'blackhole'\n" + + ")\n"); + } + + @Test + void testRunnerValidationDuplicate() { + final TableTestProgram program1 = + TableTestProgram.of(ID, DESCRIPTION).runSql("SELECT 1").build(); + + final TableTestProgram program2 = + TableTestProgram.of(ID, DESCRIPTION).runSql("SELECT 1").build(); + + final LimitedTableTestProgramRunner runner = new LimitedTableTestProgramRunner(); + runner.programs = Arrays.asList(program1, program2); + + assertThatThrownBy(runner::supportedPrograms) + .hasMessageContaining("Duplicate test program id found: [id]"); + } + + @Test + void testRunnerValidationUnsupported() { + final LimitedTableTestProgramRunner runner = new LimitedTableTestProgramRunner(); + + final TableTestProgram program = + TableTestProgram.of(ID, DESCRIPTION).setupSql("SELECT 1").build(); + + runner.programs = Collections.singletonList(program); + + assertThatThrownBy(runner::supportedPrograms) + .hasMessageContaining("Test runner does not support setup step: SQL"); + } + + private static class LimitedTableTestProgramRunner implements TableTestProgramRunner { + + List programs; + + @Override + public List programs() { + return programs; + } + + @Override + public EnumSet supportedSetupSteps() { + return EnumSet.of(TestKind.SOURCE_WITH_DATA); + } + + @Override + public EnumSet supportedRunSteps() { + return EnumSet.of(TestKind.SQL); + } + } +} diff --git a/flink-table/flink-table-planner/src/test/resources/jsonplan/testGetJsonPlan.out b/flink-table/flink-table-planner/src/test/resources/jsonplan/testGetJsonPlan.out index 5eb4e8941c8ad..57549b10481a0 100644 --- a/flink-table/flink-table-planner/src/test/resources/jsonplan/testGetJsonPlan.out +++ b/flink-table/flink-table-planner/src/test/resources/jsonplan/testGetJsonPlan.out @@ -27,8 +27,8 @@ }, "partitionKeys": [], "options": { - "connector": "values", - "bounded": "false" + "bounded": "false", + "connector": "values" } } } diff --git a/flink-table/flink-table-planner/src/test/resources/org/apache/flink/table/planner/plan/common/ViewsExpandingTest.xml b/flink-table/flink-table-planner/src/test/resources/org/apache/flink/table/planner/plan/common/ViewsExpandingTest.xml index d8bf60b49529f..ad37a1cdc1a82 100644 --- a/flink-table/flink-table-planner/src/test/resources/org/apache/flink/table/planner/plan/common/ViewsExpandingTest.xml +++ b/flink-table/flink-table-planner/src/test/resources/org/apache/flink/table/planner/plan/common/ViewsExpandingTest.xml @@ -16,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - + @@ -34,7 +34,7 @@ BoundedStreamScan(table=[[default_catalog, default_database, t1]], fields=[a, b, ]]> - + @@ -52,7 +52,7 @@ DataStreamScan(table=[[default_catalog, default_database, t1]], fields=[a, b, c] ]]> - + @@ -75,7 +75,7 @@ Calc(select=[f0, name AS f1]) ]]> - + @@ -94,7 +94,7 @@ BoundedStreamScan(table=[[default_catalog, default_database, t1]], fields=[a, b, ]]> - + @@ -113,7 +113,7 @@ DataStreamScan(table=[[default_catalog, default_database, t1]], fields=[a, b, c] ]]> - + - + - + @@ -159,7 +159,7 @@ Calc(select=[CAST(a AS INTEGER) AS a, b, CAST(EXPR$2 AS INTEGER) AS c]) ]]> - + @@ -182,7 +182,7 @@ Calc(select=[f0, name AS f1]) ]]> - + diff --git a/flink-table/flink-table-planner/src/test/resources/org/apache/flink/table/planner/plan/hint/OptionsHintTest.xml b/flink-table/flink-table-planner/src/test/resources/org/apache/flink/table/planner/plan/hint/OptionsHintTest.xml index 568039e1c24d8..c128d22e19a0c 100644 --- a/flink-table/flink-table-planner/src/test/resources/org/apache/flink/table/planner/plan/hint/OptionsHintTest.xml +++ b/flink-table/flink-table-planner/src/test/resources/org/apache/flink/table/planner/plan/hint/OptionsHintTest.xml @@ -16,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - + @@ -34,7 +34,7 @@ Calc(select=[a, b, (a + 1) AS c]) ]]> - + @@ -52,7 +52,7 @@ Calc(select=[a, b, (a + 1) AS c]) ]]> - + - + - + @@ -129,7 +129,7 @@ Calc(select=[a, b, (a + 1) AS c]) ]]> - + - + - + - + @@ -232,7 +232,7 @@ Calc(select=[a, b, (a + 1) AS c]) ]]> - + + + + + + TABLE MyTable, + TIMECOL => DESCRIPTOR(rowtime), + SIZE => INTERVAL '15' MINUTE, + `OFFSET` => INTERVAL '5' MINUTE)) +]]> + + + + + + @@ -335,6 +366,37 @@ LogicalProject(a=[$0], b=[$1], c=[$2], d=[$3], rowtime=[$4], proctime=[$5], wind + + + + + TABLE MyTable, + TIMECOL => DESCRIPTOR(rowtime), + SIZE => INTERVAL '15' MINUTE, + `OFFSET` => INTERVAL '5' MINUTE)) +]]> + + + + + + $1", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 0, + "type" : "BIGINT" + }, { + "kind" : "LITERAL", + "value" : 420, + "type" : "BIGINT NOT NULL" + } ], + "type" : "BOOLEAN" + } ] + } ] + }, + "outputType" : "ROW<`a` BIGINT, `b` DOUBLE>", + "description" : "TableSourceScan(table=[[default_catalog, default_database, source_t, filter=[>(a, 420:BIGINT)]]], fields=[a, b])", + "inputProperties" : [ ] + }, { + "id" : 2, + "type" : "stream-exec-sink_1", + "configuration" : { + "table.exec.sink.keyed-shuffle" : "AUTO", + "table.exec.sink.not-null-enforcer" : "ERROR", + "table.exec.sink.rowtime-inserter" : "ENABLED", + "table.exec.sink.type-length-enforcer" : "IGNORE", + "table.exec.sink.upsert-materialize" : "AUTO" + }, + "dynamicTableSink" : { + "table" : { + "identifier" : "`default_catalog`.`default_database`.`sink_t`", + "resolvedTable" : { + "schema" : { + "columns" : [ { + "name" : "a", + "dataType" : "BIGINT" + }, { + "name" : "b", + "dataType" : "DOUBLE" + } ], + "watermarkSpecs" : [ ] + }, + "partitionKeys" : [ ] + } + } + }, + "inputChangelogMode" : [ "INSERT" ], + "inputProperties" : [ { + "requiredDistribution" : { + "type" : "UNKNOWN" + }, + "damBehavior" : "PIPELINED", + "priority" : 0 + } ], + "outputType" : "ROW<`a` BIGINT, `b` DOUBLE>", + "description" : "Sink(table=[default_catalog.default_database.sink_t], fields=[a, b])" + } ], + "edges" : [ { + "source" : 1, + "target" : 2, + "shuffle" : { + "type" : "FORWARD" + }, + "shuffleMode" : "PIPELINED" + } ] +} \ No newline at end of file diff --git a/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-filter-pushdown/savepoint/_metadata b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-filter-pushdown/savepoint/_metadata new file mode 100644 index 0000000000000..c2093c6d15df9 Binary files /dev/null and b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-filter-pushdown/savepoint/_metadata differ diff --git a/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-filter/plan/calc-filter.json b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-filter/plan/calc-filter.json new file mode 100644 index 0000000000000..b1d3b7bc1f984 --- /dev/null +++ b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-filter/plan/calc-filter.json @@ -0,0 +1,141 @@ +{ + "flinkVersion" : "1.19", + "nodes" : [ { + "id" : 1, + "type" : "stream-exec-table-source-scan_1", + "scanTableSource" : { + "table" : { + "identifier" : "`default_catalog`.`default_database`.`source_t`", + "resolvedTable" : { + "schema" : { + "columns" : [ { + "name" : "a", + "dataType" : "BIGINT" + }, { + "name" : "b", + "dataType" : "INT" + }, { + "name" : "c", + "dataType" : "DOUBLE" + }, { + "name" : "d", + "dataType" : "VARCHAR(2147483647)" + } ], + "watermarkSpecs" : [ ] + }, + "partitionKeys" : [ ] + } + }, + "abilities" : [ { + "type" : "FilterPushDown", + "predicates" : [ ] + } ] + }, + "outputType" : "ROW<`a` BIGINT, `b` INT, `c` DOUBLE, `d` VARCHAR(2147483647)>", + "description" : "TableSourceScan(table=[[default_catalog, default_database, source_t, filter=[]]], fields=[a, b, c, d])", + "inputProperties" : [ ] + }, { + "id" : 2, + "type" : "stream-exec-calc_1", + "projection" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 0, + "type" : "BIGINT" + }, { + "kind" : "INPUT_REF", + "inputIndex" : 1, + "type" : "INT" + }, { + "kind" : "INPUT_REF", + "inputIndex" : 2, + "type" : "DOUBLE" + }, { + "kind" : "INPUT_REF", + "inputIndex" : 3, + "type" : "VARCHAR(2147483647)" + } ], + "condition" : { + "kind" : "CALL", + "syntax" : "BINARY", + "internalName" : "$>$1", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 1, + "type" : "INT" + }, { + "kind" : "LITERAL", + "value" : 0, + "type" : "INT NOT NULL" + } ], + "type" : "BOOLEAN" + }, + "inputProperties" : [ { + "requiredDistribution" : { + "type" : "UNKNOWN" + }, + "damBehavior" : "PIPELINED", + "priority" : 0 + } ], + "outputType" : "ROW<`a` BIGINT, `b` INT, `c` DOUBLE, `d` VARCHAR(2147483647)>", + "description" : "Calc(select=[a, b, c, d], where=[(b > 0)])" + }, { + "id" : 3, + "type" : "stream-exec-sink_1", + "configuration" : { + "table.exec.sink.keyed-shuffle" : "AUTO", + "table.exec.sink.not-null-enforcer" : "ERROR", + "table.exec.sink.rowtime-inserter" : "ENABLED", + "table.exec.sink.type-length-enforcer" : "IGNORE", + "table.exec.sink.upsert-materialize" : "AUTO" + }, + "dynamicTableSink" : { + "table" : { + "identifier" : "`default_catalog`.`default_database`.`sink_t`", + "resolvedTable" : { + "schema" : { + "columns" : [ { + "name" : "a", + "dataType" : "BIGINT" + }, { + "name" : "b", + "dataType" : "INT" + }, { + "name" : "c", + "dataType" : "DOUBLE" + }, { + "name" : "d", + "dataType" : "VARCHAR(2147483647)" + } ], + "watermarkSpecs" : [ ] + }, + "partitionKeys" : [ ] + } + } + }, + "inputChangelogMode" : [ "INSERT" ], + "inputProperties" : [ { + "requiredDistribution" : { + "type" : "UNKNOWN" + }, + "damBehavior" : "PIPELINED", + "priority" : 0 + } ], + "outputType" : "ROW<`a` BIGINT, `b` INT, `c` DOUBLE, `d` VARCHAR(2147483647)>", + "description" : "Sink(table=[default_catalog.default_database.sink_t], fields=[a, b, c, d])" + } ], + "edges" : [ { + "source" : 1, + "target" : 2, + "shuffle" : { + "type" : "FORWARD" + }, + "shuffleMode" : "PIPELINED" + }, { + "source" : 2, + "target" : 3, + "shuffle" : { + "type" : "FORWARD" + }, + "shuffleMode" : "PIPELINED" + } ] +} \ No newline at end of file diff --git a/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-filter/savepoint/_metadata b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-filter/savepoint/_metadata new file mode 100644 index 0000000000000..fcefcf935ee3a Binary files /dev/null and b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-filter/savepoint/_metadata differ diff --git a/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-project-pushdown/plan/calc-project-pushdown.json b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-project-pushdown/plan/calc-project-pushdown.json new file mode 100644 index 0000000000000..dde65ade1854f --- /dev/null +++ b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-project-pushdown/plan/calc-project-pushdown.json @@ -0,0 +1,135 @@ +{ + "flinkVersion" : "1.19", + "nodes" : [ { + "id" : 1, + "type" : "stream-exec-table-source-scan_1", + "scanTableSource" : { + "table" : { + "identifier" : "`default_catalog`.`default_database`.`source_t`", + "resolvedTable" : { + "schema" : { + "columns" : [ { + "name" : "a", + "dataType" : "BIGINT" + }, { + "name" : "b", + "dataType" : "DOUBLE" + } ], + "watermarkSpecs" : [ ] + }, + "partitionKeys" : [ ] + } + }, + "abilities" : [ { + "type" : "FilterPushDown", + "predicates" : [ { + "kind" : "CALL", + "syntax" : "BINARY", + "internalName" : "$>$1", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 0, + "type" : "BIGINT" + }, { + "kind" : "LITERAL", + "value" : 1, + "type" : "BIGINT NOT NULL" + } ], + "type" : "BOOLEAN" + } ] + }, { + "type" : "ProjectPushDown", + "projectedFields" : [ [ 0 ] ], + "producedType" : "ROW<`a` BIGINT> NOT NULL" + }, { + "type" : "ReadingMetadata", + "metadataKeys" : [ ], + "producedType" : "ROW<`a` BIGINT> NOT NULL" + } ] + }, + "outputType" : "ROW<`a` BIGINT>", + "description" : "TableSourceScan(table=[[default_catalog, default_database, source_t, filter=[>(a, 1:BIGINT)], project=[a], metadata=[]]], fields=[a])", + "inputProperties" : [ ] + }, { + "id" : 2, + "type" : "stream-exec-calc_1", + "projection" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 0, + "type" : "BIGINT" + }, { + "kind" : "CALL", + "syntax" : "SPECIAL", + "internalName" : "$CAST$1", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 0, + "type" : "BIGINT" + } ], + "type" : "VARCHAR(2147483647)" + } ], + "condition" : null, + "inputProperties" : [ { + "requiredDistribution" : { + "type" : "UNKNOWN" + }, + "damBehavior" : "PIPELINED", + "priority" : 0 + } ], + "outputType" : "ROW<`a` BIGINT, `EXPR$1` VARCHAR(2147483647)>", + "description" : "Calc(select=[a, CAST(a AS VARCHAR(2147483647)) AS EXPR$1])" + }, { + "id" : 3, + "type" : "stream-exec-sink_1", + "configuration" : { + "table.exec.sink.keyed-shuffle" : "AUTO", + "table.exec.sink.not-null-enforcer" : "ERROR", + "table.exec.sink.rowtime-inserter" : "ENABLED", + "table.exec.sink.type-length-enforcer" : "IGNORE", + "table.exec.sink.upsert-materialize" : "AUTO" + }, + "dynamicTableSink" : { + "table" : { + "identifier" : "`default_catalog`.`default_database`.`sink_t`", + "resolvedTable" : { + "schema" : { + "columns" : [ { + "name" : "a", + "dataType" : "BIGINT" + }, { + "name" : "a1", + "dataType" : "VARCHAR(2147483647)" + } ], + "watermarkSpecs" : [ ] + }, + "partitionKeys" : [ ] + } + } + }, + "inputChangelogMode" : [ "INSERT" ], + "inputProperties" : [ { + "requiredDistribution" : { + "type" : "UNKNOWN" + }, + "damBehavior" : "PIPELINED", + "priority" : 0 + } ], + "outputType" : "ROW<`a` BIGINT, `EXPR$1` VARCHAR(2147483647)>", + "description" : "Sink(table=[default_catalog.default_database.sink_t], fields=[a, EXPR$1])" + } ], + "edges" : [ { + "source" : 1, + "target" : 2, + "shuffle" : { + "type" : "FORWARD" + }, + "shuffleMode" : "PIPELINED" + }, { + "source" : 2, + "target" : 3, + "shuffle" : { + "type" : "FORWARD" + }, + "shuffleMode" : "PIPELINED" + } ] +} \ No newline at end of file diff --git a/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-project-pushdown/savepoint/_metadata b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-project-pushdown/savepoint/_metadata new file mode 100644 index 0000000000000..e259d1e9f351c Binary files /dev/null and b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-project-pushdown/savepoint/_metadata differ diff --git a/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-sarg/plan/calc-sarg.json b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-sarg/plan/calc-sarg.json new file mode 100644 index 0000000000000..b8d7145648172 --- /dev/null +++ b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-sarg/plan/calc-sarg.json @@ -0,0 +1,132 @@ +{ + "flinkVersion" : "1.19", + "nodes" : [ { + "id" : 1, + "type" : "stream-exec-table-source-scan_1", + "scanTableSource" : { + "table" : { + "identifier" : "`default_catalog`.`default_database`.`source_t`", + "resolvedTable" : { + "schema" : { + "columns" : [ { + "name" : "a", + "dataType" : "INT" + } ], + "watermarkSpecs" : [ ] + }, + "partitionKeys" : [ ] + } + }, + "abilities" : [ { + "type" : "FilterPushDown", + "predicates" : [ ] + } ] + }, + "outputType" : "ROW<`a` INT>", + "description" : "TableSourceScan(table=[[default_catalog, default_database, source_t, filter=[]]], fields=[a])", + "inputProperties" : [ ] + }, { + "id" : 2, + "type" : "stream-exec-calc_1", + "projection" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 0, + "type" : "INT" + } ], + "condition" : { + "kind" : "CALL", + "syntax" : "INTERNAL", + "internalName" : "$SEARCH$1", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 0, + "type" : "INT" + }, { + "kind" : "LITERAL", + "sarg" : { + "ranges" : [ { + "lower" : { + "value" : 1, + "boundType" : "CLOSED" + }, + "upper" : { + "value" : 1, + "boundType" : "CLOSED" + } + }, { + "lower" : { + "value" : 2, + "boundType" : "CLOSED" + }, + "upper" : { + "value" : 2, + "boundType" : "CLOSED" + } + } ], + "nullAs" : "TRUE" + }, + "type" : "INT NOT NULL" + } ], + "type" : "BOOLEAN NOT NULL" + }, + "inputProperties" : [ { + "requiredDistribution" : { + "type" : "UNKNOWN" + }, + "damBehavior" : "PIPELINED", + "priority" : 0 + } ], + "outputType" : "ROW<`a` INT>", + "description" : "Calc(select=[a], where=[SEARCH(a, Sarg[1, 2; NULL AS TRUE])])" + }, { + "id" : 3, + "type" : "stream-exec-sink_1", + "configuration" : { + "table.exec.sink.keyed-shuffle" : "AUTO", + "table.exec.sink.not-null-enforcer" : "ERROR", + "table.exec.sink.rowtime-inserter" : "ENABLED", + "table.exec.sink.type-length-enforcer" : "IGNORE", + "table.exec.sink.upsert-materialize" : "AUTO" + }, + "dynamicTableSink" : { + "table" : { + "identifier" : "`default_catalog`.`default_database`.`sink_t`", + "resolvedTable" : { + "schema" : { + "columns" : [ { + "name" : "a", + "dataType" : "INT" + } ], + "watermarkSpecs" : [ ] + }, + "partitionKeys" : [ ] + } + } + }, + "inputChangelogMode" : [ "INSERT" ], + "inputProperties" : [ { + "requiredDistribution" : { + "type" : "UNKNOWN" + }, + "damBehavior" : "PIPELINED", + "priority" : 0 + } ], + "outputType" : "ROW<`a` INT>", + "description" : "Sink(table=[default_catalog.default_database.sink_t], fields=[a])" + } ], + "edges" : [ { + "source" : 1, + "target" : 2, + "shuffle" : { + "type" : "FORWARD" + }, + "shuffleMode" : "PIPELINED" + }, { + "source" : 2, + "target" : 3, + "shuffle" : { + "type" : "FORWARD" + }, + "shuffleMode" : "PIPELINED" + } ] +} \ No newline at end of file diff --git a/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-sarg/savepoint/_metadata b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-sarg/savepoint/_metadata new file mode 100644 index 0000000000000..98082d5d1c95b Binary files /dev/null and b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-sarg/savepoint/_metadata differ diff --git a/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-simple/plan/calc-simple.json b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-simple/plan/calc-simple.json new file mode 100644 index 0000000000000..486b19248602b --- /dev/null +++ b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-simple/plan/calc-simple.json @@ -0,0 +1,113 @@ +{ + "flinkVersion" : "1.19", + "nodes" : [ { + "id" : 1, + "type" : "stream-exec-table-source-scan_1", + "scanTableSource" : { + "table" : { + "identifier" : "`default_catalog`.`default_database`.`t`", + "resolvedTable" : { + "schema" : { + "columns" : [ { + "name" : "a", + "dataType" : "BIGINT" + }, { + "name" : "b", + "dataType" : "DOUBLE" + } ], + "watermarkSpecs" : [ ] + }, + "partitionKeys" : [ ] + } + } + }, + "outputType" : "ROW<`a` BIGINT, `b` DOUBLE>", + "description" : "TableSourceScan(table=[[default_catalog, default_database, t]], fields=[a, b])", + "inputProperties" : [ ] + }, { + "id" : 2, + "type" : "stream-exec-calc_1", + "projection" : [ { + "kind" : "CALL", + "syntax" : "BINARY", + "internalName" : "$+$1", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 0, + "type" : "BIGINT" + }, { + "kind" : "LITERAL", + "value" : 1, + "type" : "INT NOT NULL" + } ], + "type" : "BIGINT" + }, { + "kind" : "INPUT_REF", + "inputIndex" : 1, + "type" : "DOUBLE" + } ], + "condition" : null, + "inputProperties" : [ { + "requiredDistribution" : { + "type" : "UNKNOWN" + }, + "damBehavior" : "PIPELINED", + "priority" : 0 + } ], + "outputType" : "ROW<`EXPR$0` BIGINT, `b` DOUBLE>", + "description" : "Calc(select=[(a + 1) AS EXPR$0, b])" + }, { + "id" : 3, + "type" : "stream-exec-sink_1", + "configuration" : { + "table.exec.sink.keyed-shuffle" : "AUTO", + "table.exec.sink.not-null-enforcer" : "ERROR", + "table.exec.sink.rowtime-inserter" : "ENABLED", + "table.exec.sink.type-length-enforcer" : "IGNORE", + "table.exec.sink.upsert-materialize" : "AUTO" + }, + "dynamicTableSink" : { + "table" : { + "identifier" : "`default_catalog`.`default_database`.`sink_t`", + "resolvedTable" : { + "schema" : { + "columns" : [ { + "name" : "a", + "dataType" : "BIGINT" + }, { + "name" : "b", + "dataType" : "DOUBLE" + } ], + "watermarkSpecs" : [ ] + }, + "partitionKeys" : [ ] + } + } + }, + "inputChangelogMode" : [ "INSERT" ], + "inputProperties" : [ { + "requiredDistribution" : { + "type" : "UNKNOWN" + }, + "damBehavior" : "PIPELINED", + "priority" : 0 + } ], + "outputType" : "ROW<`EXPR$0` BIGINT, `b` DOUBLE>", + "description" : "Sink(table=[default_catalog.default_database.sink_t], fields=[EXPR$0, b])" + } ], + "edges" : [ { + "source" : 1, + "target" : 2, + "shuffle" : { + "type" : "FORWARD" + }, + "shuffleMode" : "PIPELINED" + }, { + "source" : 2, + "target" : 3, + "shuffle" : { + "type" : "FORWARD" + }, + "shuffleMode" : "PIPELINED" + } ] +} \ No newline at end of file diff --git a/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-simple/savepoint/_metadata b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-simple/savepoint/_metadata new file mode 100644 index 0000000000000..e98aee3d6c895 Binary files /dev/null and b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-simple/savepoint/_metadata differ diff --git a/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-udf-complex/plan/calc-udf-complex.json b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-udf-complex/plan/calc-udf-complex.json new file mode 100644 index 0000000000000..ac6119b496604 --- /dev/null +++ b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-udf-complex/plan/calc-udf-complex.json @@ -0,0 +1,272 @@ +{ + "flinkVersion" : "1.19", + "nodes" : [ { + "id" : 1, + "type" : "stream-exec-table-source-scan_1", + "scanTableSource" : { + "table" : { + "identifier" : "`default_catalog`.`default_database`.`source_t`", + "resolvedTable" : { + "schema" : { + "columns" : [ { + "name" : "a", + "dataType" : "BIGINT" + }, { + "name" : "b", + "dataType" : "INT NOT NULL" + }, { + "name" : "c", + "dataType" : "VARCHAR(2147483647)" + }, { + "name" : "d", + "dataType" : "TIMESTAMP(3)" + } ], + "watermarkSpecs" : [ ] + }, + "partitionKeys" : [ ] + } + }, + "abilities" : [ { + "type" : "FilterPushDown", + "predicates" : [ ] + } ] + }, + "outputType" : "ROW<`a` BIGINT, `b` INT NOT NULL, `c` VARCHAR(2147483647), `d` TIMESTAMP(3)>", + "description" : "TableSourceScan(table=[[default_catalog, default_database, source_t, filter=[]]], fields=[a, b, c, d])", + "inputProperties" : [ ] + }, { + "id" : 2, + "type" : "stream-exec-calc_1", + "projection" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 0, + "type" : "BIGINT" + }, { + "kind" : "CALL", + "syntax" : "SPECIAL", + "internalName" : "$CAST$1", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 0, + "type" : "BIGINT" + } ], + "type" : "VARCHAR(2147483647)" + }, { + "kind" : "INPUT_REF", + "inputIndex" : 1, + "type" : "INT NOT NULL" + }, { + "kind" : "CALL", + "catalogName" : "`default_catalog`.`default_database`.`udf2`", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 1, + "type" : "INT NOT NULL" + }, { + "kind" : "INPUT_REF", + "inputIndex" : 1, + "type" : "INT NOT NULL" + }, { + "kind" : "INPUT_REF", + "inputIndex" : 3, + "type" : "TIMESTAMP(3)" + } ], + "type" : "VARCHAR(2147483647)" + }, { + "kind" : "CALL", + "systemName" : "udf3", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 2, + "type" : "VARCHAR(2147483647)" + }, { + "kind" : "INPUT_REF", + "inputIndex" : 1, + "type" : "INT NOT NULL" + } ], + "type" : "VARCHAR(2147483647)" + }, { + "kind" : "CALL", + "systemName" : "udf4", + "operands" : [ { + "kind" : "CALL", + "internalName" : "$SUBSTRING$1", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 2, + "type" : "VARCHAR(2147483647)" + }, { + "kind" : "LITERAL", + "value" : 1, + "type" : "INT NOT NULL" + }, { + "kind" : "LITERAL", + "value" : 5, + "type" : "INT NOT NULL" + } ], + "type" : "VARCHAR(2147483647)" + } ], + "type" : "VARCHAR(2147483647)" + }, { + "kind" : "CALL", + "catalogName" : "`default_catalog`.`default_database`.`udf5`", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 3, + "type" : "TIMESTAMP(3)" + }, { + "kind" : "LITERAL", + "value" : 1000, + "type" : "INT NOT NULL" + } ], + "type" : "TIMESTAMP(3)" + } ], + "condition" : { + "kind" : "CALL", + "syntax" : "BINARY", + "internalName" : "$AND$1", + "operands" : [ { + "kind" : "CALL", + "syntax" : "BINARY", + "internalName" : "$OR$1", + "operands" : [ { + "kind" : "CALL", + "syntax" : "BINARY", + "internalName" : "$>$1", + "operands" : [ { + "kind" : "CALL", + "catalogName" : "`default_catalog`.`default_database`.`udf1`", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 0, + "type" : "BIGINT" + } ], + "type" : "BIGINT NOT NULL" + }, { + "kind" : "LITERAL", + "value" : 0, + "type" : "INT NOT NULL" + } ], + "type" : "BOOLEAN NOT NULL" + }, { + "kind" : "CALL", + "syntax" : "BINARY", + "internalName" : "$<$1", + "operands" : [ { + "kind" : "CALL", + "syntax" : "BINARY", + "internalName" : "$*$1", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 0, + "type" : "BIGINT" + }, { + "kind" : "INPUT_REF", + "inputIndex" : 1, + "type" : "INT NOT NULL" + } ], + "type" : "BIGINT" + }, { + "kind" : "LITERAL", + "value" : 100, + "type" : "INT NOT NULL" + } ], + "type" : "BOOLEAN" + } ], + "type" : "BOOLEAN" + }, { + "kind" : "CALL", + "syntax" : "BINARY", + "internalName" : "$>$1", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 1, + "type" : "INT NOT NULL" + }, { + "kind" : "LITERAL", + "value" : 10, + "type" : "INT NOT NULL" + } ], + "type" : "BOOLEAN NOT NULL" + } ], + "type" : "BOOLEAN" + }, + "inputProperties" : [ { + "requiredDistribution" : { + "type" : "UNKNOWN" + }, + "damBehavior" : "PIPELINED", + "priority" : 0 + } ], + "outputType" : "ROW<`a` BIGINT, `a1` VARCHAR(2147483647), `b` INT NOT NULL, `b1` VARCHAR(2147483647), `c1` VARCHAR(2147483647), `c2` VARCHAR(2147483647), `d1` TIMESTAMP(3)>", + "description" : "Calc(select=[a, CAST(a AS VARCHAR(2147483647)) AS a1, b, udf2(b, b, d) AS b1, udf3(c, b) AS c1, udf4(SUBSTRING(c, 1, 5)) AS c2, udf5(d, 1000) AS d1], where=[(((udf1(a) > 0) OR ((a * b) < 100)) AND (b > 10))])" + }, { + "id" : 3, + "type" : "stream-exec-sink_1", + "configuration" : { + "table.exec.sink.keyed-shuffle" : "AUTO", + "table.exec.sink.not-null-enforcer" : "ERROR", + "table.exec.sink.rowtime-inserter" : "ENABLED", + "table.exec.sink.type-length-enforcer" : "IGNORE", + "table.exec.sink.upsert-materialize" : "AUTO" + }, + "dynamicTableSink" : { + "table" : { + "identifier" : "`default_catalog`.`default_database`.`sink_t`", + "resolvedTable" : { + "schema" : { + "columns" : [ { + "name" : "a", + "dataType" : "BIGINT" + }, { + "name" : "a1", + "dataType" : "VARCHAR(2147483647)" + }, { + "name" : "b", + "dataType" : "INT NOT NULL" + }, { + "name" : "b1", + "dataType" : "VARCHAR(2147483647)" + }, { + "name" : "c1", + "dataType" : "VARCHAR(2147483647)" + }, { + "name" : "c2", + "dataType" : "VARCHAR(2147483647)" + }, { + "name" : "d1", + "dataType" : "TIMESTAMP(3)" + } ], + "watermarkSpecs" : [ ] + }, + "partitionKeys" : [ ] + } + } + }, + "inputChangelogMode" : [ "INSERT" ], + "inputProperties" : [ { + "requiredDistribution" : { + "type" : "UNKNOWN" + }, + "damBehavior" : "PIPELINED", + "priority" : 0 + } ], + "outputType" : "ROW<`a` BIGINT, `a1` VARCHAR(2147483647), `b` INT NOT NULL, `b1` VARCHAR(2147483647), `c1` VARCHAR(2147483647), `c2` VARCHAR(2147483647), `d1` TIMESTAMP(3)>", + "description" : "Sink(table=[default_catalog.default_database.sink_t], fields=[a, a1, b, b1, c1, c2, d1])" + } ], + "edges" : [ { + "source" : 1, + "target" : 2, + "shuffle" : { + "type" : "FORWARD" + }, + "shuffleMode" : "PIPELINED" + }, { + "source" : 2, + "target" : 3, + "shuffle" : { + "type" : "FORWARD" + }, + "shuffleMode" : "PIPELINED" + } ] +} \ No newline at end of file diff --git a/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-udf-complex/savepoint/_metadata b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-udf-complex/savepoint/_metadata new file mode 100644 index 0000000000000..79c262d72a567 Binary files /dev/null and b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-udf-complex/savepoint/_metadata differ diff --git a/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-udf-simple/plan/calc-udf-simple.json b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-udf-simple/plan/calc-udf-simple.json new file mode 100644 index 0000000000000..894d252d5b18b --- /dev/null +++ b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-udf-simple/plan/calc-udf-simple.json @@ -0,0 +1,111 @@ +{ + "flinkVersion" : "1.19", + "nodes" : [ { + "id" : 1, + "type" : "stream-exec-table-source-scan_1", + "scanTableSource" : { + "table" : { + "identifier" : "`default_catalog`.`default_database`.`source_t`", + "resolvedTable" : { + "schema" : { + "columns" : [ { + "name" : "a", + "dataType" : "INT" + } ], + "watermarkSpecs" : [ ] + }, + "partitionKeys" : [ ] + } + } + }, + "outputType" : "ROW<`a` INT>", + "description" : "TableSourceScan(table=[[default_catalog, default_database, source_t]], fields=[a])", + "inputProperties" : [ ] + }, { + "id" : 2, + "type" : "stream-exec-calc_1", + "projection" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 0, + "type" : "INT" + }, { + "kind" : "CALL", + "catalogName" : "`default_catalog`.`default_database`.`udf1`", + "operands" : [ { + "kind" : "CALL", + "syntax" : "SPECIAL", + "internalName" : "$CAST$1", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 0, + "type" : "INT" + } ], + "type" : "BIGINT" + } ], + "type" : "BIGINT NOT NULL" + } ], + "condition" : null, + "inputProperties" : [ { + "requiredDistribution" : { + "type" : "UNKNOWN" + }, + "damBehavior" : "PIPELINED", + "priority" : 0 + } ], + "outputType" : "ROW<`a` INT, `EXPR$1` BIGINT NOT NULL>", + "description" : "Calc(select=[a, udf1(CAST(a AS BIGINT)) AS EXPR$1])" + }, { + "id" : 3, + "type" : "stream-exec-sink_1", + "configuration" : { + "table.exec.sink.keyed-shuffle" : "AUTO", + "table.exec.sink.not-null-enforcer" : "ERROR", + "table.exec.sink.rowtime-inserter" : "ENABLED", + "table.exec.sink.type-length-enforcer" : "IGNORE", + "table.exec.sink.upsert-materialize" : "AUTO" + }, + "dynamicTableSink" : { + "table" : { + "identifier" : "`default_catalog`.`default_database`.`sink_t`", + "resolvedTable" : { + "schema" : { + "columns" : [ { + "name" : "a", + "dataType" : "INT" + }, { + "name" : "a1", + "dataType" : "BIGINT" + } ], + "watermarkSpecs" : [ ] + }, + "partitionKeys" : [ ] + } + } + }, + "inputChangelogMode" : [ "INSERT" ], + "inputProperties" : [ { + "requiredDistribution" : { + "type" : "UNKNOWN" + }, + "damBehavior" : "PIPELINED", + "priority" : 0 + } ], + "outputType" : "ROW<`a` INT, `EXPR$1` BIGINT NOT NULL>", + "description" : "Sink(table=[default_catalog.default_database.sink_t], fields=[a, EXPR$1])" + } ], + "edges" : [ { + "source" : 1, + "target" : 2, + "shuffle" : { + "type" : "FORWARD" + }, + "shuffleMode" : "PIPELINED" + }, { + "source" : 2, + "target" : 3, + "shuffle" : { + "type" : "FORWARD" + }, + "shuffleMode" : "PIPELINED" + } ] +} \ No newline at end of file diff --git a/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-udf-simple/savepoint/_metadata b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-udf-simple/savepoint/_metadata new file mode 100644 index 0000000000000..c110557cab5c1 Binary files /dev/null and b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-calc_1/calc-udf-simple/savepoint/_metadata differ diff --git a/flink-table/flink-table-planner/src/test/resources/org/apache/flink/table/planner/plan/nodes/exec/stream/LimitJsonPlanTest_jsonplan/testLimit.out b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-limit_1/limit/plan/limit.json similarity index 70% rename from flink-table/flink-table-planner/src/test/resources/org/apache/flink/table/planner/plan/nodes/exec/stream/LimitJsonPlanTest_jsonplan/testLimit.out rename to flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-limit_1/limit/plan/limit.json index 8690fc66fbdb8..15cb677b4e450 100644 --- a/flink-table/flink-table-planner/src/test/resources/org/apache/flink/table/planner/plan/nodes/exec/stream/LimitJsonPlanTest_jsonplan/testLimit.out +++ b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-limit_1/limit/plan/limit.json @@ -1,50 +1,35 @@ { - "flinkVersion" : "", + "flinkVersion" : "1.19", "nodes" : [ { "id" : 1, "type" : "stream-exec-table-source-scan_1", "scanTableSource" : { "table" : { - "identifier" : "`default_catalog`.`default_database`.`MyTable`", + "identifier" : "`default_catalog`.`default_database`.`source_t`", "resolvedTable" : { "schema" : { "columns" : [ { "name" : "a", - "dataType" : "BIGINT" + "dataType" : "INT" }, { "name" : "b", - "dataType" : "INT NOT NULL" - }, { - "name" : "c", "dataType" : "VARCHAR(2147483647)" }, { - "name" : "d", - "dataType" : "TIMESTAMP(3)" + "name" : "c", + "dataType" : "INT" } ], "watermarkSpecs" : [ ] }, - "partitionKeys" : [ ], - "options" : { - "connector" : "values", - "bounded" : "false" - } + "partitionKeys" : [ ] } }, "abilities" : [ { - "type" : "ProjectPushDown", - "projectedFields" : [ [ 0 ] ], - "producedType" : "ROW<`a` BIGINT> NOT NULL" - }, { - "type" : "ReadingMetadata", - "metadataKeys" : [ ], - "producedType" : "ROW<`a` BIGINT> NOT NULL" - }, { "type" : "LimitPushDown", - "limit" : 10 + "limit" : 3 } ] }, - "outputType" : "ROW<`a` BIGINT>", - "description" : "TableSourceScan(table=[[default_catalog, default_database, MyTable, project=[a], metadata=[], limit=[10]]], fields=[a])", + "outputType" : "ROW<`a` INT, `b` VARCHAR(2147483647), `c` INT>", + "description" : "TableSourceScan(table=[[default_catalog, default_database, source_t, limit=[3]]], fields=[a, b, c])", "inputProperties" : [ ] }, { "id" : 2, @@ -56,7 +41,7 @@ "damBehavior" : "PIPELINED", "priority" : 0 } ], - "outputType" : "ROW<`a` BIGINT>", + "outputType" : "ROW<`a` INT, `b` VARCHAR(2147483647), `c` INT>", "description" : "Exchange(distribution=[single])" }, { "id" : 3, @@ -67,7 +52,7 @@ "rankRange" : { "type" : "Constant", "start" : 1, - "end" : 10 + "end" : 3 }, "rankStrategy" : { "type" : "AppendFast" @@ -85,8 +70,8 @@ "damBehavior" : "PIPELINED", "priority" : 0 } ], - "outputType" : "ROW<`a` BIGINT>", - "description" : "Limit(offset=[0], fetch=[10])", + "outputType" : "ROW<`a` INT, `b` VARCHAR(2147483647), `c` INT>", + "description" : "Limit(offset=[0], fetch=[3])", "rankType" : "ROW_NUMBER", "partition" : { "fields" : [ ] @@ -101,10 +86,20 @@ "projection" : [ { "kind" : "INPUT_REF", "inputIndex" : 0, - "type" : "BIGINT" + "type" : "INT" }, { "kind" : "INPUT_REF", - "inputIndex" : 0, + "inputIndex" : 1, + "type" : "VARCHAR(2147483647)" + }, { + "kind" : "CALL", + "syntax" : "SPECIAL", + "internalName" : "$CAST$1", + "operands" : [ { + "kind" : "INPUT_REF", + "inputIndex" : 2, + "type" : "INT" + } ], "type" : "BIGINT" } ], "condition" : null, @@ -115,8 +110,8 @@ "damBehavior" : "PIPELINED", "priority" : 0 } ], - "outputType" : "ROW<`a` BIGINT, `a0` BIGINT>", - "description" : "Calc(select=[a, a AS a0])" + "outputType" : "ROW<`a` INT, `b` VARCHAR(2147483647), `c` BIGINT>", + "description" : "Calc(select=[a, b, CAST(c AS BIGINT) AS c])" }, { "id" : 5, "type" : "stream-exec-sink_1", @@ -129,24 +124,22 @@ }, "dynamicTableSink" : { "table" : { - "identifier" : "`default_catalog`.`default_database`.`MySink`", + "identifier" : "`default_catalog`.`default_database`.`sink_t`", "resolvedTable" : { "schema" : { "columns" : [ { "name" : "a", - "dataType" : "BIGINT" + "dataType" : "INT" }, { "name" : "b", + "dataType" : "VARCHAR(2147483647)" + }, { + "name" : "c", "dataType" : "BIGINT" } ], "watermarkSpecs" : [ ] }, - "partitionKeys" : [ ], - "options" : { - "sink-insert-only" : "false", - "table-sink-class" : "DEFAULT", - "connector" : "values" - } + "partitionKeys" : [ ] } } }, @@ -158,8 +151,8 @@ "damBehavior" : "PIPELINED", "priority" : 0 } ], - "outputType" : "ROW<`a` BIGINT, `a0` BIGINT>", - "description" : "Sink(table=[default_catalog.default_database.MySink], fields=[a, a0])" + "outputType" : "ROW<`a` INT, `b` VARCHAR(2147483647), `c` BIGINT>", + "description" : "Sink(table=[default_catalog.default_database.sink_t], fields=[a, b, c])" } ], "edges" : [ { "source" : 1, @@ -190,4 +183,4 @@ }, "shuffleMode" : "PIPELINED" } ] -} +} \ No newline at end of file diff --git a/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-limit_1/limit/savepoint/_metadata b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-limit_1/limit/savepoint/_metadata new file mode 100644 index 0000000000000..c29a96bc807de Binary files /dev/null and b/flink-table/flink-table-planner/src/test/resources/restore-tests/stream-exec-limit_1/limit/savepoint/_metadata differ diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/api/batch/ExplainTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/api/batch/ExplainTest.scala index e6ed0bce82f65..715bee95477df 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/api/batch/ExplainTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/api/batch/ExplainTest.scala @@ -23,12 +23,12 @@ import org.apache.flink.table.api.config.ExecutionConfigOptions import org.apache.flink.table.api.internal.TableEnvironmentInternal import org.apache.flink.table.planner.utils.TableTestBase import org.apache.flink.table.types.logical.{BigIntType, IntType, VarCharType} +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.junit.{Before, Test} -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class ExplainTest(extended: Boolean) extends TableTestBase { private val extraDetails = if (extended) { @@ -46,33 +46,33 @@ class ExplainTest(extended: Boolean) extends TableTestBase { val LONG = new BigIntType() val INT = new IntType() - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_RESOURCE_DEFAULT_PARALLELISM, Int.box(4)) } - @Test + @TestTemplate def testExplainWithTableSourceScan(): Unit = { util.verifyExplain("SELECT * FROM MyTable", extraDetails: _*) } - @Test + @TestTemplate def testExplainWithDataStreamScan(): Unit = { util.verifyExplain("SELECT * FROM MyTable1", extraDetails: _*) } - @Test + @TestTemplate def testExplainWithFilter(): Unit = { util.verifyExplain("SELECT * FROM MyTable1 WHERE mod(a, 2) = 0", extraDetails: _*) } - @Test + @TestTemplate def testExplainWithAgg(): Unit = { util.verifyExplain("SELECT COUNT(*) FROM MyTable1 GROUP BY a", extraDetails: _*) } - @Test + @TestTemplate def testExplainWithJoin(): Unit = { // TODO support other join operators when them are supported util.tableEnv.getConfig @@ -80,24 +80,24 @@ class ExplainTest(extended: Boolean) extends TableTestBase { util.verifyExplain("SELECT a, b, c, e, f FROM MyTable1, MyTable2 WHERE a = d", extraDetails: _*) } - @Test + @TestTemplate def testExplainWithUnion(): Unit = { util.verifyExplain("SELECT * FROM MyTable1 UNION ALL SELECT * FROM MyTable2", extraDetails: _*) } - @Test + @TestTemplate def testExplainWithSort(): Unit = { util.verifyExplain("SELECT * FROM MyTable1 ORDER BY a LIMIT 5", extraDetails: _*) } - @Test + @TestTemplate def testExplainWithSingleSink(): Unit = { val table = util.tableEnv.sqlQuery("SELECT * FROM MyTable1 WHERE a > 10") val sink = util.createCollectTableSink(Array("a", "b", "c"), Array(INT, LONG, STRING)) util.verifyExplainInsert(table, sink, "sink", extraDetails: _*) } - @Test + @TestTemplate def testExplainWithMultiSinks(): Unit = { val stmtSet = util.tableEnv.createStatementSet() val table = util.tableEnv.sqlQuery("SELECT a, COUNT(*) AS cnt FROM MyTable1 GROUP BY a") @@ -116,7 +116,7 @@ class ExplainTest(extended: Boolean) extends TableTestBase { util.verifyExplain(stmtSet, extraDetails: _*) } - @Test + @TestTemplate def testExplainMultipleInput(): Unit = { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "NestedLoopJoin,SortMergeJoin") @@ -133,7 +133,7 @@ class ExplainTest(extended: Boolean) extends TableTestBase { } object ExplainTest { - @Parameterized.Parameters(name = "extended={0}") + @Parameters(name = "extended={0}") def parameters(): java.util.Collection[Boolean] = { java.util.Arrays.asList(true, false) } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/api/stream/ExplainTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/api/stream/ExplainTest.scala index 39d2326e332a8..8fce44b2dac87 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/api/stream/ExplainTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/api/stream/ExplainTest.scala @@ -23,15 +23,15 @@ import org.apache.flink.table.api.config.ExecutionConfigOptions import org.apache.flink.table.api.internal.TableEnvironmentInternal import org.apache.flink.table.planner.utils.TableTestBase import org.apache.flink.table.types.logical.{BigIntType, IntType, VarCharType} +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.junit.{Before, Test} -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith import java.sql.Timestamp import java.time.Duration -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class ExplainTest(extended: Boolean) extends TableTestBase { private val extraDetails = if (extended) { @@ -49,55 +49,55 @@ class ExplainTest(extended: Boolean) extends TableTestBase { val LONG = new BigIntType() val INT = new IntType() - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_RESOURCE_DEFAULT_PARALLELISM, Int.box(4)) } - @Test + @TestTemplate def testExplainTableSourceScan(): Unit = { util.verifyExplain("SELECT * FROM MyTable", extraDetails: _*) } - @Test + @TestTemplate def testExplainDataStreamScan(): Unit = { util.verifyExplain("SELECT * FROM MyTable1", extraDetails: _*) } - @Test + @TestTemplate def testExplainWithFilter(): Unit = { util.verifyExplain("SELECT * FROM MyTable1 WHERE mod(a, 2) = 0", extraDetails: _*) } - @Test + @TestTemplate def testExplainWithAgg(): Unit = { util.verifyExplain("SELECT COUNT(*) FROM MyTable1 GROUP BY a", extraDetails: _*) } - @Test + @TestTemplate def testExplainWithJoin(): Unit = { util.verifyExplain("SELECT a, b, c, e, f FROM MyTable1, MyTable2 WHERE a = d", extraDetails: _*) } - @Test + @TestTemplate def testExplainWithUnion(): Unit = { util.verifyExplain("SELECT * FROM MyTable1 UNION ALL SELECT * FROM MyTable2", extraDetails: _*) } - @Test + @TestTemplate def testExplainWithSort(): Unit = { util.verifyExplain("SELECT * FROM MyTable1 ORDER BY a LIMIT 5", extraDetails: _*) } - @Test + @TestTemplate def testExplainWithSingleSink(): Unit = { val table = util.tableEnv.sqlQuery("SELECT * FROM MyTable1 WHERE a > 10") val appendSink = util.createAppendTableSink(Array("a", "b", "c"), Array(INT, LONG, STRING)) util.verifyExplainInsert(table, appendSink, "appendSink", extraDetails: _*) } - @Test + @TestTemplate def testExplainWithMultiSinks(): Unit = { val stmtSet = util.tableEnv.createStatementSet() val table = util.tableEnv.sqlQuery("SELECT a, COUNT(*) AS cnt FROM MyTable1 GROUP BY a") @@ -120,7 +120,7 @@ class ExplainTest(extended: Boolean) extends TableTestBase { util.verifyExplain(stmtSet, extraDetails: _*) } - @Test + @TestTemplate def testMiniBatchIntervalInfer(): Unit = { val stmtSet = util.tableEnv.createStatementSet() // Test emit latency propagate among RelNodeBlocks @@ -176,7 +176,7 @@ class ExplainTest(extended: Boolean) extends TableTestBase { } object ExplainTest { - @Parameterized.Parameters(name = "extended={0}") + @Parameters(name = "extended={0}") def parameters(): java.util.Collection[Boolean] = { java.util.Arrays.asList(true, false) } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/api/stream/sql/validation/OverWindowValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/api/stream/sql/validation/OverWindowValidationTest.scala index 60a7e24789116..e171ab8c56b36 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/api/stream/sql/validation/OverWindowValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/api/stream/sql/validation/OverWindowValidationTest.scala @@ -24,7 +24,8 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedAggFunctions. import org.apache.flink.table.planner.utils.TableTestBase import org.apache.flink.types.Row -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class OverWindowValidationTest extends TableTestBase { @@ -32,7 +33,7 @@ class OverWindowValidationTest extends TableTestBase { streamUtil.addDataStream[(Int, String, Long)]("T1", 'a, 'b, 'c, 'proctime.proctime) /** All aggregates must be computed on the same window. */ - @Test(expected = classOf[TableException]) + @Test def testMultiWindow(): Unit = { val sqlQuery = "SELECT " + @@ -41,16 +42,18 @@ class OverWindowValidationTest extends TableTestBase { "sum(a) OVER (PARTITION BY b ORDER BY proctime RANGE UNBOUNDED preceding) " + "from T1" - streamUtil.tableEnv.sqlQuery(sqlQuery).toAppendStream[Row] + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => streamUtil.tableEnv.sqlQuery(sqlQuery).toAppendStream[Row]) } /** OVER clause is necessary for [[OverAgg0]] window function. */ - @Test(expected = classOf[ValidationException]) + @Test def testInvalidOverAggregation(): Unit = { streamUtil.addFunction("overAgg", new OverAgg0) val sqlQuery = "SELECT overAgg(c, a) FROM MyTable" - streamUtil.tableEnv.sqlQuery(sqlQuery) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => streamUtil.tableEnv.sqlQuery(sqlQuery)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/ArrayTypeTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/ArrayTypeTest.scala index d5465104c1d13..bc4b13615e38a 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/ArrayTypeTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/ArrayTypeTest.scala @@ -17,10 +17,12 @@ */ package org.apache.flink.table.planner.expressions +import org.apache.flink.core.testutils.FlinkAssertions.anyCauseMatches import org.apache.flink.table.api._ import org.apache.flink.table.planner.expressions.utils.ArrayTypeTestBase import org.apache.flink.table.planner.utils.DateTimeTestUtil.{localDate, localDateTime, localTime => gLocalTime} +import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.Test import java.time.{LocalDateTime => JLocalDateTime} @@ -226,9 +228,11 @@ class ArrayTypeTest extends ArrayTypeTestBase { @Test def testArrayIndexStaticCheckForTable(): Unit = { - testExpectedTableApiException( - 'f2.at(0), - "Array element access needs an index starting at 1 but was 0.") + assertThatThrownBy(() => testTableApi('f2.at(0), "1")) + .satisfies( + anyCauseMatches( + classOf[ValidationException], + "The provided index must be a valid SQL index starting from 1")) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/ScalarOperatorsTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/ScalarOperatorsTest.scala index 912b138fda5d3..44fac362f8f65 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/ScalarOperatorsTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/ScalarOperatorsTest.scala @@ -181,12 +181,21 @@ class ScalarOperatorsTest extends ScalarOperatorsTestBase { testSqlApi("'12:34:56' = f21", "TRUE") testSqlApi("'13:34:56' = f21", "FALSE") + testSqlApi("TYPEOF(f22)", "TIMESTAMP(6)") testSqlApi("f22 = '1996-11-10 12:34:56'", "TRUE") testSqlApi("f22 = '1996-11-10 12:34:57'", "FALSE") testSqlApi("f22 = cast(null as string)", "NULL") testSqlApi("'1996-11-10 12:34:56' = f22", "TRUE") testSqlApi("'1996-11-10 12:34:57' = f22", "FALSE") testSqlApi("cast(null as string) = f22", "NULL") + + testSqlApi("TYPEOF(f23)", "TIMESTAMP_LTZ(6)") + testSqlApi("f23 = '1996-11-10 12:34:56'", "TRUE") + testSqlApi("f23 = '1996-11-10 12:34:57'", "FALSE") + testSqlApi("f23 = cast(null as string)", "NULL") + testSqlApi("'1996-11-10 12:34:56' = f23", "TRUE") + testSqlApi("'1996-11-10 12:34:57' = f23", "FALSE") + testSqlApi("cast(null as string) = f23", "NULL") } @Test @@ -226,6 +235,223 @@ class ScalarOperatorsTest extends ScalarOperatorsTestBase { "date_format(f22 + interval '1' second, 'yyyy-MM-dd HH:mm:ss') = cast(f22 as timestamp_ltz)", "FALSE") testSqlApi("uuid() = cast(f22 as timestamp_ltz)", "NULL") + + testSqlApi("f23 = date_format(f23, 'yyyy-MM-dd HH:mm:ss')", "TRUE") + testSqlApi("f23 = date_format(f23 + interval '1' second, 'yyyy-MM-dd HH:mm:ss')", "FALSE") + testSqlApi("f23 = uuid()", "NULL") + testSqlApi("date_format(f23, 'yyyy-MM-dd HH:mm:ss') = f23", "TRUE") + testSqlApi("date_format(f23 + interval '1' second, 'yyyy-MM-dd HH:mm:ss') = f23", "FALSE") + testSqlApi("uuid() = f23", "NULL") + } + + @Test + def testTimePointTypeNotEqualsString(): Unit = { + testSqlApi("NOT(f15 = '1996-11-10')", "FALSE") + testSqlApi("NOT(f15 = '1996-11-11')", "TRUE") + testSqlApi("NOT('1996-11-10' = f15)", "FALSE") + testSqlApi("NOT('1996-11-11' = f15)", "TRUE") + + testSqlApi("NOT(f21 = '12:34:56')", "FALSE") + testSqlApi("NOT(f21 = '13:34:56')", "TRUE") + testSqlApi("NOT('12:34:56' = f21)", "FALSE") + testSqlApi("NOT('13:34:56' = f21)", "TRUE") + + testSqlApi("TYPEOF(f22)", "TIMESTAMP(6)") + testSqlApi("NOT(f22 = '1996-11-10 12:34:56')", "FALSE") + testSqlApi("NOT(f22 = '1996-11-10 12:34:57')", "TRUE") + testSqlApi("NOT(f22 = cast(null as string))", "NULL") + testSqlApi("NOT('1996-11-10 12:34:56' = f22)", "FALSE") + testSqlApi("NOT('1996-11-10 12:34:57' = f22)", "TRUE") + testSqlApi("NOT(cast(null as string) = f22)", "NULL") + + testSqlApi("TYPEOF(f23)", "TIMESTAMP_LTZ(6)") + testSqlApi("f23 = '1996-11-10 12:34:56'", "TRUE") + testSqlApi("f23 = '1996-11-10 12:34:57'", "FALSE") + testSqlApi("f23 = cast(null as string)", "NULL") + testSqlApi("'1996-11-10 12:34:56' = f23", "TRUE") + testSqlApi("'1996-11-10 12:34:57' = f23", "FALSE") + testSqlApi("cast(null as string) = f23", "NULL") + + testSqlApi("NOT(f15 = date_format(cast(f15 as timestamp), 'yyyy-MM-dd'))", "FALSE") + testSqlApi( + "NOT(f15 = date_format(cast(f15 as timestamp) + interval '1' day, 'yyyy-MM-dd'))", + "TRUE") + testSqlApi("NOT(f15 = uuid())", "NULL") + testSqlApi("NOT(date_format(cast(f15 as timestamp), 'yyyy-MM-dd') = f15)", "FALSE") + testSqlApi( + "NOT(date_format(cast(f15 as timestamp) + interval '1' day, 'yyyy-MM-dd')) = f15", + "TRUE") + testSqlApi("NOT(uuid() = f15)", "NULL") + + testSqlApi("NOT(f21 = date_format(cast(f21 as timestamp), 'HH:mm:ss'))", "FALSE") + testSqlApi( + "NOT(f21 = date_format(cast(f21 as timestamp) + interval '1' hour, 'HH:mm:ss'))", + "TRUE") + testSqlApi("NOT(f21 = uuid())", "NULL") + testSqlApi("NOT(date_format(cast(f21 as timestamp), 'HH:mm:ss') = f21)", "FALSE") + testSqlApi( + "NOT(date_format(cast(f21 as timestamp) + interval '1' hour, 'HH:mm:ss') = f21)", + "TRUE") + testSqlApi("NOT(uuid() = f21)", "NULL") + + testSqlApi("NOT(f22 = date_format(f22, 'yyyy-MM-dd HH:mm:ss'))", "FALSE") + testSqlApi("NOT(f22 = date_format(f22 + interval '1' second, 'yyyy-MM-dd HH:mm:ss'))", "TRUE") + testSqlApi("NOT(f22 = uuid())", "NULL") + testSqlApi("NOT(date_format(f22, 'yyyy-MM-dd HH:mm:ss') = f22)", "FALSE") + testSqlApi("NOT(date_format(f22 + interval '1' second, 'yyyy-MM-dd HH:mm:ss') = f22)", "TRUE") + testSqlApi("NOT(uuid() = f22)", "NULL") + + testSqlApi("NOT(cast(f22 as timestamp_ltz) = date_format(f22, 'yyyy-MM-dd HH:mm:ss'))", "FALSE") + testSqlApi( + "NOT(cast(f22 as timestamp_ltz) = date_format(f22 + interval '1' second, 'yyyy-MM-dd HH:mm:ss'))", + "TRUE") + testSqlApi("NOT(cast(f22 as timestamp_ltz) = uuid())", "NULL") + testSqlApi("NOT(date_format(f22, 'yyyy-MM-dd HH:mm:ss') = cast(f22 as timestamp_ltz))", "FALSE") + testSqlApi( + "NOT(date_format(f22 + interval '1' second, 'yyyy-MM-dd HH:mm:ss') = cast(f22 as timestamp_ltz))", + "TRUE") + testSqlApi("NOT(uuid() = cast(f22 as timestamp_ltz))", "NULL") + + testSqlApi("NOT(f23 = date_format(f23, 'yyyy-MM-dd HH:mm:ss'))", "FALSE") + testSqlApi("NOT(f23 = date_format(f23 + interval '1' second, 'yyyy-MM-dd HH:mm:ss'))", "TRUE") + testSqlApi("NOT(f23 = uuid())", "NULL") + testSqlApi("NOT(date_format(f23, 'yyyy-MM-dd HH:mm:ss') = f23)", "FALSE") + testSqlApi("NOT(date_format(f23 + interval '1' second, 'yyyy-MM-dd HH:mm:ss') = f23)", "TRUE") + testSqlApi("NOT(uuid() = f23)", "NULL") + } + + @Test + def testMoreEqualAndNonEqual(): Unit = { + // character string + testSqlApi("f10 = 'String'", "TRUE") + testSqlApi("f10 = 'string'", "FALSE") + testSqlApi("f10 = NULL", "NULL") + testSqlApi("f10 = CAST(NULL AS STRING)", "NULL") + testSqlApi("'String' = f10", "TRUE") + testSqlApi("'string' = f10", "FALSE") + testSqlApi("NULL = f10", "NULL") + testSqlApi("CAST(NULL AS STRING) = f10", "NULL") + + testSqlApi("NOT(f10 = 'String')", "FALSE") + testSqlApi("NOT(f10 = 'string')", "TRUE") + testSqlApi("NOT(f10 = NULL)", "NULL") + testSqlApi("NOT(f10 = CAST(NULL AS STRING))", "NULL") + testSqlApi("NOT('String' = f10)", "FALSE") + testSqlApi("NOT('string' = f10)", "TRUE") + testSqlApi("NOT(NULL = f10)", "NULL") + testSqlApi("NOT(CAST(NULL AS STRING) = f10)", "NULL") + + // numeric types + testSqlApi("f2 = 1", "TRUE") + testSqlApi("f2 = 2", "FALSE") + testSqlApi("f2 = NULL", "NULL") + testSqlApi("f2 = CAST(NULL AS INT)", "NULL") + testSqlApi("1 = f2", "TRUE") + testSqlApi("2 = f2", "FALSE") + testSqlApi("NULL = f2", "NULL") + testSqlApi("CAST(NULL AS INT) = f2", "NULL") + + testSqlApi("NOT(f2 = 1)", "FALSE") + testSqlApi("NOT(f2 = 2)", "TRUE") + testSqlApi("NOT(f2 = NULL)", "NULL") + testSqlApi("NOT(f2 = CAST(NULL AS INT))", "NULL") + testSqlApi("NOT(1 = f2)", "FALSE") + testSqlApi("NOT(2 = f2)", "TRUE") + testSqlApi("NOT(NULL = f2)", "NULL") + testSqlApi("NOT(CAST(NULL AS INT) = f2)", "NULL") + + // array + testSqlApi("f24 = ARRAY['hello', 'world']", "TRUE") + testSqlApi("f24 = ARRAY['hello1', 'world']", "FALSE") + testSqlApi("f24 = NULL", "NULL") + testSqlApi("f24 = CAST(NULL AS ARRAY)", "NULL") + testSqlApi("ARRAY['hello', 'world'] = f24", "TRUE") + testSqlApi("ARRAY['hello1', 'world'] = f24", "FALSE") + testSqlApi("NULL = f24", "NULL") + testSqlApi("CAST(NULL AS ARRAY) = f24", "NULL") + + testSqlApi("NOT(f24 = ARRAY['hello', 'world'])", "FALSE") + testSqlApi("NOT(f24 = ARRAY['hello1', 'world'])", "TRUE") + testSqlApi("NOT(f24 = NULL)", "NULL") + testSqlApi("NOT(f24 = CAST(NULL AS ARRAY))", "NULL") + testSqlApi("NOT(ARRAY['hello', 'world'] = f24)", "FALSE") + testSqlApi("NOT(ARRAY['hello1', 'world'] = f24)", "TRUE") + testSqlApi("NOT(NULL = f24)", "NULL") + testSqlApi("NOT(CAST(NULL AS ARRAY)) = f24", "NULL") + + // map + testSqlApi("f25 = MAP['a', 1, 'b', 2]", "TRUE") + testSqlApi("f25 = MAP['a', 3, 'b', 2]", "FALSE") + testSqlApi("f25 = NULL", "NULL") + testSqlApi("f25 = CAST(NULL AS MAP)", "NULL") + testSqlApi("MAP['a', 1, 'b', 2] = f25", "TRUE") + testSqlApi("MAP['a', 3, 'b', 2] = f25", "FALSE") + testSqlApi("NULL = f25", "NULL") + testSqlApi("CAST(NULL AS MAP) = f25", "NULL") + + testSqlApi("NOT(f25 = MAP['a', 1, 'b', 2])", "FALSE") + testSqlApi("NOT(f25 = MAP['a', 3, 'b', 2])", "TRUE") + testSqlApi("NOT(f25 = NULL)", "NULL") + testSqlApi("NOT(f25 = CAST(NULL AS MAP))", "NULL") + testSqlApi("NOT(MAP['a', 1, 'b', 2] = f25)", "FALSE") + testSqlApi("NOT(MAP['a', 3, 'b', 2] = f25)", "TRUE") + testSqlApi("NOT(NULL = f25)", "NULL") + testSqlApi("NOT(CAST(NULL AS MAP) = f25)", "NULL") + + // raw + testSqlApi("f27 = f29", "TRUE") + testSqlApi("f27 = f28", "FALSE") + testSqlApi("f27 = NULL", "NULL") + testSqlApi("f29 = f27", "TRUE") + testSqlApi("f28 = f27", "FALSE") + testSqlApi("NULL = f27", "NULL") + + testSqlApi("NOT(f27 = f29)", "FALSE") + testSqlApi("NOT(f27 = f28)", "TRUE") + testSqlApi("NOT(f27 = NULL)", "NULL") + testSqlApi("NOT(f29 = f27)", "FALSE") + testSqlApi("NOT(f28 = f27)", "TRUE") + testSqlApi("NOT(NULL = f27)", "NULL") + + // non comparable types + testSqlApi("f30 = ROW('abc', 'def')", "TRUE") + testSqlApi("f30 = ROW('abc', 'xyz')", "FALSE") + testSqlApi("f30 = NULL", "NULL") + testSqlApi("f30 = CAST(NULL AS ROW)", "NULL") + testSqlApi("ROW('abc', 'def') = f30", "TRUE") + testSqlApi("ROW('abc', 'xyz') = f30", "FALSE") + testSqlApi("CAST(NULL AS ROW) = f30", "NULL") + + testSqlApi("NOT(f30 = ROW('abc', 'def'))", "FALSE") + testSqlApi("NOT(f30 = ROW('abc', 'xyz'))", "TRUE") + testSqlApi("NOT(f30 = NULL)", "NULL") + testSqlApi("NOT(f30 = CAST(NULL AS ROW))", "NULL") + testSqlApi("NOT(ROW('abc', 'def') = f30)", "FALSE") + testSqlApi("NOT(ROW('abc', 'xyz') = f30)", "TRUE") + testSqlApi("NOT(CAST(NULL AS ROW) = f30)", "NULL") + + // time interval, comparable + testSqlApi("f31 = f33", "TRUE") + testSqlApi("f31 = f32", "FALSE") + testSqlApi("f31 = NULL", "NULL") + testSqlApi("f31 = f34", "NULL") + testSqlApi("f31 = CAST(NULL AS INTERVAL DAY)", "NULL") + testSqlApi("f33 = f31", "TRUE") + testSqlApi("f32 = f31", "FALSE") + testSqlApi("NULL = f31", "NULL") + testSqlApi("f34 = f31", "NULL") + testSqlApi("CAST(NULL AS INTERVAL DAY) = f31", "NULL") + + testSqlApi("NOT(f31 = f33)", "FALSE") + testSqlApi("NOT(f31 = f32)", "TRUE") + testSqlApi("NOT(f31 = NULL)", "NULL") + testSqlApi("NOT(f31 = f34)", "NULL") + testSqlApi("NOT(f31 = CAST(NULL AS INTERVAL DAY))", "NULL") + testSqlApi("NOT(f33 = f31)", "FALSE") + testSqlApi("NOT(f32 = f31)", "TRUE") + testSqlApi("NOT(NULL = f31)", "NULL") + testSqlApi("NOT(f34 = f31)", "NULL") + testSqlApi("NOT(CAST(NULL AS INTERVAL DAY) = f31)", "NULL") } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/utils/ExpressionTestBase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/utils/ExpressionTestBase.scala index 48e972d275ab7..d46ab187b75ca 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/utils/ExpressionTestBase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/utils/ExpressionTestBase.scala @@ -247,6 +247,7 @@ abstract class ExpressionTestBase(isStreaming: Boolean = true) { val converter = DataStructureConverters .getConverter(resolvedDataType) .asInstanceOf[DataStructureConverter[RowData, Row]] + converter.open(getClass.getClassLoader) converter.toInternalOrNull(testData) } try { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/utils/ScalarOperatorsTestBase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/utils/ScalarOperatorsTestBase.scala index b32c5618f4521..78d91c3562d79 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/utils/ScalarOperatorsTestBase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/utils/ScalarOperatorsTestBase.scala @@ -21,13 +21,18 @@ import org.apache.flink.table.api.DataTypes import org.apache.flink.table.data.DecimalDataUtils import org.apache.flink.table.functions.ScalarFunction import org.apache.flink.table.planner.utils.DateTimeTestUtil._ +import org.apache.flink.table.planner.utils.TableConfigUtils import org.apache.flink.table.types.AbstractDataType import org.apache.flink.types.Row +import java.time.{DayOfWeek, Duration} + +import scala.collection.JavaConverters.mapAsJavaMapConverter + abstract class ScalarOperatorsTestBase extends ExpressionTestBase { override def testData: Row = { - val testData = new Row(23) + val testData = new Row(35) testData.setField(0, 1: Byte) testData.setField(1, 1: Short) testData.setField(2, 1) @@ -51,6 +56,22 @@ abstract class ScalarOperatorsTestBase extends ExpressionTestBase { testData.setField(20, "who".getBytes()) testData.setField(21, localTime("12:34:56")) testData.setField(22, localDateTime("1996-11-10 12:34:56")) + testData.setField( + 23, + localDateTime("1996-11-10 12:34:56") + .atZone(TableConfigUtils.getLocalTimeZone(tableConfig)) + .toInstant) + testData.setField(24, Array("hello", "world")) + testData.setField(25, Map("a" -> 1, "b" -> 2).asJava) + testData.setField(26, Map("a" -> 1, "b" -> 2).asJava) + testData.setField(27, DayOfWeek.SUNDAY) + testData.setField(28, DayOfWeek.MONDAY) + testData.setField(29, DayOfWeek.SUNDAY) + testData.setField(30, Row.of("abc", "def")) + testData.setField(31, Duration.ofDays(2)) + testData.setField(32, Duration.ofDays(3)) + testData.setField(33, Duration.ofDays(2)) + testData.setField(34, null) testData } @@ -82,7 +103,23 @@ abstract class ScalarOperatorsTestBase extends ExpressionTestBase { DataTypes.FIELD("f19", DataTypes.VARBINARY(200).notNull()), DataTypes.FIELD("f20", DataTypes.VARBINARY(200)), DataTypes.FIELD("f21", DataTypes.TIME()), - DataTypes.FIELD("f22", DataTypes.TIMESTAMP()) + DataTypes.FIELD("f22", DataTypes.TIMESTAMP()), + DataTypes.FIELD("f23", DataTypes.TIMESTAMP_LTZ()), + DataTypes.FIELD("f24", DataTypes.ARRAY(DataTypes.STRING())), + DataTypes.FIELD("f25", DataTypes.MAP(DataTypes.STRING(), DataTypes.INT())), + DataTypes.FIELD("f26", DataTypes.MULTISET(DataTypes.STRING())), + DataTypes.FIELD("f27", DataTypes.RAW(classOf[DayOfWeek])), + DataTypes.FIELD("f28", DataTypes.RAW(classOf[DayOfWeek])), + DataTypes.FIELD("f29", DataTypes.RAW(classOf[DayOfWeek])), + DataTypes.FIELD( + "f30", + DataTypes.ROW( + DataTypes.FIELD("f0", DataTypes.STRING()), + DataTypes.FIELD("f1", DataTypes.STRING()))), + DataTypes.FIELD("f31", DataTypes.INTERVAL(DataTypes.DAY)), + DataTypes.FIELD("f32", DataTypes.INTERVAL(DataTypes.DAY)), + DataTypes.FIELD("f33", DataTypes.INTERVAL(DataTypes.DAY)), + DataTypes.FIELD("f34", DataTypes.INTERVAL(DataTypes.DAY)) ) } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/validation/ArrayTypeValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/validation/ArrayTypeValidationTest.scala index ce47498ebfeeb..ae71cfbb60468 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/validation/ArrayTypeValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/validation/ArrayTypeValidationTest.scala @@ -31,12 +31,6 @@ class ArrayTypeValidationTest extends ArrayTypeTestBase { .isThrownBy(() => testSqlApi("ARRAY['string', 12]", "FAIL")) } - @Test - def testObviousInvalidIndexTableApi(): Unit = { - assertThatExceptionOfType(classOf[ValidationException]) - .isThrownBy(() => testTableApi('f2.at(0), "FAIL")) - } - @Test def testEmptyArraySql(): Unit = { assertThatExceptionOfType(classOf[ValidationException]) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/validation/ScalarFunctionsValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/validation/ScalarFunctionsValidationTest.scala index 1ce90d8e9e989..4a604ae733c41 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/validation/ScalarFunctionsValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/validation/ScalarFunctionsValidationTest.scala @@ -123,15 +123,6 @@ class ScalarFunctionsValidationTest extends ScalarTypesTestBase { // Sub-query functions // ---------------------------------------------------------------------------------------------- - @Test - def testInValidationExceptionMoreThanOneTypes(): Unit = { - assertThatExceptionOfType(classOf[ValidationException]) - .isThrownBy(() => testTableApi('f2.in('f3, 'f8), "TRUE")) - - assertThatExceptionOfType(classOf[ValidationException]) - .isThrownBy(() => testTableApi('f2.in('f3, 'f4, 4), "FALSE")) - } - @Test def scalaInValidationExceptionDifferentOperandsTest(): Unit = { assertThatExceptionOfType(classOf[ValidationException]) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/validation/ScalarOperatorsValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/validation/ScalarOperatorsValidationTest.scala index f319924c035dd..189591c691daf 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/validation/ScalarOperatorsValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/expressions/validation/ScalarOperatorsValidationTest.scala @@ -47,12 +47,6 @@ class ScalarOperatorsValidationTest extends ScalarOperatorsTestBase { // Sub-query functions // ---------------------------------------------------------------------------------------------- - @Test - def testInMoreThanOneTypes(): Unit = { - assertThatExceptionOfType(classOf[ValidationException]) - .isThrownBy(() => testTableApi('f2.in('f3, 'f4, 4), "FAIL")) - } - @Test def testInDifferentOperands(): Unit = { assertThatExceptionOfType(classOf[ValidationException]) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/CalcTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/CalcTest.scala index 09a13014aff0d..6c0767f09ca8f 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/CalcTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/CalcTest.scala @@ -25,7 +25,8 @@ import org.apache.flink.table.planner.plan.utils.MyPojo import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctions.NonDeterministicUdf import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.{BeforeEach, Test} import java.sql.{Date, Time, Timestamp} @@ -33,7 +34,7 @@ class CalcTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource[(Long, Int, String)]("MyTable", 'a, 'b, 'c) util.addFunction("random_udf", new NonDeterministicUdf) @@ -103,9 +104,10 @@ class CalcTest extends TableTestBase { util.verifyExecPlan("SELECT MyTable2.a.*, c, MyTable2.b.* FROM MyTable2") } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidFields(): Unit = { - util.tableEnv.sqlQuery("SELECT a, foo FROM MyTable") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.tableEnv.sqlQuery("SELECT a, foo FROM MyTable")) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/CompactManagedTableTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/CompactManagedTableTest.scala index c43c6380bed56..b68d9b84129e1 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/CompactManagedTableTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/CompactManagedTableTest.scala @@ -22,7 +22,7 @@ import org.apache.flink.table.catalog.{CatalogPartitionSpec, ObjectIdentifier} import org.apache.flink.table.factories.TestManagedTableFactory import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{After, Before, Test} +import org.junit.jupiter.api.{AfterEach, BeforeEach, Test} import java.util import java.util.Collections @@ -34,7 +34,7 @@ class CompactManagedTableTest extends TableTestBase { ObjectIdentifier.of("default_catalog", "default_database", "ManagedTable") private val testUtil = batchTestUtil() - @Before + @BeforeEach def before(): Unit = { val tableRef = new AtomicReference[util.Map[String, String]] TestManagedTableFactory.MANAGED_TABLES.put(tableIdentifier, tableRef) @@ -60,7 +60,7 @@ class CompactManagedTableTest extends TableTestBase { TestManagedTableFactory.MANAGED_TABLE_FILE_ENTRIES.put(tableIdentifier, fileRef) } - @After + @AfterEach def after(): Unit = { val ddl = "DROP TABLE ManagedTable" testUtil.tableEnv.executeSql(ddl) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/DagOptimizationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/DagOptimizationTest.scala index 8f4a4dd55d998..9a18da17f6f28 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/DagOptimizationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/DagOptimizationTest.scala @@ -25,7 +25,7 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctio import org.apache.flink.table.planner.utils.{TableFunc1, TableTestBase} import org.apache.flink.table.types.logical._ -import org.junit.Test +import org.junit.jupiter.api.Test import java.sql.Timestamp diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/DeadlockBreakupTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/DeadlockBreakupTest.scala index cd6abc57dcbec..20868e298da9c 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/DeadlockBreakupTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/DeadlockBreakupTest.scala @@ -24,13 +24,13 @@ import org.apache.flink.table.api._ import org.apache.flink.table.api.config.{ExecutionConfigOptions, OptimizerConfigOptions} import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} class DeadlockBreakupTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig .set(ExecutionOptions.BATCH_SHUFFLE_MODE, BatchShuffleMode.ALL_EXCHANGES_PIPELINED) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LegacyLimitTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LegacyLimitTest.scala index 7d968732dad67..9eb463351112a 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LegacyLimitTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LegacyLimitTest.scala @@ -21,13 +21,14 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api.{SqlParserException, _} import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.{BeforeEach, Test} class LegacyLimitTest extends TableTestBase { protected val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource[(Int, Long, String)]("MyTable", 'a, 'b, 'c) val ddl = @@ -54,9 +55,10 @@ class LegacyLimitTest extends TableTestBase { util.verifyExecPlan("SELECT * FROM MyTable LIMIT 0") } - @Test(expected = classOf[SqlParserException]) + @Test def testNegativeLimitWithoutOffset(): Unit = { - util.verifyExecPlan("SELECT * FROM MyTable LIMIT -1") + assertThatExceptionOfType(classOf[SqlParserException]) + .isThrownBy(() => util.verifyExecPlan("SELECT * FROM MyTable LIMIT -1")) } @Test @@ -79,9 +81,10 @@ class LegacyLimitTest extends TableTestBase { util.verifyExecPlan("SELECT a, c FROM MyTable LIMIT 0 OFFSET 10") } - @Test(expected = classOf[SqlParserException]) + @Test def testLimitWithNegativeOffset(): Unit = { - util.verifyExecPlan("SELECT a, c FROM MyTable LIMIT 10 OFFSET -1") + assertThatExceptionOfType(classOf[SqlParserException]) + .isThrownBy(() => util.verifyExecPlan("SELECT a, c FROM MyTable LIMIT 10 OFFSET -1")) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LegacySinkTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LegacySinkTest.scala index a1a8b9bf05beb..873b10a090973 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LegacySinkTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LegacySinkTest.scala @@ -19,13 +19,12 @@ package org.apache.flink.table.planner.plan.batch.sql import org.apache.flink.api.scala._ import org.apache.flink.table.api._ -import org.apache.flink.table.api.config.TableConfigOptions import org.apache.flink.table.api.internal.TableEnvironmentInternal import org.apache.flink.table.planner.plan.optimize.RelNodeBlockPlanBuilder import org.apache.flink.table.planner.utils.TableTestBase import org.apache.flink.table.types.logical.{BigIntType, IntType} -import org.junit.Test +import org.junit.jupiter.api.Test class LegacySinkTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LegacyTableSourceTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LegacyTableSourceTest.scala index e7350e2fb1bd2..a4da620ec0ebf 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LegacyTableSourceTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LegacyTableSourceTest.scala @@ -20,14 +20,14 @@ package org.apache.flink.table.planner.plan.batch.sql import org.apache.flink.api.common.typeinfo.TypeInformation import org.apache.flink.api.java.typeutils.RowTypeInfo import org.apache.flink.table.api.{DataTypes, TableSchema, Types, ValidationException} -import org.apache.flink.table.api.config.TableConfigOptions import org.apache.flink.table.api.internal.TableEnvironmentInternal import org.apache.flink.table.planner.expressions.utils.Func1 import org.apache.flink.table.planner.utils._ import org.apache.flink.table.runtime.types.TypeInfoDataTypeConverter import org.apache.flink.types.Row -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.{BeforeEach, Test} class LegacyTableSourceTest extends TableTestBase { @@ -37,7 +37,7 @@ class LegacyTableSourceTest extends TableTestBase { .fields(Array("a", "b", "c"), Array(DataTypes.INT(), DataTypes.BIGINT(), DataTypes.STRING())) .build() - @Before + @BeforeEach def setup(): Unit = { util.tableEnv .asInstanceOf[TableEnvironmentInternal] @@ -69,9 +69,10 @@ class LegacyTableSourceTest extends TableTestBase { @Test def testUnboundedStreamTableSource(): Unit = { TestTableSource.createTemporaryTable(util.tableEnv, isBounded = false, tableSchema, "MyTable") - thrown.expect(classOf[ValidationException]) - thrown.expectMessage("Only bounded StreamTableSource can be used in batch mode.") - util.verifyExecPlan("SELECT * FROM MyTable") + + assertThatThrownBy(() => util.verifyExecPlan("SELECT * FROM MyTable")) + .hasMessageContaining("Only bounded StreamTableSource can be used in batch mode.") + .isInstanceOf[ValidationException] } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LimitTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LimitTest.scala index d7762eacd4063..86fe1c7c96522 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LimitTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/LimitTest.scala @@ -21,6 +21,8 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.connector.source.abilities.SupportsProjectionPushDown +import org.junit.jupiter.api.BeforeEach + /** * The plan of following unit test in LimitTest.xml is a bit diffirent from LegacyLimitTest.xml. * Because the TestValuesTableSource has implemented [[SupportsProjectionPushDown]] while the @@ -30,6 +32,7 @@ import org.apache.flink.table.connector.source.abilities.SupportsProjectionPushD */ class LimitTest extends LegacyLimitTest { + @BeforeEach override def setup(): Unit = { util.addTableSource[(Int, Long, String)]("MyTable", 'a, 'b, 'c) val ddl = diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/MultipleInputCreationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/MultipleInputCreationTest.scala index 43a2944e1fef9..11a8f5c5ef514 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/MultipleInputCreationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/MultipleInputCreationTest.scala @@ -27,19 +27,18 @@ import org.apache.flink.configuration.JobManagerOptions.SchedulerType import org.apache.flink.table.api._ import org.apache.flink.table.api.config.{ExecutionConfigOptions, OptimizerConfigOptions} import org.apache.flink.table.planner.utils.{TableTestBase, TableTestUtil} +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.junit.{Before, Test} -import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.junit.runners.Parameterized.Parameters +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: SchedulerType) extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def before(): Unit = { util.addTableSource[(Int, Long, String, Int)]("x", 'a, 'b, 'c, 'nx) util.addTableSource[(Int, Long, String, Int)]("y", 'd, 'e, 'f, 'ny) @@ -49,7 +48,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.tableConfig.set(JobManagerOptions.SCHEDULER, schedulerType) } - @Test + @TestTemplate def testBasicMultipleInput(): Unit = { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "NestedLoopJoin,SortMergeJoin") @@ -64,7 +63,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testManyMultipleInputs(): Unit = { // y z t y t // | | | | | @@ -102,7 +101,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinWithAggAsProbe(): Unit = { util.tableEnv.getConfig.set( ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, @@ -119,7 +118,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testKeepMultipleInputWithOneMemberForChainableSource(): Unit = { createChainableTableSource() util.tableEnv.getConfig @@ -128,7 +127,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testAvoidIncludingUnionFromInputSide(): Unit = { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashJoin,SortMergeJoin") @@ -141,7 +140,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testIncludeUnionForChainableSource(): Unit = { createChainableTableSource() util.tableEnv.getConfig @@ -155,7 +154,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testAvoidIncludingCalcAfterNonChainableSource(): Unit = { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashJoin,SortMergeJoin") @@ -169,7 +168,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testIncludeCalcForChainableSource(): Unit = { createChainableTableSource() util.tableEnv.getConfig @@ -184,7 +183,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testAvoidIncludingSingleton(): Unit = { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashJoin,SortMergeJoin,HashAgg") @@ -202,7 +201,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testNoPriorityConstraint(): Unit = { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashJoin,NestedLoopJoin") @@ -215,7 +214,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testRelatedInputs(): Unit = { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashJoin,SortMergeJoin") @@ -233,7 +232,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testRelatedInputsWithAgg(): Unit = { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashJoin,SortMergeJoin,SortAgg") @@ -251,7 +250,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testRemoveRedundantUnion(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_REUSE_SOURCE_ENABLED, Boolean.box(false)) @@ -273,7 +272,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testRemoveOneInputOperatorFromRoot(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_REUSE_SOURCE_ENABLED, Boolean.box(false)) @@ -292,7 +291,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testCleanUpMultipleInputWithOneMember(): Unit = { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "NestedLoopJoin,SortMergeJoin") @@ -308,7 +307,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testKeepUsefulUnion(): Unit = { createChainableTableSource() util.tableEnv.getConfig @@ -328,7 +327,7 @@ class MultipleInputCreationTest(shuffleMode: BatchShuffleMode, schedulerType: Sc util.verifyExecPlan(sql) } - @Test + @TestTemplate def testDeadlockCausedByExchangeInAncestor(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_REUSE_SOURCE_ENABLED, Boolean.box(true)) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/OperatorFusionCodegenTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/OperatorFusionCodegenTest.scala index 19e1a0b2066f1..02859f707f8b0 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/OperatorFusionCodegenTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/OperatorFusionCodegenTest.scala @@ -23,21 +23,21 @@ import org.apache.flink.table.api.config.ExecutionConfigOptions import org.apache.flink.table.plan.stats.TableStats import org.apache.flink.table.planner.plan.stats.FlinkStatistic import org.apache.flink.table.planner.utils.TableTestBase +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} import com.google.common.collect.ImmutableSet -import org.junit.{Before, Test} -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith import java.util /** Tests for operator fusion codegen. */ -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class OperatorFusionCodegenTest(fusionCodegenEnabled: Boolean) extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource( "T1", @@ -75,21 +75,21 @@ class OperatorFusionCodegenTest(fusionCodegenEnabled: Boolean) extends TableTest Boolean.box(fusionCodegenEnabled)) } - @Test + @TestTemplate def testHashAggAsMutltipleInputRoot(): Unit = { util.verifyExecPlan( "SELECT a1, b1, a2, b2, COUNT(c1) FROM " + "(SELECT * FROM T1, T2 WHERE a1 = b2) t GROUP BY a1, b1, a2, b2") } - @Test + @TestTemplate def testLocalHashAggAsMutltipleInputRoot(): Unit = { util.verifyExecPlan( "SELECT a2, b2, a3, b3, COUNT(c2), AVG(d3) FROM " + "(SELECT * FROM T2, T3 WHERE b2 = a3) t GROUP BY a2, b2, a3, b3") } - @Test + @TestTemplate def testCalcAsMutltipleInputRoot(): Unit = { util.verifyExecPlan( "SELECT a1, b1, a2, b2, a3, b3, COUNT(c1) FROM " + @@ -98,7 +98,7 @@ class OperatorFusionCodegenTest(fusionCodegenEnabled: Boolean) extends TableTest } object OperatorFusionCodegenTest { - @Parameterized.Parameters(name = "fusionCodegenEnabled={0}") + @Parameters(name = "fusionCodegenEnabled={0}") def parameters(): util.Collection[Boolean] = { util.Arrays.asList(true, false) } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/PartitionableSinkTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/PartitionableSinkTest.scala index 2d239cb0543bc..80ddcb839ec07 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/PartitionableSinkTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/PartitionableSinkTest.scala @@ -21,7 +21,8 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class PartitionableSinkTest extends TableTestBase { @@ -66,14 +67,20 @@ class PartitionableSinkTest extends TableTestBase { util.verifyExecPlanInsert("INSERT INTO sink PARTITION (b=1) SELECT a, c FROM MyTable") } - @Test(expected = classOf[ValidationException]) + @Test def testWrongStatic(): Unit = { - util.verifyExecPlanInsert("INSERT INTO sink PARTITION (a=1) SELECT b, c FROM MyTable") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + util.verifyExecPlanInsert("INSERT INTO sink PARTITION (a=1) SELECT b, c FROM MyTable")) } - @Test(expected = classOf[ValidationException]) + @Test def testWrongFields(): Unit = { - util.verifyExecPlanInsert("INSERT INTO sink PARTITION (b=1) SELECT a, b, c FROM MyTable") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + util.verifyExecPlanInsert("INSERT INTO sink PARTITION (b=1) SELECT a, b, c FROM MyTable")) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/PartitionableSourceTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/PartitionableSourceTest.scala index 007648c9e32dc..d2d4935e63b83 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/PartitionableSourceTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/PartitionableSourceTest.scala @@ -21,22 +21,22 @@ import org.apache.flink.table.catalog.{CatalogPartitionImpl, CatalogPartitionSpe import org.apache.flink.table.planner.expressions.utils.Func1 import org.apache.flink.table.planner.factories.TestValuesCatalog import org.apache.flink.table.planner.utils.TableTestBase +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.junit.{Before, Test} -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith import java.util import scala.collection.JavaConversions._ -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class PartitionableSourceTest(val sourceFetchPartitions: Boolean, val useCatalogFilter: Boolean) extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val partitionableTable = """ @@ -112,50 +112,50 @@ class PartitionableSourceTest(val sourceFetchPartitions: Boolean, val useCatalog } } - @Test + @TestTemplate def testSimplePartitionFieldPredicate1(): Unit = { util.verifyExecPlan("SELECT * FROM PartitionableTable WHERE part1 = 'A'") } - @Test + @TestTemplate def testPartialPartitionFieldPredicatePushDown(): Unit = { util.verifyExecPlan( "SELECT * FROM PartitionableTable WHERE (id > 2 OR part1 = 'A') AND part2 > 1") } - @Test + @TestTemplate def testWithUdfAndVirtualColumn(): Unit = { util.addFunction("MyUdf", Func1) util.verifyExecPlan("SELECT * FROM PartitionableTable WHERE id > 2 AND MyUdf(part2) < 3") } - @Test + @TestTemplate def testUnconvertedExpression(): Unit = { util.verifyExecPlan("select * from PartitionableTable where trim(part1) = 'A' and part2 > 1") } - @Test + @TestTemplate def testPushDownPartitionAndFiltersContainPartitionKeys(): Unit = { util.verifyExecPlan( "select * from PartitionableAndFilterableTable " + "where part1 = 'A' and part2 > 1 and id > 1") } - @Test + @TestTemplate def testPushDownPartitionAndFiltersContainPartitionKeysWithSingleProjection(): Unit = { util.verifyExecPlan( "select name from PartitionableAndFilterableTable " + "where part1 = 'A' and part2 > 1 and id > 1") } - @Test + @TestTemplate def testPushDownNonExistentPartition(): Unit = { util.verifyExecPlan("SELECT * FROM PartitionableTable WHERE part2 = 4") } } object PartitionableSourceTest { - @Parameterized.Parameters(name = "sourceFetchPartitions={0}, useCatalogFilter={1}") + @Parameters(name = "sourceFetchPartitions={0}, useCatalogFilter={1}") def parameters(): util.Collection[Array[Any]] = { Seq[Array[Any]]( Array(true, false), diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/RankTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/RankTest.scala index bc18eebfe0b01..49ff3aaa033bd 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/RankTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/RankTest.scala @@ -21,8 +21,8 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.assertj.core.api.Assertions.assertThatThrownBy -import org.junit.Test +import org.assertj.core.api.Assertions.{assertThatExceptionOfType, assertThatThrownBy} +import org.junit.jupiter.api.Test class RankTest extends TableTestBase { @@ -59,7 +59,7 @@ class RankTest extends TableTestBase { "please re-check the over window statement.") } - @Test(expected = classOf[RuntimeException]) + @Test def testRowNumberWithMultiGroups(): Unit = { val sqlQuery = """ @@ -67,19 +67,22 @@ class RankTest extends TableTestBase { | ROW_NUMBER() over (partition by b) as b | FROM MyTable """.stripMargin - util.verifyExecPlan(sqlQuery) + + assertThatExceptionOfType(classOf[RuntimeException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } - @Test(expected = classOf[ValidationException]) + @Test def testRankWithoutOrderBy(): Unit = { val sqlQuery = """ |SELECT RANK() over (partition by a) FROM MyTable """.stripMargin - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } - @Test(expected = classOf[ValidationException]) + @Test def testRankWithMultiGroups(): Unit = { val sqlQuery = """ @@ -87,19 +90,21 @@ class RankTest extends TableTestBase { | RANK() over (partition by b) as b | FROM MyTable """.stripMargin - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } - @Test(expected = classOf[ValidationException]) + @Test def testDenseRankWithoutOrderBy(): Unit = { val sqlQuery = """ |SELECT dense_rank() over (partition by a) FROM MyTable """.stripMargin - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } - @Test(expected = classOf[ValidationException]) + @Test def testDenseRankWithMultiGroups(): Unit = { val sqlQuery = """ @@ -107,7 +112,8 @@ class RankTest extends TableTestBase { | DENSE_RANK() over (partition by b) as b | FROM MyTable """.stripMargin - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/RemoveCollationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/RemoveCollationTest.scala index 1e803359eec05..1abdd4e80074b 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/RemoveCollationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/RemoveCollationTest.scala @@ -29,13 +29,13 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedTableFunction import org.apache.flink.table.planner.utils.{TableFunc1, TableTestBase} import com.google.common.collect.ImmutableSet -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} class RemoveCollationTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource( "x", diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/RemoveShuffleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/RemoveShuffleTest.scala index 66839e280d941..c65f341a29a77 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/RemoveShuffleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/RemoveShuffleTest.scala @@ -26,13 +26,13 @@ import org.apache.flink.table.planner.plan.rules.physical.batch.{BatchPhysicalJo import org.apache.flink.table.planner.plan.stats.FlinkStatistic import org.apache.flink.table.planner.utils.{TableFunc1, TableTestBase} -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} class RemoveShuffleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource( "x", diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SetOperatorsTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SetOperatorsTest.scala index eaa5998d73c5e..943e6c766e01b 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SetOperatorsTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SetOperatorsTest.scala @@ -25,13 +25,14 @@ import org.apache.flink.table.api.config.ExecutionConfigOptions import org.apache.flink.table.planner.plan.utils.NonPojo import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.{BeforeEach, Test} class SetOperatorsTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig.set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "SortAgg") util.addTableSource[(Int, Long, String)]("T1", 'a, 'b, 'c) @@ -39,16 +40,19 @@ class SetOperatorsTest extends TableTestBase { util.addTableSource[(Int, Long, Int, String, Long)]("T3", 'a, 'b, 'd, 'c, 'e) } - @Test(expected = classOf[ValidationException]) + @Test def testUnionDifferentColumnSize(): Unit = { // must fail. Union inputs have different column size. - util.verifyExecPlan("SELECT * FROM T1 UNION ALL SELECT * FROM T3") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan("SELECT * FROM T1 UNION ALL SELECT * FROM T3")) } - @Test(expected = classOf[ValidationException]) + @Test def testUnionDifferentFieldTypes(): Unit = { // must fail. Union inputs have different field types. - util.verifyExecPlan("SELECT a, b, c FROM T1 UNION ALL SELECT d, c, e FROM T3") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => util.verifyExecPlan("SELECT a, b, c FROM T1 UNION ALL SELECT d, c, e FROM T3")) } @Test @@ -56,10 +60,12 @@ class SetOperatorsTest extends TableTestBase { util.verifyExecPlan("SELECT c FROM T1 INTERSECT ALL SELECT f FROM T2") } - @Test(expected = classOf[ValidationException]) + @Test def testIntersectDifferentFieldTypes(): Unit = { // must fail. Intersect inputs have different field types. - util.verifyExecPlan("SELECT a, b, c FROM T1 INTERSECT SELECT d, c, e FROM T3") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => util.verifyExecPlan("SELECT a, b, c FROM T1 INTERSECT SELECT d, c, e FROM T3")) } @Test @@ -67,10 +73,11 @@ class SetOperatorsTest extends TableTestBase { util.verifyExecPlan("SELECT c FROM T1 EXCEPT ALL SELECT f FROM T2") } - @Test(expected = classOf[ValidationException]) + @Test def testMinusDifferentFieldTypes(): Unit = { // must fail. Minus inputs have different field types. - util.verifyExecPlan("SELECT a, b, c FROM T1 EXCEPT SELECT d, c, e FROM T3") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan("SELECT a, b, c FROM T1 EXCEPT SELECT d, c, e FROM T3")) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SortLimitTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SortLimitTest.scala index e3027e4e83ea4..924417803693c 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SortLimitTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SortLimitTest.scala @@ -23,7 +23,7 @@ import org.apache.flink.table.api.config.ExecutionConfigOptions import org.apache.flink.table.planner.plan.rules.physical.batch.BatchPhysicalSortRule.TABLE_EXEC_RANGE_SORT_ENABLED import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test class SortLimitTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SortTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SortTest.scala index 9722d139d963e..32149d42f9c29 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SortTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SortTest.scala @@ -23,7 +23,7 @@ import org.apache.flink.table.api.config.ExecutionConfigOptions import org.apache.flink.table.planner.plan.rules.physical.batch.BatchPhysicalSortRule.TABLE_EXEC_RANGE_SORT_ENABLED import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test class SortTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SubplanReuseTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SubplanReuseTest.scala index 4bda75a5b3fec..5718002822f01 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SubplanReuseTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/SubplanReuseTest.scala @@ -28,13 +28,13 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedTableFunction import org.apache.flink.table.planner.utils.TableTestBase import org.apache.flink.table.runtime.functions.aggregate.FirstValueAggFunction -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} class SubplanReuseTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_REUSE_SUB_PLAN_ENABLED, Boolean.box(true)) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/TableScanTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/TableScanTest.scala index 932f9153583fd..2b2e658cb3ac4 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/TableScanTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/TableScanTest.scala @@ -18,18 +18,18 @@ package org.apache.flink.table.planner.plan.batch.sql import org.apache.flink.api.scala._ -import org.apache.flink.table.api import org.apache.flink.table.api._ import org.apache.flink.table.planner.expressions.utils.Func0 import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.{BeforeEach, Test} class TableScanTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def before(): Unit = { util.tableEnv.registerFunction("my_udf", Func0) @@ -96,11 +96,12 @@ class TableScanTest extends TableTestBase { | 'bounded' = 'false' |) """.stripMargin) - thrown.expect(classOf[ValidationException]) - thrown.expectMessage( - "Querying an unbounded table 'default_catalog.default_database.src' in batch mode is not " + - "allowed. The table source is unbounded.") - util.verifyExecPlan("SELECT * FROM src WHERE a > 1") + + assertThatThrownBy(() => util.verifyExecPlan("SELECT * FROM src WHERE a > 1")) + .hasMessageContaining( + "Querying an unbounded table 'default_catalog.default_database.src' in batch mode is not " + + "allowed. The table source is unbounded.") + .isInstanceOf[ValidationException] } @Test @@ -116,12 +117,13 @@ class TableScanTest extends TableTestBase { | 'changelog-mode' = 'I,UA,UB' |) """.stripMargin) - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "Querying a table in batch mode is currently only possible for INSERT-only table sources. " + - "But the source for table 'default_catalog.default_database.src' produces other changelog " + - "messages than just INSERT.") - util.verifyExecPlan("SELECT * FROM src WHERE a > 1") + + assertThatThrownBy(() => util.verifyExecPlan("SELECT * FROM src WHERE a > 1")) + .hasMessageContaining( + "Querying a table in batch mode is currently only possible for INSERT-only table sources. " + + "But the source for table 'default_catalog.default_database.src' produces other changelog " + + "messages than just INSERT.") + .isInstanceOf[TableException] } @Test @@ -138,12 +140,13 @@ class TableScanTest extends TableTestBase { | 'changelog-mode' = 'UA,D' |) """.stripMargin) - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "Querying a table in batch mode is currently only possible for INSERT-only table sources. " + - "But the source for table 'default_catalog.default_database.src' produces other changelog " + - "messages than just INSERT.") - util.verifyExecPlan("SELECT * FROM src WHERE a > 1") + + assertThatThrownBy(() => util.verifyExecPlan("SELECT * FROM src WHERE a > 1")) + .hasMessageContaining( + "Querying a table in batch mode is currently only possible for INSERT-only table sources. " + + "But the source for table 'default_catalog.default_database.src' produces other changelog " + + "messages than just INSERT.") + .isInstanceOf[TableException] } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/TableSinkTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/TableSinkTest.scala index 49a8190cb282c..3a950be62bfd5 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/TableSinkTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/TableSinkTest.scala @@ -19,15 +19,11 @@ package org.apache.flink.table.planner.plan.batch.sql import org.apache.flink.api.scala._ import org.apache.flink.table.api._ -import org.apache.flink.table.api.config.TableConfigOptions -import org.apache.flink.table.planner.factories.TestValuesTableFactory import org.apache.flink.table.planner.plan.optimize.RelNodeBlockPlanBuilder -import org.apache.flink.table.planner.runtime.utils.BatchAbstractTestBase -import org.apache.flink.table.planner.runtime.utils.TestData.smallData3 -import org.apache.flink.table.planner.utils.{TableTestBase, TableTestUtil} +import org.apache.flink.table.planner.utils.TableTestBase import org.apache.flink.table.types.logical.{BigIntType, IntType} -import org.junit.{Assert, Test} +import org.junit.jupiter.api.Test class TableSinkTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/TableSourceTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/TableSourceTest.scala index 65ccd9b75c244..418258d0a331e 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/TableSourceTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/TableSourceTest.scala @@ -20,13 +20,13 @@ package org.apache.flink.table.planner.plan.batch.sql import org.apache.flink.table.planner.plan.optimize.RelNodeBlockPlanBuilder import org.apache.flink.table.planner.utils._ -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} class TableSourceTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val ddl = s""" diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/UnionTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/UnionTest.scala index 1646da14c530a..f3b7bce440982 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/UnionTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/UnionTest.scala @@ -21,14 +21,14 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} // TODO add more union case after aggregation and join supported class UnionTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def before(): Unit = { util.addTableSource[(Int, Long, String)]("MyTable1", 'a, 'b, 'c) util.addTableSource[(Int, Long, String)]("MyTable2", 'a, 'b, 'c) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/ValuesTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/ValuesTest.scala index f8e57bb005ae3..6bcaedd89bc94 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/ValuesTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/ValuesTest.scala @@ -19,7 +19,7 @@ package org.apache.flink.table.planner.plan.batch.sql import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test class ValuesTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/WindowTableFunctionTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/WindowTableFunctionTest.scala index d7565eedb92d2..b8c67d9f63d9f 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/WindowTableFunctionTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/WindowTableFunctionTest.scala @@ -21,7 +21,8 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.{BeforeEach, Test} import java.sql.Timestamp @@ -29,7 +30,7 @@ class WindowTableFunctionTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def before(): Unit = { util.addTableSource[(Timestamp, Long, Int, String)]("MyTable", 'ts, 'a, 'b, 'c) util.addTableSource[(Int, Long, String, Int, Timestamp)]("MyTable1", 'a, 'b, 'c, 'd, 'ts) @@ -51,12 +52,12 @@ class WindowTableFunctionTest extends TableTestBase { |SELECT * |FROM TABLE(TUMBLE(TABLE MyTable1, DESCRIPTOR(b), INTERVAL '15' MINUTE)) |""".stripMargin - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "The window function TUMBLE(TABLE table_name, DESCRIPTOR(timecol), datetime interval" - + "[, datetime interval]) requires the timecol to be TIMESTAMP or TIMESTAMP_LTZ, " - + "but is BIGINT.") - util.verifyExplain(sql) + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining( + "The window function TUMBLE(TABLE table_name, DESCRIPTOR(timecol), datetime interval" + + "[, datetime interval]) requires the timecol to be TIMESTAMP or TIMESTAMP_LTZ, " + + "but is BIGINT.") + .isInstanceOf[ValidationException] } @Test @@ -76,9 +77,10 @@ class WindowTableFunctionTest extends TableTestBase { |SELECT * |FROM TABLE(TUMBLE(TABLE MyTable2, DESCRIPTOR(c), INTERVAL '15' MINUTE)) |""".stripMargin - expectedException.expect(classOf[TableException]) - expectedException.expectMessage("Processing time Window TableFunction is not supported yet.") - util.verifyExplain(sql) + + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("Processing time Window TableFunction is not supported yet.") + .isInstanceOf[TableException] } @Test @@ -98,9 +100,10 @@ class WindowTableFunctionTest extends TableTestBase { |SELECT * |FROM TABLE(HOP(TABLE MyTable2, DESCRIPTOR(c), INTERVAL '1' HOUR, INTERVAL '2' HOUR)) |""".stripMargin - expectedException.expect(classOf[TableException]) - expectedException.expectMessage("Processing time Window TableFunction is not supported yet.") - util.verifyExplain(sql) + + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("Processing time Window TableFunction is not supported yet.") + .isInstanceOf[TableException] } @Test @@ -122,9 +125,10 @@ class WindowTableFunctionTest extends TableTestBase { |FROM TABLE( | CUMULATE(TABLE MyTable2, DESCRIPTOR(c), INTERVAL '10' MINUTE, INTERVAL '1' HOUR)) |""".stripMargin - expectedException.expect(classOf[TableException]) - expectedException.expectMessage("Processing time Window TableFunction is not supported yet.") - util.verifyExplain(sql) + + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("Processing time Window TableFunction is not supported yet.") + .isInstanceOf[TableException] } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/AggregateTestBase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/AggregateTestBase.scala index a4d431d35f318..3b3d092d94411 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/AggregateTestBase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/AggregateTestBase.scala @@ -24,7 +24,8 @@ import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.{Va import org.apache.flink.table.planner.utils.{BatchTableTestUtil, TableTestBase} import org.apache.flink.table.runtime.typeutils.DecimalDataTypeInfo -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.TestTemplate abstract class AggregateTestBase extends TableTestBase { @@ -63,7 +64,7 @@ abstract class AggregateTestBase extends TableTestBase { ) util.addTableSource[(Int, Long, String)]("MyTable1", 'a, 'b, 'c) - @Test + @TestTemplate def testAvg(): Unit = { util.verifyRelPlanWithType(""" |SELECT AVG(`byte`), @@ -78,7 +79,7 @@ abstract class AggregateTestBase extends TableTestBase { """.stripMargin) } - @Test + @TestTemplate def testSum(): Unit = { util.verifyRelPlanWithType(""" |SELECT SUM(`byte`), @@ -93,7 +94,7 @@ abstract class AggregateTestBase extends TableTestBase { """.stripMargin) } - @Test + @TestTemplate def testCount(): Unit = { util.verifyRelPlanWithType(""" |SELECT COUNT(`byte`), @@ -113,12 +114,12 @@ abstract class AggregateTestBase extends TableTestBase { """.stripMargin) } - @Test + @TestTemplate def testCountStart(): Unit = { util.verifyRelPlanWithType("SELECT COUNT(*) FROM MyTable") } - @Test + @TestTemplate def testCountStartWithProjectPushDown(): Unit = { // the test values table source supports projection push down by default util.tableEnv.executeSql(""" @@ -133,15 +134,16 @@ abstract class AggregateTestBase extends TableTestBase { util.verifyRelPlanWithType("SELECT COUNT(*) FROM src") } - @Test + @TestTemplate def testCannotCountOnMultiFields(): Unit = { val sql = "SELECT b, COUNT(a, c) FROM MyTable1 GROUP BY b" - thrown.expect(classOf[TableException]) - thrown.expectMessage("We now only support the count of one field") - util.verifyExecPlan(sql) + + assertThatThrownBy(() => util.verifyExecPlan(sql)) + .hasMessageContaining("We now only support the count of one field") + .isInstanceOf[TableException] } - @Test + @TestTemplate def testMinWithFixLengthType(): Unit = { util.verifyRelPlanWithType(""" |SELECT MIN(`byte`), @@ -160,12 +162,12 @@ abstract class AggregateTestBase extends TableTestBase { """.stripMargin) } - @Test + @TestTemplate def testMinWithVariableLengthType(): Unit = { util.verifyRelPlanWithType("SELECT MIN(`string`) FROM MyTable") } - @Test + @TestTemplate def testMaxWithFixLengthType(): Unit = { util.verifyRelPlanWithType(""" |SELECT MAX(`byte`), @@ -184,55 +186,55 @@ abstract class AggregateTestBase extends TableTestBase { """.stripMargin) } - @Test + @TestTemplate def testMaxWithVariableLengthType(): Unit = { util.verifyRelPlanWithType("SELECT MAX(`string`) FROM MyTable") } - @Test + @TestTemplate def testAggregateWithoutFunction(): Unit = { util.verifyExecPlan("SELECT a, b FROM MyTable1 GROUP BY a, b") } - @Test + @TestTemplate def testAggregateWithoutGroupBy(): Unit = { util.verifyExecPlan("SELECT AVG(a), SUM(b), COUNT(c) FROM MyTable1") } - @Test + @TestTemplate def testAggregateWithFilter(): Unit = { util.verifyExecPlan("SELECT AVG(a), SUM(b), COUNT(c) FROM MyTable1 WHERE a = 1") } - @Test + @TestTemplate def testAggregateWithFilterOnNestedFields(): Unit = { util.addTableSource[(Int, Long, (Int, Long))]("MyTable2", 'a, 'b, 'c) util.verifyExecPlan("SELECT AVG(a), SUM(b), COUNT(c), SUM(c._1) FROM MyTable2 WHERE a = 1") } - @Test + @TestTemplate def testGroupAggregate(): Unit = { util.verifyExecPlan("SELECT a, SUM(b), COUNT(c) FROM MyTable1 GROUP BY a") } - @Test + @TestTemplate def testGroupAggregateWithFilter(): Unit = { util.verifyExecPlan("SELECT a, SUM(b), count(c) FROM MyTable1 WHERE a = 1 GROUP BY a") } - @Test + @TestTemplate def testAggNotSupportMerge(): Unit = { util.addFunction("var_sum", new VarSum2AggFunction) util.verifyExecPlan("SELECT b, var_sum(a) FROM MyTable1 GROUP BY b") } - @Test + @TestTemplate def testPojoAccumulator(): Unit = { util.addFunction("var_sum", new VarSum1AggFunction) util.verifyExecPlan("SELECT b, var_sum(a) FROM MyTable1 GROUP BY b") } - @Test + @TestTemplate def testGroupByWithConstantKey(): Unit = { val sql = """ diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/GroupWindowTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/GroupWindowTest.scala index d0f8720a1ff67..c6dcac1a54bcb 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/GroupWindowTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/GroupWindowTest.scala @@ -22,22 +22,23 @@ import org.apache.flink.table.api._ import org.apache.flink.table.api.config.OptimizerConfigOptions import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedAggFunctions.WeightedAvgWithMerge import org.apache.flink.table.planner.utils.{AggregatePhaseStrategy, CountAggFunction, TableTestBase} +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.junit.{Before, Test} -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.assertj.core.api.Assertions.{assertThatExceptionOfType, assertThatThrownBy} +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith import java.sql.Timestamp import java.util import scala.collection.JavaConversions._ -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_AGG_PHASE_STRATEGY, aggStrategy.toString) @@ -56,59 +57,61 @@ class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase |""".stripMargin) } - @Test + @TestTemplate def testHopWindowNoOffset(): Unit = { val sqlQuery = "SELECT SUM(a) AS sumA, COUNT(b) AS cntB FROM MyTable2 " + "GROUP BY HOP(ts, INTERVAL '1' HOUR, INTERVAL '2' HOUR, TIME '10:00:00')" - expectedException.expect(classOf[TableException]) - expectedException.expectMessage("HOP window with alignment is not supported yet.") - util.verifyExecPlan(sqlQuery) + assertThatThrownBy(() => util.verifyExecPlan(sqlQuery)) + .hasMessageContaining("HOP window with alignment is not supported yet.") + .isInstanceOf[TableException] } - @Test + @TestTemplate def testSessionWindowNoOffset(): Unit = { val sqlQuery = "SELECT SUM(a) AS sumA, COUNT(b) AS cntB FROM MyTable2 " + "GROUP BY SESSION(ts, INTERVAL '2' HOUR, TIME '10:00:00')" - expectedException.expect(classOf[TableException]) - expectedException.expectMessage("SESSION window with alignment is not supported yet.") - util.verifyExecPlan(sqlQuery) + assertThatThrownBy(() => util.verifyExecPlan(sqlQuery)) + .hasMessageContaining("SESSION window with alignment is not supported yet.") + .isInstanceOf[TableException] } - @Test + @TestTemplate def testVariableWindowSize(): Unit = { - expectedException.expect(classOf[TableException]) - expectedException.expectMessage("Only constant window descriptors are supported") - util.verifyExecPlan( - "SELECT COUNT(*) FROM MyTable2 GROUP BY TUMBLE(ts, b * INTERVAL '1' MINUTE)") + assertThatThrownBy( + () => + util.verifyExecPlan( + "SELECT COUNT(*) FROM MyTable2 GROUP BY TUMBLE(ts, b * INTERVAL '1' MINUTE)")) + .hasMessageContaining("Only constant window descriptors are supported") + .isInstanceOf[TableException] } - @Test + @TestTemplate def testTumbleWindowWithInvalidUdAggArgs(): Unit = { val weightedAvg = new WeightedAvgWithMerge util.addFunction("weightedAvg", weightedAvg) val sql = "SELECT weightedAvg(c, a) AS wAvg FROM MyTable2 " + "GROUP BY TUMBLE(ts, INTERVAL '4' MINUTE)" - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "SQL validation failed. " + + assertThatThrownBy(() => util.verifyExecPlan(sql)) + .hasMessageContaining("SQL validation failed. " + "Given parameters of function 'weightedAvg' do not match any signature.") - util.verifyExecPlan(sql) + .isInstanceOf[ValidationException] } - @Test + @TestTemplate def testWindowProctime(): Unit = { val sqlQuery = "SELECT TUMBLE_PROCTIME(ts, INTERVAL '4' MINUTE) FROM MyTable2 " + "GROUP BY TUMBLE(ts, INTERVAL '4' MINUTE), c" - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("PROCTIME window property is not supported in batch queries.") - util.verifyExecPlan(sqlQuery) + assertThatThrownBy(() => util.verifyExecPlan(sqlQuery)) + .hasMessageContaining("PROCTIME window property is not supported in batch queries.") + .isInstanceOf[ValidationException] } - @Test(expected = classOf[AssertionError]) + @TestTemplate def testWindowAggWithGroupSets(): Unit = { // TODO supports group sets // currently, the optimized plan is not collect, and an exception will be thrown in code-gen @@ -119,48 +122,50 @@ class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase |FROM MyTable1 | GROUP BY rollup(TUMBLE(ts, INTERVAL '15' MINUTE), b) """.stripMargin - util.verifyRelPlanNotExpected(sql, "TUMBLE(ts") + + assertThatExceptionOfType(classOf[AssertionError]) + .isThrownBy(() => util.verifyRelPlanNotExpected(sql, "TUMBLE(ts")) } - @Test + @TestTemplate def testNoGroupingTumblingWindow(): Unit = { val sqlQuery = "SELECT AVG(c), SUM(a) FROM MyTable GROUP BY TUMBLE(b, INTERVAL '3' SECOND)" util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testTumblingWindowSortAgg1(): Unit = { val sqlQuery = "SELECT MAX(c) FROM MyTable1 GROUP BY a, TUMBLE(ts, INTERVAL '3' SECOND)" util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testTumblingWindowSortAgg2(): Unit = { val sqlQuery = "SELECT AVG(c), countFun(a) FROM MyTable " + "GROUP BY a, d, TUMBLE(b, INTERVAL '3' SECOND)" util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testTumblingWindowHashAgg1(): Unit = { val sqlQuery = "SELECT COUNT(c) FROM MyTable1 GROUP BY a, TUMBLE(ts, INTERVAL '3' SECOND)" util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testTumblingWindowHashAgg2(): Unit = { val sql = "SELECT AVG(c), COUNT(a) FROM MyTable GROUP BY a, d, TUMBLE(b, INTERVAL '3' SECOND)" util.verifyExecPlan(sql) } - @Test + @TestTemplate def testNonPartitionedTumblingWindow(): Unit = { val sqlQuery = "SELECT SUM(a) AS sumA, COUNT(b) AS cntB FROM MyTable2 GROUP BY TUMBLE(ts, INTERVAL '2' HOUR)" util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testPartitionedTumblingWindow(): Unit = { val sqlQuery = """ @@ -176,7 +181,7 @@ class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testTumblingWindowWithUdAgg(): Unit = { util.addFunction("weightedAvg", new WeightedAvgWithMerge) val sql = "SELECT weightedAvg(b, a) AS wAvg FROM MyTable2 " + @@ -184,16 +189,16 @@ class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase util.verifyExecPlan(sql) } - @Test + @TestTemplate def testTumblingWindowWithProctime(): Unit = { val sql = "select sum(a), max(b) from MyTable3 group by TUMBLE(c, INTERVAL '1' SECOND)" - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Window can not be defined over a proctime attribute column for batch mode") - util.verifyExecPlan(sql) + assertThatThrownBy(() => util.verifyExecPlan(sql)) + .hasMessageContaining( + "Window can not be defined over a proctime attribute column for batch mode") + .isInstanceOf[ValidationException] } - @Test + @TestTemplate def testNoGroupingSlidingWindow(): Unit = { val sqlQuery = """ @@ -206,42 +211,42 @@ class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testSlidingWindowSortAgg1(): Unit = { val sqlQuery = "SELECT MAX(c) FROM MyTable1 " + "GROUP BY a, HOP(ts, INTERVAL '3' SECOND, INTERVAL '1' HOUR)" util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testSlidingWindowSortAgg2(): Unit = { val sqlQuery = "SELECT MAX(c) FROM MyTable1 " + "GROUP BY b, HOP(ts, INTERVAL '0.111' SECOND(1,3), INTERVAL '1' SECOND)" util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testSlidingWindowSortAgg3(): Unit = { val sqlQuery = "SELECT countFun(c) FROM MyTable " + " GROUP BY a, d, HOP(b, INTERVAL '3' SECOND, INTERVAL '1' HOUR)" util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testSlidingWindowSortAggWithPaneOptimization(): Unit = { val sqlQuery = "SELECT COUNT(c) FROM MyTable1 " + "GROUP BY a, HOP(ts, INTERVAL '3' SECOND, INTERVAL '1' HOUR)" util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testSlidingWindowHashAgg(): Unit = { val sqlQuery = "SELECT count(c) FROM MyTable1 " + "GROUP BY b, HOP(ts, INTERVAL '3' SECOND, INTERVAL '1' HOUR)" util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testNonPartitionedSlidingWindow(): Unit = { val sqlQuery = "SELECT SUM(a) AS sumA, COUNT(b) AS cntB " + @@ -251,7 +256,7 @@ class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testPartitionedSlidingWindow(): Unit = { val sqlQuery = "SELECT " + @@ -267,7 +272,7 @@ class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testSlidingWindowWithProctime(): Unit = { val sql = s""" @@ -275,22 +280,22 @@ class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase |from MyTable3 |group by HOP(c, INTERVAL '1' SECOND, INTERVAL '1' MINUTE) |""".stripMargin - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Window can not be defined over a proctime attribute column for batch mode") - util.verifyExecPlan(sql) + assertThatThrownBy(() => util.verifyExecPlan(sql)) + .hasMessageContaining("Window can not be defined over a proctime attribute column for " + + "batch mode") + .isInstanceOf[ValidationException] } - @Test + @TestTemplate // TODO session window is not supported now def testNonPartitionedSessionWindow(): Unit = { val sqlQuery = "SELECT COUNT(*) AS cnt FROM MyTable2 GROUP BY SESSION(ts, INTERVAL '30' MINUTE)" - expectedException.expect(classOf[TableException]) - expectedException.expectMessage("Cannot generate a valid execution plan for the given query") - util.verifyExecPlan(sqlQuery) + assertThatThrownBy(() => util.verifyExecPlan(sqlQuery)) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } - @Test + @TestTemplate // TODO session window is not supported now def testPartitionedSessionWindow(): Unit = { val sqlQuery = @@ -304,12 +309,12 @@ class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase |FROM MyTable2 | GROUP BY SESSION(ts, INTERVAL '12' HOUR), c, d """.stripMargin - expectedException.expect(classOf[TableException]) - expectedException.expectMessage("Cannot generate a valid execution plan for the given query") - util.verifyExecPlan(sqlQuery) + assertThatThrownBy(() => util.verifyExecPlan(sqlQuery)) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } - @Test + @TestTemplate def testSessionWindowWithProctime(): Unit = { val sql = s""" @@ -317,13 +322,13 @@ class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase |from MyTable3 |group by SESSION(c, INTERVAL '1' MINUTE) |""".stripMargin - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Window can not be defined over a proctime attribute column for batch mode") - util.verifyExecPlan(sql) + assertThatThrownBy(() => util.verifyExecPlan(sql)) + .hasMessageContaining( + "Window can not be defined over a proctime attribute column for batch mode") + .isInstanceOf[ValidationException] } - @Test + @TestTemplate def testWindowEndOnly(): Unit = { val sqlQuery = "SELECT TUMBLE_END(ts, INTERVAL '4' MINUTE) FROM MyTable2 " + @@ -331,7 +336,7 @@ class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testExpressionOnWindowHavingFunction(): Unit = { val sql = """ @@ -346,7 +351,7 @@ class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase util.verifyExecPlan(sql) } - @Test + @TestTemplate def testDecomposableAggFunctions(): Unit = { val sql = """ @@ -363,7 +368,7 @@ class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase } // TODO: fix the plan regression when FLINK-19668 is fixed. - @Test + @TestTemplate def testReturnTypeInferenceForWindowAgg() = { val sql = @@ -385,7 +390,7 @@ class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase util.verifyExecPlan(sql) } - @Test + @TestTemplate def testWindowAggregateWithDifferentWindows(): Unit = { // This test ensures that the LogicalWindowAggregate node' digest contains the window specs. // This allows the planner to make the distinction between similar aggregations using different @@ -415,7 +420,7 @@ class GroupWindowTest(aggStrategy: AggregatePhaseStrategy) extends TableTestBase object GroupWindowTest { - @Parameterized.Parameters(name = "aggStrategy={0}") + @Parameters(name = "aggStrategy={0}") def parameters(): util.Collection[AggregatePhaseStrategy] = { Seq[AggregatePhaseStrategy]( AggregatePhaseStrategy.AUTO, diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/GroupingSetsTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/GroupingSetsTest.scala index 010c648433d26..55cd258d578d5 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/GroupingSetsTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/GroupingSetsTest.scala @@ -22,8 +22,8 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.utils.FlinkRelOptUtil import org.apache.flink.table.planner.utils.{TableTestBase, TableTestUtil} -import org.junit.Assert.assertEquals -import org.junit.Test +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test import java.sql.Date @@ -501,7 +501,7 @@ class GroupingSetsTest extends TableTestBase { val table2 = util.tableEnv.sqlQuery(sql2) val optimized1 = util.getPlanner.optimize(TableTestUtil.toRelNode(table1)) val optimized2 = util.getPlanner.optimize(TableTestUtil.toRelNode(table2)) - assertEquals(FlinkRelOptUtil.toString(optimized1), FlinkRelOptUtil.toString(optimized2)) + assertThat(FlinkRelOptUtil.toString(optimized2)).isEqualTo(FlinkRelOptUtil.toString(optimized1)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/HashAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/HashAggregateTest.scala index 185b53bcadab2..e53e8845ac791 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/HashAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/HashAggregateTest.scala @@ -21,19 +21,20 @@ import org.apache.flink.table.api.TableException import org.apache.flink.table.api.config.{ExecutionConfigOptions, OptimizerConfigOptions} import org.apache.flink.table.planner.plan.utils.OperatorType import org.apache.flink.table.planner.utils.AggregatePhaseStrategy +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.junit.Before -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith import java.util import scala.collection.JavaConversions._ -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class HashAggregateTest(aggStrategy: AggregatePhaseStrategy) extends AggregateTestBase { - @Before + @BeforeEach def before(): Unit = { // disable sort agg util.tableEnv.getConfig @@ -42,28 +43,31 @@ class HashAggregateTest(aggStrategy: AggregatePhaseStrategy) extends AggregateTe .set(OptimizerConfigOptions.TABLE_OPTIMIZER_AGG_PHASE_STRATEGY, aggStrategy.toString) } + @TestTemplate override def testMinWithVariableLengthType(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testMinWithVariableLengthType() + assertThatThrownBy(() => super.testMinWithVariableLengthType()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } + @TestTemplate override def testMaxWithVariableLengthType(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testMaxWithVariableLengthType() + assertThatThrownBy(() => super.testMaxWithVariableLengthType()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } + @TestTemplate override def testPojoAccumulator(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testPojoAccumulator() + assertThatThrownBy(() => super.testPojoAccumulator()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } } object HashAggregateTest { - @Parameterized.Parameters(name = "aggStrategy={0}") + @Parameters(name = "aggStrategy={0}") def parameters(): util.Collection[AggregatePhaseStrategy] = { Seq[AggregatePhaseStrategy]( AggregatePhaseStrategy.AUTO, diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/OverAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/OverAggregateTest.scala index d0753274fc0c8..e95d40c8c8483 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/OverAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/OverAggregateTest.scala @@ -22,7 +22,8 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedAggFunctions.OverAgg0 import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test import java.sql.Timestamp @@ -216,7 +217,7 @@ class OverAggregateTest extends TableTestBase { util.verifyExecPlan(sqlQuery) } - @Test(expected = classOf[ValidationException]) + @Test def testOverWindowRangeProhibitType(): Unit = { val sqlQuery = """ @@ -224,7 +225,8 @@ class OverAggregateTest extends TableTestBase { | COUNT(*) OVER (PARTITION BY c ORDER BY c RANGE BETWEEN -1 PRECEDING AND 10 FOLLOWING) |FROM MyTable """.stripMargin - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } @Test @@ -253,7 +255,7 @@ class OverAggregateTest extends TableTestBase { util.verifyExecPlan(sqlQuery) } - @Test(expected = classOf[ValidationException]) + @Test def testRowsWindowWithNegative(): Unit = { val sqlQuery = """ @@ -261,7 +263,8 @@ class OverAggregateTest extends TableTestBase { | COUNT(*) OVER (PARTITION BY c ORDER BY a ROWS BETWEEN -1 PRECEDING AND 10 FOLLOWING) |FROM MyTable """.stripMargin - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } @Test @@ -320,7 +323,7 @@ class OverAggregateTest extends TableTestBase { util.verifyExecPlan(sqlQuery) } - @Test(expected = classOf[RuntimeException]) + @Test def testDistinct(): Unit = { val sqlQuery = """ @@ -328,22 +331,27 @@ class OverAggregateTest extends TableTestBase { | OVER (PARTITION BY c ORDER BY a RANGE BETWEEN -1 FOLLOWING AND 10 FOLLOWING) |FROM MyTable """.stripMargin - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[RuntimeException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } /** OVER clause is necessary for [[OverAgg0]] window function. */ - @Test(expected = classOf[ValidationException]) + @Test def testInvalidOverAggregation(): Unit = { util.addFunction("overAgg", new OverAgg0) - util.verifyExecPlan("SELECT overAgg(b, a) FROM MyTable") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan("SELECT overAgg(b, a) FROM MyTable")) } /** OVER clause is necessary for [[OverAgg0]] window function. */ - @Test(expected = classOf[ValidationException]) + @Test def testInvalidOverAggregation2(): Unit = { util.addTableSource[(Int, Long, String, Timestamp)]("T", 'a, 'b, 'c, 'ts) util.addFunction("overAgg", new OverAgg0) - util.verifyExecPlan("SELECT overAgg(b, a) FROM T GROUP BY TUMBLE(ts, INTERVAL '2' HOUR)") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + util.verifyExecPlan("SELECT overAgg(b, a) FROM T GROUP BY TUMBLE(ts, INTERVAL '2' HOUR)")) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/SortAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/SortAggregateTest.scala index c1fb17d3493f9..86dfdd18b8dab 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/SortAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/agg/SortAggregateTest.scala @@ -20,19 +20,19 @@ package org.apache.flink.table.planner.plan.batch.sql.agg import org.apache.flink.table.api.config.{ExecutionConfigOptions, OptimizerConfigOptions} import org.apache.flink.table.planner.plan.utils.OperatorType import org.apache.flink.table.planner.utils.AggregatePhaseStrategy +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.junit.{Before, Test} -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith import java.util import scala.collection.JavaConversions._ -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class SortAggregateTest(aggStrategy: AggregatePhaseStrategy) extends AggregateTestBase { - @Before + @BeforeEach def before(): Unit = { // disable hash agg util.tableEnv.getConfig @@ -41,7 +41,7 @@ class SortAggregateTest(aggStrategy: AggregatePhaseStrategy) extends AggregateTe .set(OptimizerConfigOptions.TABLE_OPTIMIZER_AGG_PHASE_STRATEGY, aggStrategy.toString) } - @Test + @TestTemplate def testApproximateCountDistinct(): Unit = { val sql = """ @@ -66,7 +66,7 @@ class SortAggregateTest(aggStrategy: AggregatePhaseStrategy) extends AggregateTe object SortAggregateTest { - @Parameterized.Parameters(name = "aggStrategy={0}") + @Parameters(name = "aggStrategy={0}") def parameters(): util.Collection[AggregatePhaseStrategy] = { Seq[AggregatePhaseStrategy]( AggregatePhaseStrategy.AUTO, diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/BroadcastHashJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/BroadcastHashJoinTest.scala index bab7c14257fb8..0f3c494ee5e6a 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/BroadcastHashJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/BroadcastHashJoinTest.scala @@ -20,11 +20,12 @@ package org.apache.flink.table.planner.plan.batch.sql.join import org.apache.flink.table.api.TableException import org.apache.flink.table.api.config.{ExecutionConfigOptions, OptimizerConfigOptions} -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.{BeforeEach, Test} class BroadcastHashJoinTest extends JoinTestBase { - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_BROADCAST_JOIN_THRESHOLD, Long.box(Long.MaxValue)) @@ -35,106 +36,106 @@ class BroadcastHashJoinTest extends JoinTestBase { @Test override def testInnerJoinWithoutJoinPred(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testInnerJoinWithoutJoinPred() + assertThatThrownBy(() => super.testInnerJoinWithoutJoinPred()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testLeftOuterJoinNoEquiPred(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testLeftOuterJoinNoEquiPred() + assertThatThrownBy(() => super.testLeftOuterJoinNoEquiPred()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testLeftOuterJoinOnTrue(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testLeftOuterJoinOnTrue() + assertThatThrownBy(() => super.testLeftOuterJoinOnTrue()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testLeftOuterJoinOnFalse(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testLeftOuterJoinOnFalse() + assertThatThrownBy(() => super.testLeftOuterJoinOnFalse()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testRightOuterJoinOnTrue(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testRightOuterJoinOnTrue() + assertThatThrownBy(() => super.testRightOuterJoinOnTrue()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testRightOuterJoinOnFalse(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testRightOuterJoinOnFalse() + assertThatThrownBy(() => super.testRightOuterJoinOnFalse()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testRightOuterJoinWithNonEquiPred(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testRightOuterJoinWithNonEquiPred() + assertThatThrownBy(() => super.testRightOuterJoinWithNonEquiPred()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testFullOuterJoinWithEquiPred(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testFullOuterJoinWithEquiPred() + assertThatThrownBy(() => super.testFullOuterJoinWithEquiPred()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testFullOuterJoinWithEquiAndLocalPred(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testFullOuterJoinWithEquiAndLocalPred() + assertThatThrownBy(() => super.testFullOuterJoinWithEquiAndLocalPred()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testFullOuterJoinWithEquiAndNonEquiPred(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testFullOuterJoinWithEquiAndNonEquiPred() + assertThatThrownBy(() => super.testFullOuterJoinWithEquiAndNonEquiPred()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testFullOuterJoinWithNonEquiPred(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testFullOuterJoinWithNonEquiPred() + assertThatThrownBy(() => super.testFullOuterJoinWithNonEquiPred()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testFullOuterJoinOnFalse(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testFullOuterJoinOnFalse() + assertThatThrownBy(() => super.testFullOuterJoinOnFalse()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testFullOuterWithUsing(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testFullOuterWithUsing() + assertThatThrownBy(() => super.testFullOuterWithUsing()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testFullOuterJoinOnTrue(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testFullOuterJoinOnTrue() + assertThatThrownBy(() => super.testFullOuterJoinOnTrue()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testCrossJoin(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testCrossJoin() + assertThatThrownBy(() => super.testCrossJoin()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/BroadcastHashSemiAntiJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/BroadcastHashSemiAntiJoinTest.scala index 8eef862af55c1..bae55c7b394b1 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/BroadcastHashSemiAntiJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/BroadcastHashSemiAntiJoinTest.scala @@ -20,11 +20,12 @@ package org.apache.flink.table.planner.plan.batch.sql.join import org.apache.flink.table.api.TableException import org.apache.flink.table.api.config.{ExecutionConfigOptions, OptimizerConfigOptions} -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.{BeforeEach, Test} class BroadcastHashSemiAntiJoinTest extends SemiAntiJoinTestBase { - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_BROADCAST_JOIN_THRESHOLD, Long.box(Long.MaxValue)) @@ -39,141 +40,141 @@ class BroadcastHashSemiAntiJoinTest extends SemiAntiJoinTestBase { // because NestedLoopJoin(non-singleRowJoin) is disabled. @Test override def testNotInWithCorrelated_NonEquiCondition1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithCorrelated_NonEquiCondition1() + assertThatThrownBy(() => super.testNotInWithCorrelated_NonEquiCondition1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithCorrelated_NonEquiCondition2(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithCorrelated_NonEquiCondition2() + assertThatThrownBy(() => super.testNotInWithCorrelated_NonEquiCondition2()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testInNotInExistsNotExists(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testInNotInExistsNotExists() + assertThatThrownBy(() => super.testInNotInExistsNotExists()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testInWithUncorrelated_ComplexCondition3(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testInWithUncorrelated_ComplexCondition3() + assertThatThrownBy(() => super.testInWithUncorrelated_ComplexCondition3()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testExistsWithUncorrelated_JoinInSubQuery(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testExistsWithUncorrelated_JoinInSubQuery() + assertThatThrownBy(() => super.testExistsWithUncorrelated_JoinInSubQuery()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotExistsWithCorrelated_NonEquiCondition1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotExistsWithCorrelated_NonEquiCondition1() + assertThatThrownBy(() => super.testNotExistsWithCorrelated_NonEquiCondition1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotExistsWithCorrelated_NonEquiCondition2(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotExistsWithCorrelated_NonEquiCondition2() + assertThatThrownBy(() => super.testNotExistsWithCorrelated_NonEquiCondition2()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_ComplexCondition1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_ComplexCondition1() + assertThatThrownBy(() => super.testNotInWithUncorrelated_ComplexCondition1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_ComplexCondition2(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_ComplexCondition2() + assertThatThrownBy(() => super.testNotInWithUncorrelated_ComplexCondition2()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_ComplexCondition3(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_ComplexCondition3() + assertThatThrownBy(() => super.testNotInWithUncorrelated_ComplexCondition3()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testExistsWithCorrelated_NonEquiCondition1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testExistsWithCorrelated_NonEquiCondition1() + assertThatThrownBy(() => super.testExistsWithCorrelated_NonEquiCondition1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testExistsWithCorrelated_NonEquiCondition2(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testExistsWithCorrelated_NonEquiCondition2() + assertThatThrownBy(() => super.testExistsWithCorrelated_NonEquiCondition2()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testMultiExistsWithCorrelate1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testMultiExistsWithCorrelate1() + assertThatThrownBy(() => super.testMultiExistsWithCorrelate1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_MultiFields(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_MultiFields() + assertThatThrownBy(() => super.testNotInWithUncorrelated_MultiFields()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testMultiNotInWithCorrelated(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testMultiNotInWithCorrelated() + assertThatThrownBy(() => super.testMultiNotInWithCorrelated()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testInWithCorrelated_ComplexCondition3(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testInWithCorrelated_ComplexCondition3() + assertThatThrownBy(() => super.testInWithCorrelated_ComplexCondition3()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_SimpleCondition1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_SimpleCondition1() + assertThatThrownBy(() => super.testNotInWithUncorrelated_SimpleCondition1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_SimpleCondition2(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_SimpleCondition2() + assertThatThrownBy(() => super.testNotInWithUncorrelated_SimpleCondition2()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_SimpleCondition3(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_SimpleCondition3() + assertThatThrownBy(() => super.testNotInWithUncorrelated_SimpleCondition3()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testInWithUncorrelated_JoinInSubQuery(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testInWithUncorrelated_JoinInSubQuery() + assertThatThrownBy(() => super.testInWithUncorrelated_JoinInSubQuery()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/JoinReorderTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/JoinReorderTest.scala index 8b00f7112ebff..2649202ed7d3b 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/JoinReorderTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/JoinReorderTest.scala @@ -19,12 +19,12 @@ package org.apache.flink.table.planner.plan.batch.sql.join import org.apache.flink.table.planner.plan.common.JoinReorderTestBase import org.apache.flink.table.planner.utils.TableTestUtil +import org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.junit.jupiter.api.extension.ExtendWith /** The plan test for join reorder in batch mode. */ -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class JoinReorderTest(isBushyJoinReorder: Boolean) extends JoinReorderTestBase(isBushyJoinReorder) { override protected def getTableTestUtil: TableTestUtil = batchTestUtil() } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/JoinTestBase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/JoinTestBase.scala index 4f412e21d2580..f4c0301d13a7b 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/JoinTestBase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/JoinTestBase.scala @@ -21,7 +21,8 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.{BatchTableTestUtil, TableTestBase} -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test abstract class JoinTestBase extends TableTestBase { @@ -29,9 +30,10 @@ abstract class JoinTestBase extends TableTestBase { util.addTableSource[(Int, Long, String)]("MyTable1", 'a, 'b, 'c) util.addTableSource[(Int, Long, Int, String, Long)]("MyTable2", 'd, 'e, 'f, 'g, 'h) - @Test(expected = classOf[ValidationException]) + @Test def testJoinNonExistingKey(): Unit = { - util.verifyExecPlan("SELECT c, g FROM MyTable1, MyTable2 WHERE foo = e") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan("SELECT c, g FROM MyTable1, MyTable2 WHERE foo = e")) } @Test @@ -60,16 +62,18 @@ abstract class JoinTestBase extends TableTestBase { util.verifyExecPlan("SELECT d, e, f FROM MyTable1 LEFT JOIN MyTable2 ON a = d where d = null") } - @Test(expected = classOf[TableException]) + @Test def testJoinNonMatchingKeyTypes(): Unit = { // INTEGER and VARCHAR(65536) does not have common type now - util.verifyExecPlan("SELECT c, g FROM MyTable1, MyTable2 WHERE a = g") + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan("SELECT c, g FROM MyTable1, MyTable2 WHERE a = g")) } - @Test(expected = classOf[ValidationException]) + @Test def testJoinWithAmbiguousFields(): Unit = { util.addTableSource[(Int, Long, String)]("MyTable0", 'a0, 'b0, 'c) - util.verifyExecPlan("SELECT a, c FROM MyTable1, MyTable0 WHERE a = a0") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan("SELECT c FROM MyTable1, MyTable0 WHERE a = a0")) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/LookupJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/LookupJoinTest.scala index 080f3654f2563..7121e02d723bb 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/LookupJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/LookupJoinTest.scala @@ -24,24 +24,26 @@ import org.apache.flink.table.planner.plan.optimize.program.FlinkBatchProgram import org.apache.flink.table.planner.plan.stream.sql.join.TestTemporalTable import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctions.PythonScalarFunction import org.apache.flink.table.planner.utils.TableTestBase +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} import _root_.java.lang.{Boolean => JBoolean} import _root_.java.util.{Collection => JCollection} import _root_.scala.collection.JavaConversions._ -import org.junit.{Assume, Before, Test} -import org.junit.Assert.{assertTrue, fail} -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.assertj.core.api.Assertions.{assertThat, assertThatExceptionOfType, assertThatThrownBy, catchThrowable} +import org.assertj.core.api.Assumptions.assumeThat +import org.assertj.core.api.ThrowableAssert.ThrowingCallable +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith /** * The physical plans for legacy [[org.apache.flink.table.sources.LookupableTableSource]] and new * [[org.apache.flink.table.connector.source.LookupTableSource]] should be identical. */ -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase { private val testUtil = batchTestUtil() - @Before + @BeforeEach def before(): Unit = { testUtil.addDataStream[(Int, String, Long)]("T0", 'a, 'b, 'c) testUtil.addDataStream[(Int, String, Long, Double)]("T1", 'a, 'b, 'c, 'd) @@ -76,7 +78,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase { } } - @Test + @TestTemplate def testJoinInvalidJoinTemporalTable(): Unit = { // must follow a period specification expectExceptionThrown( @@ -113,7 +115,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase { ) } - @Test + @TestTemplate def testNotDistinctFromInJoinCondition(): Unit = { // does not support join condition contains `IS NOT DISTINCT` @@ -135,13 +137,8 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase { ) } - @Test + @TestTemplate def testPythonUDFInJoinCondition(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "Only inner join condition with equality predicates supports the " + - "Python UDF taking the inputs from the left table and the right table at the same time, " + - "e.g., ON T1.id = T2.id && pythonUdf(T1.a, T2.b)") testUtil.addFunction("pyFunc", new PythonScalarFunction("pyFunc")) val sql = """ @@ -149,10 +146,15 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase { |LEFT OUTER JOIN LookupTable FOR SYSTEM_TIME AS OF T.proctime AS D |ON T.a = D.id AND D.age = 10 AND pyFunc(D.age, T.a) > 100 """.stripMargin - testUtil.verifyExecPlan(sql) + + assertThatThrownBy(() => testUtil.verifyExecPlan(sql)) + .hasMessageContaining("Only inner join condition with equality predicates supports the " + + "Python UDF taking the inputs from the left table and the right table at the same time, " + + "e.g., ON T1.id = T2.id && pythonUdf(T1.a, T2.b)") + .isInstanceOf[TableException] } - @Test + @TestTemplate def testLogicalPlan(): Unit = { val sql1 = """ @@ -181,37 +183,37 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase { testUtil.verifyRelPlan(sql) } - @Test + @TestTemplate def testLogicalPlanWithImplicitTypeCast(): Unit = { val programs = FlinkBatchProgram.buildProgram(testUtil.tableEnv.getConfig) programs.remove(FlinkBatchProgram.PHYSICAL) testUtil.replaceBatchProgram(programs) - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "implicit type conversion between VARCHAR(2147483647) and INTEGER " + + assertThatThrownBy( + () => + testUtil.verifyRelPlan( + "SELECT * FROM MyTable AS T JOIN LookupTable " + + "FOR SYSTEM_TIME AS OF T.proctime AS D ON T.b = D.id")) + .hasMessageContaining("implicit type conversion between VARCHAR(2147483647) and INTEGER " + "is not supported on join's condition now") - - testUtil.verifyRelPlan( - "SELECT * FROM MyTable AS T JOIN LookupTable " - + "FOR SYSTEM_TIME AS OF T.proctime AS D ON T.b = D.id") + .isInstanceOf[TableException] } - @Test + @TestTemplate def testJoinTemporalTable(): Unit = { val sql = "SELECT * FROM MyTable AS T JOIN LookupTable " + "FOR SYSTEM_TIME AS OF T.proctime AS D ON T.a = D.id" testUtil.verifyExecPlan(sql) } - @Test + @TestTemplate def testLeftJoinTemporalTable(): Unit = { val sql = "SELECT * FROM MyTable AS T LEFT JOIN LookupTable " + "FOR SYSTEM_TIME AS OF T.proctime AS D ON T.a = D.id" testUtil.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithNestedQuery(): Unit = { val sql = "SELECT * FROM " + "(SELECT a, b, proctime FROM MyTable WHERE c > 1000) AS T " + @@ -220,7 +222,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase { testUtil.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithProjectionPushDown(): Unit = { val sql = """ @@ -232,7 +234,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase { testUtil.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithFilterPushDown(): Unit = { val sql = """ @@ -244,7 +246,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase { testUtil.verifyExecPlan(sql) } - @Test + @TestTemplate def testAvoidAggregatePushDown(): Unit = { val sql1 = """ @@ -270,12 +272,8 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase { testUtil.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithTrueCondition(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "Temporal table join requires an equality condition on fields of " + - "table [default_catalog.default_database.LookupTable]") val sql = """ |SELECT * FROM MyTable AS T @@ -283,13 +281,17 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase { |ON true |WHERE T.c > 1000 """.stripMargin - testUtil.verifyExplain(sql) + + assertThatThrownBy(() => testUtil.verifyExplain(sql)) + .hasMessageContaining("Temporal table join requires an equality condition on fields of " + + "table [default_catalog.default_database.LookupTable]") + .isInstanceOf[TableException] } - @Test + @TestTemplate def testJoinTemporalTableWithComputedColumn(): Unit = { // Computed column do not support in legacyTableSource. - Assume.assumeFalse(legacyTableSource) + assumeThat(legacyTableSource).isFalse val sql = """ |SELECT @@ -301,10 +303,10 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase { testUtil.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithComputedColumnAndPushDown(): Unit = { // Computed column do not support in legacyTableSource. - Assume.assumeFalse(legacyTableSource) + assumeThat(legacyTableSource).isFalse val sql = """ |SELECT @@ -316,7 +318,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase { testUtil.verifyExecPlan(sql) } - @Test + @TestTemplate def testReusing(): Unit = { testUtil.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_REUSE_SUB_PLAN_ENABLED, Boolean.box(true)) @@ -357,26 +359,20 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase { sql: String, keywords: String, clazz: Class[_ <: Throwable] = classOf[ValidationException]): Unit = { - try { - testUtil.verifyExplain(sql) - fail(s"Expected a $clazz, but no exception is thrown.") - } catch { - case e if e.getClass == clazz => - if (keywords != null) { - assertTrue( - s"The actual exception message \n${e.getMessage}\n" + - s"doesn't contain expected keyword \n$keywords\n", - e.getMessage.contains(keywords)) - } - case e: Throwable => - e.printStackTrace() - fail(s"Expected throw ${clazz.getSimpleName}, but is $e.") + val callable: ThrowingCallable = () => testUtil.verifyExplain(sql) + if (keywords != null) { + assertThatExceptionOfType(clazz) + .isThrownBy(callable) + .withMessageContaining(keywords) + } else { + assertThatExceptionOfType(clazz) + .isThrownBy(callable) } } } object LookupJoinTest { - @Parameterized.Parameters(name = "LegacyTableSource={0}") + @Parameters(name = "LegacyTableSource={0}") def parameters(): JCollection[Array[Object]] = { Seq[Array[AnyRef]](Array(JBoolean.TRUE), Array(JBoolean.FALSE)) } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/NestedLoopJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/NestedLoopJoinTest.scala index f725d350d0d1a..b4d9a7a1c1b63 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/NestedLoopJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/NestedLoopJoinTest.scala @@ -19,11 +19,11 @@ package org.apache.flink.table.planner.plan.batch.sql.join import org.apache.flink.table.api.config.ExecutionConfigOptions -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} class NestedLoopJoinTest extends JoinTestBase { - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "SortMergeJoin, HashJoin") diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/NestedLoopSemiAntiJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/NestedLoopSemiAntiJoinTest.scala index 57e27dce442b8..7901d6a51a454 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/NestedLoopSemiAntiJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/NestedLoopSemiAntiJoinTest.scala @@ -19,11 +19,11 @@ package org.apache.flink.table.planner.plan.batch.sql.join import org.apache.flink.table.api.config.ExecutionConfigOptions -import org.junit.Before +import org.junit.jupiter.api.BeforeEach class NestedLoopSemiAntiJoinTest extends SemiAntiJoinTestBase { - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "SortMergeJoin, HashJoin") diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SemiAntiJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SemiAntiJoinTest.scala index 745b300dbae24..0705c00ca6862 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SemiAntiJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SemiAntiJoinTest.scala @@ -17,7 +17,7 @@ */ package org.apache.flink.table.planner.plan.batch.sql.join -import org.junit.Test +import org.junit.jupiter.api.Test /** Test SEMI/ANTI Join, the join operators are chose based on cost. */ class SemiAntiJoinTest extends SemiAntiJoinTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SemiAntiJoinTestBase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SemiAntiJoinTestBase.scala index 77a5d3762cc3c..d9afccaddcf52 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SemiAntiJoinTestBase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SemiAntiJoinTestBase.scala @@ -19,11 +19,10 @@ package org.apache.flink.table.planner.plan.batch.sql.join import org.apache.flink.api.scala._ import org.apache.flink.table.api._ -import org.apache.flink.table.api.bridge.scala._ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedTableFunctions.StringSplit import org.apache.flink.table.planner.utils.{BatchTableTestUtil, TableTestBase} -import org.junit.Test +import org.junit.jupiter.api.Test abstract class SemiAntiJoinTestBase extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/ShuffledHashJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/ShuffledHashJoinTest.scala index b76f098897c27..a9c4fa1189018 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/ShuffledHashJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/ShuffledHashJoinTest.scala @@ -20,11 +20,12 @@ package org.apache.flink.table.planner.plan.batch.sql.join import org.apache.flink.table.api.TableException import org.apache.flink.table.api.config.{ExecutionConfigOptions, OptimizerConfigOptions} -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.{BeforeEach, Test} class ShuffledHashJoinTest extends JoinTestBase { - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig.getConfiguration .setLong(OptimizerConfigOptions.TABLE_OPTIMIZER_BROADCAST_JOIN_THRESHOLD, 1L) @@ -35,78 +36,78 @@ class ShuffledHashJoinTest extends JoinTestBase { @Test override def testInnerJoinWithoutJoinPred(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testInnerJoinWithoutJoinPred() + assertThatThrownBy(() => super.testInnerJoinWithoutJoinPred()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testLeftOuterJoinNoEquiPred(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testLeftOuterJoinNoEquiPred() + assertThatThrownBy(() => super.testLeftOuterJoinNoEquiPred()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testLeftOuterJoinOnTrue(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testLeftOuterJoinOnTrue() + assertThatThrownBy(() => super.testLeftOuterJoinOnTrue()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testLeftOuterJoinOnFalse(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testLeftOuterJoinOnFalse() + assertThatThrownBy(() => super.testLeftOuterJoinOnFalse()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testRightOuterJoinOnTrue(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testRightOuterJoinOnTrue() + assertThatThrownBy(() => super.testRightOuterJoinOnTrue()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testRightOuterJoinOnFalse(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testRightOuterJoinOnFalse() + assertThatThrownBy(() => super.testRightOuterJoinOnFalse()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testRightOuterJoinWithNonEquiPred(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testRightOuterJoinWithNonEquiPred() + assertThatThrownBy(() => super.testRightOuterJoinWithNonEquiPred()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testFullOuterJoinWithNonEquiPred(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testFullOuterJoinWithNonEquiPred() + assertThatThrownBy(() => super.testFullOuterJoinWithNonEquiPred()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testFullOuterJoinOnFalse(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testFullOuterJoinOnFalse() + assertThatThrownBy(() => super.testFullOuterJoinOnFalse()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testFullOuterJoinOnTrue(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testFullOuterJoinOnTrue() + assertThatThrownBy(() => super.testFullOuterJoinOnTrue()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testCrossJoin(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testCrossJoin() + assertThatThrownBy(() => super.testCrossJoin()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/ShuffledHashSemiAntiJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/ShuffledHashSemiAntiJoinTest.scala index 0d37c2ee03179..e5f3eaeb5a84e 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/ShuffledHashSemiAntiJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/ShuffledHashSemiAntiJoinTest.scala @@ -20,11 +20,12 @@ package org.apache.flink.table.planner.plan.batch.sql.join import org.apache.flink.table.api.TableException import org.apache.flink.table.api.config.ExecutionConfigOptions -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.{BeforeEach, Test} class ShuffledHashSemiAntiJoinTest extends SemiAntiJoinTestBase { - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig.set( ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, @@ -37,128 +38,128 @@ class ShuffledHashSemiAntiJoinTest extends SemiAntiJoinTestBase { // because NestedLoopJoin(non-singleRowJoin) is disabled. @Test override def testNotInWithCorrelated_NonEquiCondition1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithCorrelated_NonEquiCondition1() + assertThatThrownBy(() => super.testNotInWithCorrelated_NonEquiCondition1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithCorrelated_NonEquiCondition2(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithCorrelated_NonEquiCondition2() + assertThatThrownBy(() => super.testNotInWithCorrelated_NonEquiCondition2()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testInNotInExistsNotExists(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testInNotInExistsNotExists() + assertThatThrownBy(() => super.testInNotInExistsNotExists()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testInWithUncorrelated_ComplexCondition3(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testInWithUncorrelated_ComplexCondition3() + assertThatThrownBy(() => super.testInWithUncorrelated_ComplexCondition3()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotExistsWithCorrelated_NonEquiCondition1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotExistsWithCorrelated_NonEquiCondition1() + assertThatThrownBy(() => super.testNotExistsWithCorrelated_NonEquiCondition1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotExistsWithCorrelated_NonEquiCondition2(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotExistsWithCorrelated_NonEquiCondition2() + assertThatThrownBy(() => super.testNotExistsWithCorrelated_NonEquiCondition2()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_ComplexCondition1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_ComplexCondition1() + assertThatThrownBy(() => super.testNotInWithUncorrelated_ComplexCondition1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_ComplexCondition2(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_ComplexCondition2() + assertThatThrownBy(() => super.testNotInWithUncorrelated_ComplexCondition2()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_ComplexCondition3(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_ComplexCondition3() + assertThatThrownBy(() => super.testNotInWithUncorrelated_ComplexCondition3()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testExistsWithCorrelated_NonEquiCondition1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testExistsWithCorrelated_NonEquiCondition1() + assertThatThrownBy(() => super.testExistsWithCorrelated_NonEquiCondition1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testExistsWithCorrelated_NonEquiCondition2(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testExistsWithCorrelated_NonEquiCondition2() + assertThatThrownBy(() => super.testExistsWithCorrelated_NonEquiCondition2()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testMultiExistsWithCorrelate1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testMultiExistsWithCorrelate1() + assertThatThrownBy(() => super.testMultiExistsWithCorrelate1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_MultiFields(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_MultiFields() + assertThatThrownBy(() => super.testNotInWithUncorrelated_MultiFields()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testMultiNotInWithCorrelated(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testMultiNotInWithCorrelated() + assertThatThrownBy(() => super.testMultiNotInWithCorrelated()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testInWithCorrelated_ComplexCondition3(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testInWithCorrelated_ComplexCondition3() + assertThatThrownBy(() => super.testInWithCorrelated_ComplexCondition3()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_SimpleCondition1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_SimpleCondition1() + assertThatThrownBy(() => super.testNotInWithUncorrelated_SimpleCondition1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_SimpleCondition2(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_SimpleCondition2() + assertThatThrownBy(() => super.testNotInWithUncorrelated_SimpleCondition2()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_SimpleCondition3(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_SimpleCondition3() + assertThatThrownBy(() => super.testNotInWithUncorrelated_SimpleCondition3()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SingleRowJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SingleRowJoinTest.scala index 5a5597fd6665e..19aeecbe0550f 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SingleRowJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SingleRowJoinTest.scala @@ -22,7 +22,7 @@ import org.apache.flink.table.api._ import org.apache.flink.table.api.bridge.scala._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test class SingleRowJoinTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SortMergeJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SortMergeJoinTest.scala index f8dc8d4ad9255..2d7cf24fab4d0 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SortMergeJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SortMergeJoinTest.scala @@ -20,11 +20,12 @@ package org.apache.flink.table.planner.plan.batch.sql.join import org.apache.flink.table.api.TableException import org.apache.flink.table.api.config.ExecutionConfigOptions -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.{BeforeEach, Test} class SortMergeJoinTest extends JoinTestBase { - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashJoin, NestedLoopJoin") @@ -32,79 +33,79 @@ class SortMergeJoinTest extends JoinTestBase { @Test override def testInnerJoinWithoutJoinPred(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testInnerJoinWithoutJoinPred() + assertThatThrownBy(() => super.testInnerJoinWithoutJoinPred()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testLeftOuterJoinNoEquiPred(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testLeftOuterJoinNoEquiPred() + assertThatThrownBy(() => super.testLeftOuterJoinNoEquiPred()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testLeftOuterJoinOnTrue(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testLeftOuterJoinOnTrue() + assertThatThrownBy(() => super.testLeftOuterJoinOnTrue()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testLeftOuterJoinOnFalse(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testLeftOuterJoinOnFalse() + assertThatThrownBy(() => super.testLeftOuterJoinOnFalse()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testRightOuterJoinOnTrue(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testRightOuterJoinOnTrue() + assertThatThrownBy(() => super.testRightOuterJoinOnTrue()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testRightOuterJoinOnFalse(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testRightOuterJoinOnFalse() + assertThatThrownBy(() => super.testRightOuterJoinOnFalse()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testRightOuterJoinWithNonEquiPred(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testRightOuterJoinWithNonEquiPred() + assertThatThrownBy(() => super.testRightOuterJoinWithNonEquiPred()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testFullOuterJoinWithNonEquiPred(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testFullOuterJoinWithNonEquiPred() + assertThatThrownBy(() => super.testFullOuterJoinWithNonEquiPred()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testFullOuterJoinOnFalse(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testFullOuterJoinOnFalse() + assertThatThrownBy(() => super.testFullOuterJoinOnFalse()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testFullOuterJoinOnTrue(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testFullOuterJoinOnTrue() + assertThatThrownBy(() => super.testFullOuterJoinOnTrue()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testCrossJoin(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testCrossJoin() + assertThatThrownBy(() => super.testCrossJoin()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SortMergeSemiAntiJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SortMergeSemiAntiJoinTest.scala index 5399af2f9a6b9..b72bda06091b6 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SortMergeSemiAntiJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/SortMergeSemiAntiJoinTest.scala @@ -20,11 +20,12 @@ package org.apache.flink.table.planner.plan.batch.sql.join import org.apache.flink.table.api.TableException import org.apache.flink.table.api.config.ExecutionConfigOptions -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.{BeforeEach, Test} class SortMergeSemiAntiJoinTest extends SemiAntiJoinTestBase { - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_DISABLED_OPERATORS, "HashJoin, NestedLoopJoin") @@ -36,127 +37,127 @@ class SortMergeSemiAntiJoinTest extends SemiAntiJoinTestBase { // because NestedLoopJoin(non-singleRowJoin) is disabled. @Test override def testNotInWithCorrelated_NonEquiCondition1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithCorrelated_NonEquiCondition1() + assertThatThrownBy(() => super.testNotInWithCorrelated_NonEquiCondition1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithCorrelated_NonEquiCondition2(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithCorrelated_NonEquiCondition2() + assertThatThrownBy(() => super.testNotInWithCorrelated_NonEquiCondition2()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testInNotInExistsNotExists(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testInNotInExistsNotExists() + assertThatThrownBy(() => super.testInNotInExistsNotExists()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testInWithUncorrelated_ComplexCondition3(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testInWithUncorrelated_ComplexCondition3() + assertThatThrownBy(() => super.testInWithUncorrelated_ComplexCondition3()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotExistsWithCorrelated_NonEquiCondition1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotExistsWithCorrelated_NonEquiCondition1() + assertThatThrownBy(() => super.testNotExistsWithCorrelated_NonEquiCondition1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotExistsWithCorrelated_NonEquiCondition2(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotExistsWithCorrelated_NonEquiCondition2() + assertThatThrownBy(() => super.testNotExistsWithCorrelated_NonEquiCondition2()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_ComplexCondition1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_ComplexCondition1() + assertThatThrownBy(() => super.testNotInWithUncorrelated_ComplexCondition1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_ComplexCondition2(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_ComplexCondition2() + assertThatThrownBy(() => super.testNotInWithUncorrelated_ComplexCondition2()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_ComplexCondition3(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_ComplexCondition3() + assertThatThrownBy(() => super.testNotInWithUncorrelated_ComplexCondition3()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testExistsWithCorrelated_NonEquiCondition1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testExistsWithCorrelated_NonEquiCondition1() + assertThatThrownBy(() => super.testExistsWithCorrelated_NonEquiCondition1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testExistsWithCorrelated_NonEquiCondition2(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testExistsWithCorrelated_NonEquiCondition2() + assertThatThrownBy(() => super.testExistsWithCorrelated_NonEquiCondition2()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testMultiExistsWithCorrelate1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testMultiExistsWithCorrelate1() + assertThatThrownBy(() => super.testMultiExistsWithCorrelate1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_MultiFields(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_MultiFields() + assertThatThrownBy(() => super.testNotInWithUncorrelated_MultiFields()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testMultiNotInWithCorrelated(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testMultiNotInWithCorrelated() + assertThatThrownBy(() => super.testMultiNotInWithCorrelated()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testInWithCorrelated_ComplexCondition3(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testInWithCorrelated_ComplexCondition3() + assertThatThrownBy(() => super.testInWithCorrelated_ComplexCondition3()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_SimpleCondition1(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_SimpleCondition1() + assertThatThrownBy(() => super.testNotInWithUncorrelated_SimpleCondition1()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_SimpleCondition2(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_SimpleCondition2() + assertThatThrownBy(() => super.testNotInWithUncorrelated_SimpleCondition2()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test override def testNotInWithUncorrelated_SimpleCondition3(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - super.testNotInWithUncorrelated_SimpleCondition3() + assertThatThrownBy(() => super.testNotInWithUncorrelated_SimpleCondition3()) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/TemporalFunctionJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/TemporalFunctionJoinTest.scala index 911f8a523c1ed..e52041d5430d8 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/TemporalFunctionJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/TemporalFunctionJoinTest.scala @@ -21,8 +21,8 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.{BatchTableTestUtil, TableTestBase} -import org.hamcrest.Matchers.containsString -import org.junit.Test +import org.assertj.core.api.Assertions.{assertThatExceptionOfType, assertThatThrownBy} +import org.junit.jupiter.api.Test import java.sql.Timestamp @@ -42,16 +42,15 @@ class TemporalFunctionJoinTest extends TableTestBase { @Test def testSimpleJoin(): Unit = { - expectedException.expect(classOf[TableException]) - expectedException.expectMessage("Cannot generate a valid execution plan for the given query") - val sqlQuery = "SELECT " + "o_amount * rate as rate " + "FROM Orders AS o, " + "LATERAL TABLE (Rates(o_rowtime)) AS r " + "WHERE currency = o_currency" - util.verifyExplain(sqlQuery) + assertThatThrownBy(() => util.verifyExplain(sqlQuery)) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } /** @@ -59,7 +58,7 @@ class TemporalFunctionJoinTest extends TableTestBase { * complex OR join condition and there are some columns that are not being used (are being * pruned). */ - @Test(expected = classOf[TableException]) + @Test def testComplexJoin(): Unit = { val util = batchTestUtil() util.addDataStream[(String, Int)]("Table3", 't3_comment, 't3_secondary_key) @@ -92,30 +91,29 @@ class TemporalFunctionJoinTest extends TableTestBase { "Table3 " + "WHERE t3_secondary_key = secondary_key" - util.verifyExplain(sqlQuery) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExplain(sqlQuery)) } @Test def testUncorrelatedJoin(): Unit = { - expectedException.expect(classOf[TableException]) - expectedException.expectMessage(containsString("Cannot generate a valid execution plan")) - val sqlQuery = "SELECT " + "o_amount * rate as rate " + "FROM Orders AS o, " + "LATERAL TABLE (Rates(TIMESTAMP '2016-06-27 10:10:42.123')) AS r " + "WHERE currency = o_currency" - util.verifyExplain(sqlQuery) + assertThatThrownBy(() => util.verifyExplain(sqlQuery)) + .hasMessageContaining("Cannot generate a valid execution plan") + .isInstanceOf[TableException] } @Test def testTemporalTableFunctionScan(): Unit = { - expectedException.expect(classOf[TableException]) - expectedException.expectMessage(containsString("Cannot generate a valid execution plan")) - val sqlQuery = "SELECT * FROM LATERAL TABLE (Rates(TIMESTAMP '2016-06-27 10:10:42.123'))"; - util.verifyExplain(sqlQuery) + assertThatThrownBy(() => util.verifyExplain(sqlQuery)) + .hasMessageContaining("Cannot generate a valid execution plan") + .isInstanceOf[TableException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/TemporalJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/TemporalJoinTest.scala index a8f46a959b9ba..89292aea47a17 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/TemporalJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/sql/join/TemporalJoinTest.scala @@ -20,7 +20,8 @@ package org.apache.flink.table.planner.plan.batch.sql.join import org.apache.flink.table.api.TableException import org.apache.flink.table.planner.utils.{BatchTableTestUtil, TableTestBase} -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.{BeforeEach, Test} /** * Test temporal join in batch mode. @@ -31,7 +32,7 @@ class TemporalJoinTest extends TableTestBase { val util: BatchTableTestUtil = batchTestUtil() - @Before + @BeforeEach def before(): Unit = { util.addTable(""" |CREATE TABLE Orders ( @@ -104,7 +105,7 @@ class TemporalJoinTest extends TableTestBase { "GROUP BY currency ") } - @Test(expected = classOf[TableException]) + @Test def testSimpleJoin(): Unit = { val sqlQuery = "SELECT " + "o_amount * rate as rate " + @@ -112,10 +113,11 @@ class TemporalJoinTest extends TableTestBase { "RatesHistoryWithPK FOR SYSTEM_TIME AS OF o.o_rowtime as r " + "on o.o_currency = r.currency" - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } - @Test(expected = classOf[TableException]) + @Test def testSimpleRowtimeVersionedViewJoin(): Unit = { val sqlQuery = "SELECT " + "o_amount * rate as rate " + @@ -124,10 +126,11 @@ class TemporalJoinTest extends TableTestBase { "FOR SYSTEM_TIME AS OF o.o_rowtime as r1 " + "on o.o_currency = r1.currency" - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } - @Test(expected = classOf[TableException]) + @Test def testSimpleProctimeVersionedViewJoin(): Unit = { val sqlQuery = "SELECT " + "o_amount * rate as rate " + @@ -136,10 +139,11 @@ class TemporalJoinTest extends TableTestBase { "FOR SYSTEM_TIME AS OF o.o_proctime as r1 " + "on o.o_currency = r1.currency" - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } - @Test(expected = classOf[TableException]) + @Test def testSimpleViewProcTimeJoin(): Unit = { val sqlQuery = "SELECT " + @@ -149,6 +153,7 @@ class TemporalJoinTest extends TableTestBase { "FOR SYSTEM_TIME AS OF o.o_proctime as r1 " + "on o.o_currency = r1.currency" - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/AggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/AggregateTest.scala index 496e7cff50616..91ad63060b94e 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/AggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/AggregateTest.scala @@ -21,7 +21,7 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test /** Test for testing aggregate plans. */ class AggregateTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/CalcTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/CalcTest.scala index 92294643de20a..0ce5bc82917db 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/CalcTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/CalcTest.scala @@ -23,7 +23,7 @@ import org.apache.flink.table.functions.ScalarFunction import org.apache.flink.table.planner.plan.batch.table.CalcTest.{giveMeCaseClass, MyHashCode, TestCaseClass, WC} import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test class CalcTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/ColumnFunctionsTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/ColumnFunctionsTest.scala index 8edd75ea627c3..bfc45f93d0cf1 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/ColumnFunctionsTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/ColumnFunctionsTest.scala @@ -22,7 +22,7 @@ import org.apache.flink.table.api._ import org.apache.flink.table.functions.ScalarFunction import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test /** Tests for column functions. */ class ColumnFunctionsTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/CorrelateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/CorrelateTest.scala index 3b84724b4cc4c..da8f7a5e9f7c3 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/CorrelateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/CorrelateTest.scala @@ -24,7 +24,7 @@ import org.apache.flink.table.planner.utils.{MockPythonTableFunction, TableFunc0 import org.apache.calcite.rel.rules.CoreRules import org.apache.calcite.tools.RuleSets -import org.junit.Test +import org.junit.jupiter.api.Test class CorrelateTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/GroupWindowTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/GroupWindowTest.scala index 72d749b879bda..4c5bd55eeb747 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/GroupWindowTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/GroupWindowTest.scala @@ -22,7 +22,8 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.WeightedAvgWithMerge import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test import java.sql.Timestamp @@ -32,7 +33,7 @@ class GroupWindowTest extends TableTestBase { // Common test // =============================================================================================== - @Test(expected = classOf[TableException]) + @Test def testEventTimeTumblingGroupWindowOverCount(): Unit = { val util = batchTestUtil() val table = util.addTableSource[(Long, Int, String)]('long, 'int, 'string) @@ -41,7 +42,8 @@ class GroupWindowTest extends TableTestBase { .window(Tumble.over(2.rows).on('long).as('w)) .groupBy('w, 'string) .select('string, 'int.count) - util.verifyExecPlan(windowedTable) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(windowedTable)) } @Test @@ -82,7 +84,7 @@ class GroupWindowTest extends TableTestBase { util.verifyExecPlan(windowedTable) } - @Test(expected = classOf[TableException]) + @Test def testAllEventTimeTumblingGroupWindowOverCount(): Unit = { val util = batchTestUtil() val table = util.addTableSource[(Long, Int, String)]('long, 'int, 'string) @@ -92,7 +94,8 @@ class GroupWindowTest extends TableTestBase { .groupBy('w) .select('int.count) - util.verifyExecPlan(windowedTable) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(windowedTable)) } @Test @@ -138,7 +141,7 @@ class GroupWindowTest extends TableTestBase { util.verifyExecPlan(windowedTable) } - @Test(expected = classOf[TableException]) + @Test def testEventTimeSlidingGroupWindowOverCount(): Unit = { val util = batchTestUtil() val table = util.addTableSource[(Long, Int, String)]('long, 'int, 'string) @@ -148,7 +151,8 @@ class GroupWindowTest extends TableTestBase { .groupBy('w, 'string) .select('string, 'int.count) - util.verifyExecPlan(windowedTable) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(windowedTable)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/JoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/JoinTest.scala index ff73c8aaa4342..be5228bcaf243 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/JoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/JoinTest.scala @@ -23,7 +23,8 @@ import org.apache.flink.table.functions.ScalarFunction import org.apache.flink.table.planner.plan.batch.table.JoinTest.Merger import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class JoinTest extends TableTestBase { @@ -140,31 +141,34 @@ class JoinTest extends TableTestBase { util.verifyExecPlan(results) } - @Test(expected = classOf[ValidationException]) + @Test def testFullJoinNoEquiJoinPredicate(): Unit = { val util = batchTestUtil() val ds1 = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) val ds2 = util.addTableSource[(Int, Long, Int, String, Long)]("Table5", 'd, 'e, 'f, 'g, 'h) - util.verifyExecPlan(ds2.fullOuterJoin(ds1, 'b < 'd).select('c, 'g)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan(ds2.fullOuterJoin(ds1, 'b < 'd).select('c, 'g))) } - @Test(expected = classOf[ValidationException]) + @Test def testLeftJoinNoEquiJoinPredicate(): Unit = { val util = batchTestUtil() val ds1 = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) val ds2 = util.addTableSource[(Int, Long, Int, String, Long)]("Table5", 'd, 'e, 'f, 'g, 'h) - util.verifyExecPlan(ds2.leftOuterJoin(ds1, 'b < 'd).select('c, 'g)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan(ds2.leftOuterJoin(ds1, 'b < 'd).select('c, 'g))) } - @Test(expected = classOf[ValidationException]) + @Test def testRightJoinNoEquiJoinPredicate(): Unit = { val util = batchTestUtil() val ds1 = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) val ds2 = util.addTableSource[(Int, Long, Int, String, Long)]("Table5", 'd, 'e, 'f, 'g, 'h) - util.verifyExecPlan(ds2.rightOuterJoin(ds1, 'b < 'd).select('c, 'g)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan(ds2.rightOuterJoin(ds1, 'b < 'd).select('c, 'g))) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonAggregateTest.scala index 8c50e8ea99365..f09e7604294a7 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonAggregateTest.scala @@ -22,7 +22,8 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedAggFunctions.PandasAggregateFunction import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class PythonAggregateTest extends TableTestBase { @@ -50,7 +51,7 @@ class PythonAggregateTest extends TableTestBase { util.verifyExecPlan(resultTable) } - @Test(expected = classOf[TableException]) + @Test def testMixedUsePandasAggAndJavaAgg(): Unit = { val util = batchTestUtil() val sourceTable = util.addTableSource[(Int, Long, Int)]("MyTable", 'a, 'b, 'c) @@ -60,6 +61,7 @@ class PythonAggregateTest extends TableTestBase { .groupBy('b) .select('b, func('a, 'c), 'a.count()) - util.verifyExecPlan(resultTable) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(resultTable)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonCalcTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonCalcTest.scala index f1bd371e35db2..9cf2d1d2151ee 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonCalcTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonCalcTest.scala @@ -22,12 +22,12 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctions.PythonScalarFunction import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} class PythonCalcTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource[(Int, Int, Int)]("MyTable", 'a, 'b, 'c) util.addFunction("pyFunc1", new PythonScalarFunction("pyFunc1")) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonGroupWindowAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonGroupWindowAggregateTest.scala index 47ad3ee5b6415..d27d74922d9ff 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonGroupWindowAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonGroupWindowAggregateTest.scala @@ -22,7 +22,8 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedAggFunctions.{PandasAggregateFunction, TestPythonAggregateFunction} import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class PythonGroupWindowAggregateTest extends TableTestBase { @@ -41,7 +42,7 @@ class PythonGroupWindowAggregateTest extends TableTestBase { util.verifyExecPlan(resultTable) } - @Test(expected = classOf[TableException]) + @Test def testPandasEventTimeTumblingGroupWindowOverCount(): Unit = { val util = batchTestUtil() val sourceTable = @@ -53,7 +54,8 @@ class PythonGroupWindowAggregateTest extends TableTestBase { .groupBy('w, 'b) .select('b, func('a, 'c)) - util.verifyExecPlan(resultTable) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(resultTable)) } @Test @@ -71,7 +73,7 @@ class PythonGroupWindowAggregateTest extends TableTestBase { util.verifyExecPlan(resultTable) } - @Test(expected = classOf[TableException]) + @Test def testPandasEventTimeSlidingGroupWindowOverCount(): Unit = { val util = batchTestUtil() val sourceTable = @@ -83,7 +85,8 @@ class PythonGroupWindowAggregateTest extends TableTestBase { .groupBy('w, 'b) .select('b, func('a, 'c)) - util.verifyExecPlan(resultTable) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(resultTable)) } @Test @@ -101,7 +104,7 @@ class PythonGroupWindowAggregateTest extends TableTestBase { util.verifyExecPlan(resultTable) } - @Test(expected = classOf[TableException]) + @Test def testGeneralEventTimeTumblingGroupWindowOverTime(): Unit = { val util = batchTestUtil() val sourceTable = @@ -113,6 +116,7 @@ class PythonGroupWindowAggregateTest extends TableTestBase { .groupBy('w, 'b) .select('b, 'w.start, 'w.end, func('a, 'c)) - util.verifyExecPlan(resultTable) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(resultTable)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonOverWindowAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonOverWindowAggregateTest.scala index 5232dcce5cbc5..a337ae7a2a6b2 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonOverWindowAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/PythonOverWindowAggregateTest.scala @@ -22,7 +22,8 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedAggFunctions.{PandasAggregateFunction, TestPythonAggregateFunction} import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class PythonOverWindowAggregateTest extends TableTestBase { @@ -64,7 +65,7 @@ class PythonOverWindowAggregateTest extends TableTestBase { util.verifyExecPlan(resultTable) } - @Test(expected = classOf[TableException]) + @Test def testGeneralRangeOverWindowAggregate(): Unit = { val util = batchTestUtil() val sourceTable = @@ -80,6 +81,7 @@ class PythonOverWindowAggregateTest extends TableTestBase { .as('w)) .select('b, func('a, 'c).over('w)) - util.verifyExecPlan(resultTable) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(resultTable)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/SetOperatorsTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/SetOperatorsTest.scala index 4caceecf87a3d..8efbce6e873e9 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/SetOperatorsTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/SetOperatorsTest.scala @@ -24,7 +24,7 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.utils.NonPojo import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test import java.sql.Timestamp diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/TemporalTableFunctionJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/TemporalTableFunctionJoinTest.scala index c799402b93d5b..5d7d914e61a70 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/TemporalTableFunctionJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/TemporalTableFunctionJoinTest.scala @@ -21,8 +21,8 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.{TableTestBase, TableTestUtil} -import org.hamcrest.Matchers.containsString -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test import java.sql.Timestamp @@ -41,23 +41,19 @@ class TemporalTableFunctionJoinTest extends TableTestBase { @Test def testSimpleJoin(): Unit = { - expectedException.expect(classOf[TableException]) - expectedException.expectMessage("Cannot generate a valid execution plan for the given query") - val result = orders .as("o_amount", "o_currency", "o_rowtime") .joinLateral(rates('o_rowtime), 'currency === 'o_currency) .select($"o_amount" * $"rate") .as("rate") - util.verifyExecPlan(result) + assertThatThrownBy(() => util.verifyExecPlan(result)) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[TableException] } @Test def testUncorrelatedJoin(): Unit = { - expectedException.expect(classOf[TableException]) - expectedException.expectMessage(containsString("Cannot generate a valid execution plan")) - val result = orders .as("o_amount", "o_currency", "o_rowtime") .joinLateral( @@ -65,7 +61,9 @@ class TemporalTableFunctionJoinTest extends TableTestBase { 'o_currency === 'currency) .select($"o_amount" * $"rate") - util.verifyExecPlan(result) + assertThatThrownBy(() => util.verifyExecPlan(result)) + .hasMessageContaining("Cannot generate a valid execution plan") + .isInstanceOf[TableException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/AggregateValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/AggregateValidationTest.scala index 928d078eb50ad..6fb62a24756e5 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/AggregateValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/AggregateValidationTest.scala @@ -22,59 +22,66 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.WeightedAvgWithMergeAndReset import org.apache.flink.table.planner.utils.TableTestBase -import org.junit._ +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class AggregateValidationTest extends TableTestBase { - @Test(expected = classOf[ValidationException]) + @Test def testNonWorkingAggregationDataTypes(): Unit = { val util = batchTestUtil() val t = util.addTableSource[(String, Int)]("Table2") // Must fail. Field '_1 is not a numeric type. - t.select('_1.sum) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.select('_1.sum)) } - @Test(expected = classOf[ValidationException]) + @Test def testNoNestedAggregations(): Unit = { val util = batchTestUtil() val t = util.addTableSource[(String, Int)]("Table2") // Must fail. Sum aggregation can not be chained. - t.select('_2.sum.sum) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.select('_2.sum.sum)) } - @Test(expected = classOf[ValidationException]) + @Test def testGroupingOnNonExistentField(): Unit = { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) // must fail. '_foo not a valid field - t.groupBy('_foo).select('a.avg) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.groupBy('_foo).select('a.avg)) } - @Test(expected = classOf[ValidationException]) + @Test def testGroupingInvalidSelection(): Unit = { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - t.groupBy('a, 'b) - // must fail. 'c is not a grouping key or aggregation - .select('c) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + t.groupBy('a, 'b) + // must fail. 'c is not a grouping key or aggregation + .select('c)) } - @Test(expected = classOf[ValidationException]) + @Test def testAggregationOnNonExistingField(): Unit = { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) // Must fail. Field 'foo does not exist. - t.select('foo.avg) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.select('foo.avg)) } - @Test(expected = classOf[ValidationException]) - @throws[Exception] + @Test def testInvalidUdAggArgs() { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) @@ -82,116 +89,124 @@ class AggregateValidationTest extends TableTestBase { val myWeightedAvg = new WeightedAvgWithMergeAndReset // must fail. UDAGG does not accept String type - t.select(myWeightedAvg('c, 'a)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.select(myWeightedAvg('c, 'a))) } - @Test(expected = classOf[ValidationException]) - @throws[Exception] + @Test def testGroupingInvalidUdAggArgs() { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) val myWeightedAvg = new WeightedAvgWithMergeAndReset - t.groupBy('b) - // must fail. UDAGG does not accept String type - .select(myWeightedAvg('c, 'a)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + t.groupBy('b) + // must fail. UDAGG does not accept String type + .select(myWeightedAvg('c, 'a))) } - @Test(expected = classOf[ValidationException]) - @throws[Exception] + @Test def testGroupingNestedUdAgg() { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) val myWeightedAvg = new WeightedAvgWithMergeAndReset - t.groupBy('c) - // must fail. UDAGG does not accept String type - .select(myWeightedAvg(myWeightedAvg('b, 'a), 'a)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + t.groupBy('c) + // must fail. UDAGG does not accept String type + .select(myWeightedAvg(myWeightedAvg('b, 'a), 'a))) } - @Test(expected = classOf[ValidationException]) - @throws[Exception] + @Test def testAggregationOnNonExistingFieldJava() { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - t.select($"foo".avg) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.select($"foo".avg)) } - @Test(expected = classOf[ValidationException]) - @throws[Exception] + @Test def testNonWorkingAggregationDataTypesJava() { val util = batchTestUtil() val t = util.addTableSource[(Long, String)]("Table2", 'b, 'c) // Must fail. Cannot compute SUM aggregate on String field. - t.select($"c".sum) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.select($"c".sum)) } - @Test(expected = classOf[ValidationException]) - @throws[Exception] + @Test def testNoNestedAggregationsJava() { val util = batchTestUtil() val t = util.addTableSource[(Long, String)]("Table2", 'b, 'c) // Must fail. Aggregation on aggregation not allowed. - t.select($"b".sum.sum) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.select($"b".sum.sum)) } - @Test(expected = classOf[ValidationException]) - @throws[Exception] + @Test def testNoDeeplyNestedAggregationsJava() { val util = batchTestUtil() val t = util.addTableSource[(Long, String)]("Table2", 'b, 'c) // Must fail. Aggregation on aggregation not allowed. - t.select(($"b".sum + 1).sum) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.select(($"b".sum + 1).sum)) } - @Test(expected = classOf[ValidationException]) + @Test @throws[Exception] def testGroupingOnNonExistentFieldJava() { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) // must fail. Field foo is not in input - t.groupBy($"foo") - .select($"a".avg) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.groupBy($"foo").select($"a".avg)) } - @Test(expected = classOf[ValidationException]) - @throws[Exception] + @Test def testGroupingInvalidSelectionJava() { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - t.groupBy($"a", $"b") - // must fail. Field c is not a grouping key or aggregation - .select($"c") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + t.groupBy($"a", $"b") + // must fail. Field c is not a grouping key or aggregation + .select($"c")) } - @Test(expected = classOf[ValidationException]) - @throws[Exception] + @Test def testUnknownUdAggJava() { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) // must fail. unknown is not known - t.select(call("unknown", $"c")) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.select(call("unknown", $"c"))) } - @Test(expected = classOf[ValidationException]) - @throws[Exception] + @Test def testGroupingUnknownUdAggJava() { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - t.groupBy($"a", $"b") - // must fail. unknown is not known - .select(call("unknown", $"c")) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + t.groupBy($"a", $"b") + // must fail. unknown is not known + .select(call("unknown", $"c"))) } - @Test(expected = classOf[ValidationException]) - @throws[Exception] + @Test def testInvalidUdAggArgsJava() { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) @@ -200,11 +215,11 @@ class AggregateValidationTest extends TableTestBase { util.addFunction("myWeightedAvg", myWeightedAvg) // must fail. UDAGG does not accept String type - t.select(call("myWeightedAvg", $"c", $"a")) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.select(call("myWeightedAvg", $"c", $"a"))) } - @Test(expected = classOf[ValidationException]) - @throws[Exception] + @Test def testGroupingInvalidUdAggArgsJava() { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) @@ -212,8 +227,11 @@ class AggregateValidationTest extends TableTestBase { val myWeightedAvg = new WeightedAvgWithMergeAndReset util.addFunction("myWeightedAvg", myWeightedAvg) - t.groupBy($"b") - // must fail. UDAGG does not accept String type - .select(call("myWeightedAvg", $"c", $"a")) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + t.groupBy($"b") + // must fail. UDAGG does not accept String type + .select(call("myWeightedAvg", $"c", $"a"))) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/CalcValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/CalcValidationTest.scala index 42aff351917a6..c81918456ff58 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/CalcValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/CalcValidationTest.scala @@ -21,88 +21,86 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit._ -import org.junit.Assert._ +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class CalcValidationTest extends TableTestBase { @Test def testSelectInvalidFieldFields(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("Cannot resolve field [foo], input field list:[a, b, c].") val util = batchTestUtil() - util - .addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - // must fail. Field 'foo does not exist - .select('a, 'foo) + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + util + .addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) + // must fail. Field 'foo does not exist + .select('a, 'foo)) + .withMessageContaining("Cannot resolve field [foo], input field list:[a, b, c].") } - @Test(expected = classOf[ValidationException]) + @Test def testFilterInvalidFieldName(): Unit = { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) // must fail. Field 'foo does not exist - t.filter('foo === 2) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.filter('foo === 2)) } - @Test(expected = classOf[ValidationException]) + @Test def testSelectInvalidField() { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) // Must fail. Field foo does not exist - t.select($"a" + 1, $"foo" + 2) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.select($"a" + 1, $"foo" + 2)) } - @Test(expected = classOf[ValidationException]) + @Test def testSelectAmbiguousFieldNames() { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) // Must fail. Field foo does not exist - t.select(($"a" + 1).as("foo"), ($"b" + 2).as("foo")) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.select(($"a" + 1).as("foo"), ($"b" + 2).as("foo"))) } - @Test(expected = classOf[ValidationException]) + @Test def testFilterInvalidField() { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) // Must fail. Field foo does not exist. - t.filter($"foo" === 17) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.filter($"foo" === 17)) } @Test def testAliasStarException(): Unit = { val util = batchTestUtil() - try { - util.addTableSource[(Int, Long, String)]("Table1", '*, 'b, 'c) - fail("TableException expected") - } catch { - case _: ValidationException => // ignore - } - - try { - util.addTableSource[(Int, Long, String)]("Table3").as("*", "b", "c") - fail("ValidationException expected") - } catch { - case _: ValidationException => // ignore - } - try { - util.addTableSource[(Int, Long, String)]("Table4", 'a, 'b, 'c).select('*, 'b) - fail("ValidationException expected") - } catch { - case _: ValidationException => // ignore - } + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.addTableSource[(Int, Long, String)]("Table1", '*, 'b, 'c)) + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.addTableSource[(Int, Long, String)]("Table3").as("*", "b", "c")) + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => util.addTableSource[(Int, Long, String)]("Table4", 'a, 'b, 'c).select('*, 'b)) } - @Test(expected = classOf[ValidationException]) + @Test def testDuplicateFlattening(): Unit = { val util = batchTestUtil() val table = util.addTableSource[((Int, Long), (String, Boolean), String)]("MyTable", 'a, 'b, 'c) - table.select('a.flatten(), 'a.flatten()) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => table.select('a.flatten(), 'a.flatten())) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/CorrelateValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/CorrelateValidationTest.scala index 20605062b1e9e..ae28906e82345 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/CorrelateValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/CorrelateValidationTest.scala @@ -21,7 +21,8 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.{TableFunc1, TableTestBase} -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class CorrelateValidationTest extends TableTestBase { @@ -29,14 +30,19 @@ class CorrelateValidationTest extends TableTestBase { * Due to the improper translation of TableFunction left outer join (see CALCITE-2004), the join * predicate can only be empty or literal true (the restriction should be removed in FLINK-7865). */ - @Test(expected = classOf[ValidationException]) + @Test def testLeftOuterJoinWithPredicates(): Unit = { val util = batchTestUtil() val table = util.addTableSource[(Int, Long, String)]("MyTable", 'a, 'b, 'c) val func = new TableFunc1 - val result = table - .leftOuterJoinLateral(func('c).as('s), 'c === 's) - .select('c, 's) - util.verifyExecPlan(result) + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => { + val result = table + .leftOuterJoinLateral(func('c).as('s), 'c === 's) + .select('c, 's) + util.verifyExecPlan(result) + }) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/GroupWindowValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/GroupWindowValidationTest.scala index b0bed858099ad..45f1704e0822d 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/GroupWindowValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/GroupWindowValidationTest.scala @@ -22,7 +22,8 @@ import org.apache.flink.table.api.{Session, Slide, Tumble, ValidationException, import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.WeightedAvgWithMerge import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class GroupWindowValidationTest extends TableTestBase { @@ -30,141 +31,170 @@ class GroupWindowValidationTest extends TableTestBase { // Common test // =============================================================================================== - @Test(expected = classOf[ValidationException]) + @Test def testGroupByWithoutWindowAlias(): Unit = { val util = batchTestUtil() val table = util.addTableSource[(Long, Int, String)]('long, 'int, 'string) - table - .window(Tumble.over(5.milli).on('long).as('w)) - .groupBy('string) - .select('string, 'int.count) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + .window(Tumble.over(5.milli).on('long).as('w)) + .groupBy('string) + .select('string, 'int.count)) } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidRowTimeRef(): Unit = { val util = batchTestUtil() val table = util.addTableSource[(Long, Int, String)]('long, 'int, 'string) - table - .window(Tumble.over(5.milli).on('long).as('w)) - .groupBy('w, 'string) - .select('string, 'int.count) - .window(Slide.over(5.milli).every(1.milli).on('int).as('w2)) // 'Int does not exist in input. - .groupBy('w2) - .select('string) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + .window(Tumble.over(5.milli).on('long).as('w)) + .groupBy('w, 'string) + .select('string, 'int.count) + .window( + Slide.over(5.milli).every(1.milli).on('int).as('w2) + ) // 'Int does not exist in input. + .groupBy('w2) + .select('string)) } // =============================================================================================== // Tumbling Windows // =============================================================================================== - @Test(expected = classOf[ValidationException]) + @Test def testInvalidProcessingTimeDefinition(): Unit = { val util = batchTestUtil() // proctime is not allowed - util.addTableSource[(Int, String)]('proctime, 'int, 'string) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.addTableSource[(Int, String)]('proctime, 'int, 'string)) } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidProcessingTimeDefinition2(): Unit = { val util = batchTestUtil() // proctime is not allowed - util.addTableSource[(Long, Int, String)]('long, 'int, 'string, 'proctime) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.addTableSource[(Long, Int, String)]('long, 'int, 'string, 'proctime)) } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidEventTimeDefinition(): Unit = { val util = batchTestUtil() // definition must not extend schema - util.addTableSource[(Long, Int, String)]('long, 'int, 'string, 'rowtime) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.addTableSource[(Long, Int, String)]('long, 'int, 'string, 'rowtime)) } - @Test(expected = classOf[ValidationException]) + @Test def testTumblingGroupWindowWithInvalidUdAggArgs(): Unit = { val util = batchTestUtil() val table = util.addTableSource[(Long, Int, String)]('long, 'int, 'string) val myWeightedAvg = new WeightedAvgWithMerge - table - .window(Tumble.over(2.minutes).on('rowtime).as('w)) - .groupBy('w, 'long) - // invalid function arguments - .select(myWeightedAvg('int, 'string)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + .window(Tumble.over(2.minutes).on('rowtime).as('w)) + .groupBy('w, 'long) + // invalid function arguments + .select(myWeightedAvg('int, 'string))) } - @Test(expected = classOf[ValidationException]) + @Test def testAllTumblingGroupWindowWithInvalidUdAggArgs(): Unit = { val util = batchTestUtil() val table = util.addTableSource[(Long, Int, String)]('long, 'int, 'string) val myWeightedAvg = new WeightedAvgWithMerge - table - .window(Tumble.over(2.minutes).on('rowtime).as('w)) - .groupBy('w) - // invalid function arguments - .select(myWeightedAvg('int, 'string)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + .window(Tumble.over(2.minutes).on('rowtime).as('w)) + .groupBy('w) + // invalid function arguments + .select(myWeightedAvg('int, 'string))) } // =============================================================================================== // Sliding Windows // =============================================================================================== - @Test(expected = classOf[ValidationException]) + @Test def testSlidingGroupWindowWithInvalidUdAggArgs(): Unit = { val util = batchTestUtil() val table = util.addTableSource[(Long, Int, String)]('long, 'int, 'string) val myWeightedAvg = new WeightedAvgWithMerge - table - .window(Slide.over(2.minutes).every(1.minute).on('rowtime).as('w)) - .groupBy('w, 'long) - // invalid function arguments - .select(myWeightedAvg('int, 'string)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + .window(Slide.over(2.minutes).every(1.minute).on('rowtime).as('w)) + .groupBy('w, 'long) + // invalid function arguments + .select(myWeightedAvg('int, 'string))) } - @Test(expected = classOf[ValidationException]) + @Test def testAllSlidingGroupWindowWithInvalidUdAggArgs(): Unit = { val util = batchTestUtil() val table = util.addTableSource[(Long, Int, String)]('long, 'int, 'string) val myWeightedAvg = new WeightedAvgWithMerge - table - .window(Slide.over(2.minutes).every(1.minute).on('long).as('w)) - .groupBy('w) - // invalid function arguments - .select(myWeightedAvg('int, 'string)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + .window(Slide.over(2.minutes).every(1.minute).on('long).as('w)) + .groupBy('w) + // invalid function arguments + .select(myWeightedAvg('int, 'string))) } - @Test(expected = classOf[ValidationException]) + @Test def testSessionGroupWindowWithInvalidUdAggArgs(): Unit = { val util = batchTestUtil() val table = util.addTableSource[(Long, Int, String)]('long, 'int, 'string) val myWeightedAvg = new WeightedAvgWithMerge - table - .window(Session.withGap(2.minutes).on('rowtime).as('w)) - .groupBy('w, 'long) - // invalid function arguments - .select(myWeightedAvg('int, 'string)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + .window(Session.withGap(2.minutes).on('rowtime).as('w)) + .groupBy('w, 'long) + // invalid function arguments + .select(myWeightedAvg('int, 'string))) } - @Test(expected = classOf[ValidationException]) + @Test def testAllSessionGroupWindowWithInvalidUdAggArgs(): Unit = { val util = batchTestUtil() val table = util.addTableSource[(Long, Int, String)]('long, 'int, 'string) val myWeightedAvg = new WeightedAvgWithMerge - table - .window(Session.withGap(2.minutes).on('rowtime).as('w)) - .groupBy('w) - // invalid function arguments - .select(myWeightedAvg('int, 'string)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + .window(Session.withGap(2.minutes).on('rowtime).as('w)) + .groupBy('w) + // invalid function arguments + .select(myWeightedAvg('int, 'string))) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/JoinValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/JoinValidationTest.scala index 2012af5b04edb..860f88ec69755 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/JoinValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/JoinValidationTest.scala @@ -24,78 +24,90 @@ import org.apache.flink.table.planner.runtime.utils.CollectionBatchExecTable import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctions.PythonScalarFunction import org.apache.flink.table.planner.utils.TableTestBase -import org.junit._ +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class JoinValidationTest extends TableTestBase { - @Test(expected = classOf[ValidationException]) + @Test def testJoinNonExistingKey(): Unit = { val util = batchTestUtil() val ds1 = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) val ds2 = util.addTableSource[(Int, Long, Int, String, Long)]("Table5", 'd, 'e, 'f, 'g, 'h) - ds1 - .join(ds2) - // must fail. Field 'foo does not exist - .where('foo === 'e) - .select('c, 'g) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + ds1 + .join(ds2) + // must fail. Field 'foo does not exist + .where('foo === 'e) + .select('c, 'g)) } - @Test(expected = classOf[ValidationException]) + @Test def testJoinWithNonMatchingKeyTypes(): Unit = { val util = batchTestUtil() val ds1 = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) val ds2 = util.addTableSource[(Int, Long, Int, String, Long)]("Table5", 'd, 'e, 'f, 'g, 'h) - ds1 - .join(ds2) - // must fail. Field 'a is Int, and 'g is String - .where('a === 'g) - .select('c, 'g) - + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + ds1 + .join(ds2) + // must fail. Field 'a is Int, and 'g is String + .where('a === 'g) + .select('c, 'g)) } - @Test(expected = classOf[ValidationException]) + @Test def testJoinWithAmbiguousFields(): Unit = { val util = batchTestUtil() val ds1 = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) val ds2 = util.addTableSource[(Int, Long, Int, String, Long)]("Table5", 'd, 'e, 'f, 'g, 'c) - ds1 - .join(ds2) - // must fail. Both inputs share the same field 'c - .where('a === 'd) - .select('c, 'g) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + ds1 + .join(ds2) + // must fail. Both inputs share the same field 'c + .where('a === 'd) + .select('c, 'g)) } - @Test(expected = classOf[ValidationException]) + @Test def testLeftJoinNoEquiJoinPredicate(): Unit = { val util = batchTestUtil() val ds1 = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) val ds2 = util.addTableSource[(Int, Long, Int, String, Long)]("Table5", 'd, 'e, 'f, 'g, 'h) - ds2.leftOuterJoin(ds1, 'b < 'd).select('c, 'g) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds2.leftOuterJoin(ds1, 'b < 'd).select('c, 'g)) } - @Test(expected = classOf[ValidationException]) + @Test def testRightJoinNoEquiJoinPredicate(): Unit = { val util = batchTestUtil() val ds1 = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) val ds2 = util.addTableSource[(Int, Long, Int, String, Long)]("Table5", 'd, 'e, 'f, 'g, 'h) - ds2.rightOuterJoin(ds1, 'b < 'd).select('c, 'g) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds2.rightOuterJoin(ds1, 'b < 'd).select('c, 'g)) } - @Test(expected = classOf[ValidationException]) + @Test def testFullJoinNoEquiJoinPredicate(): Unit = { val util = batchTestUtil() val ds1 = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) val ds2 = util.addTableSource[(Int, Long, Int, String, Long)]("Table5", 'd, 'e, 'f, 'g, 'h) - ds2.fullOuterJoin(ds1, 'b < 'd).select('c, 'g) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds2.fullOuterJoin(ds1, 'b < 'd).select('c, 'g)) } - @Test(expected = classOf[ValidationException]) + @Test def testJoinTablesFromDifferentEnvs(): Unit = { val settings = EnvironmentSettings.newInstance().inBatchMode().build() val tEnv1 = TableEnvironmentImpl.create(settings) @@ -104,10 +116,11 @@ class JoinValidationTest extends TableTestBase { val ds2 = CollectionBatchExecTable.get5TupleDataSet(tEnv2, "d, e, f, g, c") // Must fail. Tables are bound to different TableEnvironments. - ds1.join(ds2).where('b === 'e).select('c, 'g) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds1.join(ds2).where('b === 'e).select('c, 'g)) } - @Test(expected = classOf[ValidationException]) + @Test def testJoinTablesFromDifferentEnvsJava() { val settings = EnvironmentSettings.newInstance().inBatchMode().build() val tEnv1 = TableEnvironmentImpl.create(settings) @@ -115,16 +128,18 @@ class JoinValidationTest extends TableTestBase { val ds1 = CollectionBatchExecTable.getSmall3TupleDataSet(tEnv1, "a, b, c") val ds2 = CollectionBatchExecTable.get5TupleDataSet(tEnv2, "d, e, f, g, c") // Must fail. Tables are bound to different TableEnvironments. - ds1.join(ds2).where($"a" === $"d").select($"g".count) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds1.join(ds2).where($"a" === $"d").select($"g".count)) } - @Test(expected = classOf[TableException]) + @Test def testOuterJoinWithPythonFunctionInCondition(): Unit = { val util = batchTestUtil() val left = util.addTableSource[(Int, Long, String)]("left", 'a, 'b, 'c) val right = util.addTableSource[(Int, Long, String)]("right", 'd, 'e, 'f) val pyFunc = new PythonScalarFunction("pyFunc") val result = left.leftOuterJoin(right, 'a === 'd && pyFunc('a, 'd) === 'a + 'd) - util.verifyExecPlan(result) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(result)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/OverWindowValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/OverWindowValidationTest.scala index 40735f950de9b..060c58b1bf9bf 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/OverWindowValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/OverWindowValidationTest.scala @@ -22,29 +22,34 @@ import org.apache.flink.table.api.{Tumble, ValidationException, _} import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedAggFunctions.OverAgg0 import org.apache.flink.table.planner.utils.TableTestBase -import org.junit._ +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class OverWindowValidationTest extends TableTestBase { /** OVER clause is necessary for [[OverAgg0]] window function. */ - @Test(expected = classOf[ValidationException]) + @Test def testInvalidOverAggregation(): Unit = { val util = batchTestUtil() val t = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) val overAgg = new OverAgg0 - t.select('c.count, overAgg('b, 'a)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => t.select('c.count, overAgg('b, 'a))) } /** OVER clause is necessary for [[OverAgg0]] window function. */ - @Test(expected = classOf[ValidationException]) + @Test def testInvalidOverAggregation2(): Unit = { val util = batchTestUtil() val table = util.addTableSource[(Long, Int, String)]('long, 'int, 'string) val overAgg = new OverAgg0 - table - .window(Tumble.over(5.milli).on('long).as('w)) - .groupBy('string, 'w) - .select(overAgg('long, 'int)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + .window(Tumble.over(5.milli).on('long).as('w)) + .groupBy('string, 'w) + .select(overAgg('long, 'int))) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/SetOperatorsValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/SetOperatorsValidationTest.scala index 444a63f97290a..acff005d7af34 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/SetOperatorsValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/SetOperatorsValidationTest.scala @@ -23,21 +23,23 @@ import org.apache.flink.table.api.internal.TableEnvironmentImpl import org.apache.flink.table.planner.runtime.utils.CollectionBatchExecTable import org.apache.flink.table.planner.utils.TableTestBase -import org.junit._ +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class SetOperatorsValidationTest extends TableTestBase { - @Test(expected = classOf[ValidationException]) + @Test def testUnionDifferentColumnSize(): Unit = { val util = batchTestUtil() val ds1 = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) val ds2 = util.addTableSource[(Int, Long, Int, String, Long)]("Table5", 'a, 'b, 'd, 'c, 'e) // must fail. Union inputs have different column size. - ds1.unionAll(ds2) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds1.unionAll(ds2)) } - @Test(expected = classOf[ValidationException]) + @Test def testUnionDifferentFieldTypes(): Unit = { val util = batchTestUtil() val ds1 = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) @@ -46,10 +48,11 @@ class SetOperatorsValidationTest extends TableTestBase { .select('a, 'b, 'c) // must fail. Union inputs have different field types. - ds1.unionAll(ds2) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds1.unionAll(ds2)) } - @Test(expected = classOf[ValidationException]) + @Test def testUnionTablesFromDifferentEnvs(): Unit = { val settings = EnvironmentSettings.newInstance().inBatchMode().build() val tEnv1 = TableEnvironmentImpl.create(settings) @@ -59,10 +62,11 @@ class SetOperatorsValidationTest extends TableTestBase { val ds2 = CollectionBatchExecTable.getSmall3TupleDataSet(tEnv2) // Must fail. Tables are bound to different TableEnvironments. - ds1.unionAll(ds2).select('c) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds1.unionAll(ds2).select('c)) } - @Test(expected = classOf[ValidationException]) + @Test def testMinusDifferentFieldTypes(): Unit = { val util = batchTestUtil() val ds1 = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) @@ -71,10 +75,11 @@ class SetOperatorsValidationTest extends TableTestBase { .select('a, 'b, 'c) // must fail. Minus inputs have different field types. - ds1.minus(ds2) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds1.minus(ds2)) } - @Test(expected = classOf[ValidationException]) + @Test def testMinusAllTablesFromDifferentEnvs(): Unit = { val settings = EnvironmentSettings.newInstance().inBatchMode().build() val tEnv1 = TableEnvironmentImpl.create(settings) @@ -84,10 +89,11 @@ class SetOperatorsValidationTest extends TableTestBase { val ds2 = CollectionBatchExecTable.getSmall3TupleDataSet(tEnv2) // Must fail. Tables are bound to different TableEnvironments. - ds1.minusAll(ds2).select('c) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds1.minusAll(ds2).select('c)) } - @Test(expected = classOf[ValidationException]) + @Test def testIntersectWithDifferentFieldTypes(): Unit = { val util = batchTestUtil() val ds1 = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) @@ -96,10 +102,11 @@ class SetOperatorsValidationTest extends TableTestBase { .select('a, 'b, 'c) // must fail. Intersect inputs have different field types. - ds1.intersect(ds2) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds1.intersect(ds2)) } - @Test(expected = classOf[ValidationException]) + @Test def testIntersectTablesFromDifferentEnvs(): Unit = { val settings = EnvironmentSettings.newInstance().inBatchMode().build() val tEnv1 = TableEnvironmentImpl.create(settings) @@ -109,6 +116,7 @@ class SetOperatorsValidationTest extends TableTestBase { val ds2 = CollectionBatchExecTable.getSmall3TupleDataSet(tEnv2) // Must fail. Tables are bound to different TableEnvironments. - ds1.intersect(ds2).select('c) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds1.intersect(ds2).select('c)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/SortValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/SortValidationTest.scala index cfe82e50b89b5..823a1758c663e 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/SortValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/batch/table/validation/SortValidationTest.scala @@ -21,31 +21,35 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit._ +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class SortValidationTest extends TableTestBase { - @Test(expected = classOf[ValidationException]) + @Test def testFetchBeforeOffset(): Unit = { val util = batchTestUtil() val ds = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - ds.orderBy('a.asc).fetch(5).offset(10) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds.orderBy('a.asc).fetch(5).offset(10)) } - @Test(expected = classOf[ValidationException]) + @Test def testOffsetBeforeOffset(): Unit = { val util = batchTestUtil() val ds = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - ds.orderBy('a.asc).offset(10).offset(5) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds.orderBy('a.asc).offset(10).offset(5)) } - @Test(expected = classOf[ValidationException]) + @Test def testNegativeFetch(): Unit = { val util = batchTestUtil() val ds = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - ds.orderBy('a.asc).offset(-1) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds.orderBy('a.asc).offset(-1)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/AggregateReduceGroupingTestBase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/AggregateReduceGroupingTestBase.scala index 16542dde1cceb..6b5a2619b6ba7 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/AggregateReduceGroupingTestBase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/AggregateReduceGroupingTestBase.scala @@ -27,12 +27,12 @@ import org.apache.flink.table.planner.utils.{BatchTableTestUtil, TableTestBase} import com.google.common.collect.ImmutableSet import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} abstract class AggregateReduceGroupingTestBase(withExecPlan: Boolean) extends TableTestBase { protected val util: BatchTableTestUtil = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource( "T1", diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/DistinctAggregateTestBase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/DistinctAggregateTestBase.scala index fc761ba9d1ba0..cba75862fad26 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/DistinctAggregateTestBase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/DistinctAggregateTestBase.scala @@ -22,12 +22,13 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.{BatchTableTestUtil, TableTestBase} -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.{BeforeEach, Test} abstract class DistinctAggregateTestBase(withExecPlan: Boolean) extends TableTestBase { protected val util: BatchTableTestUtil = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource[(Int, Long, Int)]("MyTable", 'a, 'b, 'c) util.addTableSource[(Int, Long, String, String, String)]("MyTable2", 'a, 'b, 'c, 'd, 'e) @@ -192,7 +193,7 @@ abstract class DistinctAggregateTestBase(withExecPlan: Boolean) extends TableTes verifyPlan(sqlQuery) } - @Test(expected = classOf[RuntimeException]) + @Test def testTooManyDistinctAggOnDifferentColumn(): Unit = { // max group count must be less than 64 val fieldNames = (0 until 64).map(i => s"f$i").toArray @@ -203,7 +204,8 @@ abstract class DistinctAggregateTestBase(withExecPlan: Boolean) extends TableTes val maxList = fieldNames.map(f => s"MAX($f)").mkString(", ") val sqlQuery = s"SELECT $distinctList, $maxList FROM MyTable64" - verifyPlan(sqlQuery) + assertThatExceptionOfType(classOf[RuntimeException]) + .isThrownBy(() => verifyPlan(sqlQuery)) } private def verifyPlan(sqlQuery: String): Unit = { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/JoinReorderTestBase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/JoinReorderTestBase.scala index ee563d138b837..02aae7ee59278 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/JoinReorderTestBase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/JoinReorderTestBase.scala @@ -24,9 +24,9 @@ import org.apache.flink.table.plan.stats.{ColumnStats, TableStats} import org.apache.flink.table.planner.plan.rules.logical.JoinDeriveNullFilterRule import org.apache.flink.table.planner.plan.stats.FlinkStatistic import org.apache.flink.table.planner.utils.{TableTestBase, TableTestUtil} +import org.apache.flink.testutils.junit.extensions.parameterized.Parameters -import org.junit.{Before, Test} -import org.junit.runners.Parameterized +import org.junit.jupiter.api.{BeforeEach, TestTemplate} import java.util @@ -44,7 +44,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes protected def getTableTestUtil: TableTestUtil - @Before + @BeforeEach def setup(): Unit = { val types = Array[TypeInformation[_]](Types.INT, Types.LONG, Types.STRING) @@ -145,7 +145,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes } - @Test + @TestTemplate def testStarJoinCondition1(): Unit = { val sql = s""" @@ -155,7 +155,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testStarJoinCondition2(): Unit = { val sql = s""" @@ -165,7 +165,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testBushyJoinCondition1(): Unit = { val sql = s""" @@ -175,7 +175,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testBushyJoinCondition2(): Unit = { val sql = s""" @@ -185,7 +185,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testWithoutColumnStats(): Unit = { val sql = s""" @@ -195,7 +195,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testJoinWithProject(): Unit = { val sql = s""" @@ -209,7 +209,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testJoinWithFilter(): Unit = { val sql = s""" @@ -222,7 +222,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testAllInnerJoin(): Unit = { val sql = s""" @@ -236,7 +236,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testInnerAndLeftOuterJoin(): Unit = { val sql = s""" @@ -250,7 +250,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testInnerAndRightOuterJoin(): Unit = { val sql = s""" @@ -264,7 +264,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testInnerAndFullOuterJoin(): Unit = { val sql = s""" @@ -277,7 +277,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testAllLeftOuterJoin(): Unit = { val sql = s""" @@ -292,7 +292,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testAllRightOuterJoin(): Unit = { val sql = s""" @@ -306,7 +306,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testAllFullOuterJoin(): Unit = { val sql = s""" @@ -320,7 +320,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testInnerJoinLeftOuterJoinInnerJoinLeftOuterJoin(): Unit = { val sql = s""" @@ -334,7 +334,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testLeftOuterJoinInnerJoinLeftOuterJoinInnerJoin(): Unit = { val sql = s""" @@ -348,7 +348,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testInnerJoinRightOuterJoinInnerJoinRightOuterJoin(): Unit = { val sql = s""" @@ -362,7 +362,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testRightOuterJoinInnerJoinRightOuterJoinInnerJoin(): Unit = { val sql = s""" @@ -376,7 +376,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testInnerJoinSemiJoin(): Unit = { val sql = s""" @@ -390,7 +390,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testInnerJoinAntiJoin(): Unit = { val sql = s""" @@ -404,14 +404,14 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testCrossJoin(): Unit = { val sql = "SELECT * FROM T1, T2, T3, T4, T5" // All table can reorder. util.verifyRelPlan(sql) } - @Test + @TestTemplate def testInnerJoinCrossJoin(): Unit = { val sql = s""" @@ -422,7 +422,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testInnerJoinLeftOuterJoinCrossJoin(): Unit = { val sql = s""" @@ -433,7 +433,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testInnerJoinWithBushyTypeJoinCondition(): Unit = { // This case is to test whether can build a bushy join tree. // If variable isBushyJoinReorder is true, it can be built to @@ -450,7 +450,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes util.verifyRelPlan(sql) } - @Test + @TestTemplate def testDeriveNullFilterAfterJoinReorder(): Unit = { val types = Array[TypeInformation[_]](Types.INT, Types.LONG) val builderA = ColumnStats.Builder @@ -524,7 +524,7 @@ abstract class JoinReorderTestBase(isBushyJoinReorder: Boolean) extends TableTes } object JoinReorderTestBase { - @Parameterized.Parameters(name = "isBushyJoinReorder={0}") + @Parameters(name = "isBushyJoinReorder={0}") def parameters(): util.Collection[Boolean] = { util.Arrays.asList(true, false) } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/PartialInsertTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/PartialInsertTest.scala index 35405ebd00807..2c91b5b421fc2 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/PartialInsertTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/PartialInsertTest.scala @@ -20,12 +20,13 @@ package org.apache.flink.table.planner.plan.common import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.assertj.core.api.Assertions.{assertThatExceptionOfType, assertThatThrownBy} +import org.junit.jupiter.api.TestTemplate +import org.junit.jupiter.api.extension.ExtendWith -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class PartialInsertTest(isBatch: Boolean) extends TableTestBase { private val util = if (isBatch) batchTestUtil() else streamTestUtil() @@ -103,36 +104,36 @@ class PartialInsertTest(isBatch: Boolean) extends TableTestBase { | 'writable-metadata' = 'f:BIGINT, g:INT' |)""".stripMargin) - @Test + @TestTemplate def testPartialInsertWithComplexReorder(): Unit = { util.verifyRelPlanInsert( "INSERT INTO sink (b,e,a,g,f,c,d) " + "SELECT b,e,a,456,123,c,d FROM MyTable GROUP BY a,b,c,d,e") } - @Test + @TestTemplate def testPartialInsertWithComplexReorderAndComputedColumn(): Unit = { util.verifyRelPlanInsert( "INSERT INTO partitioned_sink (e,a,g,f,c,d) " + "SELECT e,a,456,123,c,d FROM MyTable GROUP BY a,b,c,d,e") } - @Test + @TestTemplate def testPartialInsertWithUnion(): Unit = { testPartialInsertWithSetOperator("UNION") } - @Test + @TestTemplate def testPartialInsertWithUnionAll(): Unit = { testPartialInsertWithSetOperator("UNION ALL") } - @Test + @TestTemplate def testPartialInsertWithIntersectAll(): Unit = { testPartialInsertWithSetOperator("INTERSECT ALL") } - @Test + @TestTemplate def testPartialInsertWithExceptAll(): Unit = { testPartialInsertWithSetOperator("EXCEPT ALL") } @@ -145,7 +146,7 @@ class PartialInsertTest(isBatch: Boolean) extends TableTestBase { "SELECT e,a,789,456,c,d FROM MyTable GROUP BY a,b,c,d,e ") } - @Test + @TestTemplate def testPartialInsertWithUnionAllNested(): Unit = { util.verifyRelPlanInsert( "INSERT INTO partitioned_sink (e,a,g,f,c,d) " + @@ -156,14 +157,14 @@ class PartialInsertTest(isBatch: Boolean) extends TableTestBase { "SELECT e,a,123,456,c,d FROM MyTable GROUP BY a,b,c,d,e ") } - @Test + @TestTemplate def testPartialInsertWithOrderBy(): Unit = { util.verifyRelPlanInsert( "INSERT INTO partitioned_sink (e,a,g,f,c,d) " + "SELECT e,a,456,123,c,d FROM MyTable ORDER BY a,e,c,d") } - @Test + @TestTemplate def testPartialInsertWithPersistedMetadata(): Unit = { util.verifyRelPlanInsert( "INSERT INTO metadata_sink (a,b,c,d,e,f) " + @@ -171,46 +172,53 @@ class PartialInsertTest(isBatch: Boolean) extends TableTestBase { ) } - @Test + @TestTemplate def testPartialInsertWithVirtualMetaDataColumn(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "SQL validation failed. At line 1, column 38: Unknown target column 'g'") - util.verifyRelPlanInsert( - "INSERT INTO metadata_sink (a,b,c,d,e,g) " + - "SELECT a,b,c,d,e,123 FROM MyTable" - ) + assertThatThrownBy( + () => + util.verifyRelPlanInsert( + "INSERT INTO metadata_sink (a,b,c,d,e,g) " + + "SELECT a,b,c,d,e,123 FROM MyTable" + )) + .hasMessageContaining( + "SQL validation failed. At line 1, column 38: Unknown target column 'g'") + .isInstanceOf[ValidationException] } - @Test + @TestTemplate def testPartialInsertWithComputedColumn(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "SQL validation failed. At line 1, column 38: Unknown target column 'h'") - util.verifyRelPlanInsert( - "INSERT INTO metadata_sink (a,b,c,d,e,h) " + - "SELECT a,b,c,d,e,123 FROM MyTable" - ) + assertThatThrownBy( + () => + util.verifyRelPlanInsert( + "INSERT INTO metadata_sink (a,b,c,d,e,h) " + + "SELECT a,b,c,d,e,123 FROM MyTable" + )) + .hasMessageContaining( + "SQL validation failed. At line 1, column 38: Unknown target column 'h'") + .isInstanceOf[ValidationException] } - @Test + @TestTemplate def testPartialInsertWithGroupBy(): Unit = { util.verifyExplainInsert( "INSERT INTO partitioned_sink (e,a,d) " + "SELECT e,a,d FROM MyTable GROUP BY a,b,c,d,e") } - @Test(expected = classOf[ValidationException]) + @TestTemplate def testPartialInsertCompositeType(): Unit = { // TODO this should be updated after FLINK-31301 fixed - util.verifyExplainInsert( - "INSERT INTO complex_type_sink (a,b.b1,c.c2,f) " + - "SELECT a,b.b1,c.c2,f FROM complex_type_src") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + util.verifyExplainInsert( + "INSERT INTO complex_type_sink (a,b.b1,c.c2,f) " + + "SELECT a,b.b1,c.c2,f FROM complex_type_src")) } } object PartialInsertTest { - @Parameterized.Parameters(name = "isBatch: {0}") + @Parameters(name = "isBatch: {0}") def parameters(): java.util.Collection[Boolean] = { java.util.Arrays.asList(true, false) } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/TableFactoryTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/TableFactoryTest.scala index 8a15558ff3dd6..9d5003c08da4b 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/TableFactoryTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/TableFactoryTest.scala @@ -22,25 +22,26 @@ import org.apache.flink.table.factories.TableFactory import org.apache.flink.table.planner.factories.utils.TestCollectionTableFactory import org.apache.flink.table.planner.plan.utils.TestContextTableFactory import org.apache.flink.table.planner.utils.TableTestBase +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.junit.{Assert, Before, Test} -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith import java.util.Optional -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class TableFactoryTest(isBatch: Boolean) extends TableTestBase { private val util = if (isBatch) batchTestUtil() else streamTestUtil() - @Before + @BeforeEach def before(): Unit = { // we should clean the data to avoid serialization exception due to dirty data TestCollectionTableFactory.reset() } - @Test + @TestTemplate def testTableSourceSinkFactory(): Unit = { val factory = new TestContextTableFactory( ObjectIdentifier.of("cat", "default", "t1"), @@ -87,13 +88,13 @@ class TableFactoryTest(isBatch: Boolean) extends TableTestBase { util.tableEnv.executeSql(sinkDDL) util.tableEnv.explainSql(query) - Assert.assertTrue(factory.hasInvokedSource) - Assert.assertTrue(factory.hasInvokedSink) + assertThat(factory.hasInvokedSource).isTrue + assertThat(factory.hasInvokedSink).isTrue } } object TableFactoryTest { - @Parameterized.Parameters(name = "isBatch: {0}") + @Parameters(name = "isBatch: {0}") def parameters(): java.util.Collection[Boolean] = { java.util.Arrays.asList(true, false) } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/UnnestTestBase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/UnnestTestBase.scala index c119dca088429..00e87af032e8c 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/UnnestTestBase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/UnnestTestBase.scala @@ -22,7 +22,7 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.{TableTestBase, TableTestUtil} -import org.junit.Test +import org.junit.jupiter.api.Test import java.sql.Timestamp diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/ViewsExpandingTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/ViewsExpandingTest.scala index 6420991531960..0e5e449d7ea5e 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/ViewsExpandingTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/common/ViewsExpandingTest.scala @@ -23,20 +23,18 @@ import org.apache.flink.table.catalog._ import org.apache.flink.table.functions.ScalarFunction import org.apache.flink.table.planner.plan.common.ViewsExpandingTest.PrimitiveScalarFunction import org.apache.flink.table.planner.utils.{TableFunc0, TableTestBase, TableTestUtil, TableTestUtilBase} +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.hamcrest.CoreMatchers.is -import org.junit.Assert.assertThat -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.junit.runners.Parameterized.Parameters +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.TestTemplate +import org.junit.jupiter.api.extension.ExtendWith import java.util -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class ViewsExpandingTest(tableTestUtil: TableTestBase => TableTestUtil) extends TableTestBase { - @Test + @TestTemplate def testMixedSqlTableViewExpanding(): Unit = { val tableUtil = tableTestUtil(this) val tableEnv = tableUtil.tableEnv @@ -56,7 +54,7 @@ class ViewsExpandingTest(tableTestUtil: TableTestBase => TableTestUtil) extends tableUtil.verifyExecPlan("select * from view4") } - @Test + @TestTemplate def testTableApiExpanding(): Unit = { val tableUtil = tableTestUtil(this) val tableEnv = tableUtil.tableEnv @@ -69,7 +67,7 @@ class ViewsExpandingTest(tableTestUtil: TableTestBase => TableTestUtil) extends tableUtil.verifyExecPlan(query) } - @Test + @TestTemplate def testSqlExpanding(): Unit = { val tableUtil = tableTestUtil(this) val tableEnv = tableUtil.tableEnv @@ -92,7 +90,7 @@ class ViewsExpandingTest(tableTestUtil: TableTestBase => TableTestUtil) extends tableUtil.verifyExecPlan(query) } - @Test + @TestTemplate def testViewExpandingWithMismatchRowType(): Unit = { val tableUtil = tableTestUtil(this) val tableEnv = tableUtil.tableEnv @@ -117,7 +115,7 @@ class ViewsExpandingTest(tableTestUtil: TableTestBase => TableTestUtil) extends tableUtil.verifyExecPlan("select * from view1") } - @Test + @TestTemplate def testViewExpandingWithLateralTableFunction(): Unit = { val tableUtil = tableTestUtil(this) val tableEnv = tableUtil.tableEnv @@ -135,7 +133,7 @@ class ViewsExpandingTest(tableTestUtil: TableTestBase => TableTestUtil) extends tableUtil.verifyExecPlan("select * from tmp_view") } - @Test + @TestTemplate def testViewExpandingWithBuiltinFunction(): Unit = { val tableUtil = tableTestUtil(this) val tableEnv = tableUtil.tableEnv @@ -152,11 +150,11 @@ class ViewsExpandingTest(tableTestUtil: TableTestBase => TableTestUtil) extends .get() .getTable(objectID.toObjectPath) assertThat( - view.asInstanceOf[CatalogView].getExpandedQuery, - is("SELECT `CONCAT`('a', 'bc', 'def')")) + view.asInstanceOf[CatalogView].getExpandedQuery + ).isEqualTo("SELECT `CONCAT`('a', 'bc', 'def')") } - @Test + @TestTemplate def testViewExpandingWithUDF(): Unit = { val tableUtil = tableTestUtil(this) val tableEnv = tableUtil.tableEnv @@ -174,11 +172,11 @@ class ViewsExpandingTest(tableTestUtil: TableTestBase => TableTestUtil) extends .get() .getTable(objectID.toObjectPath) assertThat( - view.asInstanceOf[CatalogView].getExpandedQuery, - is("SELECT `default_catalog`.`default_database`.`func`(1, 2, 'abc')")) + view.asInstanceOf[CatalogView].getExpandedQuery + ).isEqualTo("SELECT `default_catalog`.`default_database`.`func`(1, 2, 'abc')") } - @Test + @TestTemplate def testExpandQueryWithSystemAlias(): Unit = { val tableUtil = tableTestUtil(this) val tableEnv = tableUtil.tableEnv @@ -202,15 +200,14 @@ class ViewsExpandingTest(tableTestUtil: TableTestBase => TableTestUtil) extends .get() .getTable(objectID.toObjectPath) assertThat( - view.asInstanceOf[CatalogView].getExpandedQuery, - is( - "SELECT *\n" - + "FROM (SELECT `source`.`f0`, " - + "ROW_NUMBER() " - + "OVER (PARTITION BY `source`.`f0` ORDER BY `source`.`f0` DESC) AS `rowNum`\n" - + "FROM `default_catalog`.`default_database`.`source`)\n" - + "WHERE `rowNum` = 1") - ) + view.asInstanceOf[CatalogView].getExpandedQuery + ).isEqualTo( + "SELECT *\n" + + "FROM (SELECT `source`.`f0`, " + + "ROW_NUMBER() " + + "OVER (PARTITION BY `source`.`f0` ORDER BY `source`.`f0` DESC) AS `rowNum`\n" + + "FROM `default_catalog`.`default_database`.`source`)\n" + + "WHERE `rowNum` = 1") } private def createSqlView(originTable: String): CatalogView = { @@ -233,8 +230,8 @@ class ViewsExpandingTest(tableTestUtil: TableTestBase => TableTestUtil) extends object ViewsExpandingTest { @Parameters - def parameters(): Array[TableTestBase => TableTestUtilBase] = { - Array(_.batchTestUtil(), _.streamTestUtil()) + def parameters(): util.Collection[TableTestBase => TableTestUtilBase] = { + util.Arrays.asList(_.batchTestUtil(), _.streamTestUtil()) } // -------------------------------------------------------------------------------------------- diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/hint/OptionsHintTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/hint/OptionsHintTest.scala index f66034fec7284..aad9d7b9a47de 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/hint/OptionsHintTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/hint/OptionsHintTest.scala @@ -25,23 +25,22 @@ import org.apache.flink.table.planner.JHashMap import org.apache.flink.table.planner.plan.hint.OptionsHintTest.{IS_BOUNDED, Param} import org.apache.flink.table.planner.plan.nodes.calcite.LogicalLegacySink import org.apache.flink.table.planner.utils.{OptionsTableSink, TableTestBase, TableTestUtil} +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.assertj.core.api.Assertions.assertThatThrownBy -import org.hamcrest.Matchers._ -import org.junit.{Before, Test} -import org.junit.Assert.{assertEquals, assertThat} -import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.junit.runners.Parameterized.Parameters +import org.assertj.core.api.Assertions.{assertThat, assertThatThrownBy} +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith + +import java.util import scala.collection.JavaConversions._ -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class OptionsHintTest(param: Param) extends TableTestBase { private val util = param.utilSupplier.apply(this) private val is_bounded = param.isBounded - @Before + @BeforeEach def before(): Unit = { util.addTable(s""" |create table t1( @@ -70,18 +69,19 @@ class OptionsHintTest(param: Param) extends TableTestBase { """.stripMargin) } - @Test + @TestTemplate def testOptionsWithGlobalConfDisabled(): Unit = { util.tableEnv.getConfig .set(TableConfigOptions.TABLE_DYNAMIC_TABLE_OPTIONS_ENABLED, Boolean.box(false)) - expectedException.expect(isA(classOf[ValidationException])) - expectedException.expectMessage( - s"OPTIONS hint is allowed only when " + + assertThatThrownBy( + () => util.verifyExecPlan("select * from t1/*+ OPTIONS(connector='COLLECTION', k2='#v2') */")) + .hasMessageContaining(s"OPTIONS hint is allowed only when " + s"${TableConfigOptions.TABLE_DYNAMIC_TABLE_OPTIONS_ENABLED.key} is set to true") - util.verifyExecPlan("select * from t1/*+ OPTIONS(connector='COLLECTION', k2='#v2') */") + .isInstanceOf[ValidationException] } - @Test + @TestTemplate def testInsertWithDynamicOptions(): Unit = { val sql = s""" @@ -92,23 +92,24 @@ class OptionsHintTest(param: Param) extends TableTestBase { stmtSet.addInsertSql(sql) val testStmtSet = stmtSet.asInstanceOf[StatementSetImpl[_]] val relNodes = testStmtSet.getOperations.map(util.getPlanner.translateToRel) - assertThat(relNodes.length, is(1)) + assertThat(relNodes.length).isOne assert(relNodes.head.isInstanceOf[LogicalLegacySink]) val sink = relNodes.head.asInstanceOf[LogicalLegacySink] - assertEquals("{k1=#v1, k2=v2, k5=v5}", sink.sink.asInstanceOf[OptionsTableSink].props.toString) + assertThat(sink.sink.asInstanceOf[OptionsTableSink].props.toString) + .isEqualTo("{k1=#v1, k2=v2, k5=v5}") } - @Test + @TestTemplate def testAppendOptions(): Unit = { util.verifyExecPlan("select * from t1/*+ OPTIONS(k5='v5', 'a.b.c'='fakeVal') */") } - @Test + @TestTemplate def testOverrideOptions(): Unit = { util.verifyExecPlan("select * from t1/*+ OPTIONS(k1='#v1', k2='#v2') */") } - @Test + @TestTemplate def testJoinWithAppendedOptions(): Unit = { val sql = s""" @@ -121,7 +122,7 @@ class OptionsHintTest(param: Param) extends TableTestBase { util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinWithOverriddenOptions(): Unit = { val sql = s""" @@ -134,7 +135,7 @@ class OptionsHintTest(param: Param) extends TableTestBase { util.verifyExecPlan(sql) } - @Test + @TestTemplate def testOptionsHintOnTableApiView(): Unit = { val view1 = util.tableEnv.sqlQuery("select * from t1 join t2 on t1.a = t2.d") util.tableEnv.createTemporaryView("view1", view1) @@ -146,7 +147,7 @@ class OptionsHintTest(param: Param) extends TableTestBase { .isInstanceOf[ValidationException] } - @Test + @TestTemplate def testOptionsHintOnSQLView(): Unit = { // Equivalent SQL: // select * from t1 join t2 on t1.a = t2.d @@ -182,7 +183,7 @@ class OptionsHintTest(param: Param) extends TableTestBase { .isInstanceOf[ValidationException] } - @Test + @TestTemplate def testOptionsHintInsideView(): Unit = { util.tableEnv.executeSql( "create view v1 as select * from t1 /*+ OPTIONS(k1='#v111', k4='#v444')*/") @@ -199,8 +200,10 @@ object OptionsHintTest { override def toString: String = s"$IS_BOUNDED=$isBounded" } - @Parameters(name = "{index}: {0}") - def parameters(): Array[Param] = { - Array(Param(_.batchTestUtil(), isBounded = true), Param(_.streamTestUtil(), isBounded = false)) + @Parameters(name = "{0}") + def parameters(): util.Collection[Param] = { + util.Arrays.asList( + Param(_.batchTestUtil(), isBounded = true), + Param(_.streamTestUtil(), isBounded = false)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/AggregateReduceGroupingRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/AggregateReduceGroupingRuleTest.scala index e122196f95f8b..28ca3b7ecee32 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/AggregateReduceGroupingRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/AggregateReduceGroupingRuleTest.scala @@ -21,12 +21,12 @@ import org.apache.flink.table.planner.plan.common.AggregateReduceGroupingTestBas import org.apache.flink.table.planner.plan.optimize.program.FlinkBatchProgram import org.apache.calcite.tools.RuleSets -import org.junit.Before +import org.junit.jupiter.api.BeforeEach /** Test for [[AggregateReduceGroupingRule]]. */ class AggregateReduceGroupingRuleTest extends AggregateReduceGroupingTestBase(false) { - @Before + @BeforeEach override def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.LOGICAL_REWRITE) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CalcPruneAggregateCallRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CalcPruneAggregateCallRuleTest.scala index 2bb626855395c..08ab09fe3d920 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CalcPruneAggregateCallRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CalcPruneAggregateCallRuleTest.scala @@ -22,10 +22,12 @@ import org.apache.flink.table.planner.plan.optimize.program.{FlinkBatchProgram, import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.rel.rules.CoreRules import org.apache.calcite.tools.RuleSets +import org.junit.jupiter.api.BeforeEach /** Test for [[PruneAggregateCallRule]]#CALC_ON_AGGREGATE. */ class CalcPruneAggregateCallRuleTest extends PruneAggregateCallRuleTestBase { + @BeforeEach override def setup(): Unit = { super.setup() util.buildBatchProgram(FlinkBatchProgram.LOGICAL) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CalcPythonCorrelateTransposeRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CalcPythonCorrelateTransposeRuleTest.scala index 45b3d773f4514..ba5c2e075ea76 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CalcPythonCorrelateTransposeRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CalcPythonCorrelateTransposeRuleTest.scala @@ -26,13 +26,13 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctio import org.apache.flink.table.planner.utils.{MockPythonTableFunction, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} class CalcPythonCorrelateTransposeRuleTest extends TableTestBase { private val util = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[StreamOptimizeContext]() // query decorrelation diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CalcRankTransposeRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CalcRankTransposeRuleTest.scala index b499d13f47c9d..1b36fd04333f4 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CalcRankTransposeRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CalcRankTransposeRuleTest.scala @@ -22,13 +22,13 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.optimize.program.FlinkStreamProgram import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[CalcRankTransposeRule]]. */ class CalcRankTransposeRuleTest extends TableTestBase { private val util = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildStreamProgram(FlinkStreamProgram.PHYSICAL) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ConvertToNotInOrInRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ConvertToNotInOrInRuleTest.scala index ffdf8f95ef124..83f022d536352 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ConvertToNotInOrInRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ConvertToNotInOrInRuleTest.scala @@ -24,13 +24,13 @@ import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[ConvertToNotInOrInRule]]. */ class ConvertToNotInOrInRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CorrelateSortToRankRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CorrelateSortToRankRuleTest.scala index e8836883f5738..e47f6090fc6cb 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CorrelateSortToRankRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/CorrelateSortToRankRuleTest.scala @@ -22,13 +22,13 @@ import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[CalcRankTransposeRule]]. */ class CorrelateSortToRankRuleTest extends TableTestBase { private val util = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[StreamOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/DecomposeGroupingSetsRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/DecomposeGroupingSetsRuleTest.scala index 2391abdc0a3a7..4b011eb459f62 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/DecomposeGroupingSetsRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/DecomposeGroupingSetsRuleTest.scala @@ -23,7 +23,8 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.optimize.program.FlinkBatchProgram import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test /** Test for [[DecomposeGroupingSetsRule]]. */ class DecomposeGroupingSetsRuleTest extends TableTestBase { @@ -126,7 +127,7 @@ class DecomposeGroupingSetsRuleTest extends TableTestBase { util.verifyRelPlan(sqlQuery) } - @Test(expected = classOf[RuntimeException]) + @Test def testTooManyGroupingFields(): Unit = { // max group count must be less than 64 val fieldNames = (0 until 64).map(i => s"f$i").toArray @@ -136,6 +137,7 @@ class DecomposeGroupingSetsRuleTest extends TableTestBase { val fields = fieldNames.mkString(",") val sqlQuery = s"SELECT $fields FROM MyTable64 GROUP BY GROUPING SETS ($fields)" - util.verifyRelPlan(sqlQuery) + assertThatExceptionOfType(classOf[RuntimeException]) + .isThrownBy(() => util.verifyRelPlan(sqlQuery)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ExpressionReductionRulesTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ExpressionReductionRulesTest.scala index 166fb7902136c..943b6e4c7fafe 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ExpressionReductionRulesTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ExpressionReductionRulesTest.scala @@ -24,7 +24,7 @@ import org.apache.flink.table.functions.python.{PythonEnv, PythonFunction} import org.apache.flink.table.planner.expressions.utils.{Func1, RichFunc1} import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test /** Test for [[org.apache.flink.table.planner.codegen.ExpressionReducer]]. */ class ExpressionReductionRulesTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkAggregateExpandDistinctAggregatesRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkAggregateExpandDistinctAggregatesRuleTest.scala index adc279f768e70..7dac86ae882e9 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkAggregateExpandDistinctAggregatesRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkAggregateExpandDistinctAggregatesRuleTest.scala @@ -20,12 +20,12 @@ package org.apache.flink.table.planner.plan.rules.logical import org.apache.flink.table.planner.plan.common.DistinctAggregateTestBase import org.apache.flink.table.planner.plan.optimize.program.FlinkBatchProgram -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[FlinkAggregateExpandDistinctAggregatesRule]]. */ class FlinkAggregateExpandDistinctAggregatesRuleTest extends DistinctAggregateTestBase(false) { - @Before + @BeforeEach override def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.PHYSICAL) super.setup() diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkAggregateJoinTransposeRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkAggregateJoinTransposeRuleTest.scala index cae28bd043672..46606382e9d63 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkAggregateJoinTransposeRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkAggregateJoinTransposeRuleTest.scala @@ -28,13 +28,13 @@ import com.google.common.collect.ImmutableSet import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.rel.rules._ import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[FlinkAggregateJoinTransposeRule]]. */ class FlinkAggregateJoinTransposeRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val program = new FlinkChainedProgram[BatchOptimizeContext]() program.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkAggregateRemoveRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkAggregateRemoveRuleTest.scala index 127f6805911df..bf245d686dcdb 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkAggregateRemoveRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkAggregateRemoveRuleTest.scala @@ -31,13 +31,13 @@ import com.google.common.collect.ImmutableSet import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.rel.rules._ import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[FlinkAggregateRemoveRule]]. */ class FlinkAggregateRemoveRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkCalcMergeRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkCalcMergeRuleTest.scala index fab16c1895f6b..4b5165e4ab1d4 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkCalcMergeRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkCalcMergeRuleTest.scala @@ -29,13 +29,13 @@ import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.rel.rules._ import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[FlinkCalcMergeRule]]. */ class FlinkCalcMergeRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkJoinPushExpressionsRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkJoinPushExpressionsRuleTest.scala index 675a15bbe5a4e..8ae98ea755714 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkJoinPushExpressionsRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkJoinPushExpressionsRuleTest.scala @@ -24,14 +24,14 @@ import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Tests for [[FlinkJoinPushExpressionsRule]]. */ class FlinkJoinPushExpressionsRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkJoinToMultiJoinRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkJoinToMultiJoinRuleTest.scala index fcde31f624777..5b3d6aaddfbcb 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkJoinToMultiJoinRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkJoinToMultiJoinRuleTest.scala @@ -25,13 +25,13 @@ import org.apache.flink.table.planner.utils.{TableConfigUtils, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.rel.rules.CoreRules import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Tests for [[org.apache.flink.table.planner.plan.rules.logical.FlinkJoinToMultiJoinRule]]. */ class FlinkJoinToMultiJoinRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE) val calciteConfig = TableConfigUtils.getCalciteConfig(util.tableEnv.getConfig) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkLimit0RemoveRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkLimit0RemoveRuleTest.scala index a48e5e82abf94..cb65eb99bd667 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkLimit0RemoveRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkLimit0RemoveRuleTest.scala @@ -24,14 +24,14 @@ import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[FlinkLimit0RemoveRule]]. */ class FlinkLimit0RemoveRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkLogicalRankRuleForConstantRangeTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkLogicalRankRuleForConstantRangeTest.scala index 9b9464153cb12..e12d306e602ce 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkLogicalRankRuleForConstantRangeTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkLogicalRankRuleForConstantRangeTest.scala @@ -22,7 +22,7 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.optimize.program.FlinkBatchProgram import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test /** Test for [[FlinkLogicalRankRuleForConstantRange]]. */ class FlinkLogicalRankRuleForConstantRangeTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkLogicalRankRuleForRangeEndTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkLogicalRankRuleForRangeEndTest.scala index d937ebae9939a..e06a78ba2246d 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkLogicalRankRuleForRangeEndTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkLogicalRankRuleForRangeEndTest.scala @@ -22,7 +22,8 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.optimize.program.FlinkStreamProgram import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test /** Test for [[FlinkLogicalRankRuleForRangeEnd]]. */ class FlinkLogicalRankRuleForRangeEndTest extends TableTestBase { @@ -77,9 +78,9 @@ class FlinkLogicalRankRuleForRangeEndTest extends TableTestBase { | SELECT a, b, RANK() OVER (PARTITION BY b ORDER BY a, c) rk FROM MyTable) t |WHERE rk > 2 """.stripMargin - thrown.expectMessage("Rank end is not specified.") - thrown.expect(classOf[TableException]) - util.verifyRelPlan(sqlQuery) + assertThatThrownBy(() => util.verifyRelPlan(sqlQuery)) + .hasMessageContaining("Rank end is not specified.") + .isInstanceOf[TableException] } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkPruneEmptyRulesTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkPruneEmptyRulesTest.scala index 0d5926d644b7c..1285d0600eb0b 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkPruneEmptyRulesTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkPruneEmptyRulesTest.scala @@ -25,14 +25,14 @@ import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.rel.rules.{CoreRules, PruneEmptyRules} import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[FlinkPruneEmptyRules]]. */ class FlinkPruneEmptyRulesTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkSemiAntiJoinFilterTransposeRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkSemiAntiJoinFilterTransposeRuleTest.scala index 79d6fa8fd1514..7aa04829f42a6 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkSemiAntiJoinFilterTransposeRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkSemiAntiJoinFilterTransposeRuleTest.scala @@ -24,14 +24,14 @@ import org.apache.flink.table.planner.utils.{TableConfigUtils, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[FlinkSemiAntiJoinFilterTransposeRule]]. */ class FlinkSemiAntiJoinFilterTransposeRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE) val calciteConfig = TableConfigUtils.getCalciteConfig(util.tableEnv.getConfig) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkSemiAntiJoinJoinTransposeRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkSemiAntiJoinJoinTransposeRuleTest.scala index ab809d14ad8a1..1d82048b2ec93 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkSemiAntiJoinJoinTransposeRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkSemiAntiJoinJoinTransposeRuleTest.scala @@ -24,14 +24,14 @@ import org.apache.flink.table.planner.utils.{TableConfigUtils, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[FlinkSemiAntiJoinJoinTransposeRule]]. */ class FlinkSemiAntiJoinJoinTransposeRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE) val calciteConfig = TableConfigUtils.getCalciteConfig(util.tableEnv.getConfig) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkSemiAntiJoinProjectTransposeRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkSemiAntiJoinProjectTransposeRuleTest.scala index c4063d863a914..5fdfd81603798 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkSemiAntiJoinProjectTransposeRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/FlinkSemiAntiJoinProjectTransposeRuleTest.scala @@ -24,14 +24,14 @@ import org.apache.flink.table.planner.utils.{TableConfigUtils, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[FlinkSemiAntiJoinProjectTransposeRule]]. */ class FlinkSemiAntiJoinProjectTransposeRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE) val calciteConfig = TableConfigUtils.getCalciteConfig(util.tableEnv.getConfig) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinConditionEqualityTransferRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinConditionEqualityTransferRuleTest.scala index 91749b845d0c3..edcabf8454fa1 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinConditionEqualityTransferRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinConditionEqualityTransferRuleTest.scala @@ -24,14 +24,14 @@ import org.apache.flink.table.planner.utils.{TableConfigUtils, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[JoinConditionEqualityTransferRule]]. */ class JoinConditionEqualityTransferRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE) val calciteConfig = TableConfigUtils.getCalciteConfig(util.tableEnv.getConfig) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinConditionTypeCoerceRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinConditionTypeCoerceRuleTest.scala index 2d85026f5c22b..d22d325be637a 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinConditionTypeCoerceRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinConditionTypeCoerceRuleTest.scala @@ -24,7 +24,7 @@ import org.apache.flink.table.planner.utils.{TableConfigUtils, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** * Test for [[JoinConditionTypeCoerceRule]]. Now only semi-join rewrite will lost the type @@ -33,7 +33,7 @@ import org.junit.{Before, Test} class JoinConditionTypeCoerceRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE) val calciteConfig = TableConfigUtils.getCalciteConfig(util.tableEnv.getConfig) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinDependentConditionDerivationRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinDependentConditionDerivationRuleTest.scala index 5fc010132a231..649121c0c75f5 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinDependentConditionDerivationRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinDependentConditionDerivationRuleTest.scala @@ -25,14 +25,14 @@ import org.apache.flink.table.planner.utils.{TableConfigUtils, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.rel.rules.CoreRules import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[JoinDependentConditionDerivationRule]]. */ class JoinDependentConditionDerivationRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE) val calciteConfig = TableConfigUtils.getCalciteConfig(util.tableEnv.getConfig) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinDeriveNullFilterRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinDeriveNullFilterRuleTest.scala index e05122b291858..802b107fd4c8e 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinDeriveNullFilterRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/JoinDeriveNullFilterRuleTest.scala @@ -26,7 +26,7 @@ import org.apache.flink.table.planner.utils.{TableConfigUtils, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} import scala.collection.JavaConversions._ @@ -35,7 +35,7 @@ class JoinDeriveNullFilterRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE) val calciteConfig = TableConfigUtils.getCalciteConfig(util.tableEnv.getConfig) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/LogicalCorrelateToJoinFromTemporalTableRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/LogicalCorrelateToJoinFromTemporalTableRuleTest.scala index c5f4498d77b3b..f7885029d50be 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/LogicalCorrelateToJoinFromTemporalTableRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/LogicalCorrelateToJoinFromTemporalTableRuleTest.scala @@ -25,14 +25,15 @@ import org.apache.flink.table.planner.utils.{StreamTableTestUtil, TableTestBase, import org.apache.calcite.plan.RelOptRule import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.{assertThat, assertThatThrownBy} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[LogicalCorrelateToJoinFromTemporalTableRule]]. */ class LogicalCorrelateToJoinFromTemporalTableRuleTest extends TableTestBase { protected val util: StreamTableTestUtil = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTable(""" |CREATE TABLE T1 ( @@ -116,45 +117,51 @@ class LogicalCorrelateToJoinFromTemporalTableRuleTest extends TableTestBase { @Test def testProcTimeTemporalJoinOnTrue(): Unit = { setUpCurrentRule(LogicalCorrelateToJoinFromTemporalTableRule.WITHOUT_FILTER) - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Currently the join key in " + + assertThatThrownBy( + () => + util.verifyRelPlan( + "SELECT * FROM T1 JOIN T3 FOR SYSTEM_TIME AS OF T1.rowtime AS dimTable " + + "ON TRUE")) + .hasMessageContaining("Currently the join key in " + "Temporal Table Join can not be empty.") - util.verifyRelPlan( - "SELECT * FROM T1 JOIN T3 FOR SYSTEM_TIME AS OF T1.rowtime AS dimTable " + - "ON TRUE") + .isInstanceOf[ValidationException] } @Test def testRowTimeTemporalJoinOnTrue(): Unit = { setUpCurrentRule(LogicalCorrelateToJoinFromTemporalTableRule.WITHOUT_FILTER) - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Currently the join key in " + + assertThatThrownBy( + () => + util.verifyRelPlan( + "SELECT * FROM T1 JOIN T3 FOR SYSTEM_TIME AS OF T1.proctime AS dimTable " + + "ON TRUE")) + .hasMessageContaining("Currently the join key in " + "Temporal Table Join can not be empty.") - util.verifyRelPlan( - "SELECT * FROM T1 JOIN T3 FOR SYSTEM_TIME AS OF T1.proctime AS dimTable " + - "ON TRUE") + .isInstanceOf[ValidationException] } @Test def testRightTemporalJoin(): Unit = { setUpCurrentRule(LogicalCorrelateToJoinFromTemporalTableRule.WITH_FILTER) - expectedException.expect(classOf[AssertionError]) - expectedException.expectMessage("Correlate has invalid join type RIGHT") - util.verifyRelPlan( - "SELECT * FROM T1 RIGHT JOIN T3 FOR SYSTEM_TIME AS OF T1.rowtime AS dimTable " + - "ON T1.id = dimTable.id AND dimTable.rate > 10") + assertThatThrownBy( + () => + util.verifyRelPlan( + "SELECT * FROM T1 RIGHT JOIN T3 FOR SYSTEM_TIME AS OF T1.proctime AS dimTable " + + "ON T1.id = dimTable.id AND dimTable.rate > 10")) + .hasMessageContaining("Correlate has invalid join type RIGHT") + .isInstanceOf[AssertionError] } @Test def testFullTemporalJoin(): Unit = { setUpCurrentRule(LogicalCorrelateToJoinFromTemporalTableRule.WITH_FILTER) - expectedException.expect(classOf[AssertionError]) - expectedException.expectMessage("Correlate has invalid join type FULL") - util.verifyRelPlan( - "SELECT * FROM T1 FULL JOIN T3 FOR SYSTEM_TIME AS OF T1.rowtime AS dimTable " + - "ON T1.id = dimTable.id AND dimTable.rate > 10") + assertThatThrownBy( + () => + util.verifyRelPlan( + "SELECT * FROM T1 FULL JOIN T3 FOR SYSTEM_TIME AS OF T1.proctime AS dimTable " + + "ON T1.id = dimTable.id AND dimTable.rate > 10")) + .hasMessageContaining("Correlate has invalid join type FULL") + .isInstanceOf[AssertionError] } def setUpCurrentRule(rule: RelOptRule): Unit = { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/LogicalUnnestRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/LogicalUnnestRuleTest.scala index 8a791494fbacf..41ccd810e23d4 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/LogicalUnnestRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/LogicalUnnestRuleTest.scala @@ -23,14 +23,14 @@ import org.apache.flink.table.planner.utils.{BatchTableTestUtil, TableTestUtil} import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.Before +import org.junit.jupiter.api.BeforeEach /** Test for [[LogicalUnnestRule]]. */ class LogicalUnnestRuleTest extends UnnestTestBase(false) { override protected def getTableTestUtil: TableTestUtil = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ProjectPruneAggregateCallRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ProjectPruneAggregateCallRuleTest.scala index 8ee8b4f80050e..02c255ced9578 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ProjectPruneAggregateCallRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ProjectPruneAggregateCallRuleTest.scala @@ -23,10 +23,12 @@ import org.apache.flink.table.planner.utils.TableConfigUtils import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.rel.rules.CoreRules import org.apache.calcite.tools.RuleSets +import org.junit.jupiter.api.BeforeEach /** Test for [[PruneAggregateCallRule]]#PROJECT_ON_AGGREGATE. */ class ProjectPruneAggregateCallRuleTest extends PruneAggregateCallRuleTestBase { + @BeforeEach override def setup(): Unit = { super.setup() util.buildBatchProgram(FlinkBatchProgram.LOGICAL) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ProjectSemiAntiJoinTransposeRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ProjectSemiAntiJoinTransposeRuleTest.scala index 403a3985729ae..2ec64e94bc612 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ProjectSemiAntiJoinTransposeRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ProjectSemiAntiJoinTransposeRuleTest.scala @@ -25,14 +25,14 @@ import org.apache.flink.table.planner.utils.{TableConfigUtils, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[ProjectSemiAntiJoinTransposeRule]]. */ class ProjectSemiAntiJoinTransposeRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE) val calciteConfig = TableConfigUtils.getCalciteConfig(util.tableEnv.getConfig) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ProjectWindowTableFunctionTransposeRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ProjectWindowTableFunctionTransposeRuleTest.scala index 0887d5dace2ed..4dbdac2789f29 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ProjectWindowTableFunctionTransposeRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ProjectWindowTableFunctionTransposeRuleTest.scala @@ -23,13 +23,13 @@ import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[ProjectWindowTableFunctionTransposeRule]]. */ class ProjectWindowTableFunctionTransposeRuleTest extends TableTestBase { private val util = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[StreamOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PruneAggregateCallRuleTestBase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PruneAggregateCallRuleTestBase.scala index d93251215f27f..9ceaec78c00cf 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PruneAggregateCallRuleTestBase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PruneAggregateCallRuleTestBase.scala @@ -24,13 +24,13 @@ import org.apache.flink.table.planner.plan.stats.FlinkStatistic import org.apache.flink.table.planner.utils.{BatchTableTestUtil, TableTestBase} import com.google.common.collect.ImmutableSet -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Base test class for [[PruneAggregateCallRule]]. */ abstract class PruneAggregateCallRuleTestBase extends TableTestBase { protected val util: BatchTableTestUtil = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource( "T1", diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushFilterIntoLegacyTableSourceScanRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushFilterIntoLegacyTableSourceScanRuleTest.scala index b2c2a4da7f0e8..d418dbeeffbee 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushFilterIntoLegacyTableSourceScanRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushFilterIntoLegacyTableSourceScanRuleTest.scala @@ -26,13 +26,13 @@ import org.apache.flink.types.Row import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.rel.rules.CoreRules import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[PushFilterIntoLegacyTableSourceScanRule]]. */ class PushFilterIntoLegacyTableSourceScanRuleTest extends PushFilterIntoTableSourceScanRuleTestBase { - @Before + @BeforeEach def setup(): Unit = { util = batchTestUtil() util.asInstanceOf[BatchTableTestUtil].buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushLimitIntoLegacyTableSourceScanRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushLimitIntoLegacyTableSourceScanRuleTest.scala index eccaf446e0390..4f30db4c81386 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushLimitIntoLegacyTableSourceScanRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushLimitIntoLegacyTableSourceScanRuleTest.scala @@ -25,13 +25,14 @@ import org.apache.flink.table.planner.utils.{TableConfigUtils, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.rel.rules.CoreRules import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[PushLimitIntoLegacyTableSourceScanRule]]. */ class PushLimitIntoLegacyTableSourceScanRuleTest extends TableTestBase { protected val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE) val calciteConfig = TableConfigUtils.getCalciteConfig(util.tableEnv.getConfig) @@ -64,19 +65,22 @@ class PushLimitIntoLegacyTableSourceScanRuleTest extends TableTestBase { util.tableEnv.executeSql(ddl) } - @Test(expected = classOf[SqlParserException]) + @Test def testLimitWithNegativeOffset(): Unit = { - util.verifyRelPlan("SELECT a, c FROM LimitTable LIMIT 10 OFFSET -1") + assertThatExceptionOfType(classOf[SqlParserException]) + .isThrownBy(() => util.verifyRelPlan("SELECT a, c FROM LimitTable LIMIT 10 OFFSET -1")) } - @Test(expected = classOf[SqlParserException]) + @Test def testNegativeLimitWithoutOffset(): Unit = { - util.verifyRelPlan("SELECT a, c FROM LimitTable LIMIT -1") + assertThatExceptionOfType(classOf[SqlParserException]) + .isThrownBy(() => util.verifyRelPlan("SELECT a, c FROM LimitTable LIMIT -1")) } - @Test(expected = classOf[SqlParserException]) + @Test def testMysqlLimit(): Unit = { - util.verifyRelPlan("SELECT a, c FROM LimitTable LIMIT 1, 10") + assertThatExceptionOfType(classOf[SqlParserException]) + .isThrownBy(() => util.verifyRelPlan("SELECT a, c FROM LimitTable LIMIT 1, 10")) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushPartitionIntoLegacyTableSourceScanRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushPartitionIntoLegacyTableSourceScanRuleTest.scala index 206ae9a30f580..03b3fa9c7462e 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushPartitionIntoLegacyTableSourceScanRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushPartitionIntoLegacyTableSourceScanRuleTest.scala @@ -23,20 +23,20 @@ import org.apache.flink.table.expressions.utils.ResolvedExpressionMock import org.apache.flink.table.planner.expressions.utils.Func1 import org.apache.flink.table.planner.plan.optimize.program.{FlinkBatchProgram, FlinkHepRuleSetProgramBuilder, HEP_RULES_EXECUTION_TYPE} import org.apache.flink.table.planner.utils.{BatchTableTestUtil, TableConfigUtils, TableTestBase, TestPartitionableSourceFactory} +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.rel.rules.CoreRules import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith import java.util import scala.collection.JavaConversions._ /** Test for [[PushPartitionIntoLegacyTableSourceScanRule]]. */ -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class PushPartitionIntoLegacyTableSourceScanRuleTest( val sourceFetchPartitions: Boolean, val useCatalogFilter: Boolean) @@ -44,7 +44,7 @@ class PushPartitionIntoLegacyTableSourceScanRuleTest( protected val util: BatchTableTestUtil = batchTestUtil() @throws(classOf[Exception]) - @Before + @BeforeEach def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE) val calciteConfig = TableConfigUtils.getCalciteConfig(util.tableEnv.getConfig) @@ -93,93 +93,93 @@ class PushPartitionIntoLegacyTableSourceScanRuleTest( isBounded = true) } - @Test + @TestTemplate def testNoPartitionFieldPredicate(): Unit = { util.verifyRelPlan("SELECT * FROM MyTable WHERE id > 2") } - @Test + @TestTemplate def testNoPartitionFieldPredicateWithVirtualColumn(): Unit = { util.verifyRelPlan("SELECT * FROM VirtualTable WHERE id > 2") } - @Test + @TestTemplate def testOnlyPartitionFieldPredicate1(): Unit = { util.verifyRelPlan("SELECT * FROM MyTable WHERE part1 = 'A'") } - @Test + @TestTemplate def testOnlyPartitionFieldPredicate1WithVirtualColumn(): Unit = { util.verifyRelPlan("SELECT * FROM VirtualTable WHERE part1 = 'A'") } - @Test + @TestTemplate def testOnlyPartitionFieldPredicate2(): Unit = { util.verifyRelPlan("SELECT * FROM MyTable WHERE part2 > 1") } - @Test + @TestTemplate def testOnlyPartitionFieldPredicate2WithVirtualColumn(): Unit = { util.verifyRelPlan("SELECT * FROM VirtualTable WHERE part2 > 1") } - @Test + @TestTemplate def testOnlyPartitionFieldPredicate3(): Unit = { util.verifyRelPlan("SELECT * FROM MyTable WHERE part1 = 'A' AND part2 > 1") } - @Test + @TestTemplate def testOnlyPartitionFieldPredicate3WithVirtualColumn(): Unit = { util.verifyRelPlan("SELECT * FROM VirtualTable WHERE part1 = 'A' AND part2 > 1") } - @Test + @TestTemplate def testOnlyPartitionFieldPredicate4(): Unit = { util.verifyRelPlan("SELECT * FROM MyTable WHERE part1 = 'A' OR part2 > 1") } - @Test + @TestTemplate def testOnlyPartitionFieldPredicate4WithVirtualColumn(): Unit = { util.verifyRelPlan("SELECT * FROM VirtualTable WHERE part1 = 'A' OR part2 > 1") } - @Test + @TestTemplate def testPartitionFieldPredicateAndOtherPredicate(): Unit = { util.verifyRelPlan("SELECT * FROM MyTable WHERE id > 2 AND part1 = 'A'") } - @Test + @TestTemplate def testPartitionFieldPredicateAndOtherPredicateWithVirtualColumn(): Unit = { util.verifyRelPlan("SELECT * FROM VirtualTable WHERE id > 2 AND part1 = 'A'") } - @Test + @TestTemplate def testPartitionFieldPredicateOrOtherPredicate(): Unit = { util.verifyRelPlan("SELECT * FROM MyTable WHERE id > 2 OR part1 = 'A'") } - @Test + @TestTemplate def testPartitionFieldPredicateOrOtherPredicateWithVirtualColumn(): Unit = { util.verifyRelPlan("SELECT * FROM VirtualTable WHERE id > 2 OR part1 = 'A'") } - @Test + @TestTemplate def testPartialPartitionFieldPredicatePushDown(): Unit = { util.verifyRelPlan("SELECT * FROM MyTable WHERE (id > 2 OR part1 = 'A') AND part2 > 1") } - @Test + @TestTemplate def testPartialPartitionFieldPredicatePushDownWithVirtualColumn(): Unit = { util.verifyRelPlan("SELECT * FROM VirtualTable WHERE (id > 2 OR part1 = 'A') AND part2 > 1") } - @Test + @TestTemplate def testWithUdf(): Unit = { util.addFunction("MyUdf", Func1) util.verifyRelPlan("SELECT * FROM MyTable WHERE id > 2 AND MyUdf(part2) < 3") } - @Test + @TestTemplate def testWithUdfAndVirtualColumn(): Unit = { util.addFunction("MyUdf", Func1) util.verifyRelPlan("SELECT * FROM VirtualTable WHERE id > 2 AND MyUdf(part2) < 3") @@ -187,7 +187,7 @@ class PushPartitionIntoLegacyTableSourceScanRuleTest( } object PushPartitionIntoLegacyTableSourceScanRuleTest { - @Parameterized.Parameters(name = "sourceFetchPartitions={0}, useCatalogFilter={1}") + @Parameters(name = "sourceFetchPartitions={0}, useCatalogFilter={1}") def parameters(): util.Collection[Array[Any]] = { Seq[Array[Any]]( Array(true, false), diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushProjectIntoLegacyTableSourceScanRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushProjectIntoLegacyTableSourceScanRuleTest.scala index 2a46f5c0facc6..4fe843c71e9ef 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushProjectIntoLegacyTableSourceScanRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PushProjectIntoLegacyTableSourceScanRuleTest.scala @@ -27,14 +27,14 @@ import org.apache.flink.table.planner.utils.{BatchTableTestUtil, TableConfigUtil import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[PushProjectIntoLegacyTableSourceScanRule]]. */ class PushProjectIntoLegacyTableSourceScanRuleTest extends TableTestBase { protected val util: BatchTableTestUtil = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE) val calciteConfig = TableConfigUtils.getCalciteConfig(util.tableEnv.getConfig) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PythonCalcSplitRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PythonCalcSplitRuleTest.scala index b7b0ea6403001..567bbd34d0c6c 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PythonCalcSplitRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PythonCalcSplitRuleTest.scala @@ -26,14 +26,14 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctio import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[PythonCalcSplitRule]]. */ class PythonCalcSplitRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PythonCorrelateSplitRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PythonCorrelateSplitRuleTest.scala index 3dc9cebaa8457..82af9cf2eadce 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PythonCorrelateSplitRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PythonCorrelateSplitRuleTest.scala @@ -26,12 +26,12 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctio import org.apache.flink.table.planner.utils.{MockPythonTableFunction, TableFunc1, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} class PythonCorrelateSplitRuleTest extends TableTestBase { private val util = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[StreamOptimizeContext]() // query decorrelation diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PythonMapMergeRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PythonMapMergeRuleTest.scala index cf53137957f8b..fbee5a25a05d5 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PythonMapMergeRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/PythonMapMergeRuleTest.scala @@ -26,13 +26,13 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctio import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[PythonMapMergeRule]]. */ class PythonMapMergeRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RankNumberColumnRemoveRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RankNumberColumnRemoveRuleTest.scala index 2dd5c73a163f0..5334a51dc8849 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RankNumberColumnRemoveRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RankNumberColumnRemoveRuleTest.scala @@ -22,13 +22,13 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.optimize.program.FlinkStreamProgram import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[RankNumberColumnRemoveRule]]. */ class RankNumberColumnRemoveRuleTest extends TableTestBase { private val util = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildStreamProgram(FlinkStreamProgram.PHYSICAL) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RemoveSingleAggregateRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RemoveSingleAggregateRuleTest.scala index 06ac8f798ad0a..93af994c6c97b 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RemoveSingleAggregateRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RemoveSingleAggregateRuleTest.scala @@ -21,14 +21,14 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for RemoveSingleAggregateRule. */ class RemoveSingleAggregateRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource[(Int, Int)]("foo", 'x, 'y) util.addTableSource[(Int, String)]("bar", 'i, 's) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ReplaceIntersectWithSemiJoinRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ReplaceIntersectWithSemiJoinRuleTest.scala index 89e6a0f141636..4804949b5c066 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ReplaceIntersectWithSemiJoinRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ReplaceIntersectWithSemiJoinRuleTest.scala @@ -24,14 +24,14 @@ import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[ReplaceIntersectWithSemiJoinRule]]. */ class ReplaceIntersectWithSemiJoinRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ReplaceMinusWithAntiJoinRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ReplaceMinusWithAntiJoinRuleTest.scala index 39b2a0b4a64bc..d1cec8e88de5c 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ReplaceMinusWithAntiJoinRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/ReplaceMinusWithAntiJoinRuleTest.scala @@ -24,14 +24,14 @@ import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[ReplaceMinusWithAntiJoinRule]]. */ class ReplaceMinusWithAntiJoinRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RewriteIntersectAllRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RewriteIntersectAllRuleTest.scala index 9398164923873..6af0c912f9376 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RewriteIntersectAllRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RewriteIntersectAllRuleTest.scala @@ -24,14 +24,14 @@ import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[RewriteIntersectAllRule]]. */ class RewriteIntersectAllRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RewriteMinusAllRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RewriteMinusAllRuleTest.scala index bd8e22f02064a..aa2a3eb04d9c3 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RewriteMinusAllRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RewriteMinusAllRuleTest.scala @@ -24,14 +24,14 @@ import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[RewriteMinusAllRule]]. */ class RewriteMinusAllRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RewriteMultiJoinConditionRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RewriteMultiJoinConditionRuleTest.scala index 776e4d0d1ea64..00cd68a7e6521 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RewriteMultiJoinConditionRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/RewriteMultiJoinConditionRuleTest.scala @@ -26,13 +26,13 @@ import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.rel.rules.CoreRules import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[RewriteMultiJoinConditionRule]]. */ class RewriteMultiJoinConditionRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val program = new FlinkChainedProgram[BatchOptimizeContext]() program.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SimplifyFilterConditionRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SimplifyFilterConditionRuleTest.scala index a7799c565197b..fae4bb6d70517 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SimplifyFilterConditionRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SimplifyFilterConditionRuleTest.scala @@ -24,14 +24,14 @@ import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Tests for [[SimplifyFilterConditionRule]]. */ class SimplifyFilterConditionRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SimplifyJoinConditionRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SimplifyJoinConditionRuleTest.scala index e731597fb626b..0b39809683576 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SimplifyJoinConditionRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SimplifyJoinConditionRuleTest.scala @@ -24,14 +24,14 @@ import org.apache.flink.table.planner.utils.{TableConfigUtils, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Tests for [[SimplifyJoinConditionRule]]. */ class SimplifyJoinConditionRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.DEFAULT_REWRITE) val calciteConfig = TableConfigUtils.getCalciteConfig(util.tableEnv.getConfig) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SplitAggregateRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SplitAggregateRuleTest.scala index 656c03895dae5..be22d7b46a467 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SplitAggregateRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SplitAggregateRuleTest.scala @@ -23,7 +23,7 @@ import org.apache.flink.table.api.config.OptimizerConfigOptions import org.apache.flink.table.planner.plan.optimize.program.FlinkStreamProgram import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test /** IncrementalAggregateTest Test for [[SplitAggregateRule]]. */ class SplitAggregateRuleTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SplitPythonConditionFromCorrelateRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SplitPythonConditionFromCorrelateRuleTest.scala index 3d67ed3e724b6..fd67aeac49753 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SplitPythonConditionFromCorrelateRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SplitPythonConditionFromCorrelateRuleTest.scala @@ -26,14 +26,14 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctio import org.apache.flink.table.planner.utils.{TableFunc2, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[SplitPythonConditionFromCorrelateRule]]. */ class SplitPythonConditionFromCorrelateRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() // query decorrelation diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SplitPythonConditionFromJoinRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SplitPythonConditionFromJoinRuleTest.scala index 6f446b166b6a7..a2aa865016dba 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SplitPythonConditionFromJoinRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/SplitPythonConditionFromJoinRuleTest.scala @@ -26,14 +26,14 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctio import org.apache.flink.table.planner.utils.TableTestBase import org.apache.calcite.plan.hep.HepMatchOrder -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[SplitPythonConditionFromJoinRule]]. */ class SplitPythonConditionFromJoinRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { val programs = new FlinkChainedProgram[BatchOptimizeContext]() programs.addLast( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/TemporalJoinRewriteWithUniqueKeyRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/TemporalJoinRewriteWithUniqueKeyRuleTest.scala index 68ede4fb31637..a4313aceedd67 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/TemporalJoinRewriteWithUniqueKeyRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/TemporalJoinRewriteWithUniqueKeyRuleTest.scala @@ -24,13 +24,14 @@ import org.apache.flink.table.planner.utils.{StreamTableTestUtil, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[TemporalJoinRewriteWithUniqueKeyRule]]. */ class TemporalJoinRewriteWithUniqueKeyRuleTest extends TableTestBase { protected val util: StreamTableTestUtil = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildStreamProgram(PHYSICAL) val chainedProgram = util.getStreamProgram() @@ -116,13 +117,14 @@ class TemporalJoinRewriteWithUniqueKeyRuleTest extends TableTestBase { @Test def testPrimaryKeyInTemporalJoinOnTrue(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Currently the join key in " + + assertThatThrownBy( + () => + util.verifyRelPlan( + "SELECT * FROM T1 JOIN T2 FOR SYSTEM_TIME AS OF T1.rowtime AS T " + + "ON TRUE")) + .hasMessageContaining("Currently the join key in " + "Temporal Table Join can not be empty.") - util.verifyRelPlan( - "SELECT * FROM T1 JOIN T2 FOR SYSTEM_TIME AS OF T1.rowtime AS T " + - "ON TRUE") + .isInstanceOf[ValidationException] } @Test @@ -139,15 +141,17 @@ class TemporalJoinRewriteWithUniqueKeyRuleTest extends TableTestBase { |) """.stripMargin) - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Temporal Table Join requires primary key in versioned table," + - " but no primary key can be found. The physical plan is:\nFlinkLogicalJoin(" + - "condition=[AND(=($0, $4), __INITIAL_TEMPORAL_JOIN_CONDITION($3, $6," + - " __TEMPORAL_JOIN_LEFT_KEY($0), __TEMPORAL_JOIN_RIGHT_KEY($4)))], joinType=[left])") - util.verifyRelPlan( - "SELECT * FROM T1 LEFT JOIN noPkTable FOR SYSTEM_TIME AS OF " + - "T1.rowtime AS T ON T1.id = T.id") + assertThatThrownBy( + () => + util.verifyRelPlan( + "SELECT * FROM T1 LEFT JOIN noPkTable FOR SYSTEM_TIME AS OF " + + "T1.rowtime AS T ON T1.id = T.id")) + .hasMessageContaining( + "Temporal Table Join requires primary key in versioned table," + + " but no primary key can be found. The physical plan is:\nFlinkLogicalJoin(" + + "condition=[AND(=($0, $4), __INITIAL_TEMPORAL_JOIN_CONDITION($3, $6," + + " __TEMPORAL_JOIN_LEFT_KEY($0), __TEMPORAL_JOIN_RIGHT_KEY($4)))], joinType=[left])") + .isInstanceOf[ValidationException] } @Test @@ -160,25 +164,27 @@ class TemporalJoinRewriteWithUniqueKeyRuleTest extends TableTestBase { " ) T " + " WHERE rowNum = 2") - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Temporal Table Join requires primary key in versioned table," + + assertThatThrownBy( + () => + util.verifyRelPlan( + "SELECT * FROM T1 JOIN noPkView FOR SYSTEM_TIME AS OF " + + "T1.rowtime AS T ON T1.id = T.id")) + .hasMessageContaining("Temporal Table Join requires primary key in versioned table," + " but no primary key can be found. The physical plan is:\n" + "FlinkLogicalJoin(condition=[AND(=($0, $4), __INITIAL_TEMPORAL_JOIN_CONDITION(" + "$3, $6, __TEMPORAL_JOIN_LEFT_KEY($0), __TEMPORAL_JOIN_RIGHT_KEY($4)))], joinType=[inner])") - util.verifyRelPlan( - "SELECT * FROM T1 JOIN noPkView FOR SYSTEM_TIME AS OF " + - "T1.rowtime AS T ON T1.id = T.id") + .isInstanceOf[ValidationException] } @Test def testInferredPrimaryKeyInTemporalJoinOnTrue(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Currently the join key in " + + assertThatThrownBy( + () => + util.verifyRelPlan( + "SELECT * FROM T1 JOIN DeduplicatedView FOR SYSTEM_TIME AS OF " + + "T1.rowtime AS T ON TRUE")) + .hasMessageContaining("Currently the join key in " + "Temporal Table Join can not be empty.") - util.verifyRelPlan( - "SELECT * FROM T1 JOIN DeduplicatedView FOR SYSTEM_TIME AS OF " + - "T1.rowtime AS T ON TRUE") + .isInstanceOf[ValidationException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/WindowGroupReorderRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/WindowGroupReorderRuleTest.scala index e4b4d81824091..36840e1861054 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/WindowGroupReorderRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/WindowGroupReorderRuleTest.scala @@ -22,13 +22,13 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.optimize.program._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[WindowGroupReorderRule]]. */ class WindowGroupReorderRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildBatchProgram(FlinkBatchProgram.LOGICAL) util.addTableSource[(Int, Int, String)]("MyTable", 'a, 'b, 'c) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/FlinkRewriteSubQueryRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/FlinkRewriteSubQueryRuleTest.scala index 82a464820d21e..a18f74148f731 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/FlinkRewriteSubQueryRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/FlinkRewriteSubQueryRuleTest.scala @@ -20,12 +20,12 @@ package org.apache.flink.table.planner.plan.rules.logical.subquery import org.apache.flink.api.scala._ import org.apache.flink.table.api._ -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[org.apache.flink.table.planner.plan.rules.logical.FlinkRewriteSubQueryRule]]. */ class FlinkRewriteSubQueryRuleTest extends SubQueryTestBase { - @Before + @BeforeEach def setup(): Unit = { util.addTableSource[(Int, Long, String)]("x", 'a, 'b, 'c) util.addTableSource[(Int, Long, String)]("y", 'd, 'e, 'f) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/SubQueryAntiJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/SubQueryAntiJoinTest.scala index b72f3760feb67..03509beb0b330 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/SubQueryAntiJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/SubQueryAntiJoinTest.scala @@ -21,7 +21,8 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedTableFunctions.StringSplit -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test /** * Tests for [[org.apache.flink.table.planner.plan.rules.logical.FlinkSubQueryRemoveRule]], this @@ -210,14 +211,14 @@ class SubQueryAntiJoinTest extends SubQueryTestBase { def testNotInWithUncorrelatedOnWhere_Case7(): Unit = { util.addTableSource[(Int)]("t1", 'i) - // TODO some bugs in SubQueryRemoveRule - thrown.expect(classOf[RuntimeException]) - // TODO Calcite does not support project with correlated expressions. val sqlQuery = "SELECT b FROM l WHERE " + "(CASE WHEN a NOT IN (SELECT i FROM t1 WHERE l.a = t1.i) THEN 1 ELSE 2 END) " + "NOT IN (SELECT d FROM r)" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[anti]") + + // TODO some bugs in SubQueryRemoveRule + assertThatExceptionOfType(classOf[RuntimeException]) + .isThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[anti]")) } @Test @@ -616,13 +617,14 @@ class SubQueryAntiJoinTest extends SubQueryTestBase { util.verifyRelPlanNotExpected(sqlQuery4, "joinType=[anti]") } - @Test(expected = classOf[AssertionError]) + @Test def testNotExistsWithCorrelatedOnWhere_UnsupportedCondition2(): Unit = { // TODO Calcite decorrelateRel error val sqlQuery = "SELECT * FROM l WHERE NOT EXISTS " + " (SELECT * FROM (SELECT * FROM r WHERE r.d = l.a AND r.e > 100) s " + "LEFT JOIN t ON s.f = t.k AND l.b = t.j)" - util.verifyRelPlan(sqlQuery) + assertThatExceptionOfType(classOf[AssertionError]) + .isThrownBy(() => util.verifyRelPlan(sqlQuery)) } @Test @@ -727,17 +729,17 @@ class SubQueryAntiJoinTest extends SubQueryTestBase { def testNotInNotExists3(): Unit = { util.addTableSource[(Int, Long, String)]("t2", 'l, 'm, 'n) - // TODO some bugs in SubQueryRemoveRule - // the result RelNode (LogicalJoin(condition=[=($1, $11)], joinType=[left])) - // after SubQueryRemoveRule is unexpected - thrown.expect(classOf[AssertionError]) - // TODO Calcite does not support project with correlated expressions. val sqlQuery = "SELECT c FROM l WHERE (" + " (CASE WHEN NOT EXISTS (SELECT * FROM t WHERE l.a = t.i) THEN 1 ELSE 2 END), " + " (CASE WHEN b NOT IN (SELECT m FROM t2) THEN 3 ELSE 4 END)) " + " NOT IN (SELECT d, e FROM r)" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[anti]") + + // TODO some bugs in SubQueryRemoveRule + // the result RelNode (LogicalJoin(condition=[=($1, $11)], joinType=[left])) + // after SubQueryRemoveRule is unexpected + assertThatExceptionOfType(classOf[AssertionError]) + .isThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[anti]")) } @Test @@ -756,16 +758,16 @@ class SubQueryAntiJoinTest extends SubQueryTestBase { def testInNotInExistsNotExists2(): Unit = { util.addTableSource[(Int, Long, String)]("t2", 'l, 'm, 'n) - // TODO some bugs in SubQueryRemoveRule - thrown.expect(classOf[RuntimeException]) - // TODO Calcite does not support project with correlated expressions. val sqlQuery = "SELECT c FROM l WHERE (" + " (CASE WHEN b IN (SELECT j FROM t WHERE l.a = t.i) THEN 1 ELSE 2 END), " + " (CASE WHEN NOT EXISTS (SELECT m FROM t2) THEN 3 " + " WHEN EXISTS (select i FROM t) THEN 4 ELSE 5 END)) " + " NOT IN (SELECT d, e FROM r)" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[anti]") + + // TODO some bugs in SubQueryRemoveRule + assertThatExceptionOfType(classOf[RuntimeException]) + .isThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[anti]")) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/SubQuerySemiJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/SubQuerySemiJoinTest.scala index f78017b77fd6d..6170dcb29986c 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/SubQuerySemiJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/SubQuerySemiJoinTest.scala @@ -21,7 +21,8 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedTableFunctions.StringSplit -import org.junit.Test +import org.assertj.core.api.Assertions.{assertThatExceptionOfType, assertThatThrownBy} +import org.junit.jupiter.api.Test /** * Tests for [[org.apache.flink.table.planner.plan.rules.logical.FlinkSubQueryRemoveRule]], this @@ -116,13 +117,13 @@ class SubQuerySemiJoinTest extends SubQueryTestBase { @Test def testInWithUncorrelatedOnWhere_UnsupportedCondition1(): Unit = { - thrown.expect(classOf[TableException]) - // correlate variable id is unstable, ignore here - thrown.expectMessage("unexpected correlate variable $cor") - // these queries will not be converted to joinType=[semi] val sqlQuery = "SELECT * FROM x WHERE a IN (SELECT c FROM y WHERE x.b IN (SELECT e FROM z))" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + + assertThatThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) + // correlate variable id is unstable, ignore here + .hasMessageContaining("unexpected correlate variable $cor") + .isInstanceOf[TableException] } @Test @@ -206,13 +207,13 @@ class SubQuerySemiJoinTest extends SubQueryTestBase { util.addTableSource[(Int)]("t1", 'i) util.addTableSource[(Int)]("t2", 'j) - // TODO some bugs in SubQueryRemoveRule - thrown.expect(classOf[RuntimeException]) - // TODO Calcite does not support project with correlated expressions. val sqlQuery = "SELECT b FROM l WHERE" + " (CASE WHEN a IN (SELECT i FROM t1 WHERE l.a = t1.i) THEN 1 ELSE 2 END) IN (SELECT d FROM r)" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + + // TODO some bugs in SubQueryRemoveRule + assertThatExceptionOfType(classOf[RuntimeException]) + .isThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) } @Test @@ -245,13 +246,13 @@ class SubQuerySemiJoinTest extends SubQueryTestBase { @Test def testInWithUncorrelatedOnWhere_ScalarQuery5(): Unit = { - thrown.expect(classOf[TableException]) - // correlate variable id is unstable, ignore here - thrown.expectMessage("unexpected correlate variable $cor") - val sqlQuery = "SELECT b FROM x WHERE a IN (SELECT c FROM y WHERE d > " + "(SELECT 0.5 * SUM(e) FROM z WHERE x.a = z.e AND z.f < 100))" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + + assertThatThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) + // correlate variable id is unstable, ignore here + .hasMessageContaining("unexpected correlate variable $cor") + .isInstanceOf[TableException] } @Test @@ -474,13 +475,15 @@ class SubQuerySemiJoinTest extends SubQueryTestBase { util.verifyRelPlanNotExpected(sqlQuery4, "joinType=[semi]") } - @Test(expected = classOf[AssertionError]) + @Test def testInWithCorrelatedOnWhere_UnsupportedCondition2(): Unit = { // TODO java.lang.RuntimeException: While invoking method // 'public RelDecorrelator$Frame RelDecorrelator.decorrelateRel(LogicalProject)' val sqlQuery = "SELECT * FROM l WHERE a IN (SELECT d FROM r WHERE l.b IN (SELECT j FROM t) " + "AND l.c = r.f)" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + + assertThatExceptionOfType(classOf[AssertionError]) + .isThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) } @Test @@ -570,15 +573,15 @@ class SubQuerySemiJoinTest extends SubQueryTestBase { @Test def testInWithCorrelatedOnWhere_ScalarQuery4(): Unit = { - thrown.expect(classOf[TableException]) - // correlate variable id is unstable, ignore here - thrown.expectMessage("unexpected correlate variable $cor") - // nested correlation can not be converted joinType=[semi] now val sqlQuery = "SELECT a FROM x WHERE " + "(SELECT MAX(d) FROM y WHERE c IN (SELECT e FROM z WHERE x.b = z.f))" + " IN (SELECT f FROM z WHERE z.e = x.a)" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + + assertThatThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) + // correlate variable id is unstable, ignore here + .hasMessageContaining("unexpected correlate variable $cor") + .isInstanceOf[TableException] } @Test @@ -602,13 +605,14 @@ class SubQuerySemiJoinTest extends SubQueryTestBase { util.verifyRelPlan(sqlQuery) } - @Test(expected = classOf[AssertionError]) + @Test def testInWithCorrelatedOnWhere_ScalarQuery8(): Unit = { // nested correlation can not be converted joinType=[semi] now // TODO There are some bugs when decorrelating in RelDecorrelator val sqlQuery = "SELECT b FROM x WHERE a IN (SELECT c FROM y WHERE x.b = y.d AND c > " + "(SELECT 0.5 * SUM(e) FROM z WHERE x.a = z.e AND z.f < 100))" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + assertThatExceptionOfType(classOf[AssertionError]) + .isThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) } @Test @@ -831,29 +835,29 @@ class SubQuerySemiJoinTest extends SubQueryTestBase { @Test def testInWithCorrelatedOnWhere_Union1(): Unit = { - thrown.expect(classOf[TableException]) - // correlate variable id is unstable, ignore here - thrown.expectMessage("unexpected correlate variable $cor") - // UNION with correlation can not be converted to semi-join val sqlQuery = "SELECT a FROM l WHERE b IN " + "(SELECT e FROM r WHERE l.a = r.d AND d > 10 " + "UNION " + "SELECT i FROM t WHERE i < 100)" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + + assertThatThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) + // correlate variable id is unstable, ignore here + .hasMessageContaining("unexpected correlate variable $cor") + .isInstanceOf[TableException] } @Test def testInWithCorrelatedOnWhere_Union2(): Unit = { - thrown.expect(classOf[TableException]) - // correlate variable id is unstable, ignore here - thrown.expectMessage("unexpected correlate variable $cor") - val sqlQuery = "SELECT a FROM l WHERE b IN " + "(SELECT e FROM r WHERE l.a = r.d AND d > 10 " + "UNION " + "SELECT i FROM t WHERE l.c = t.k AND i < 100)" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + + assertThatThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) + // correlate variable id is unstable, ignore here + .hasMessageContaining("unexpected correlate variable $cor") + .isInstanceOf[TableException] } @Test @@ -991,38 +995,37 @@ class SubQuerySemiJoinTest extends SubQueryTestBase { @Test def testInWithCorrelatedOnLateralTable2(): Unit = { - thrown.expect(classOf[TableException]) - // correlate variable id is unstable, ignore here - thrown.expectMessage("unexpected correlate variable $cor") - val sqlQuery = "SELECT * FROM l WHERE c IN (" + "SELECT f1 FROM r, LATERAL TABLE(table_func(f)) AS T(f1) " + "WHERE d IN (SELECT i FROM t WHERE l.b = t.j))" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + + assertThatThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) + // correlate variable id is unstable, ignore here + .hasMessageContaining("unexpected correlate variable $cor") + .isInstanceOf[TableException] } @Test def testInWithCorrelatedOnLateralTable3(): Unit = { - thrown.expect(classOf[TableException]) - // correlate variable id is unstable, ignore here - thrown.expectMessage("unexpected correlate variable $cor") - val sqlQuery = "SELECT * FROM l WHERE c IN (" + "SELECT f1 FROM (SELECT * FROM r WHERE d IN (" + "SELECT i FROM t WHERE t.j = l.b)) m, LATERAL TABLE(table_func(f)) AS T(f1))" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + + assertThatThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) + // correlate variable id is unstable, ignore here + .hasMessageContaining("unexpected correlate variable $cor") + .isInstanceOf[TableException] } @Test def testInWithCorrelatedOnLateralTable4(): Unit = { - thrown.expect(classOf[TableException]) - // correlate variable id is unstable, ignore here - thrown.expectMessage("unexpected correlate variable $cor") - val sqlQuery = "SELECT * FROM l WHERE c IN (" + "SELECT f1 FROM (SELECT * FROM r LEFT JOIN LATERAL TABLE(table_func(f)) AS T(f1) ON TRUE " + "WHERE d IN (SELECT i FROM t WHERE l.b = t.j)))" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + assertThatThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) + // correlate variable id is unstable, ignore here + .hasMessageContaining("unexpected correlate variable $cor") + .isInstanceOf[TableException] } @Test @@ -1257,12 +1260,13 @@ class SubQuerySemiJoinTest extends SubQueryTestBase { util.verifyRelPlanNotExpected(sqlQuery4, "joinType=[semi]") } - @Test(expected = classOf[AssertionError]) + @Test def testExistsWithCorrelatedOnWhere_UnsupportedCondition2(): Unit = { val sqlQuery = "SELECT * FROM l WHERE EXISTS " + " (SELECT * FROM (SELECT * FROM r WHERE r.d = l.a AND r.e > 100) s " + "LEFT JOIN t ON s.f = t.k AND l.b = t.j)" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + assertThatExceptionOfType(classOf[AssertionError]) + .isThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) } @Test @@ -1432,29 +1436,27 @@ class SubQuerySemiJoinTest extends SubQueryTestBase { @Test def testExistsWithCorrelatedOnWhere_Union1(): Unit = { - thrown.expect(classOf[TableException]) - // correlate variable id is unstable, ignore here - thrown.expectMessage("unexpected correlate variable $cor") - // UNION with correlation is not supported val sqlQuery = "SELECT a FROM l WHERE EXISTS " + "(SELECT e FROM r WHERE l.a = r.d AND d > 10 " + "UNION " + "SELECT i FROM t WHERE i < 100)" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + assertThatThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) + // correlate variable id is unstable, ignore here + .hasMessageContaining("unexpected correlate variable $cor") + .isInstanceOf[TableException] } @Test def testExistsWithCorrelatedOnWhere_Union2(): Unit = { - thrown.expect(classOf[TableException]) - // correlate variable id is unstable, ignore here - thrown.expectMessage("unexpected correlate variable $cor") - val sqlQuery = "SELECT a FROM l WHERE EXISTS " + "(SELECT e FROM r WHERE l.a = r.d AND d > 10 " + "UNION " + "SELECT i FROM t WHERE l.c = t.k AND i < 100)" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + assertThatThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) + // correlate variable id is unstable, ignore here + .hasMessageContaining("unexpected correlate variable $cor") + .isInstanceOf[TableException] } @Test @@ -1609,38 +1611,36 @@ class SubQuerySemiJoinTest extends SubQueryTestBase { @Test def testExistsWithCorrelatedOnLateralTable2(): Unit = { - thrown.expect(classOf[TableException]) - // correlate variable id is unstable, ignore here - thrown.expectMessage("unexpected correlate variable $cor") - val sqlQuery = "SELECT * FROM l WHERE EXISTS (" + "SELECT * FROM r, LATERAL TABLE(table_func(f)) AS T(f1) " + "WHERE EXISTS (SELECT * FROM t WHERE l.b = t.j))" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + assertThatThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) + // correlate variable id is unstable, ignore here + .hasMessageContaining("unexpected correlate variable $cor") + .isInstanceOf[TableException] } @Test def testExistsWithCorrelatedOnLateralTable3(): Unit = { - thrown.expect(classOf[TableException]) - // correlate variable id is unstable, ignore here - thrown.expectMessage("unexpected correlate variable $cor") - val sqlQuery = "SELECT * FROM l WHERE EXISTS (" + "SELECT * FROM (SELECT * FROM r WHERE EXISTS (" + "SELECT * FROM t WHERE t.j = l.b)) m, LATERAL TABLE(table_func(f)) AS T(f1))" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + + assertThatThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) + // correlate variable id is unstable, ignore here + .hasMessageContaining("unexpected correlate variable $cor") + .isInstanceOf[TableException] } @Test def testExistsWithCorrelatedOnLateralTable4(): Unit = { - thrown.expect(classOf[TableException]) - // correlate variable id is unstable, ignore here - thrown.expectMessage("unexpected correlate variable $cor") - val sqlQuery = "SELECT * FROM l WHERE EXISTS (" + "SELECT * FROM (SELECT * FROM r LEFT JOIN LATERAL TABLE(table_func(f)) AS T(f1) ON TRUE " + "WHERE EXISTS (SELECT i FROM t WHERE l.b = t.j)))" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + assertThatThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) + // correlate variable id is unstable, ignore here + .hasMessageContaining("unexpected correlate variable $cor") + .isInstanceOf[TableException] } @Test @@ -1661,17 +1661,17 @@ class SubQuerySemiJoinTest extends SubQueryTestBase { def testInExists3(): Unit = { util.addTableSource[(Int, Long, String)]("t2", 'l, 'm, 'n) - // TODO some bugs in SubQueryRemoveRule - // the result RelNode (LogicalJoin(condition=[=($1, $8)], joinType=[left])) - // after SubQueryRemoveRule is unexpected - thrown.expect(classOf[AssertionError]) - // TODO Calcite does not support project with correlated expressions. val sqlQuery = "SELECT c FROM l WHERE (" + " (CASE WHEN EXISTS (SELECT * FROM t WHERE l.a = t.i) THEN 1 ELSE 2 END), " + " (CASE WHEN b IN (SELECT m FROM t2) THEN 3 ELSE 4 END)) " + " IN (SELECT d, e FROM r)" - util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]") + + // TODO some bugs in SubQueryRemoveRule + // the result RelNode (LogicalJoin(condition=[=($1, $8)], joinType=[left])) + // after SubQueryRemoveRule is unexpected + assertThatExceptionOfType(classOf[AssertionError]) + .isThrownBy(() => util.verifyRelPlanNotExpected(sqlQuery, "joinType=[semi]")) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/SubqueryCorrelateVariablesValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/SubqueryCorrelateVariablesValidationTest.scala index bde051543f401..8b9339913e5d3 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/SubqueryCorrelateVariablesValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/logical/subquery/SubqueryCorrelateVariablesValidationTest.scala @@ -20,7 +20,8 @@ package org.apache.flink.table.planner.plan.rules.logical.subquery import org.apache.flink.api.scala._ import org.apache.flink.table.api._ -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test import java.sql.{Date, Timestamp} @@ -83,7 +84,7 @@ class SubqueryCorrelateVariablesValidationTest extends SubQueryTestBase { util.verifyRelPlan(sqlQuery) } - @Test(expected = classOf[RuntimeException]) + @Test def testWithProjectJoinCorrelate(): Unit = { val sqlQuery = """ @@ -93,7 +94,8 @@ class SubqueryCorrelateVariablesValidationTest extends SubQueryTestBase { |FROM t1 | WHERE t1a = 'val1b' """.stripMargin - util.verifyRelPlan(sqlQuery) + assertThatExceptionOfType(classOf[RuntimeException]) + .isThrownBy(() => util.verifyRelPlan(sqlQuery)) } @Test @@ -109,7 +111,7 @@ class SubqueryCorrelateVariablesValidationTest extends SubQueryTestBase { util.verifyRelPlan(sqlQuery) } - @Test(expected = classOf[TableException]) + @Test def testWithFilterInCorrelate(): Unit = { val sqlQuery = """ @@ -120,10 +122,11 @@ class SubqueryCorrelateVariablesValidationTest extends SubQueryTestBase { | WHERE t1.t1e | IN (select t2e from t2)) """.stripMargin - util.verifyRelPlan(sqlQuery) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyRelPlan(sqlQuery)) } - @Test(expected = classOf[TableException]) + @Test def testWithFilterExistsCorrelate(): Unit = { val sqlQuery = """ @@ -133,10 +136,11 @@ class SubqueryCorrelateVariablesValidationTest extends SubQueryTestBase { | FROM t3 | WHERE EXISTS(select * from t3 WHERE t1.t1a = t3.t3a)) """.stripMargin - util.verifyRelPlan(sqlQuery) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyRelPlan(sqlQuery)) } - @Test(expected = classOf[AssertionError]) + @Test // TODO some bugs in RelDecorrelator.AdjustProjectForCountAggregateRule def testWithProjectCaseWhenCorrelate(): Unit = { val sqlQuery = @@ -149,7 +153,8 @@ class SubqueryCorrelateVariablesValidationTest extends SubQueryTestBase { |FROM t1 | WHERE t1a = 'test' """.stripMargin - util.verifyRelPlan(sqlQuery) + assertThatExceptionOfType(classOf[AssertionError]) + .isThrownBy(() => util.verifyRelPlan(sqlQuery)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/EnforceLocalAggRuleTestBase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/EnforceLocalAggRuleTestBase.scala index 4f4a2e15b8311..714baa21623b9 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/EnforceLocalAggRuleTestBase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/EnforceLocalAggRuleTestBase.scala @@ -21,12 +21,12 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} abstract class EnforceLocalAggRuleTestBase extends TableTestBase { protected val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource[(Int, Long, String, Int)]("t", 'a, 'b, 'c, 'd) } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/EnforceLocalHashAggRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/EnforceLocalHashAggRuleTest.scala index 4435a04857a30..31cd921531f16 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/EnforceLocalHashAggRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/EnforceLocalHashAggRuleTest.scala @@ -26,12 +26,12 @@ import org.apache.flink.table.planner.utils.TableConfigUtils import org.apache.calcite.rel.core.Aggregate import org.apache.calcite.tools.RuleSets -import org.junit.Before +import org.junit.jupiter.api.BeforeEach /** Test for [[EnforceLocalHashAggRule]]. */ class EnforceLocalHashAggRuleTest extends EnforceLocalAggRuleTestBase { - @Before + @BeforeEach override def setup(): Unit = { super.setup() val program = FlinkBatchProgram.buildProgram(util.tableEnv.getConfig) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/EnforceLocalSortAggRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/EnforceLocalSortAggRuleTest.scala index cd566a4c2bf56..72aceb8ab7594 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/EnforceLocalSortAggRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/EnforceLocalSortAggRuleTest.scala @@ -28,12 +28,12 @@ import org.apache.flink.table.planner.utils.TableConfigUtils import org.apache.calcite.rel.core.Aggregate import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[EnforceLocalSortAggRule]]. */ class EnforceLocalSortAggRuleTest extends EnforceLocalAggRuleTestBase { - @Before + @BeforeEach override def setup(): Unit = { super.setup() util.addFunction("weightedAvg", new WeightedAvg) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/RemoveRedundantLocalHashAggRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/RemoveRedundantLocalHashAggRuleTest.scala index 99bd7be733eba..52f3f53f894cd 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/RemoveRedundantLocalHashAggRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/RemoveRedundantLocalHashAggRuleTest.scala @@ -22,14 +22,14 @@ import org.apache.flink.table.api._ import org.apache.flink.table.api.config.{ExecutionConfigOptions, OptimizerConfigOptions} import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[RemoveRedundantLocalHashAggRule]]. */ class RemoveRedundantLocalHashAggRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource[(Int, Long, String)]("x", 'a, 'b, 'c) util.addTableSource[(Int, Long, String)]("y", 'd, 'e, 'f) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/RemoveRedundantLocalRankRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/RemoveRedundantLocalRankRuleTest.scala index 56e7c2d69d051..56a90cb44ba2e 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/RemoveRedundantLocalRankRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/RemoveRedundantLocalRankRuleTest.scala @@ -21,14 +21,14 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Tests for [[RemoveRedundantLocalRankRule]]. */ class RemoveRedundantLocalRankRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource[(Int, Long, String)]("x", 'a, 'b, 'c) } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/RemoveRedundantLocalSortAggRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/RemoveRedundantLocalSortAggRuleTest.scala index d407adcfca4cd..11be16997f2ae 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/RemoveRedundantLocalSortAggRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/batch/RemoveRedundantLocalSortAggRuleTest.scala @@ -22,14 +22,14 @@ import org.apache.flink.table.api._ import org.apache.flink.table.api.config.{ExecutionConfigOptions, OptimizerConfigOptions} import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Test for [[RemoveRedundantLocalSortAggRule]]. */ class RemoveRedundantLocalSortAggRuleTest extends TableTestBase { private val util = batchTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource[(Int, Long, String)]("x", 'a, 'b, 'c) util.addTableSource[(Int, Long, String)]("y", 'd, 'e, 'f) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/stream/ChangelogModeInferenceTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/stream/ChangelogModeInferenceTest.scala index cb6266d6c5518..a40028bd35766 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/stream/ChangelogModeInferenceTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/stream/ChangelogModeInferenceTest.scala @@ -24,14 +24,14 @@ import org.apache.flink.table.planner.plan.optimize.RelNodeBlockPlanBuilder import org.apache.flink.table.planner.plan.optimize.program.FlinkChangelogModeInferenceProgram import org.apache.flink.table.planner.utils.{AggregatePhaseStrategy, TableTestBase} -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Tests for [[FlinkChangelogModeInferenceProgram]]. */ class ChangelogModeInferenceTest extends TableTestBase { private val util = streamTestUtil() - @Before + @BeforeEach def before(): Unit = { util.addTable(""" |CREATE TABLE MyTable ( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/stream/ExpandWindowTableFunctionTransposeRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/stream/ExpandWindowTableFunctionTransposeRuleTest.scala index 456afb9e7a478..881f7c7dddcc8 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/stream/ExpandWindowTableFunctionTransposeRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/stream/ExpandWindowTableFunctionTransposeRuleTest.scala @@ -27,13 +27,13 @@ import org.apache.flink.table.planner.utils.{StreamTableTestUtil, TableTestBase} import org.apache.calcite.plan.hep.HepMatchOrder import org.apache.calcite.tools.RuleSets -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Tests for [[ExpandWindowTableFunctionTransposeRule]]. */ class ExpandWindowTableFunctionTransposeRuleTest extends TableTestBase { private val util: StreamTableTestUtil = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.buildStreamProgram(PHYSICAL) val chainedProgram = util.getStreamProgram() diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/stream/WatermarkAssignerChangelogNormalizeTransposeRuleTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/stream/WatermarkAssignerChangelogNormalizeTransposeRuleTest.scala index c02376ef48c8d..9d37d60bcd5f0 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/stream/WatermarkAssignerChangelogNormalizeTransposeRuleTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/rules/physical/stream/WatermarkAssignerChangelogNormalizeTransposeRuleTest.scala @@ -20,13 +20,13 @@ package org.apache.flink.table.planner.plan.rules.physical.stream import org.apache.flink.table.api.ExplainDetail import org.apache.flink.table.planner.utils.{StreamTableTestUtil, TableTestBase} -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Tests for [[WatermarkAssignerChangelogNormalizeTransposeRule]] */ class WatermarkAssignerChangelogNormalizeTransposeRuleTest extends TableTestBase { private val util: StreamTableTestUtil = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTable(s""" |CREATE TABLE simple_src ( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/CalcTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/CalcTest.scala index 3e74c00480fd8..ce0b864d31172 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/CalcTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/CalcTest.scala @@ -25,14 +25,15 @@ import org.apache.flink.table.planner.plan.utils.MyPojo import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctions.NonDeterministicUdf import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.{BeforeEach, Test} import java.sql.{Date, Time, Timestamp} class CalcTest extends TableTestBase { private val util = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource[(Long, Int, String)]("MyTable", 'a, 'b, 'c) util.addFunction("random_udf", new NonDeterministicUdf) @@ -102,9 +103,10 @@ class CalcTest extends TableTestBase { util.verifyExecPlan("SELECT MyTable2.a.*, c, MyTable2.b.* FROM MyTable2") } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidFields(): Unit = { - util.tableEnv.sqlQuery("SELECT a, foo FROM MyTable") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.tableEnv.sqlQuery("SELECT a, foo FROM MyTable")) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/DagOptimizationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/DagOptimizationTest.scala index c98a049c5f0c1..d2f10011ce178 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/DagOptimizationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/DagOptimizationTest.scala @@ -25,7 +25,7 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctio import org.apache.flink.table.planner.utils.{TableFunc1, TableTestBase} import org.apache.flink.table.types.logical.{BigIntType, IntType, VarCharType} -import org.junit.Test +import org.junit.jupiter.api.Test class DagOptimizationTest extends TableTestBase { private val util = streamTestUtil() diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/DeduplicateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/DeduplicateTest.scala index dcb594b748ee3..093b3a12415d0 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/DeduplicateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/DeduplicateTest.scala @@ -22,7 +22,7 @@ import org.apache.flink.table.api._ import org.apache.flink.table.api.config.ExecutionConfigOptions.{TABLE_EXEC_MINIBATCH_ALLOW_LATENCY, TABLE_EXEC_MINIBATCH_ENABLED, TABLE_EXEC_MINIBATCH_SIZE} import org.apache.flink.table.planner.utils.{StreamTableTestUtil, TableTestBase} -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} import java.time.Duration @@ -30,7 +30,7 @@ class DeduplicateTest extends TableTestBase { var util: StreamTableTestUtil = _ - @Before + @BeforeEach def setUp(): Unit = { util = streamTestUtil() util.addDataStream[(Int, String, Long)]( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/FilterableSourceTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/FilterableSourceTest.scala index 89cf7c0f70ddd..5c10b6285bbea 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/FilterableSourceTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/FilterableSourceTest.scala @@ -20,13 +20,13 @@ package org.apache.flink.table.planner.plan.stream.sql import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctions.JavaFunc5 import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Tests for pushing filter into table scan */ class FilterableSourceTest extends TableTestBase { private val util = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { val ddl = """ diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LegacySinkTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LegacySinkTest.scala index 9b066752afc51..82cdbfbf3715d 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LegacySinkTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LegacySinkTest.scala @@ -23,7 +23,8 @@ import org.apache.flink.table.api.internal.TableEnvironmentInternal import org.apache.flink.table.planner.utils.TableTestBase import org.apache.flink.table.types.logical.{BigIntType, IntType, VarCharType} -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test class LegacySinkTest extends TableTestBase { @@ -39,10 +40,10 @@ class LegacySinkTest extends TableTestBase { val table = util.tableEnv.sqlQuery("SELECT COUNT(*) AS cnt FROM MyTable GROUP BY a") val appendSink = util.createAppendTableSink(Array("a"), Array(LONG)) - thrown.expect(classOf[TableException]) - thrown.expectMessage("AppendStreamTableSink doesn't support consuming update " + - "changes which is produced by node GroupAggregate(groupBy=[a], select=[a, COUNT(*) AS cnt])") - util.verifyRelPlanInsert(table, appendSink, "appendSink") + assertThatThrownBy(() => util.verifyRelPlanInsert(table, appendSink, "appendSink")) + .hasMessageContaining("AppendStreamTableSink doesn't support consuming update " + + "changes which is produced by node GroupAggregate(groupBy=[a], select=[a, COUNT(*) AS cnt])") + .isInstanceOf[TableException] } @Test @@ -65,11 +66,10 @@ class LegacySinkTest extends TableTestBase { .registerTableSinkInternal("retractSink2", retractSink2) stmtSet.addInsert("retractSink2", table2) - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "OverAggregate doesn't support consuming update changes " + + assertThatThrownBy(() => util.verifyRelPlan(stmtSet)) + .hasMessageContaining("OverAggregate doesn't support consuming update changes " + "which is produced by node Calc(select=[cnt]") - util.verifyRelPlan(stmtSet) + .isInstanceOf[TableException] } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LegacyTableSourceTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LegacyTableSourceTest.scala index 2c72dfa178be4..73ddddb52c7af 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LegacyTableSourceTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LegacyTableSourceTest.scala @@ -25,7 +25,7 @@ import org.apache.flink.table.planner.expressions.utils.Func1 import org.apache.flink.table.planner.utils._ import org.apache.flink.types.Row -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} class LegacyTableSourceTest extends TableTestBase { @@ -36,7 +36,7 @@ class LegacyTableSourceTest extends TableTestBase { .fields(Array("a", "b", "c"), Array(DataTypes.INT(), DataTypes.BIGINT(), DataTypes.STRING())) .build() - @Before + @BeforeEach def setup(): Unit = { TestLegacyFilterableTableSource.createTemporaryTable( util.tableEnv, diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LimitTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LimitTest.scala index 0556ea9fb9a7c..2d5333ddca9a2 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LimitTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LimitTest.scala @@ -21,13 +21,14 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.{BeforeEach, Test} class LimitTest extends TableTestBase { protected val util = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addDataStream[(Int, String, Long)]( "MyTable", @@ -48,9 +49,10 @@ class LimitTest extends TableTestBase { util.verifyExecPlan("SELECT * FROM MyTable LIMIT 0") } - @Test(expected = classOf[SqlParserException]) + @Test def testNegativeLimitWithoutOffset(): Unit = { - util.verifyExecPlan("SELECT * FROM MyTable LIMIT -1") + assertThatExceptionOfType(classOf[SqlParserException]) + .isThrownBy(() => util.verifyExecPlan("SELECT * FROM MyTable LIMIT -1")) } @Test @@ -73,9 +75,10 @@ class LimitTest extends TableTestBase { util.verifyExecPlan("SELECT a, c FROM MyTable LIMIT 0 OFFSET 10") } - @Test(expected = classOf[SqlParserException]) + @Test def testLimitWithNegativeOffset(): Unit = { - util.verifyExecPlan("SELECT a, c FROM MyTable LIMIT 10 OFFSET -1") + assertThatExceptionOfType(classOf[SqlParserException]) + .isThrownBy(() => util.verifyExecPlan("SELECT a, c FROM MyTable LIMIT 10 OFFSET -1")) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LimitableSourceTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LimitableSourceTest.scala index 8321aacd8df01..2aaea867fda19 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LimitableSourceTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/LimitableSourceTest.scala @@ -19,12 +19,12 @@ package org.apache.flink.table.planner.plan.stream.sql import org.apache.flink.table.planner.plan.rules.logical.PushLimitIntoTableSourceScanRule -import org.junit.Before +import org.junit.jupiter.api.BeforeEach /** Test for [[PushLimitIntoTableSourceScanRule]]. */ class LimitableSourceTest extends LimitTest { - @Before + @BeforeEach override def setup(): Unit = { val ddl = s""" diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/MatchRecognizeTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/MatchRecognizeTest.scala index f1e3755ea175b..9267831de172c 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/MatchRecognizeTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/MatchRecognizeTest.scala @@ -20,13 +20,14 @@ package org.apache.flink.table.planner.plan.stream.sql import org.apache.flink.table.api.ValidationException import org.apache.flink.table.planner.utils.{StreamTableTestUtil, TableTestBase} -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.{BeforeEach, Test} class MatchRecognizeTest extends TableTestBase { protected val util: StreamTableTestUtil = streamTestUtil() - @Before + @BeforeEach def before(): Unit = { val ddl = """ @@ -223,13 +224,6 @@ class MatchRecognizeTest extends TableTestBase { @Test def testMatchRowtimeWithoutArgumentOnRowtimeLTZ(): Unit = { - thrown.expectMessage( - "MATCH_ROWTIME(rowtimeField) should be used when input stream " + - "contains rowtime attribute with TIMESTAMP_LTZ type.\n" + - "Please pass rowtime attribute field as input argument of " + - "MATCH_ROWTIME(rowtimeField) function.") - thrown.expect(classOf[AssertionError]) - val sqlQuery = s""" |SELECT @@ -252,14 +246,18 @@ class MatchRecognizeTest extends TableTestBase { |) AS T |GROUP BY symbol, TUMBLE(matchRowtime, interval '3' second) |""".stripMargin - util.verifyRelPlan(sqlQuery) + + assertThatThrownBy(() => util.verifyRelPlan(sqlQuery)) + .hasMessageContaining( + "MATCH_ROWTIME(rowtimeField) should be used when input stream " + + "contains rowtime attribute with TIMESTAMP_LTZ type.\n" + + "Please pass rowtime attribute field as input argument of " + + "MATCH_ROWTIME(rowtimeField) function.") + .isInstanceOf[AssertionError] } @Test def testMatchRowtimeWithMultipleArgs(): Unit = { - thrown.expectMessage("Invalid number of arguments to function 'MATCH_ROWTIME'.") - thrown.expect(classOf[ValidationException]) - val sqlQuery = s""" |SELECT @@ -282,16 +280,14 @@ class MatchRecognizeTest extends TableTestBase { |) AS T |GROUP BY symbol, TUMBLE(matchRowtime, interval '3' second) |""".stripMargin - util.verifyRelPlan(sqlQuery) + + assertThatThrownBy(() => util.verifyRelPlan(sqlQuery)) + .hasMessageContaining("Invalid number of arguments to function 'MATCH_ROWTIME'.") + .isInstanceOf[ValidationException] } @Test def testMatchRowtimeWithNonRowTimeAttributeAsArgs(): Unit = { - thrown.expectMessage( - "The function MATCH_ROWTIME requires argument to be a row time attribute type, " + - "but is 'INTEGER'.") - thrown.expect(classOf[ValidationException]) - val sqlQuery = s""" |SELECT @@ -314,16 +310,16 @@ class MatchRecognizeTest extends TableTestBase { |) AS T |GROUP BY symbol, TUMBLE(matchRowtime, interval '3' second) |""".stripMargin - util.verifyRelPlan(sqlQuery) + + assertThatThrownBy(() => util.verifyRelPlan(sqlQuery)) + .hasMessageContaining( + "The function MATCH_ROWTIME requires argument to be a row time attribute type, " + + "but is 'INTEGER'.") + .isInstanceOf[ValidationException] } @Test def testMatchRowtimeWithRexCallAsArg(): Unit = { - thrown.expectMessage( - "The function MATCH_ROWTIME requires a field reference as argument, " + - "but actual argument is not a simple field reference.") - thrown.expect(classOf[ValidationException]) - val sqlQuery = s""" |SELECT @@ -346,6 +342,10 @@ class MatchRecognizeTest extends TableTestBase { |) AS T |GROUP BY symbol, TUMBLE(matchRowtime, interval '3' second) |""".stripMargin - util.verifyRelPlan(sqlQuery) + + assertThatThrownBy(() => util.verifyRelPlan(sqlQuery)) + .hasMessageContaining("The function MATCH_ROWTIME requires a field reference as argument, " + + "but actual argument is not a simple field reference.") + .isInstanceOf[ValidationException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/MiniBatchIntervalInferTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/MiniBatchIntervalInferTest.scala index 0e0c57bdc6d02..55dde3373ca57 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/MiniBatchIntervalInferTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/MiniBatchIntervalInferTest.scala @@ -26,7 +26,7 @@ import org.apache.flink.table.planner.plan.utils.WindowEmitStrategy.{TABLE_EXEC_ import org.apache.flink.table.planner.utils.TableTestBase import org.apache.flink.table.types.logical.{BigIntType, IntType, VarCharType} -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} import java.time.Duration @@ -37,7 +37,7 @@ class MiniBatchIntervalInferTest extends TableTestBase { val LONG = new BigIntType() val INT = new IntType() - @Before + @BeforeEach def setup(): Unit = { util.addDataStream[(Int, String, Long)]( "MyDataStream1", diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/ModifiedMonotonicityTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/ModifiedMonotonicityTest.scala index f053cb4d779a6..6a22ccbb889bd 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/ModifiedMonotonicityTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/ModifiedMonotonicityTest.scala @@ -27,8 +27,8 @@ import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.Wei import org.apache.flink.table.planner.utils.{StreamTableTestUtil, TableTestBase, TableTestUtil} import org.apache.calcite.sql.validate.SqlMonotonicity.{CONSTANT, DECREASING, INCREASING, NOT_MONOTONIC} -import org.junit.Assert.assertEquals -import org.junit.Test +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test import java.time.Duration @@ -258,7 +258,7 @@ class ModifiedMonotonicityTest extends TableTestBase { val actualMono = FlinkRelMetadataQuery .reuseOrCreate(optimized.getCluster.getMetadataQuery) .getRelModifiedMonotonicity(optimized) - assertEquals(expect, actualMono) + assertThat(actualMono).isEqualTo(expect) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/NonDeterministicDagTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/NonDeterministicDagTest.scala index 6ee0e9f6f441d..6a3285bacf5e3 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/NonDeterministicDagTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/NonDeterministicDagTest.scala @@ -35,14 +35,16 @@ import org.apache.flink.table.sinks.UpsertStreamTableSink import org.apache.flink.table.types.DataType import org.apache.flink.table.types.logical.{BigIntType, IntType, VarCharType} import org.apache.flink.table.types.utils.TypeConversions +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.junit.{Before, Test} -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.assertj.core.api.Assertions.{assertThatCode, assertThatThrownBy} +import org.assertj.core.api.ThrowableAssert.ThrowingCallable +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith import java.util -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUpdateStrategy) extends TableTestBase { @@ -50,7 +52,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp private val tryResolve = nonDeterministicUpdateStrategy == NonDeterministicUpdateStrategy.TRY_RESOLVE - @Before + @BeforeEach def before(): Unit = { util.tableConfig.getConfiguration.set( OptimizerConfigOptions.TABLE_OPTIMIZER_NONDETERMINISTIC_UPDATE_STRATEGY, @@ -217,7 +219,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp util.tableEnv.createTemporaryFunction("str_split", new StringSplit()) } - @Test + @TestTemplate def testCdcWithMetaSinkWithPk(): Unit = { util.verifyExecPlanInsert(s""" |insert into sink_with_pk @@ -225,45 +227,55 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |from cdc_with_meta |""".stripMargin) } - @Test + @TestTemplate def testNonDeterministicProjectionWithSinkWithoutPk(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert( + """ + |insert into sink_without_pk + |select + | a, + | if(a > 100, b+d, b) as b, + | case when d > 100 then json_value(c, '$.count') + | else cast(b as string) || '#' end as c + |from ( + |select a, b, c, d from ( + | select *, row_number() over(partition by a order by d desc) as rn + | from ( + | select a, d as b, c, ndFunc(b) as d from cdc + | ) tmp + |) tmp where rn = 1) tmp + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "The column(s): d(generated by non-deterministic function: ndFunc ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "The column(s): d(generated by non-deterministic function: ndFunc ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(""" - |insert into sink_without_pk - |select - | a, - | if(a > 100, b+d, b) as b, - | case when d > 100 then json_value(c, '$.count') - | else cast(b as string) || '#' end as c - |from ( - |select a, b, c, d from ( - | select *, row_number() over(partition by a order by d desc) as rn - | from ( - | select a, d as b, c, ndFunc(b) as d from cdc - | ) tmp - |) tmp where rn = 1) tmp - |""".stripMargin) } - @Test + @TestTemplate def testCdcWithMetaSinkWithoutPk(): Unit = { + val callable: ThrowingCallable = () => util.verifyExecPlanInsert(s""" + |insert into sink_without_pk + |select a, metadata_3, c + |from cdc_with_meta + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "metadata column(s): 'metadata_3' in cdc source may cause wrong result or error") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "metadata column(s): 'metadata_3' in cdc source may cause wrong result or error") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_without_pk - |select a, metadata_3, c - |from cdc_with_meta - |""".stripMargin) } - @Test + @TestTemplate def testCdcWithMetaLegacySinkWithPk(): Unit = { val sinkWithPk = new TestingUpsertSink( Array("a"), @@ -282,13 +294,8 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testCdcWithMetaLegacySinkWithoutPk(): Unit = { - if (tryResolve) { - thrown.expectMessage( - "metadata column(s): 'metadata_3' in cdc source may cause wrong result or error") - thrown.expect(classOf[TableException]) - } val retractSink = util.createRetractTableSink( Array("a", "b", "c"), @@ -297,34 +304,44 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp .asInstanceOf[TableEnvironmentInternal] .registerTableSinkInternal("legacy_retract_sink", retractSink) - util.verifyExecPlanInsert(s""" - |insert into legacy_retract_sink - |select a, metadata_3, c - |from cdc_with_meta - |""".stripMargin) + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert(s""" + |insert into legacy_retract_sink + |select a, metadata_3, c + |from cdc_with_meta + |""".stripMargin) + + if (tryResolve) { + assertThatThrownBy(callable) + .hasMessageContaining( + "metadata column(s): 'metadata_3' in cdc source may cause wrong result or error") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() + } } - @Test + @TestTemplate def testCdcWithMetaSinkWithCompositePk(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert(s""" + |insert into sink_with_composite_pk + |select a, b, c, metadata_3 + |from cdc_with_meta + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "metadata column(s): 'metadata_3' in cdc source may cause wrong result or error") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "metadata column(s): 'metadata_3' in cdc source may cause wrong result or error") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_with_composite_pk - |select a, b, c, metadata_3 - |from cdc_with_meta - |""".stripMargin) } - @Test + @TestTemplate def testCdcWithMetaRenameSinkWithCompositePk(): Unit = { - if (tryResolve) { - thrown.expectMessage( - "metadata column(s): 'metadata_3' in cdc source may cause wrong result or error") - thrown.expect(classOf[TableException]) - } util.tableEnv.executeSql(""" |create temporary table cdc_with_meta_rename ( | a int, @@ -340,30 +357,43 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp | 'readable-metadata' = 'metadata_3:BIGINT' |)""".stripMargin) - util.verifyExecPlanInsert(s""" - |insert into sink_with_composite_pk - |select a, b, c, e from cdc_with_meta_rename - |""".stripMargin) - } + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert(s""" + |insert into sink_with_composite_pk + |select a, b, c, e from cdc_with_meta_rename + |""".stripMargin) - @Test - def testSourceWithComputedColumnSinkWithPk(): Unit = { if (tryResolve) { - thrown.expectMessage( - "column(s): day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "metadata column(s): 'metadata_3' in cdc source may cause wrong result or error") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } + } + @TestTemplate + def testSourceWithComputedColumnSinkWithPk(): Unit = { // can not infer pk from cdc source with computed column(s) - util.verifyExecPlanInsert(s""" - |insert into sink_with_pk - |select a, b, `day` - |from cdc_with_computed_col - |where b > 100 - |""".stripMargin) + val callable: ThrowingCallable = () => util.verifyExecPlanInsert(s""" + |insert into sink_with_pk + |select a, b, `day` + |from cdc_with_computed_col + |where b > 100 + |""".stripMargin) + + if (tryResolve) { + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() + } } - @Test + @TestTemplate def testSourceWithComputedColumnMultiSink(): Unit = { val stmtSet = util.tableEnv.createStatementSet() stmtSet.addInsertSql(s""" @@ -378,45 +408,60 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |from cdc_with_computed_col |where b > 100 |""".stripMargin) + + val callable: ThrowingCallable = () => util.verifyExecPlan(stmtSet) + if (tryResolve) { - thrown.expectMessage( - "column(s): day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlan(stmtSet) } - @Test + @TestTemplate def testCdcCorrelateNonDeterministicFuncSinkWithPK(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert(s""" + |insert into sink_with_pk + |select + | t1.a, t1.b, a1 + |from cdc t1, lateral table(ndTableFunc(a)) as T(a1) + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "column(s): EXPR$0(generated by non-deterministic function: ndTableFunc ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): EXPR$0(generated by non-deterministic function: ndTableFunc ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_with_pk - |select - | t1.a, t1.b, a1 - |from cdc t1, lateral table(ndTableFunc(a)) as T(a1) - |""".stripMargin) } - @Test + @TestTemplate def testCdcCorrelateNonDeterministicFuncNoLeftOutput(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert(s""" + |insert into sink_with_pk(a) + |select + | cast(a1 as integer) a + |from cdc t1, lateral table(ndTableFunc(a)) as T(a1) + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "column(s): EXPR$0(generated by non-deterministic function: ndTableFunc ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): EXPR$0(generated by non-deterministic function: ndTableFunc ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_with_pk(a) - |select - | cast(a1 as integer) a - |from cdc t1, lateral table(ndTableFunc(a)) as T(a1) - |""".stripMargin) } - @Test + @TestTemplate def testCdcCorrelateNonDeterministicFuncNoRightOutput(): Unit = { util.verifyExecPlanInsert(s""" |insert into sink_with_pk @@ -425,37 +470,45 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testCdcCorrelateOnNonDeterministicCondition(): Unit = { // TODO update this after FLINK-7865 was fixed - thrown.expectMessage("unexpected correlate variable $cor0 in the plan") - thrown.expect(classOf[TableException]) - util.verifyExecPlanInsert(s""" - |insert into sink_with_pk - |select a, b, c - |from cdc t1 join lateral table(str_split(c)) as T(c1) - | -- the join predicate can only be empty or literal true for now - | on ndFunc(b) > 100 - |""".stripMargin) - } - - @Test + assertThatThrownBy( + () => + util.verifyExecPlanInsert( + s""" + |insert into sink_with_pk + |select a, b, c + |from cdc t1 join lateral table(str_split(c)) as T(c1) + | -- the join predicate can only be empty or literal true for now + | on ndFunc(b) > 100 + |""".stripMargin)) + .hasMessageContaining("unexpected correlate variable $cor0 in the plan") + .isInstanceOf[TableException] + } + + @TestTemplate def testCdcWithMetaCorrelateSinkWithPk(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert(s""" + |insert into sink_with_pk + |select t1.a, t1.metadata_1, T.c1 + |from cdc_with_meta t1, lateral table(str_split(c)) as T(c1) + |""".stripMargin) + // Under ignore mode, the generated execution plan may cause wrong result though // upsertMaterialize has been enabled in sink, because if (tryResolve) { - thrown.expectMessage( - "metadata column(s): 'metadata_1' in cdc source may cause wrong result or error on downstream operators") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "metadata column(s): 'metadata_1' in cdc source may cause wrong result or error on downstream operators") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_with_pk - |select t1.a, t1.metadata_1, T.c1 - |from cdc_with_meta t1, lateral table(str_split(c)) as T(c1) - |""".stripMargin) } - @Test + @TestTemplate def testCdcWithNonDeterministicFuncSinkWithPk(): Unit = { util.verifyExecPlanInsert(s""" |insert into sink_with_pk @@ -464,21 +517,25 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testCdcWithNonDeterministicFuncSinkWithoutPk(): Unit = { + val callable: ThrowingCallable = () => util.verifyExecPlanInsert(s""" + |insert into sink_without_pk + |select a, ndFunc(b), c + |from cdc + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "column(s): EXPR$1(generated by non-deterministic function: ndFunc ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): EXPR$1(generated by non-deterministic function: ndFunc ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_without_pk - |select a, ndFunc(b), c - |from cdc - |""".stripMargin) } - @Test + @TestTemplate def testCdcWithNonDeterministicFilter(): Unit = { // TODO should throw error if tryResolve is true after FLINK-28737 was fixed util.verifyExecPlanInsert(s""" @@ -489,7 +546,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testCdcJoinDimWithPkSinkWithPk(): Unit = { // The lookup key contains the dim table's pk, there will be no materialization. util.verifyExecPlanInsert(s""" @@ -502,7 +559,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testCdcJoinDimWithoutPkSinkWithPk(): Unit = { // This case shows how costly is if the dim table does not define a pk. // The lookup key doesn't contain the dim table's pk, there will be two more costly @@ -517,7 +574,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testCdcLeftJoinDimWithPkSinkWithPk(): Unit = { // The lookup key contains the dim table's pk, there will be no materialization. util.verifyExecPlanInsert(s""" @@ -530,7 +587,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testCdcJoinDimWithPkSinkWithoutPk(): Unit = { util.verifyExecPlanInsert(s""" |insert into sink_without_pk @@ -542,7 +599,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testCdcJoinDimWithoutPkSinkWithoutPk(): Unit = { util.verifyExecPlanInsert(s""" |insert into sink_without_pk @@ -554,7 +611,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testCdcJoinDimWithPkOnlySinkWithoutPk(): Unit = { // only select lookup key field, expect not affect NDU util.verifyExecPlanInsert(s""" @@ -567,7 +624,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testCdcLeftJoinDimWithoutPkSinkWithoutPk(): Unit = { util.verifyExecPlanInsert( s""" @@ -580,7 +637,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testCdcJoinDimWithPkOutputNoPkSinkWithoutPk(): Unit = { // non lookup pk selected, expect materialize if tryResolve util.verifyExecPlanInsert(s""" @@ -593,174 +650,220 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testCdcJoinDimWithPkNonDeterministicFuncSinkWithoutPk(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert(s""" + |insert into sink_without_pk + |select ndFunc(t2.a) a, t1.b, t1.c + |from ( + | select *, proctime() proctime from cdc + |) t1 join dim_with_pk for system_time as of t1.proctime as t2 + |on t1.a = t2.a + |""".stripMargin) + if (tryResolve) { // only select lookup key field, but with ND-call, expect exception - thrown.expectMessage( - "column(s): a(generated by non-deterministic function: ndFunc ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): a(generated by non-deterministic function: ndFunc ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_without_pk - |select ndFunc(t2.a) a, t1.b, t1.c - |from ( - | select *, proctime() proctime from cdc - |) t1 join dim_with_pk for system_time as of t1.proctime as t2 - |on t1.a = t2.a - |""".stripMargin) } - @Test + @TestTemplate def testCdcJoinDimWithPkNonDeterministicLocalCondition(): Unit = { // use user defined function + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert(s""" + |insert into sink_without_pk + |select t1.a, t1.b, t1.c + |from ( + | select *, proctime() proctime from cdc + |) t1 join dim_with_pk for system_time as of t1.proctime as t2 + |on t1.a = t2.a and ndFunc(t2.b) > 100 + |""".stripMargin) + if (tryResolve) { // not select lookup source field, but with NonDeterministicCondition, expect exception - thrown.expectMessage( - "exists non deterministic function: 'ndFunc' in condition: '>(ndFunc($1), 100)' which may cause wrong result") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "exists non deterministic function: 'ndFunc' in condition: '>(ndFunc($1), 100)' which may cause wrong result") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_without_pk - |select t1.a, t1.b, t1.c - |from ( - | select *, proctime() proctime from cdc - |) t1 join dim_with_pk for system_time as of t1.proctime as t2 - |on t1.a = t2.a and ndFunc(t2.b) > 100 - |""".stripMargin) } - @Test + @TestTemplate def testCdcJoinDimWithPkNonDeterministicLocalCondition2(): Unit = { // use builtin temporal function + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert(s""" + |insert into sink_with_pk + |select t1.a, t2.b as version, t2.c + |from ( + | select *, proctime() proctime from cdc + |) t1 join dim_with_pk for system_time as of t1.proctime as t2 + |on t1.a = t2.a + | -- check dim table data's freshness + | and t2.b > UNIX_TIMESTAMP() - 300 + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "exists non deterministic function: 'UNIX_TIMESTAMP' in condition: '>($1, -(UNIX_TIMESTAMP(), 300))' which may cause wrong result") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "exists non deterministic function: 'UNIX_TIMESTAMP' in condition: '>($1, -(UNIX_TIMESTAMP(), 300))' which may cause wrong result") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_with_pk - |select t1.a, t2.b as version, t2.c - |from ( - | select *, proctime() proctime from cdc - |) t1 join dim_with_pk for system_time as of t1.proctime as t2 - |on t1.a = t2.a - | -- check dim table data's freshness - | and t2.b > UNIX_TIMESTAMP() - 300 - |""".stripMargin) } - @Test + @TestTemplate def testCdcJoinDimNonDeterministicRemainingCondition(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert(s""" + |insert into sink_with_pk + |select t1.a, t2.b, t2.c + |from ( + | select *, proctime() proctime from cdc + |) t1 join dim_with_pk for system_time as of t1.proctime as t2 + |on t1.a = t2.a + | -- non deterministic function in remaining condition + | and t1.b > ndFunc(t2.b) + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "exists non deterministic function: 'ndFunc' in condition: '>($1, ndFunc($3))' which may cause wrong result") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "exists non deterministic function: 'ndFunc' in condition: '>($1, ndFunc($3))' which may cause wrong result") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_with_pk - |select t1.a, t2.b, t2.c - |from ( - | select *, proctime() proctime from cdc - |) t1 join dim_with_pk for system_time as of t1.proctime as t2 - |on t1.a = t2.a - | -- non deterministic function in remaining condition - | and t1.b > ndFunc(t2.b) - |""".stripMargin) } - @Test + @TestTemplate def testCdcLeftJoinDimWithNonDeterministicPreFilter(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert( + s""" + |insert into sink_with_pk + |select t1.a, t2.b as version, t2.c + |from ( + | select *, proctime() proctime from cdc + |) t1 left join dim_with_pk for system_time as of t1.proctime as t2 + |on t1.a = t2.a + | and t1.b > UNIX_TIMESTAMP() - 300 + |""".stripMargin) + // use builtin temporal function if (tryResolve) { - thrown.expectMessage( - "exists non deterministic function: 'UNIX_TIMESTAMP' in condition: '>($1, -(UNIX_TIMESTAMP(), 300))' which may cause wrong result") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "exists non deterministic function: 'UNIX_TIMESTAMP' in condition: '>($1, -(UNIX_TIMESTAMP(), 300))' which may cause wrong result") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_with_pk - |select t1.a, t2.b as version, t2.c - |from ( - | select *, proctime() proctime from cdc - |) t1 left join dim_with_pk for system_time as of t1.proctime as t2 - |on t1.a = t2.a - | and t1.b > UNIX_TIMESTAMP() - 300 - |""".stripMargin) } - @Test + @TestTemplate def testGroupByNonDeterministicFuncWithCdcSource(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert( + s""" + |insert into sink_with_pk + |select + | a, count(*) cnt, `day` + |from ( + | select *, DATE_FORMAT(CURRENT_TIMESTAMP, 'yyMMdd') `day` from cdc + |) t + |group by `day`, a + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "column(s): day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_with_pk - |select - | a, count(*) cnt, `day` - |from ( - | select *, DATE_FORMAT(CURRENT_TIMESTAMP, 'yyMMdd') `day` from cdc - |) t - |group by `day`, a - |""".stripMargin) } - @Test + @TestTemplate def testGroupByNonDeterministicUdfWithCdcSource(): Unit = { + val callable: ThrowingCallable = () => util.verifyExecPlanInsert(s""" + |insert into sink_with_pk + |select + | ndFunc(a), count(*) cnt, c + |from cdc + |group by ndFunc(a), c + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "column(s): EXPR$0(generated by non-deterministic function: ndFunc ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): EXPR$0(generated by non-deterministic function: ndFunc ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_with_pk - |select - | ndFunc(a), count(*) cnt, c - |from cdc - |group by ndFunc(a), c - |""".stripMargin) } - @Test + @TestTemplate def testNestedAggWithNonDeterministicGroupingKeys(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert( + s""" + |insert into sink_with_pk + |select + | a, sum(b) qmt, DATE_FORMAT(CURRENT_TIMESTAMP, 'yyMMdd') `day` + |from ( + | select *, row_number() over (partition by a order by PROCTIME() desc) rn from src + |) t + |where rn = 1 + |group by a, DATE_FORMAT(CURRENT_TIMESTAMP, 'yyMMdd') + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "column(s): day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert( - s""" - |insert into sink_with_pk - |select - | a, sum(b) qmt, DATE_FORMAT(CURRENT_TIMESTAMP, 'yyMMdd') `day` - |from ( - | select *, row_number() over (partition by a order by PROCTIME() desc) rn from src - |) t - |where rn = 1 - |group by a, DATE_FORMAT(CURRENT_TIMESTAMP, 'yyMMdd') - |""".stripMargin) } - @Test + @TestTemplate def testGroupAggNonDeterministicFuncOnSourcePk(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlan( + s""" + |select + | `day`, count(*) cnt, sum(b) qmt + |from ( + | select *, concat(cast(a as varchar), DATE_FORMAT(CURRENT_TIMESTAMP, 'yyMMdd')) `day` from cdc + |) t + |group by `day` + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "column(s): day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlan( - s""" - |select - | `day`, count(*) cnt, sum(b) qmt - |from ( - | select *, concat(cast(a as varchar), DATE_FORMAT(CURRENT_TIMESTAMP, 'yyMMdd')) `day` from cdc - |) t - |group by `day` - |""".stripMargin) } - @Test + @TestTemplate def testAggWithNonDeterministicFilterArgs(): Unit = { util.verifyExecPlanInsert( s""" @@ -774,47 +877,57 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testAggWithNonDeterministicFilterArgsOnCdcSource(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert( + s""" + |insert into sink_with_pk + |select + | a + | ,count(*) cnt + | ,cast(count(distinct c) filter (where b > UNIX_TIMESTAMP() - 180) as varchar) valid_uv + |from cdc + |group by a + |""".stripMargin) + if (tryResolve) { // though original pk was selected and same as the sink's pk, but the valid_uv was // non-deterministic, will raise an error - thrown.expectMessage( - "column(s): $f2(generated by non-deterministic function: UNIX_TIMESTAMP ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): $f2(generated by non-deterministic function: UNIX_TIMESTAMP ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert( - s""" - |insert into sink_with_pk - |select - | a - | ,count(*) cnt - | ,cast(count(distinct c) filter (where b > UNIX_TIMESTAMP() - 180) as varchar) valid_uv - |from cdc - |group by a - |""".stripMargin) } - @Test + @TestTemplate def testAggWithNonDeterministicFilterArgsOnCdcSourceSinkWithoutPk(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert( + s""" + |insert into sink_without_pk + |select + | a + | ,count(*) cnt + | ,cast(count(distinct c) filter (where b > UNIX_TIMESTAMP() - 180) as varchar) valid_uv + |from cdc + |group by a + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "column(s): $f2(generated by non-deterministic function: UNIX_TIMESTAMP ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): $f2(generated by non-deterministic function: UNIX_TIMESTAMP ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert( - s""" - |insert into sink_without_pk - |select - | a - | ,count(*) cnt - | ,cast(count(distinct c) filter (where b > UNIX_TIMESTAMP() - 180) as varchar) valid_uv - |from cdc - |group by a - |""".stripMargin) } - @Test + @TestTemplate def testNonDeterministicAggOnAppendSourceSinkWithPk(): Unit = { util.verifyExecPlanInsert(s""" |insert into sink_with_pk @@ -827,25 +940,29 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testNonDeterministicAggOnAppendSourceSinkWithoutPk(): Unit = { + val callable: ThrowingCallable = () => util.verifyExecPlanInsert(s""" + |insert into sink_without_pk + |select + | a + | ,ndAggFunc(b) ndCnt + | ,max(c) mc + |from T + |group by a + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "column(s): ndCnt(generated by non-deterministic function: ndAggFunc ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): ndCnt(generated by non-deterministic function: ndAggFunc ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_without_pk - |select - | a - | ,ndAggFunc(b) ndCnt - | ,max(c) mc - |from T - |group by a - |""".stripMargin) } - @Test + @TestTemplate def testGlobalNonDeterministicAggOnAppendSourceSinkWithPk(): Unit = { util.verifyExecPlanInsert(s""" |insert into sink_with_pk @@ -857,24 +974,28 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testGlobalNonDeterministicAggOnAppendSourceSinkWithoutPk(): Unit = { + val callable: ThrowingCallable = () => util.verifyExecPlanInsert(s""" + |insert into sink_without_pk + |select + | max(a) + | ,ndAggFunc(b) ndCnt + | ,max(c) mc + |from T + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "column(s): ndCnt(generated by non-deterministic function: ndAggFunc ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): ndCnt(generated by non-deterministic function: ndAggFunc ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_without_pk - |select - | max(a) - | ,ndAggFunc(b) ndCnt - | ,max(c) mc - |from T - |""".stripMargin) } - @Test + @TestTemplate def testUpsertSourceSinkWithPk(): Unit = { // contains normalize util.verifyExecPlanInsert(s""" @@ -884,7 +1005,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testUpsertSourceSinkWithoutPk(): Unit = { // contains normalize util.verifyExecPlanInsert(s""" @@ -894,7 +1015,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testMultiOverWithNonDeterministicUdafSinkWithPk(): Unit = { util.verifyExecPlanInsert( """ @@ -911,7 +1032,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp ) } - @Test + @TestTemplate def testOverWithNonDeterministicUdafSinkWithoutPk(): Unit = { util.verifyExecPlanInsert( """ @@ -926,27 +1047,28 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp ) } - @Test + @TestTemplate def testMultiOverWithNonDeterministicAggFilterSinkWithPk(): Unit = { // agg with filter is not supported currently, should update this after it is supported. - thrown.expectMessage("OVER must be applied to aggregate function") - thrown.expect(classOf[ValidationException]) - util.verifyExecPlanInsert( - """ - |insert into sink_with_composite_pk - |SELECT - | a - | ,COUNT(distinct b) OVER (PARTITION BY a ORDER BY proctime - | ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) uv - | ,b - | ,SUM(a) filter (where b > UNIX_TIMESTAMP() - 180) OVER (PARTITION BY a ORDER BY proctime - | ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) nd - |FROM T1 - """.stripMargin - ) - } - - @Test + assertThatThrownBy( + () => + util.verifyExecPlanInsert( + """ + |insert into sink_with_composite_pk + |SELECT + | a + | ,COUNT(distinct b) OVER (PARTITION BY a ORDER BY proctime + | ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) uv + | ,b + | ,SUM(a) filter (where b > UNIX_TIMESTAMP() - 180) OVER (PARTITION BY a ORDER BY proctime + | ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) nd + |FROM T1 + """.stripMargin)) + .hasMessageContaining("OVER must be applied to aggregate function") + .isInstanceOf[ValidationException] + } + + @TestTemplate def testAppendRankOnMultiOverWithNonDeterministicUdafSinkWithPk(): Unit = { util.verifyExecPlanInsert( """ @@ -970,7 +1092,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp ) } - @Test + @TestTemplate def testAppendRankOnMultiOverWithNonDeterministicUdafSinkWithoutPk(): Unit = { util.verifyExecPlanInsert( """ @@ -994,7 +1116,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp ) } - @Test + @TestTemplate def testUpdateRankOutputRowNumberSinkWithPk(): Unit = { util.tableEnv.executeSql(s""" | create temporary view v1 as @@ -1014,7 +1136,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testRetractRankOutputRowNumberSinkWithPk(): Unit = { util.tableEnv.executeSql(s""" | create temporary view v1 as @@ -1034,76 +1156,95 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testUnionSinkWithCompositePk(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert(s""" + |insert into sink_with_composite_pk + |select a, b, c, d + |from src + |union + |select a, b, c, metadata_3 + |from cdc_with_meta + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "metadata column(s): 'metadata_3' in cdc source may cause wrong result or error") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "metadata column(s): 'metadata_3' in cdc source may cause wrong result or error") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_with_composite_pk - |select a, b, c, d - |from src - |union - |select a, b, c, metadata_3 - |from cdc_with_meta - |""".stripMargin) } - @Test + @TestTemplate def testUnionAllSinkWithCompositePk(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert(s""" + |insert into sink_with_composite_pk + |select a, b, c, d + |from src + |union all + |select a, b, c, metadata_3 + |from cdc_with_meta + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "metadata column(s): 'metadata_3' in cdc source may cause wrong result or error") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "metadata column(s): 'metadata_3' in cdc source may cause wrong result or error") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_with_composite_pk - |select a, b, c, d - |from src - |union all - |select a, b, c, metadata_3 - |from cdc_with_meta - |""".stripMargin) } - @Test + @TestTemplate def testUnionAllSinkWithoutPk(): Unit = { + val callable: ThrowingCallable = () => util.verifyExecPlanInsert(s""" + |insert into sink_without_pk + |select a, b, c + |from src + |union all + |select a, metadata_3, c + |from cdc_with_meta + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "metadata column(s): 'metadata_3' in cdc source may cause wrong result or error") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "metadata column(s): 'metadata_3' in cdc source may cause wrong result or error") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_without_pk - |select a, b, c - |from src - |union all - |select a, metadata_3, c - |from cdc_with_meta - |""".stripMargin) } - @Test + @TestTemplate def testCdcJoinWithNonDeterministicCondition(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert(s""" + |insert into sink_without_pk + |select + | t1.a + | ,t2.b + | ,t1.c + |from cdc t1 join cdc t2 + | on ndFunc(t1.b) = ndFunc(t2.b) + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "column(s): $f4(generated by non-deterministic function: ndFunc ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): $f4(generated by non-deterministic function: ndFunc ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(s""" - |insert into sink_without_pk - |select - | t1.a - | ,t2.b - | ,t1.c - |from cdc t1 join cdc t2 - | on ndFunc(t1.b) = ndFunc(t2.b) - |""".stripMargin) } - @Test + @TestTemplate def testProctimeIntervalJoinSinkWithoutPk(): Unit = { util.verifyExecPlanInsert(""" |insert into sink_without_pk @@ -1112,7 +1253,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp """.stripMargin) } - @Test + @TestTemplate def testCdcProctimeIntervalJoinOnPkSinkWithoutPk(): Unit = { util.verifyExecPlanInsert(""" |insert into sink_without_pk @@ -1123,22 +1264,27 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp """.stripMargin) } - @Test + @TestTemplate def testCdcProctimeIntervalJoinOnNonPkSinkWithoutPk(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert(""" + |insert into sink_without_pk + |SELECT t2.a, t2.b, t1.c FROM ( + | select *, proctime() proctime from cdc) t1 JOIN + | (select *, proctime() proctime from cdc) t2 ON + | t1.b = t2.b AND t1.proctime > t2.proctime - INTERVAL '5' SECOND + """.stripMargin) + if (tryResolve) { - thrown.expectMessage("can not satisfy the determinism requirement") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining("can not satisfy the determinism requirement") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert(""" - |insert into sink_without_pk - |SELECT t2.a, t2.b, t1.c FROM ( - | select *, proctime() proctime from cdc) t1 JOIN - | (select *, proctime() proctime from cdc) t2 ON - | t1.b = t2.b AND t1.proctime > t2.proctime - INTERVAL '5' SECOND - """.stripMargin) } - @Test + @TestTemplate def testCdcRowtimeIntervalJoinSinkWithoutPk(): Unit = { util.verifyExecPlanInsert( """ @@ -1148,7 +1294,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp """.stripMargin) } - @Test + @TestTemplate def testCdcRowtimeIntervalJoinSinkWithPk(): Unit = { util.verifyExecPlanInsert( """ @@ -1158,7 +1304,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp """.stripMargin) } - @Test + @TestTemplate def testJoinKeyContainsUk(): Unit = { util.verifyExecPlan( s""" @@ -1175,7 +1321,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testJoinHasBothSidesUk(): Unit = { util.verifyExecPlan( s""" @@ -1192,53 +1338,64 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testJoinHasBothSidesUkSinkWithoutPk(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert( + s""" + |insert into sink_with_pk + |select t1.a, t2.a, t2.`c-day` + |from ( + | select a, b, c, d + | from cdc + | ) t1 + |join ( + | select a, b, CONCAT(c, DATE_FORMAT(CURRENT_TIMESTAMP, 'yyMMdd')) as `c-day`, d + | from cdc + |) t2 + | on t1.b = t2.b + |""".stripMargin) + if (tryResolve) { // sink require all columns be deterministic though join has both side uk - thrown.expectMessage( - "column(s): c-day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): c-day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlanInsert( - s""" - |insert into sink_with_pk - |select t1.a, t2.a, t2.`c-day` - |from ( - | select a, b, c, d - | from cdc - | ) t1 - |join ( - | select a, b, CONCAT(c, DATE_FORMAT(CURRENT_TIMESTAMP, 'yyMMdd')) as `c-day`, d - | from cdc - |) t2 - | on t1.b = t2.b - |""".stripMargin) } - @Test + @TestTemplate def testJoinHasSingleSideUk(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlan( + s""" + |select t1.a, t2.`c-day`, t2.b, t2.d + |from ( + | select a, b, c, d + | from cdc + | ) t1 + |join ( + | select a, b, CONCAT(c, DATE_FORMAT(CURRENT_TIMESTAMP, 'yyMMdd')) as `c-day`, d + | from cdc + |) t2 + | on t1.b = t2.b + |""".stripMargin) + if (tryResolve) { // the input side without uk requires all columns be deterministic - thrown.expectMessage("can not satisfy the determinism requirement") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): c-day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlan( - s""" - |select t1.a, t2.`c-day`, t2.b, t2.d - |from ( - | select a, b, c, d - | from cdc - | ) t1 - |join ( - | select a, b, CONCAT(c, DATE_FORMAT(CURRENT_TIMESTAMP, 'yyMMdd')) as `c-day`, d - | from cdc - |) t2 - | on t1.b = t2.b - |""".stripMargin) } - @Test + @TestTemplate def testSemiJoinKeyContainsUk(): Unit = { util.verifyExecPlan( s""" @@ -1253,9 +1410,8 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testAntiJoinKeyContainsUk(): Unit = { - util.verifyExecPlan( s""" |select t1.a, t1.`c-day`, t1.b, t1.d @@ -1269,34 +1425,34 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |""".stripMargin) } - @Test + @TestTemplate def testSemiJoinWithNonDeterministicConditionSingleSideHasUk(): Unit = { + val callable: ThrowingCallable = () => + util.verifyExecPlan( + s""" + |select t1.a, t1.b, t1.c, t1.d + |from ( + | select a, b, c, d + | from cdc + | ) t1 + |where t1.c in ( + | select CONCAT(c, DATE_FORMAT(CURRENT_TIMESTAMP, 'yyMMdd')) c from cdc where b > 100 + |) + |""".stripMargin) + if (tryResolve) { - thrown.expectMessage( - "column(s): c(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): c(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlan( - s""" - |select t1.a, t1.b, t1.c, t1.d - |from ( - | select a, b, c, d - | from cdc - | ) t1 - |where t1.c in ( - | select CONCAT(c, DATE_FORMAT(CURRENT_TIMESTAMP, 'yyMMdd')) c from cdc where b > 100 - |) - |""".stripMargin) } - @Test + @TestTemplate def testCdcJoinWithNonDeterministicOutputSinkWithPk(): Unit = { // a real case from FLINK-27369 - if (tryResolve) { - thrown.expectMessage( - "The column(s): logistics_time(generated by non-deterministic function: NOW ) can not satisfy the determinism requirement") - thrown.expect(classOf[TableException]) - } util.tableEnv.executeSql(s""" |CREATE TEMPORARY TABLE t_order ( | order_id INT, @@ -1336,87 +1492,99 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp | 'sink-insert-only' = 'false' |)""".stripMargin) - util.verifyExecPlanInsert( - s""" - |INSERT INTO t_join_sink - |SELECT ord.order_id, - |ord.order_name, - |logistics.logistics_id, - |logistics.logistics_target, - |logistics.logistics_source, - |now() - |FROM t_order AS ord - |LEFT JOIN t_logistics AS logistics ON ord.order_id=logistics.order_id - |""".stripMargin) + val callable: ThrowingCallable = () => + util.verifyExecPlanInsert( + s""" + |INSERT INTO t_join_sink + |SELECT ord.order_id, + |ord.order_name, + |logistics.logistics_id, + |logistics.logistics_target, + |logistics.logistics_source, + |now() + |FROM t_order AS ord + |LEFT JOIN t_logistics AS logistics ON ord.order_id=logistics.order_id + |""".stripMargin) + + if (tryResolve) { + assertThatThrownBy(callable) + .hasMessageContaining( + "The column(s): logistics_time(generated by non-deterministic function: NOW ) can not satisfy the determinism requirement") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() + } } - @Test + @TestTemplate def testProctimeDedupOnCdcWithMetadataSinkWithPk(): Unit = { // TODO this should be updated after StreamPhysicalDeduplicate supports consuming update - thrown.expectMessage( - "StreamPhysicalDeduplicate doesn't support consuming update and delete changes") - thrown.expect(classOf[TableException]) - util.verifyExecPlanInsert( - """ - |insert into sink_with_pk - |SELECT a, metadata_3, c - |FROM ( - | SELECT *, - | ROW_NUMBER() OVER (PARTITION BY a ORDER BY PROCTIME() ASC) as rowNum - | FROM cdc_with_meta - |) - |WHERE rowNum = 1 - """.stripMargin - ) - } - - @Test + assertThatThrownBy( + () => + util.verifyExecPlanInsert( + """ + |insert into sink_with_pk + |SELECT a, metadata_3, c + |FROM ( + | SELECT *, + | ROW_NUMBER() OVER (PARTITION BY a ORDER BY PROCTIME() ASC) as rowNum + | FROM cdc_with_meta + |) + |WHERE rowNum = 1 + """.stripMargin)) + .hasMessageContaining( + "StreamPhysicalDeduplicate doesn't support consuming update and delete changes") + .isInstanceOf[TableException] + } + + @TestTemplate def testProctimeDedupOnCdcWithMetadataSinkWithoutPk(): Unit = { // TODO this should be updated after StreamPhysicalDeduplicate supports consuming update - thrown.expectMessage( - "StreamPhysicalDeduplicate doesn't support consuming update and delete changes") - thrown.expect(classOf[TableException]) - util.verifyExecPlanInsert( - """ - |insert into sink_without_pk - |SELECT a, metadata_3, c - |FROM ( - | SELECT *, - | ROW_NUMBER() OVER (PARTITION BY a ORDER BY PROCTIME() ASC) as rowNum - | FROM cdc_with_meta - |) - |WHERE rowNum = 1 + assertThatThrownBy( + () => + util.verifyExecPlanInsert( + """ + |insert into sink_without_pk + |SELECT a, metadata_3, c + |FROM ( + | SELECT *, + | ROW_NUMBER() OVER (PARTITION BY a ORDER BY PROCTIME() ASC) as rowNum + | FROM cdc_with_meta + |) + |WHERE rowNum = 1 """.stripMargin - ) + )) + .hasMessageContaining( + "StreamPhysicalDeduplicate doesn't support consuming update and delete changes") + .isInstanceOf[TableException] + } - @Test + @TestTemplate def testRowtimeDedupOnCdcWithMetadataSinkWithPk(): Unit = { // TODO this should be updated after StreamPhysicalDeduplicate supports consuming update - thrown.expectMessage( - "StreamPhysicalDeduplicate doesn't support consuming update and delete changes") - thrown.expect(classOf[TableException]) - util.verifyExecPlanInsert( - """ - |insert into sink_with_pk - |SELECT a, b, c - |FROM ( - | SELECT *, - | ROW_NUMBER() OVER (PARTITION BY a ORDER BY op_ts ASC) as rowNum - | FROM cdc_with_meta_and_wm - |) - |WHERE rowNum = 1 + assertThatThrownBy( + () => + util.verifyExecPlanInsert( + """ + |insert into sink_with_pk + |SELECT a, b, c + |FROM ( + | SELECT *, + | ROW_NUMBER() OVER (PARTITION BY a ORDER BY op_ts ASC) as rowNum + | FROM cdc_with_meta_and_wm + |) + |WHERE rowNum = 1 """.stripMargin - ) + )) + .hasMessageContaining( + "StreamPhysicalDeduplicate doesn't support consuming update and delete changes") + .isInstanceOf[TableException] } - @Test + @TestTemplate def testWindowDedupOnCdcWithMetadata(): Unit = { // TODO this should be updated after StreamPhysicalWindowDeduplicate supports consuming update - thrown.expectMessage( - "StreamPhysicalWindowDeduplicate doesn't support consuming update and delete changes") - thrown.expect(classOf[TableException]) - util.tableEnv.executeSql(""" |create temporary table sink1 ( | a int, @@ -1429,21 +1597,25 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp | 'sink-insert-only' = 'false' |)""".stripMargin) - util.verifyExecPlanInsert( - """ - |INSERT INTO sink1 - |SELECT a, b, c, window_start - |FROM ( - |SELECT *, - | ROW_NUMBER() OVER(PARTITION BY a, window_start, window_end - | ORDER BY op_ts DESC) as rownum - |FROM TABLE(TUMBLE(TABLE cdc_with_meta_and_wm, DESCRIPTOR(op_ts), INTERVAL '1' MINUTE)) - |) - |WHERE rownum <= 1""".stripMargin) - - } - - @Test + assertThatThrownBy( + () => + util.verifyExecPlanInsert( + """ + |INSERT INTO sink1 + |SELECT a, b, c, window_start + |FROM ( + |SELECT *, + | ROW_NUMBER() OVER(PARTITION BY a, window_start, window_end + | ORDER BY op_ts DESC) as rownum + |FROM TABLE(TUMBLE(TABLE cdc_with_meta_and_wm, DESCRIPTOR(op_ts), INTERVAL '1' MINUTE)) + |) + |WHERE rownum <= 1""".stripMargin)) + .hasMessageContaining( + "StreamPhysicalWindowDeduplicate doesn't support consuming update and delete changes") + .isInstanceOf[TableException] + } + + @TestTemplate def testNestedSourceWithMultiSink(): Unit = { val ddl = s""" @@ -1508,15 +1680,20 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |from v1 |where b > 100 |""".stripMargin) + + val callable: ThrowingCallable = () => util.verifyExecPlan(stmtSet) + if (tryResolve) { - thrown.expectMessage( - "column(s): day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlan(stmtSet) } - @Test + @TestTemplate def testMultiSinkOnJoinedView(): Unit = { util.tableEnv.executeSql(""" |create temporary table src1 ( @@ -1593,15 +1770,20 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp |from v1 |where b > 100 |""".stripMargin) + + val callable: ThrowingCallable = () => util.verifyExecPlan(stmtSet) + if (tryResolve) { - thrown.expectMessage( - "column(s): day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") - thrown.expect(classOf[TableException]) + assertThatThrownBy(callable) + .hasMessageContaining( + "column(s): day(generated by non-deterministic function: CURRENT_TIMESTAMP ) can not satisfy the determinism") + .isInstanceOf[TableException] + } else { + assertThatCode(callable).doesNotThrowAnyException() } - util.verifyExecPlan(stmtSet) } - @Test + @TestTemplate def testMatchRecognizeSinkWithPk(): Unit = { util.tableEnv.executeSql(s""" |create temporary view v1 as @@ -1629,57 +1811,61 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp ) } - @Test + @TestTemplate def testMatchRecognizeWithNonDeterministicConditionOnCdcSinkWithPk(): Unit = { // TODO this should be updated after StreamPhysicalMatch supports consuming updates - thrown.expectMessage("Match Recognize doesn't support consuming update and delete changes") - thrown.expect(classOf[TableException]) - util.verifyExecPlanInsert( - """ - |insert into sink_with_pk - |SELECT T.a, T.b, cast(T.matchRowtime as varchar) - |FROM cdc_with_meta_and_wm - |MATCH_RECOGNIZE ( - |PARTITION BY c - |ORDER BY op_ts - |MEASURES - | A.a as a, - | A.b as b, - | MATCH_ROWTIME(op_ts) as matchRowtime - |ONE ROW PER MATCH - |PATTERN (A) - |DEFINE - | A AS A.op_ts >= CURRENT_TIMESTAMP - |) AS T + assertThatThrownBy( + () => + util.verifyExecPlanInsert( + """ + |insert into sink_with_pk + |SELECT T.a, T.b, cast(T.matchRowtime as varchar) + |FROM cdc_with_meta_and_wm + |MATCH_RECOGNIZE ( + |PARTITION BY c + |ORDER BY op_ts + |MEASURES + | A.a as a, + | A.b as b, + | MATCH_ROWTIME(op_ts) as matchRowtime + |ONE ROW PER MATCH + |PATTERN (A) + |DEFINE + | A AS A.op_ts >= CURRENT_TIMESTAMP + |) AS T """.stripMargin - ) + )) + .hasMessageContaining("Match Recognize doesn't support consuming update and delete changes") + .isInstanceOf[TableException] } - @Test + @TestTemplate def testMatchRecognizeOnCdcWithMetaDataSinkWithPk(): Unit = { // TODO this should be updated after StreamPhysicalMatch supports consuming updates - thrown.expectMessage("Match Recognize doesn't support consuming update and delete changes") - thrown.expect(classOf[TableException]) - util.verifyExecPlanInsert( - """ - |insert into sink_with_pk - |SELECT T.a, T.b, cast(T.ts as varchar) - |FROM cdc_with_meta_and_wm - |MATCH_RECOGNIZE ( - |PARTITION BY c - |ORDER BY op_ts - |MEASURES - | A.a as a, - | A.b as b, - | A.op_ts as ts, - | MATCH_ROWTIME(op_ts) as matchRowtime - |ONE ROW PER MATCH - |PATTERN (A) - |DEFINE - | A AS A.a > 0 - |) AS T + assertThatThrownBy( + () => + util.verifyExecPlanInsert( + """ + |insert into sink_with_pk + |SELECT T.a, T.b, cast(T.ts as varchar) + |FROM cdc_with_meta_and_wm + |MATCH_RECOGNIZE ( + |PARTITION BY c + |ORDER BY op_ts + |MEASURES + | A.a as a, + | A.b as b, + | A.op_ts as ts, + | MATCH_ROWTIME(op_ts) as matchRowtime + |ONE ROW PER MATCH + |PATTERN (A) + |DEFINE + | A AS A.a > 0 + |) AS T """.stripMargin - ) + )) + .hasMessageContaining("Match Recognize doesn't support consuming update and delete changes") + .isInstanceOf[TableException] } /** @@ -1736,7 +1922,7 @@ class NonDeterministicDagTest(nonDeterministicUpdateStrategy: NonDeterministicUp object NonDeterministicDagTest { - @Parameterized.Parameters(name = "nonDeterministicUpdateStrategy={0}") + @Parameters(name = "nonDeterministicUpdateStrategy={0}") def parameters(): util.Collection[NonDeterministicUpdateStrategy] = { util.Arrays.asList( NonDeterministicUpdateStrategy.TRY_RESOLVE, diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/PartitionableSinkTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/PartitionableSinkTest.scala index bb0816e93f56d..90c40210984a3 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/PartitionableSinkTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/PartitionableSinkTest.scala @@ -21,7 +21,8 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class PartitionableSinkTest extends TableTestBase { @@ -66,13 +67,19 @@ class PartitionableSinkTest extends TableTestBase { util.verifyExecPlanInsert("INSERT INTO sink PARTITION (b=1) SELECT a, c FROM MyTable") } - @Test(expected = classOf[ValidationException]) + @Test def testWrongStatic(): Unit = { - util.verifyExecPlanInsert("INSERT INTO sink PARTITION (a=1) SELECT b, c FROM MyTable") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + util.verifyExecPlanInsert("INSERT INTO sink PARTITION (a=1) SELECT b, c FROM MyTable")) } - @Test(expected = classOf[ValidationException]) + @Test def testWrongFields(): Unit = { - util.verifyExecPlanInsert("INSERT INTO sink PARTITION (b=1) SELECT a, b, c FROM MyTable") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + util.verifyExecPlanInsert("INSERT INTO sink PARTITION (b=1) SELECT a, b, c FROM MyTable")) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/PartitionableSourceTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/PartitionableSourceTest.scala index 6d5862dfd0655..441bb7d1c96df 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/PartitionableSourceTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/PartitionableSourceTest.scala @@ -21,22 +21,22 @@ import org.apache.flink.table.catalog.{CatalogPartitionImpl, CatalogPartitionSpe import org.apache.flink.table.planner.expressions.utils.Func1 import org.apache.flink.table.planner.factories.TestValuesCatalog import org.apache.flink.table.planner.utils.TableTestBase +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.junit.{Before, Test} -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith import java.util import scala.collection.JavaConversions._ -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class PartitionableSourceTest(val sourceFetchPartitions: Boolean, val useCatalogFilter: Boolean) extends TableTestBase { private val util = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { val partitionableTable = """ @@ -113,43 +113,43 @@ class PartitionableSourceTest(val sourceFetchPartitions: Boolean, val useCatalog } } - @Test + @TestTemplate def testSimplePartitionFieldPredicate1(): Unit = { util.verifyExecPlan("SELECT * FROM PartitionableTable WHERE part1 = 'A'") } - @Test + @TestTemplate def testPartialPartitionFieldPredicatePushDown(): Unit = { util.verifyExecPlan( "SELECT * FROM PartitionableTable WHERE (id > 2 OR part1 = 'A') AND part2 > 1") } - @Test + @TestTemplate def testWithUdfAndVirtualColumn(): Unit = { util.addFunction("MyUdf", Func1) util.verifyExecPlan("SELECT * FROM PartitionableTable WHERE id > 2 AND MyUdf(part2) < 3") } - @Test + @TestTemplate def testUnconvertedExpression(): Unit = { util.verifyExecPlan("select * from PartitionableTable where trim(part1) = 'A' and part2 > 1") } - @Test + @TestTemplate def testPushDownPartitionAndFiltersContainPartitionKeys(): Unit = { util.verifyExecPlan( "select * from PartitionableAndFilterableTable " + "where part1 = 'A' and part2 > 1 and id > 1") } - @Test + @TestTemplate def testPushDownPartitionAndFiltersContainPartitionKeysWithSingleProjection(): Unit = { util.verifyExecPlan( "select name from PartitionableAndFilterableTable " + "where part1 = 'A' and part2 > 1 and id > 1") } - @Test + @TestTemplate def testPushDownNonExistentPartition(): Unit = { util.verifyExecPlan("SELECT * FROM PartitionableTable WHERE part2 = 4") } @@ -157,7 +157,7 @@ class PartitionableSourceTest(val sourceFetchPartitions: Boolean, val useCatalog } object PartitionableSourceTest { - @Parameterized.Parameters(name = "sourceFetchPartitions={0}, useCatalogFilter={1}") + @Parameters(name = "sourceFetchPartitions={0}, useCatalogFilter={1}") def parameters(): util.Collection[Array[Any]] = { Seq[Array[Any]]( Array(true, false), diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/RankTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/RankTest.scala index a6fcd9f0bc1b3..82547355d8286 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/RankTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/RankTest.scala @@ -22,8 +22,8 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.optimize.RelNodeBlockPlanBuilder import org.apache.flink.table.planner.utils.TableTestBase -import org.assertj.core.api.Assertions.assertThatThrownBy -import org.junit.Test +import org.assertj.core.api.Assertions.{assertThatExceptionOfType, assertThatThrownBy} +import org.junit.jupiter.api.Test class RankTest extends TableTestBase { @@ -43,9 +43,9 @@ class RankTest extends TableTestBase { |WHERE rank_num >= 10 """.stripMargin - thrown.expectMessage("Rank end is not specified.") - thrown.expect(classOf[TableException]) - util.verifyExecPlan(sql) + assertThatThrownBy(() => util.verifyExecPlan(sql)) + .hasMessageContaining("Rank end is not specified.") + .isInstanceOf[TableException] } @Test @@ -60,9 +60,9 @@ class RankTest extends TableTestBase { |WHERE rank_num <= 0 """.stripMargin - thrown.expectMessage("Rank end should not less than zero") - thrown.expect(classOf[TableException]) - util.verifyExecPlan(sql) + assertThatThrownBy(() => util.verifyExecPlan(sql)) + .hasMessageContaining("Rank end should not less than zero") + .isInstanceOf[TableException] } @Test @@ -185,7 +185,7 @@ class RankTest extends TableTestBase { util.verifyExecPlan(sql) } - @Test(expected = classOf[RuntimeException]) + @Test def testRowNumberWithOutOrderBy(): Unit = { val sql = """ @@ -195,10 +195,11 @@ class RankTest extends TableTestBase { | FROM MyTable) |WHERE row_num <= a """.stripMargin - util.verifyExecPlan(sql) + assertThatExceptionOfType(classOf[RuntimeException]) + .isThrownBy(() => util.verifyExecPlan(sql)) } - @Test(expected = classOf[ValidationException]) + @Test def testRankWithOutOrderBy(): Unit = { val sql = """ @@ -208,10 +209,11 @@ class RankTest extends TableTestBase { | FROM MyTable) |WHERE rk <= a """.stripMargin - util.verifyExecPlan(sql) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan(sql)) } - @Test(expected = classOf[ValidationException]) + @Test def testDenseRankWithOutOrderBy(): Unit = { val sql = """ @@ -221,10 +223,11 @@ class RankTest extends TableTestBase { | FROM MyTable) |WHERE rk <= a """.stripMargin - util.verifyExecPlan(sql) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan(sql)) } - @Test(expected = classOf[RuntimeException]) + @Test def testRowNumberWithMultiGroups(): Unit = { val sql = """ @@ -235,10 +238,11 @@ class RankTest extends TableTestBase { | FROM MyTable) |WHERE row_num <= a """.stripMargin - util.verifyExecPlan(sql) + assertThatExceptionOfType(classOf[RuntimeException]) + .isThrownBy(() => util.verifyExecPlan(sql)) } - @Test(expected = classOf[ValidationException]) + @Test def testRankWithMultiGroups(): Unit = { val sql = """ @@ -249,10 +253,11 @@ class RankTest extends TableTestBase { | FROM MyTable) |WHERE rk <= a """.stripMargin - util.verifyExecPlan(sql) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan(sql)) } - @Test(expected = classOf[ValidationException]) + @Test def testDenseRankWithMultiGroups(): Unit = { val sql = """ @@ -263,7 +268,8 @@ class RankTest extends TableTestBase { | FROM MyTable) |WHERE rk <= a """.stripMargin - util.verifyExecPlan(sql) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan(sql)) } @Test @@ -644,7 +650,7 @@ class RankTest extends TableTestBase { util.verifyRelPlan(sql, ExplainDetail.CHANGELOG_MODE) } - @Test(expected = classOf[ValidationException]) + @Test // FIXME remove expected exception after ADD added def testTopNForVariableSize(): Unit = { val subquery = @@ -667,7 +673,8 @@ class RankTest extends TableTestBase { |WHERE row_num <= a """.stripMargin - util.verifyRelPlan(sql, ExplainDetail.CHANGELOG_MODE) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyRelPlan(sql, ExplainDetail.CHANGELOG_MODE)) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/RelTimeIndicatorConverterTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/RelTimeIndicatorConverterTest.scala index 214d61bb635e9..2b195426e44a6 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/RelTimeIndicatorConverterTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/RelTimeIndicatorConverterTest.scala @@ -26,7 +26,7 @@ import org.apache.flink.table.planner.plan.stream.sql.RelTimeIndicatorConverterT import org.apache.flink.table.planner.utils.TableTestBase import org.apache.flink.table.types.logical.BigIntType -import org.junit.Test +import org.junit.jupiter.api.Test import java.sql.Timestamp diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SetOperatorsTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SetOperatorsTest.scala index 510ffe7baa379..40dea1bc89ec5 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SetOperatorsTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SetOperatorsTest.scala @@ -24,29 +24,33 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.utils.NonPojo import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.{BeforeEach, Test} class SetOperatorsTest extends TableTestBase { private val util = streamTestUtil() - @Before + @BeforeEach def before(): Unit = { util.addTableSource[(Int, Long, String)]("T1", 'a, 'b, 'c) util.addTableSource[(Int, Long, String)]("T2", 'd, 'e, 'f) util.addTableSource[(Int, Long, Int, String, Long)]("T3", 'a, 'b, 'd, 'c, 'e) } - @Test(expected = classOf[ValidationException]) + @Test def testUnionDifferentColumnSize(): Unit = { // must fail. Union inputs have different column size. - util.verifyExecPlan("SELECT * FROM T1 UNION ALL SELECT * FROM T3") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan("SELECT * FROM T1 UNION ALL SELECT * FROM T3")) } - @Test(expected = classOf[ValidationException]) + @Test def testUnionDifferentFieldTypes(): Unit = { // must fail. Union inputs have different field types. - util.verifyExecPlan("SELECT a, b, c FROM T1 UNION ALL SELECT d, c, e FROM T3") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => util.verifyExecPlan("SELECT a, b, c FROM T1 UNION ALL SELECT d, c, e FROM T3")) } @Test @@ -54,10 +58,12 @@ class SetOperatorsTest extends TableTestBase { util.verifyExecPlan("SELECT c FROM T1 INTERSECT ALL SELECT f FROM T2") } - @Test(expected = classOf[ValidationException]) + @Test def testIntersectDifferentFieldTypes(): Unit = { // must fail. Intersect inputs have different field types. - util.verifyExecPlan("SELECT a, b, c FROM T1 INTERSECT SELECT d, c, e FROM T3") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => util.verifyExecPlan("SELECT a, b, c FROM T1 INTERSECT SELECT d, c, e FROM T3")) } @Test @@ -65,10 +71,11 @@ class SetOperatorsTest extends TableTestBase { util.verifyExecPlan("SELECT c FROM T1 EXCEPT ALL SELECT f FROM T2") } - @Test(expected = classOf[ValidationException]) + @Test def testMinusDifferentFieldTypes(): Unit = { // must fail. Minus inputs have different field types. - util.verifyExecPlan("SELECT a, b, c FROM T1 EXCEPT SELECT d, c, e FROM T3") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan("SELECT a, b, c FROM T1 EXCEPT SELECT d, c, e FROM T3")) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SortLimitTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SortLimitTest.scala index 2406c3823b8a1..2fc7d30f67bb0 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SortLimitTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SortLimitTest.scala @@ -21,7 +21,7 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test class SortLimitTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SortTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SortTest.scala index da0c1af69a1cc..df554c3bbfc8d 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SortTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SortTest.scala @@ -21,7 +21,7 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test class SortTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SourceWatermarkTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SourceWatermarkTest.scala index 355d849729dab..29a498206623b 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SourceWatermarkTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SourceWatermarkTest.scala @@ -20,14 +20,14 @@ package org.apache.flink.table.planner.plan.stream.sql import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctions.JavaFunc5 import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} /** Tests for watermark push down. */ class SourceWatermarkTest extends TableTestBase { private val util = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.tableEnv.executeSql(s""" | CREATE TABLE VirtualTable ( diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SubplanReuseTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SubplanReuseTest.scala index a8a97b4d44ce8..5cd67937c6a7d 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SubplanReuseTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/SubplanReuseTest.scala @@ -25,13 +25,13 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedTableFunction import org.apache.flink.table.planner.utils.TableTestBase import org.apache.flink.table.runtime.functions.aggregate.FirstValueAggFunction -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} class SubplanReuseTest extends TableTestBase { private val util = streamTestUtil() - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_REUSE_SUB_PLAN_ENABLED, Boolean.box(true)) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/TableScanTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/TableScanTest.scala index d228c206aa5c2..2cd7f047e9833 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/TableScanTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/TableScanTest.scala @@ -27,7 +27,7 @@ import org.apache.flink.table.planner.factories.TestValuesTableFactory.MockedLoo import org.apache.flink.table.planner.utils.TableTestBase import org.assertj.core.api.Assertions.assertThatThrownBy -import org.junit.Test +import org.junit.jupiter.api.Test class TableScanTest extends TableTestBase { @@ -667,13 +667,15 @@ class TableScanTest extends TableTestBase { | 'changelog-mode' = 'I,UB,D' |) """.stripMargin) - thrown.expect(classOf[ValidationException]) - thrown.expectMessage( - "Invalid source for table 'default_catalog.default_database.src'. A ScanTableSource " + - "doesn't support a changelog stream that contains UPDATE_BEFORE but no UPDATE_AFTER. " + - "Please adapt the implementation of class 'org.apache.flink.table.planner.factories." + - "TestValuesTableFactory$TestValuesScanLookupTableSource'.") - util.verifyRelPlan("SELECT * FROM src WHERE a > 1", ExplainDetail.CHANGELOG_MODE) + + assertThatThrownBy( + () => util.verifyRelPlan("SELECT * FROM src WHERE a > 1", ExplainDetail.CHANGELOG_MODE)) + .hasMessageContaining( + "Invalid source for table 'default_catalog.default_database.src'. A ScanTableSource " + + "doesn't support a changelog stream that contains UPDATE_BEFORE but no UPDATE_AFTER. " + + "Please adapt the implementation of class 'org.apache.flink.table.planner.factories." + + "TestValuesTableFactory$TestValuesScanLookupTableSource'.") + .isInstanceOf[ValidationException] } @Test @@ -688,12 +690,14 @@ class TableScanTest extends TableTestBase { | 'changelog-mode' = 'I,UA,D' |) """.stripMargin) - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "Table 'default_catalog.default_database.src' produces a " + - "changelog stream that contains UPDATE_AFTER but no UPDATE_BEFORE. " + - "This requires defining a primary key constraint on the table.") - util.verifyRelPlan("SELECT * FROM src WHERE a > 1", ExplainDetail.CHANGELOG_MODE) + + assertThatThrownBy( + () => util.verifyRelPlan("SELECT * FROM src WHERE a > 1", ExplainDetail.CHANGELOG_MODE)) + .hasMessageContaining( + "Table 'default_catalog.default_database.src' produces a " + + "changelog stream that contains UPDATE_AFTER but no UPDATE_BEFORE. " + + "This requires defining a primary key constraint on the table.") + .isInstanceOf[TableException] } @Test @@ -711,12 +715,13 @@ class TableScanTest extends TableTestBase { util.tableEnv.getConfig .set(ExecutionConfigOptions.TABLE_EXEC_SOURCE_CDC_EVENTS_DUPLICATE, Boolean.box(true)) - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "Configuration 'table.exec.source.cdc-events-duplicate' is enabled " + - "which requires the changelog sources to define a PRIMARY KEY. " + - "However, table 'default_catalog.default_database.src' doesn't have a primary key.") - util.verifyRelPlan("SELECT * FROM src WHERE a > 1", ExplainDetail.CHANGELOG_MODE) + assertThatThrownBy( + () => util.verifyRelPlan("SELECT * FROM src WHERE a > 1", ExplainDetail.CHANGELOG_MODE)) + .hasMessageContaining( + "Configuration 'table.exec.source.cdc-events-duplicate' is enabled " + + "which requires the changelog sources to define a PRIMARY KEY. " + + "However, table 'default_catalog.default_database.src' doesn't have a primary key.") + .isInstanceOf[TableException] } @Test @@ -731,28 +736,29 @@ class TableScanTest extends TableTestBase { | 'table-source-class' = '${classOf[MockedLookupTableSource].getName}' |) """.stripMargin) - thrown.expect(classOf[TableException]) - thrown.expectMessage("Cannot generate a valid execution plan for the given query") - util.verifyRelPlan("SELECT * FROM src", ExplainDetail.CHANGELOG_MODE) + + assertThatThrownBy(() => util.verifyRelPlan("SELECT * FROM src", ExplainDetail.CHANGELOG_MODE)) + .hasMessageContaining("Cannot generate a valid execution plan for the given query") + .isInstanceOf[ValidationException] } @Test def testInvalidWatermarkOutputType(): Unit = { - thrown.expect(classOf[ValidationException]) - thrown.expectMessage( - "Invalid data type of expression for watermark definition. " + - "The field must be of type TIMESTAMP(p) or TIMESTAMP_LTZ(p), " + - "the supported precision 'p' is from 0 to 3, but the watermark " + - "expression type is CHAR(0) NOT NULL") - util.addTable(""" - |CREATE TABLE src ( - | ts TIMESTAMP(3), - | a INT, - | b DOUBLE, - | WATERMARK FOR `ts` AS '' - |) WITH ( - | 'connector' = 'values' - |) - """.stripMargin) + assertThatThrownBy(() => util.addTable(""" + |CREATE TABLE src ( + | ts TIMESTAMP(3), + | a INT, + | b DOUBLE, + | WATERMARK FOR `ts` AS '' + |) WITH ( + | 'connector' = 'values' + |) + """.stripMargin)) + .hasMessageContaining( + "Invalid data type of expression for watermark definition. " + + "The field must be of type TIMESTAMP(p) or TIMESTAMP_LTZ(p), " + + "the supported precision 'p' is from 0 to 3, but the watermark " + + "expression type is CHAR(0) NOT NULL") + .isInstanceOf[ValidationException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/TableSinkTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/TableSinkTest.scala index 0df9269c97f36..e230b39a3850b 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/TableSinkTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/TableSinkTest.scala @@ -29,7 +29,8 @@ import org.apache.flink.table.factories.{DynamicTableFactory, DynamicTableSource import org.apache.flink.table.planner.utils.{TableTestBase, TestingTableEnvironment} import org.assertj.core.api.Assertions -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test import java.util @@ -86,11 +87,13 @@ class TableSinkTest extends TableTestBase { | 'connector' = 'values' |) |""".stripMargin) - thrown.expect(classOf[ValidationException]) - thrown.expectMessage( - "Query schema: [a: INT, EXPR$1: CHAR(0) NOT NULL, EXPR$2: CHAR(0) NOT NULL]\n" + - "Sink schema: [name: STRING, email: STRING, message_offset: BIGINT]") - util.verifyExecPlanInsert("INSERT INTO my_sink SELECT a, '', '' FROM MyTable") + + assertThatThrownBy( + () => util.verifyExecPlanInsert("INSERT INTO my_sink SELECT a, '', '' FROM MyTable")) + .hasMessageContaining( + "Query schema: [a: INT, EXPR$1: CHAR(0) NOT NULL, EXPR$2: CHAR(0) NOT NULL]\n" + + "Sink schema: [name: STRING, email: STRING, message_offset: BIGINT]") + .isInstanceOf[ValidationException] } @Test @@ -106,12 +109,12 @@ class TableSinkTest extends TableTestBase { val stmtSet = util.tableEnv.createStatementSet() stmtSet.addInsertSql("INSERT INTO appendSink SELECT COUNT(*) AS cnt FROM MyTable GROUP BY a") - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "Table sink 'default_catalog.default_database.appendSink' doesn't " + - "support consuming update changes which is produced by node " + - "GroupAggregate(groupBy=[a], select=[a, COUNT(*) AS cnt])") - util.verifyRelPlan(stmtSet, ExplainDetail.CHANGELOG_MODE) + assertThatThrownBy(() => util.verifyRelPlan(stmtSet, ExplainDetail.CHANGELOG_MODE)) + .hasMessageContaining( + "Table sink 'default_catalog.default_database.appendSink' doesn't " + + "support consuming update changes which is produced by node " + + "GroupAggregate(groupBy=[a], select=[a, COUNT(*) AS cnt])") + .isInstanceOf[TableException] } @Test @@ -141,11 +144,10 @@ class TableSinkTest extends TableTestBase { stmtSet.addInsertSql( "INSERT INTO retractSink2 SELECT cnt, SUM(cnt) OVER (ORDER BY PROCTIME()) FROM TempTable") - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "OverAggregate doesn't support consuming update changes " + + assertThatThrownBy(() => util.verifyRelPlan(stmtSet, ExplainDetail.CHANGELOG_MODE)) + .hasMessageContaining("OverAggregate doesn't support consuming update changes " + "which is produced by node Calc(select=[cnt])") - util.verifyRelPlan(stmtSet, ExplainDetail.CHANGELOG_MODE) + .isInstanceOf[TableException] } @Test @@ -360,12 +362,11 @@ class TableSinkTest extends TableTestBase { val stmtSet = util.tableEnv.createStatementSet() stmtSet.addInsertSql(sql) - thrown.expect(classOf[ValidationException]) - thrown.expectMessage( - "Query schema: [a: INT, m_3: INT, m_2: INT, b: BIGINT, c: INT, metadata_1: STRING]\n" + - "Sink schema: [a: INT, m_2: INT, b: BIGINT, c: INT, metadata_1: STRING]") - - util.verifyRelPlan(stmtSet) + assertThatThrownBy(() => util.verifyRelPlan(stmtSet)) + .hasMessageContaining( + "Query schema: [a: INT, m_3: INT, m_2: INT, b: BIGINT, c: INT, metadata_1: STRING]\n" + + "Sink schema: [a: INT, m_2: INT, b: BIGINT, c: INT, metadata_1: STRING]") + .isInstanceOf[ValidationException] } @Test @@ -389,13 +390,12 @@ class TableSinkTest extends TableTestBase { val stmtSet = util.tableEnv.createStatementSet() stmtSet.addInsertSql(sql) - thrown.expect(classOf[ValidationException]) - thrown.expectMessage( - "Invalid data type for metadata column 'metadata_1' of table " + - "'default_catalog.default_database.MetadataTable'. The column cannot be declared as " + - "'TIMESTAMP(3)' because the type must be castable to metadata type 'BOOLEAN'.") - - util.verifyRelPlan(stmtSet) + assertThatThrownBy(() => util.verifyRelPlan(stmtSet)) + .hasMessageContaining( + "Invalid data type for metadata column 'metadata_1' of table " + + "'default_catalog.default_database.MetadataTable'. The column cannot be declared as " + + "'TIMESTAMP(3)' because the type must be castable to metadata type 'BOOLEAN'.") + .isInstanceOf[ValidationException] } @Test @@ -754,13 +754,13 @@ class TableSinkTest extends TableTestBase { val stmtSet = util.tableEnv.createStatementSet() stmtSet.addInsertSql("INSERT INTO sink SELECT * FROM MyTable") - expectedException.expect(classOf[TableException]) - expectedException.expectMessage( - s"You should enable the checkpointing for sinking to managed table " + - s"'default_catalog.default_database.sink', " + - s"managed table relies on checkpoint to commit and " + - s"the data is visible only after commit.") - util.verifyAstPlan(stmtSet, ExplainDetail.CHANGELOG_MODE) + assertThatThrownBy(() => util.verifyAstPlan(stmtSet, ExplainDetail.CHANGELOG_MODE)) + .hasMessageContaining( + s"You should enable the checkpointing for sinking to managed table " + + s"'default_catalog.default_database.sink', " + + s"managed table relies on checkpoint to commit and " + + s"the data is visible only after commit.") + .isInstanceOf[TableException] } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/TableSourceTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/TableSourceTest.scala index 755a8a541c7cf..6189763ae06e9 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/TableSourceTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/TableSourceTest.scala @@ -17,11 +17,11 @@ */ package org.apache.flink.table.planner.plan.stream.sql -import org.apache.flink.core.testutils.FlinkMatchers.containsMessage import org.apache.flink.table.api.ValidationException import org.apache.flink.table.planner.utils._ -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test class TableSourceTest extends TableTestBase { @@ -100,9 +100,6 @@ class TableSourceTest extends TableTestBase { @Test def testProctimeOnWatermarkSpec(): Unit = { - thrown.expect(classOf[ValidationException]) - thrown.expect( - containsMessage("A watermark can not be defined for a processing-time attribute.")) val ddl = s""" |CREATE TABLE procTimeT ( @@ -116,9 +113,14 @@ class TableSourceTest extends TableTestBase { | 'bounded' = 'false' |) """.stripMargin - util.tableEnv.executeSql(ddl) - util.verifyExecPlan("SELECT pTime, id, name, val FROM procTimeT") + assertThatThrownBy( + () => { + util.tableEnv.executeSql(ddl) + util.verifyExecPlan("SELECT pTime, id, name, val FROM procTimeT") + }) + .hasMessageContaining("A watermark can not be defined for a processing-time attribute.") + .isInstanceOf[ValidationException] } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/UnionTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/UnionTest.scala index 1d3c02dcfe5a5..d8e4ac1daeb84 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/UnionTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/UnionTest.scala @@ -21,14 +21,14 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} // TODO add more union case after aggregation and join supported class UnionTest extends TableTestBase { private val util = streamTestUtil() - @Before + @BeforeEach def before(): Unit = { util.addTableSource[(Int, Long, String)]("MyTable1", 'a, 'b, 'c) util.addTableSource[(Int, Long, String)]("MyTable2", 'a, 'b, 'c) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/UnnestTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/UnnestTest.scala index fd1e4ff3d21f4..0eccb2947ca94 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/UnnestTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/UnnestTest.scala @@ -22,7 +22,7 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.common.UnnestTestBase import org.apache.flink.table.planner.utils.TableTestUtil -import org.junit.Test +import org.junit.jupiter.api.Test class UnnestTest extends UnnestTestBase(true) { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/ValuesTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/ValuesTest.scala index 3d0a31c205720..0520d294f1f06 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/ValuesTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/ValuesTest.scala @@ -19,7 +19,7 @@ package org.apache.flink.table.planner.plan.stream.sql import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test class ValuesTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/WindowDeduplicateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/WindowDeduplicateTest.scala index cf3e581ecabb9..38385a6989543 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/WindowDeduplicateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/WindowDeduplicateTest.scala @@ -21,7 +21,8 @@ import org.apache.flink.table.api.TableException import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.WeightedAvgWithMerge import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test /** Tests for window deduplicate. */ class WindowDeduplicateTest extends TableTestBase { @@ -161,9 +162,9 @@ class WindowDeduplicateTest extends TableTestBase { |WHERE rownum <= 1 """.stripMargin - thrown.expectMessage("Processing time Window Deduplication is not supported yet.") - thrown.expect(classOf[TableException]) - util.verifyExplain(sql) + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("Processing time Window Deduplication is not supported yet.") + .isInstanceOf[TableException] } @Test @@ -180,9 +181,9 @@ class WindowDeduplicateTest extends TableTestBase { |WHERE rownum <= 1 """.stripMargin - thrown.expectMessage("Processing time Window Deduplication is not supported yet.") - thrown.expect(classOf[TableException]) - util.verifyExplain(sql) + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("Processing time Window Deduplication is not supported yet.") + .isInstanceOf[TableException] } // ---------------------------------------------------------------------------------------- diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/WindowRankTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/WindowRankTest.scala index 9a7b221a16459..fde6fbd50d647 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/WindowRankTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/WindowRankTest.scala @@ -21,7 +21,8 @@ import org.apache.flink.table.api.TableException import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.WeightedAvgWithMerge import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test /** Tests for window rank. */ class WindowRankTest extends TableTestBase { @@ -90,9 +91,9 @@ class WindowRankTest extends TableTestBase { |WHERE rownum <= 3 """.stripMargin - thrown.expectMessage("Processing time Window TopN is not supported yet.") - thrown.expect(classOf[TableException]) - util.verifyExplain(sql) + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("Processing time Window TopN is not supported yet.") + .isInstanceOf[TableException] } @Test @@ -141,9 +142,9 @@ class WindowRankTest extends TableTestBase { |WHERE rownum <= 3 """.stripMargin - thrown.expectMessage("Processing time Window TopN is not supported yet.") - thrown.expect(classOf[TableException]) - util.verifyExplain(sql) + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("Processing time Window TopN is not supported yet.") + .isInstanceOf[TableException] } @Test @@ -192,9 +193,9 @@ class WindowRankTest extends TableTestBase { |WHERE rownum <= 3 """.stripMargin - thrown.expectMessage("Processing time Window TopN is not supported yet.") - thrown.expect(classOf[TableException]) - util.verifyExplain(sql) + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("Processing time Window TopN is not supported yet.") + .isInstanceOf[TableException] } // ---------------------------------------------------------------------------------------- @@ -255,9 +256,9 @@ class WindowRankTest extends TableTestBase { |WHERE rownum <= 3 """.stripMargin - thrown.expect(classOf[TableException]) - thrown.expectMessage("Processing time Window TopN is not supported yet.") - util.verifyExplain(sql) + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("Processing time Window TopN is not supported yet.") + .isInstanceOf[TableException] } @Test @@ -316,9 +317,9 @@ class WindowRankTest extends TableTestBase { |WHERE rownum <= 3 """.stripMargin - thrown.expect(classOf[TableException]) - thrown.expectMessage("Processing time Window TopN is not supported yet.") - util.verifyExplain(sql) + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("Processing time Window TopN is not supported yet.") + .isInstanceOf[TableException] } @Test @@ -381,9 +382,9 @@ class WindowRankTest extends TableTestBase { |WHERE rownum <= 3 """.stripMargin - thrown.expect(classOf[TableException]) - thrown.expectMessage("Processing time Window TopN is not supported yet.") - util.verifyExplain(sql) + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("Processing time Window TopN is not supported yet.") + .isInstanceOf[TableException] } // ---------------------------------------------------------------------------------------- @@ -486,10 +487,10 @@ class WindowRankTest extends TableTestBase { |WHERE rownum <= 3 """.stripMargin - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "RANK() function is not supported on Window TopN currently, only ROW_NUMBER() is supported.") - util.verifyExplain(sql) + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining( + "RANK() function is not supported on Window TopN currently, only ROW_NUMBER() is supported.") + .isInstanceOf[TableException] } @Test @@ -518,11 +519,10 @@ class WindowRankTest extends TableTestBase { |WHERE rownum <= 3 """.stripMargin - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "DENSE_RANK() function is not supported on Window TopN currently, " + + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("DENSE_RANK() function is not supported on Window TopN currently, " + "only ROW_NUMBER() is supported.") - util.verifyExplain(sql) + .isInstanceOf[TableException] } @Test @@ -552,8 +552,9 @@ class WindowRankTest extends TableTestBase { |WHERE rownum <= max_b """.stripMargin - thrown.expect(classOf[TableException]) - thrown.expectMessage("Rank strategy rankEnd=max_b is not supported on window rank currently.") - util.verifyExplain(sql) + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining( + "Rank strategy rankEnd=max_b is not supported on window rank currently.") + .isInstanceOf[TableException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/WindowTableFunctionTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/WindowTableFunctionTest.scala index 691a80adf25c0..ff64550e35d65 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/WindowTableFunctionTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/WindowTableFunctionTest.scala @@ -17,11 +17,11 @@ */ package org.apache.flink.table.planner.plan.stream.sql -import org.apache.flink.core.testutils.FlinkMatchers.containsCause import org.apache.flink.table.api.ValidationException import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test /** Tests for window table-valued function. */ class WindowTableFunctionTest extends TableTestBase { @@ -118,12 +118,10 @@ class WindowTableFunctionTest extends TableTestBase { |FROM TABLE( | TUMBLE(TABLE v1, DESCRIPTOR(cur_time), INTERVAL '15' MINUTE)) |""".stripMargin - thrown.expectCause( - containsCause( - new ValidationException( - "The window function requires the timecol is a time attribute type, but is TIMESTAMP(3).") - )) - util.verifyRelPlan(sql) + + assertThatThrownBy(() => util.verifyRelPlan(sql)) + .hasCause(new ValidationException( + "The window function requires the timecol is a time attribute type, but is TIMESTAMP(3).")) } @Test @@ -140,9 +138,9 @@ class WindowTableFunctionTest extends TableTestBase { | TUMBLE(TABLE v1, DESCRIPTOR(rowtime), INTERVAL '15' MINUTE)) |""".stripMargin - thrown.expectMessage("Column 'window_start' is ambiguous") - thrown.expect(classOf[ValidationException]) - util.verifyRelPlan(sql) + assertThatThrownBy(() => util.verifyRelPlan(sql)) + .hasMessageContaining("Column 'window_start' is ambiguous") + .isInstanceOf[ValidationException] } @Test @@ -167,6 +165,20 @@ class WindowTableFunctionTest extends TableTestBase { util.verifyRelPlan(sql) } + @Test + def testTumbleTVFWithNamedParams(): Unit = { + val sql = + """ + |SELECT * + |FROM TABLE(TUMBLE( + | DATA => TABLE MyTable, + | TIMECOL => DESCRIPTOR(rowtime), + | SIZE => INTERVAL '15' MINUTE, + | `OFFSET` => INTERVAL '5' MINUTE)) + |""".stripMargin + util.verifyRelPlan(sql) + } + @Test def testHopTVFWithOffset(): Unit = { val sql = @@ -199,6 +211,20 @@ class WindowTableFunctionTest extends TableTestBase { util.verifyRelPlan(sql) } + @Test + def testHopTVFWithNamedParams(): Unit = { + val sql = + """ + |SELECT * + |FROM TABLE(TUMBLE( + | DATA => TABLE MyTable, + | TIMECOL => DESCRIPTOR(rowtime), + | SIZE => INTERVAL '15' MINUTE, + | `OFFSET` => INTERVAL '5' MINUTE)) + |""".stripMargin + util.verifyRelPlan(sql) + } + @Test def testCumulateTVFWithOffset(): Unit = { val sql = @@ -230,4 +256,5 @@ class WindowTableFunctionTest extends TableTestBase { |""".stripMargin util.verifyRelPlan(sql) } + } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/AggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/AggregateTest.scala index ded61ba8290b5..adf082b857fc2 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/AggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/AggregateTest.scala @@ -24,7 +24,8 @@ import org.apache.flink.table.api.config.ExecutionConfigOptions import org.apache.flink.table.planner.utils.{StreamTableTestUtil, TableTestBase} import org.apache.flink.table.runtime.typeutils.DecimalDataTypeInfo -import org.junit.Test +import org.assertj.core.api.Assertions.{assertThatExceptionOfType, assertThatThrownBy} +import org.junit.jupiter.api.Test import java.time.Duration @@ -74,21 +75,23 @@ class AggregateTest extends TableTestBase { "decimal105") ) - @Test(expected = classOf[ValidationException]) + @Test def testGroupingOnNonExistentField(): Unit = { - util.verifyExecPlan("SELECT COUNT(*) FROM MyTable GROUP BY foo") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan("SELECT COUNT(*) FROM MyTable GROUP BY foo")) } - @Test(expected = classOf[ValidationException]) + @Test def testGroupingInvalidSelection(): Unit = { - util.verifyExecPlan("SELECT b FROM MyTable GROUP BY a") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan("SELECT b FROM MyTable GROUP BY a")) } @Test def testCannotCountOnMultiFields(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage("We now only support the count of one field") - util.verifyExecPlan("SELECT b, COUNT(a, c) FROM MyTable GROUP BY b") + assertThatThrownBy(() => util.verifyExecPlan("SELECT COUNT(a, c) FROM MyTable GROUP BY b")) + .hasMessageContaining("We now only support the count of one field") + .isInstanceOf[TableException] } @Test @@ -391,9 +394,10 @@ class AggregateTest extends TableTestBase { ExplainDetail.CHANGELOG_MODE) } - @Test(expected = classOf[TableException]) + @Test def testApproximateCountDistinct(): Unit = { - util.verifyExecPlan("SELECT APPROX_COUNT_DISTINCT(b) FROM MyTable") + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan("SELECT APPROX_COUNT_DISTINCT(b) FROM MyTable")) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/DistinctAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/DistinctAggregateTest.scala index 9de0d258f187b..e4ae95bbfb49e 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/DistinctAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/DistinctAggregateTest.scala @@ -23,14 +23,14 @@ import org.apache.flink.table.api._ import org.apache.flink.table.api.config.OptimizerConfigOptions import org.apache.flink.table.planner.plan.rules.physical.stream.IncrementalAggregateRule import org.apache.flink.table.planner.utils.{AggregatePhaseStrategy, StreamTableTestUtil, TableTestBase} +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.junit.{Before, Test} -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith import java.util -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class DistinctAggregateTest( splitDistinctAggEnabled: Boolean, aggPhaseEnforcer: AggregatePhaseStrategy) @@ -39,7 +39,7 @@ class DistinctAggregateTest( protected val util: StreamTableTestUtil = streamTestUtil() util.addTableSource[(Int, Long, String)]("MyTable", 'a, 'b, 'c) - @Before + @BeforeEach def before(): Unit = { util.tableEnv.getConfig.setIdleStateRetentionTime(Time.hours(1), Time.hours(2)) util.enableMiniBatch() @@ -53,17 +53,17 @@ class DistinctAggregateTest( .set(IncrementalAggregateRule.TABLE_OPTIMIZER_INCREMENTAL_AGG_ENABLED, Boolean.box(false)) } - @Test + @TestTemplate def testSingleDistinctAgg(): Unit = { util.verifyExecPlan("SELECT COUNT(DISTINCT c) FROM MyTable") } - @Test + @TestTemplate def testMultiDistinctAggs(): Unit = { util.verifyExecPlan("SELECT COUNT(DISTINCT a), SUM(DISTINCT b) FROM MyTable") } - @Test + @TestTemplate def testSingleMaxWithDistinctAgg(): Unit = { val sqlQuery = """ @@ -74,24 +74,24 @@ class DistinctAggregateTest( util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testSingleFirstValueWithDistinctAgg(): Unit = { // FIRST_VALUE is not mergeable, so the final plan does not contain local agg util.verifyExecPlan("SELECT a, FIRST_VALUE(c), COUNT(DISTINCT b) FROM MyTable GROUP BY a") } - @Test + @TestTemplate def testSingleLastValueWithDistinctAgg(): Unit = { // LAST_VALUE is not mergeable, so the final plan does not contain local agg util.verifyExecPlan("SELECT a, LAST_VALUE(c), COUNT(DISTINCT b) FROM MyTable GROUP BY a") } - @Test + @TestTemplate def testSingleListAggWithDistinctAgg(): Unit = { util.verifyExecPlan("SELECT a, LISTAGG(c), COUNT(DISTINCT b) FROM MyTable GROUP BY a") } - @Test + @TestTemplate def testSingleDistinctAggWithAllNonDistinctAgg(): Unit = { val sqlQuery = """ @@ -102,29 +102,29 @@ class DistinctAggregateTest( util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testTwoDistinctAggregateWithNonDistinctAgg(): Unit = { util.verifyExecPlan( "SELECT c, SUM(DISTINCT a), SUM(a), COUNT(DISTINCT b) FROM MyTable GROUP BY c") } - @Test + @TestTemplate def testSingleDistinctAggWithGroupBy(): Unit = { util.verifyExecPlan("SELECT a, COUNT(DISTINCT c) FROM MyTable GROUP BY a") } - @Test + @TestTemplate def testSingleDistinctAggWithAndNonDistinctAggOnSameColumn(): Unit = { util.verifyExecPlan("SELECT a, COUNT(DISTINCT b), SUM(b), AVG(b) FROM MyTable GROUP BY a") } - @Test + @TestTemplate def testSomeColumnsBothInDistinctAggAndGroupBy(): Unit = { // TODO: the COUNT(DISTINCT a) can be optimized to literal 1 util.verifyExecPlan("SELECT a, COUNT(DISTINCT a), COUNT(b) FROM MyTable GROUP BY a") } - @Test + @TestTemplate def testAggWithFilterClause(): Unit = { val sqlQuery = s""" @@ -139,7 +139,7 @@ class DistinctAggregateTest( util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testMultiGroupBys(): Unit = { val sqlQuery = s""" @@ -155,7 +155,7 @@ class DistinctAggregateTest( util.verifyExecPlan(sqlQuery) } - @Test + @TestTemplate def testSingleDistinctWithRetraction(): Unit = { val sqlQuery = """ @@ -169,7 +169,7 @@ class DistinctAggregateTest( util.verifyRelPlan(sqlQuery, ExplainDetail.CHANGELOG_MODE) } - @Test + @TestTemplate def testSumCountWithSingleDistinctAndRetraction(): Unit = { val sqlQuery = s""" @@ -185,7 +185,7 @@ class DistinctAggregateTest( util.verifyRelPlan(sqlQuery, ExplainDetail.CHANGELOG_MODE) } - @Test + @TestTemplate def testMinMaxWithRetraction(): Unit = { val sqlQuery = s""" @@ -201,7 +201,7 @@ class DistinctAggregateTest( util.verifyRelPlan(sqlQuery, ExplainDetail.CHANGELOG_MODE) } - @Test + @TestTemplate def testFirstValueLastValueWithRetraction(): Unit = { val sqlQuery = s""" @@ -219,7 +219,7 @@ class DistinctAggregateTest( } object DistinctAggregateTest { - @Parameterized.Parameters(name = "splitDistinctAggEnabled={0}, aggPhaseEnforcer={1}") + @Parameters(name = "splitDistinctAggEnabled={0}, aggPhaseEnforcer={1}") def parameters(): util.Collection[Array[Any]] = { util.Arrays.asList( Array(true, AggregatePhaseStrategy.ONE_PHASE), diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/GroupWindowTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/GroupWindowTest.scala index 01466bccaffac..1a74d0b41584e 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/GroupWindowTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/GroupWindowTest.scala @@ -24,7 +24,8 @@ import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.Wei import org.apache.flink.table.planner.plan.utils.WindowEmitStrategy.{TABLE_EXEC_EMIT_ALLOW_LATENESS, TABLE_EXEC_EMIT_LATE_FIRE_DELAY, TABLE_EXEC_EMIT_LATE_FIRE_ENABLED} import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test import java.time.Duration @@ -44,44 +45,49 @@ class GroupWindowTest extends TableTestBase { |) |""".stripMargin) - @Test(expected = classOf[TableException]) + @Test def testTumbleWindowNoOffset(): Unit = { val sqlQuery = "SELECT SUM(a) AS sumA, COUNT(b) AS cntB FROM MyTable " + "GROUP BY TUMBLE(proctime, INTERVAL '2' HOUR, TIME '10:00:00')" - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } - @Test(expected = classOf[TableException]) + @Test def testHopWindowNoOffset(): Unit = { val sqlQuery = "SELECT SUM(a) AS sumA, COUNT(b) AS cntB FROM MyTable " + "GROUP BY HOP(proctime, INTERVAL '1' HOUR, INTERVAL '2' HOUR, TIME '10:00:00')" - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } - @Test(expected = classOf[TableException]) + @Test def testSessionWindowNoOffset(): Unit = { val sqlQuery = "SELECT SUM(a) AS sumA, COUNT(b) AS cntB FROM MyTable " + "GROUP BY SESSION(proctime, INTERVAL '2' HOUR, TIME '10:00:00')" - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } - @Test(expected = classOf[TableException]) + @Test def testVariableWindowSize(): Unit = { val sql = "SELECT COUNT(*) FROM MyTable GROUP BY TUMBLE(proctime, c * INTERVAL '1' MINUTE)" - util.verifyExecPlan(sql) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(sql)) } - @Test(expected = classOf[ValidationException]) + @Test def testWindowUdAggInvalidArgs(): Unit = { val sqlQuery = "SELECT SUM(a) AS sumA, weightedAvg(a, b) AS wAvg FROM MyTable " + "GROUP BY TUMBLE(proctime(), INTERVAL '2' HOUR, TIME '10:00:00')" - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } - @Test(expected = classOf[AssertionError]) + @Test def testWindowAggWithGroupSets(): Unit = { // TODO supports group sets // currently, the optimized plan is not collect, and an exception will be thrown in code-gen @@ -92,33 +98,32 @@ class GroupWindowTest extends TableTestBase { |FROM MyTable | GROUP BY rollup(TUMBLE(rowtime, INTERVAL '15' MINUTE), b) """.stripMargin - util.verifyRelPlanNotExpected(sql, "TUMBLE(rowtime") + assertThatExceptionOfType(classOf[AssertionError]) + .isThrownBy(() => util.verifyRelPlanNotExpected(sql, "TUMBLE(rowtime")) } @Test def testWindowWrongWindowParameter1(): Unit = { - expectedException.expect(classOf[TableException]) - expectedException.expectMessage( - "Window aggregate only support SECOND, MINUTE, HOUR, DAY as the time unit. " + - "MONTH and YEAR time unit are not supported yet.") - val sqlQuery = "SELECT COUNT(*) FROM MyTable GROUP BY TUMBLE(proctime, INTERVAL '1' MONTH)" - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) + .withMessageContaining( + "Window aggregate only support SECOND, MINUTE, HOUR, DAY as the time unit. " + + "MONTH and YEAR time unit are not supported yet.") } @Test def testWindowWrongWindowParameter2(): Unit = { - expectedException.expect(classOf[TableException]) - expectedException.expectMessage( - "Window aggregate only support SECOND, MINUTE, HOUR, DAY as the time unit. " + - "MONTH and YEAR time unit are not supported yet.") - val sqlQuery = "SELECT COUNT(*) FROM MyTable GROUP BY TUMBLE(proctime, INTERVAL '2-10' YEAR TO MONTH)" - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) + .withMessageContaining( + "Window aggregate only support SECOND, MINUTE, HOUR, DAY as the time unit. " + + "MONTH and YEAR time unit are not supported yet.") } @Test @@ -468,11 +473,11 @@ class GroupWindowTest extends TableTestBase { |FROM MyTable |GROUP BY TUMBLE(`rowtime`, INTERVAL '1' SECOND) |""".stripMargin - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "Allow-lateness [1000ms] should not be smaller than " + + + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyRelPlan(sql, ExplainDetail.CHANGELOG_MODE)) + .withMessageContaining("Allow-lateness [1000ms] should not be smaller than " + "Late-fire delay [5000ms] when enable late-fire emit strategy.") - util.verifyRelPlan(sql, ExplainDetail.CHANGELOG_MODE) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/GroupingSetsTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/GroupingSetsTest.scala index bab43fafffdff..0bfa78fc1c1a7 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/GroupingSetsTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/GroupingSetsTest.scala @@ -22,8 +22,8 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.utils.FlinkRelOptUtil import org.apache.flink.table.planner.utils.{TableTestBase, TableTestUtil} -import org.junit.Assert.assertEquals -import org.junit.Test +import org.assertj.core.api.Assertions.{assertThat, assertThatThrownBy} +import org.junit.jupiter.api.Test import java.sql.Date @@ -403,14 +403,14 @@ class GroupingSetsTest extends TableTestBase { @Test def testCALCITE1824(): Unit = { - expectedException.expect(classOf[TableException]) - expectedException.expectMessage("GROUPING SETS are currently not supported") val sqlQuery = """ |SELECT deptno, GROUP_ID() AS g, COUNT(*) AS c |FROM scott_emp GROUP BY GROUPING SETS (deptno, (), ()) """.stripMargin - util.verifyExecPlan(sqlQuery) + assertThatThrownBy(() => util.verifyExecPlan(sqlQuery)) + .hasMessageContaining("GROUPING SETS are currently not supported") + .isInstanceOf[TableException] } @Test @@ -503,7 +503,8 @@ class GroupingSetsTest extends TableTestBase { val table2 = util.tableEnv.sqlQuery(sql2) val optimized1 = util.getPlanner.optimize(TableTestUtil.toRelNode(table1)) val optimized2 = util.getPlanner.optimize(TableTestUtil.toRelNode(table2)) - assertEquals(FlinkRelOptUtil.toString(optimized1), FlinkRelOptUtil.toString(optimized2)) + assertThat(FlinkRelOptUtil.toString(optimized2)) + .isEqualTo(FlinkRelOptUtil.toString(optimized1)); } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/IncrementalAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/IncrementalAggregateTest.scala index 3daab5cf72d87..63ecf9f9608c8 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/IncrementalAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/IncrementalAggregateTest.scala @@ -19,20 +19,20 @@ package org.apache.flink.table.planner.plan.stream.sql.agg import org.apache.flink.table.planner.plan.rules.physical.stream.IncrementalAggregateRule import org.apache.flink.table.planner.utils.AggregatePhaseStrategy +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.junit.Before -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.extension.ExtendWith import java.util -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class IncrementalAggregateTest( splitDistinctAggEnabled: Boolean, aggPhaseEnforcer: AggregatePhaseStrategy) extends DistinctAggregateTest(splitDistinctAggEnabled, aggPhaseEnforcer) { - @Before + @BeforeEach override def before(): Unit = { super.before() // enable incremental agg @@ -42,7 +42,7 @@ class IncrementalAggregateTest( } object IncrementalAggregateTest { - @Parameterized.Parameters(name = "splitDistinctAggEnabled={0}, aggPhaseEnforcer={1}") + @Parameters(name = "splitDistinctAggEnabled={0}, aggPhaseEnforcer={1}") def parameters(): util.Collection[Array[Any]] = { util.Arrays.asList( Array(true, AggregatePhaseStrategy.TWO_PHASE) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/OverAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/OverAggregateTest.scala index 8e60a039136f3..3ef4d6b469432 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/OverAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/OverAggregateTest.scala @@ -23,8 +23,8 @@ import org.apache.flink.table.planner.plan.utils.FlinkRelOptUtil import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedAggFunctions.OverAgg0 import org.apache.flink.table.planner.utils.{TableTestBase, TableTestUtil} -import org.junit.Assert.assertEquals -import org.junit.Test +import org.assertj.core.api.Assertions.{assertThat, assertThatExceptionOfType} +import org.junit.jupiter.api.Test class OverAggregateTest extends TableTestBase { @@ -37,11 +37,11 @@ class OverAggregateTest extends TableTestBase { val table2 = util.tableEnv.sqlQuery(sql2) val optimized1 = util.getPlanner.optimize(TableTestUtil.toRelNode(table1)) val optimized2 = util.getPlanner.optimize(TableTestUtil.toRelNode(table2)) - assertEquals(FlinkRelOptUtil.toString(optimized1), FlinkRelOptUtil.toString(optimized2)) + assertThat(FlinkRelOptUtil.toString(optimized2)).isEqualTo(FlinkRelOptUtil.toString(optimized1)) } /** All aggregates must be computed on the same window. */ - @Test(expected = classOf[TableException]) + @Test def testMultiWindow(): Unit = { val sqlQuery = """ @@ -51,21 +51,24 @@ class OverAggregateTest extends TableTestBase { |from MyTable """.stripMargin - util.verifyExecPlan(sqlQuery) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(sqlQuery)) } /** OVER clause is necessary for [[OverAgg0]] window function. */ - @Test(expected = classOf[ValidationException]) + @Test def testInvalidOverAggregation(): Unit = { util.addFunction("overAgg", new OverAgg0) - util.verifyExecPlan("SELECT overAgg(c, a) FROM MyTable") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan("SELECT overAgg(c, a) FROM MyTable")) } /** OVER clause is necessary for [[OverAgg0]] window function. */ - @Test(expected = classOf[ValidationException]) + @Test def testInvalidOverAggregation2(): Unit = { util.addFunction("overAgg", new OverAgg0) - util.verifyExecPlan("SELECT overAgg(c, a) FROM MyTable") + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => util.verifyExecPlan("SELECT overAgg(c, a) FROM MyTable")) } @Test @@ -399,7 +402,7 @@ class OverAggregateTest extends TableTestBase { util.verifyExecPlan(sql) } - @Test(expected = classOf[TableException]) + @Test def testProcTimeBoundedPartitionedRowsOverDifferentWindows(): Unit = { val sql = """ @@ -419,8 +422,12 @@ class OverAggregateTest extends TableTestBase { "WINDOW w1 AS (PARTITION BY a ORDER BY proctime ROWS BETWEEN 3 PRECEDING AND CURRENT ROW)," + "w2 AS (PARTITION BY a ORDER BY proctime ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)" - verifyPlanIdentical(sql, sql2) - util.verifyExecPlan(sql) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy( + () => { + verifyPlanIdentical(sql, sql2) + util.verifyExecPlan(sql) + }) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/TwoStageAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/TwoStageAggregateTest.scala index 4c8060a0d1c0a..4832e8198ec9f 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/TwoStageAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/TwoStageAggregateTest.scala @@ -23,14 +23,14 @@ import org.apache.flink.table.api._ import org.apache.flink.table.api.config.OptimizerConfigOptions import org.apache.flink.table.planner.utils.{AggregatePhaseStrategy, TableTestBase} -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} class TwoStageAggregateTest extends TableTestBase { private val util = streamTestUtil() util.addTableSource[(Int, Long, String)]("MyTable", 'a, 'b, 'c) - @Before + @BeforeEach def before(): Unit = { util.enableMiniBatch() util.tableEnv.getConfig.setIdleStateRetentionTime(Time.hours(1), Time.hours(2)) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/WindowAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/WindowAggregateTest.scala index df866302cc3ba..6e0d5af0069aa 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/WindowAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/agg/WindowAggregateTest.scala @@ -25,16 +25,17 @@ import org.apache.flink.table.planner.plan.utils.WindowEmitStrategy.{TABLE_EXEC_ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedAggFunctions.TestPythonAggregateFunction import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedTableFunctions.JavaTableFunc1 import org.apache.flink.table.planner.utils.{AggregatePhaseStrategy, TableTestBase} +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} -import org.junit.{Before, Test} -import org.junit.Assume.assumeTrue -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.assertj.core.api.Assumptions.assumeThat +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith import java.util /** Tests for window aggregates based on window table-valued function. */ -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends TableTestBase { private val util = streamTestUtil() @@ -46,7 +47,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl */ private val isTwoPhase = aggPhaseEnforcer == AggregatePhaseStrategy.TWO_PHASE - @Before + @BeforeEach def before(): Unit = { util.addTemporarySystemFunction("weightedAvg", classOf[WeightedAvgWithMerge]) util.addTemporarySystemFunction("weightedAvgWithoutMerge", classOf[WeightedAvg]) @@ -87,7 +88,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl .set(OptimizerConfigOptions.TABLE_OPTIMIZER_AGG_PHASE_STRATEGY, aggPhaseEnforcer.toString) } - @Test + @TestTemplate def testTumble_OnRowtime(): Unit = { val sql = """ @@ -106,9 +107,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_OnProctime(): Unit = { - assumeTrue(isTwoPhase) + assumeThat(isTwoPhase).isTrue val sql = """ |SELECT @@ -126,7 +127,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_CalcOnTVF(): Unit = { val sql = """ @@ -149,7 +150,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_WindowColumnsAtEnd(): Unit = { // there shouldn't be any Calc on the WindowAggregate, // because fields order are align with WindowAggregate schema @@ -170,7 +171,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_GroupMultipleWindowColumns(): Unit = { val sql = """ @@ -194,7 +195,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_GroupMultipleKeys(): Unit = { val sql = """ @@ -213,7 +214,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_GroupOnlyWindowColumns(): Unit = { val sql = """ @@ -230,7 +231,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_GroupOnLiteralValue(): Unit = { val sql = """ @@ -247,7 +248,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_ProjectionPushDown(): Unit = { val sql = """ @@ -263,7 +264,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_CascadingWindow(): Unit = { util.tableEnv.executeSql( """ @@ -295,7 +296,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_CascadingWindow_RelaxForm(): Unit = { // a relax form of cascaded rowtime window which is actually supported util.verifyRelPlan( @@ -319,7 +320,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl """.stripMargin) } - @Test + @TestTemplate def testTumble_DistinctSplitEnabled(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_DISTINCT_AGG_SPLIT_ENABLED, Boolean.box(true)) @@ -339,7 +340,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_DistinctOnWindowColumns(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_DISTINCT_AGG_SPLIT_ENABLED, Boolean.box(true)) @@ -361,9 +362,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_DoNotSplitProcessingTimeWindow(): Unit = { - assumeTrue(isTwoPhase) + assumeThat(isTwoPhase).isTrue // the processing-time window aggregate with distinct shouldn't be split into two-level agg util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_DISTINCT_AGG_SPLIT_ENABLED, Boolean.box(true)) @@ -383,7 +384,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_NotOutputWindowColumns(): Unit = { val sql = """ @@ -399,9 +400,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_UdafWithoutMerge(): Unit = { - assumeTrue(isTwoPhase) + assumeThat(isTwoPhase).isTrue // the window aggregation shouldn't be split into local-global window aggregation val sql = """ @@ -420,7 +421,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testCumulate_OnRowtime(): Unit = { val sql = """ @@ -440,9 +441,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testCumulate_OnProctime(): Unit = { - assumeTrue(isTwoPhase) + assumeThat(isTwoPhase).isTrue val sql = """ |SELECT @@ -461,7 +462,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testCumulate_DistinctSplitEnabled(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_DISTINCT_AGG_SPLIT_ENABLED, Boolean.box(true)) @@ -482,7 +483,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testHop_OnRowtime(): Unit = { val sql = """ @@ -502,9 +503,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testHop_OnProctime(): Unit = { - assumeTrue(isTwoPhase) + assumeThat(isTwoPhase).isTrue val sql = """ |SELECT @@ -523,7 +524,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testHop_DistinctSplitEnabled(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_DISTINCT_AGG_SPLIT_ENABLED, Boolean.box(true)) @@ -544,7 +545,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testMultipleAggregateOnSameWindowTVF(): Unit = { util.tableEnv.executeSql( """ @@ -589,7 +590,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl // Tests for queries can't be translated to window aggregate for now // ---------------------------------------------------------------------------------------- - @Test + @TestTemplate def testCantMergeWindowTVF_FilterOnWindowStart(): Unit = { val sql = """ @@ -611,7 +612,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testCantMergeWindowTVF_UdtfOnWindowTVF(): Unit = { util.tableEnv.createTemporaryFunction("len_udtf", classOf[JavaTableFunc1]) val sql = @@ -634,9 +635,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testCantTranslateToWindowAgg_GroupOnOnlyStart(): Unit = { - assumeTrue(isTwoPhase) + assumeThat(isTwoPhase).isTrue val sql = """ |SELECT @@ -652,9 +653,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testCantTranslateToWindowAgg_PythonAggregateCall(): Unit = { - assumeTrue(isTwoPhase) + assumeThat(isTwoPhase).isTrue util.tableEnv.createTemporaryFunction("python_agg", classOf[TestPythonAggregateFunction]) val sql = """ @@ -668,7 +669,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testUnsupportedException_EarlyFire(): Unit = { val conf = new Configuration() conf.setString(TABLE_EXEC_EMIT_EARLY_FIRE_ENABLED.key(), "true") @@ -690,15 +691,15 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl |GROUP BY a, window_start, window_end """.stripMargin - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "Currently, window table function based aggregate doesn't support " + - "early-fire and late-fire configuration 'table.exec.emit.early-fire.enabled' and " + - "'table.exec.emit.late-fire.enabled'.") - util.verifyExecPlan(sql) + assertThatThrownBy(() => util.verifyExecPlan(sql)) + .hasMessageContaining( + "Currently, window table function based aggregate doesn't support " + + "early-fire and late-fire configuration 'table.exec.emit.early-fire.enabled' and " + + "'table.exec.emit.late-fire.enabled'.") + .isInstanceOf[TableException] } - @Test + @TestTemplate def testUnsupportedException_LateFire(): Unit = { val conf = new Configuration() conf.setString(TABLE_EXEC_EMIT_LATE_FIRE_ENABLED.key(), "true") @@ -720,15 +721,15 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl |GROUP BY a, window_start, window_end """.stripMargin - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "Currently, window table function based aggregate doesn't support " + - "early-fire and late-fire configuration 'table.exec.emit.early-fire.enabled' and " + - "'table.exec.emit.late-fire.enabled'.") - util.verifyExecPlan(sql) + assertThatThrownBy(() => util.verifyExecPlan(sql)) + .hasMessageContaining( + "Currently, window table function based aggregate doesn't support " + + "early-fire and late-fire configuration 'table.exec.emit.early-fire.enabled' and " + + "'table.exec.emit.late-fire.enabled'.") + .isInstanceOf[TableException] } - @Test + @TestTemplate def testUnsupportedException_HopSizeNonDivisible(): Unit = { val sql = """ @@ -742,14 +743,13 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl |GROUP BY a, window_start, window_end """.stripMargin - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "HOP table function based aggregate requires size must be an " + + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("HOP table function based aggregate requires size must be an " + "integral multiple of slide, but got size 600000 ms and slide 240000 ms") - util.verifyExplain(sql) + .isInstanceOf[TableException] } - @Test + @TestTemplate def testUnsupportedException_CumulateSizeNonDivisible(): Unit = { val sql = """ @@ -763,16 +763,15 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl |GROUP BY a, window_start, window_end """.stripMargin - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "CUMULATE table function based aggregate requires maxSize " + + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("CUMULATE table function based aggregate requires maxSize " + "must be an integral multiple of step, but got maxSize 3600000 ms and step 1500000 ms") - util.verifyExplain(sql) + .isInstanceOf[TableException] } - @Test + @TestTemplate def testCantTranslateToWindowAgg_GroupingSetsWithoutWindowStartEnd(): Unit = { - assumeTrue(isTwoPhase) + assumeThat(isTwoPhase).isTrue // Cannot translate to window aggregate because group keys don't contain both window_start // and window_end val sql = @@ -786,9 +785,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testCantTranslateToWindowAgg_GroupingSetsOnlyWithWindowStart(): Unit = { - assumeTrue(isTwoPhase) + assumeThat(isTwoPhase).isTrue val sql = """ |SELECT @@ -800,7 +799,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_GroupingSets(): Unit = { val sql = """ @@ -814,7 +813,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_GroupingSets1(): Unit = { val sql = """ @@ -828,7 +827,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_GroupingSetsDistinctSplitEnabled(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_DISTINCT_AGG_SPLIT_ENABLED, Boolean.box(true)) @@ -847,9 +846,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testCantTranslateToWindowAgg_CubeWithoutWindowStartEnd(): Unit = { - assumeTrue(isTwoPhase) + assumeThat(isTwoPhase).isTrue val sql = """ |SELECT @@ -862,9 +861,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testCantTranslateToWindowAgg_RollupWithoutWindowStartEnd(): Unit = { - assumeTrue(isTwoPhase) + assumeThat(isTwoPhase).isTrue val sql = """ |SELECT @@ -877,7 +876,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testTumble_Rollup(): Unit = { val sql = """ @@ -891,7 +890,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testCantMergeWindowTVF_GroupingSetsDistinctOnWindowColumns(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_DISTINCT_AGG_SPLIT_ENABLED, Boolean.box(true)) @@ -912,7 +911,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testHop_GroupingSets(): Unit = { val sql = """ @@ -927,7 +926,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testHop_GroupingSets_DistinctSplitEnabled(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_DISTINCT_AGG_SPLIT_ENABLED, Boolean.box(true)) @@ -945,7 +944,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testHop_Cube(): Unit = { val sql = """ @@ -960,7 +959,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testHop_Rollup(): Unit = { val sql = """ @@ -975,7 +974,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testCumulate_GroupingSets(): Unit = { val sql = """ @@ -990,7 +989,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testCumulate_GroupingSets_DistinctSplitEnabled(): Unit = { util.tableEnv.getConfig .set(OptimizerConfigOptions.TABLE_OPTIMIZER_DISTINCT_AGG_SPLIT_ENABLED, Boolean.box(true)) @@ -1008,7 +1007,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testCumulate_Cube(): Unit = { val sql = """ @@ -1023,7 +1022,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testCumulate_Rollup(): Unit = { val sql = """ @@ -1038,7 +1037,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testFieldNameConflict(): Unit = { val sql = """ @@ -1051,7 +1050,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl util.verifyRelPlan(sql) } - @Test + @TestTemplate def testProctimeWindowWithFilter(): Unit = { util.tableEnv.executeSql(s""" |CREATE TEMPORARY TABLE source ( @@ -1099,9 +1098,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl |""".stripMargin) } - @Test + @TestTemplate def testTumble_CascadingWindow_OnIndividualProctime(): Unit = { - assumeTrue(!isTwoPhase) + assumeThat(isTwoPhase).isFalse // a standard cascaded proctime window util.verifyExecPlan( """ @@ -1115,9 +1114,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl |""".stripMargin) } - @Test + @TestTemplate def testTumble_CascadingWindow_OnInheritProctime(): Unit = { - assumeTrue(!isTwoPhase) + assumeThat(isTwoPhase).isFalse // a standard cascaded proctime window util.verifyExecPlan( """ @@ -1131,9 +1130,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl |""".stripMargin) } - @Test + @TestTemplate def testInvalidRelaxFormCascadeProctimeWindow(): Unit = { - assumeTrue(!isTwoPhase) + assumeThat(isTwoPhase).isFalse // a relax form of cascaded proctime window unsupported for now, will be translated to group agg util.verifyRelPlan(""" |SELECT @@ -1146,9 +1145,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl """.stripMargin) } - @Test + @TestTemplate def testTumble_CascadeProctimeWindow_OnWindowRank(): Unit = { - assumeTrue(!isTwoPhase) + assumeThat(isTwoPhase).isFalse // create window top10 createProctimeWindowTopN("proctime_winrank", 10) @@ -1183,9 +1182,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl """.stripMargin) } - @Test + @TestTemplate def testInvalidRelaxFormCascadeProctimeWindow_OnWindowRank(): Unit = { - assumeTrue(!isTwoPhase) + assumeThat(isTwoPhase).isFalse // create window top10 createProctimeWindowTopN("proctime_winrank", 10) @@ -1201,9 +1200,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl """.stripMargin) } - @Test + @TestTemplate def testTumble_CascadeProctimeWindow_OnWindowDedup(): Unit = { - assumeTrue(!isTwoPhase) + assumeThat(isTwoPhase).isFalse // create window dedup(top1) createProctimeWindowTopN("proctime_windedup", 1) @@ -1220,9 +1219,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl """.stripMargin) } - @Test + @TestTemplate def testInvalidRelaxFormCascadeProctimeWindow_OnWindowDedup(): Unit = { - assumeTrue(!isTwoPhase) + assumeThat(isTwoPhase).isFalse // create window dedup(top1) createProctimeWindowTopN("proctime_windedup", 1) @@ -1238,9 +1237,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl """.stripMargin) } - @Test + @TestTemplate def testTumble_CascadeProctimeWindow_OnWindowJoin(): Unit = { - assumeTrue(!isTwoPhase) + assumeThat(isTwoPhase).isFalse createWindowJoin util.verifyRelPlan( @@ -1281,9 +1280,9 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl """.stripMargin) } - @Test + @TestTemplate def testInvalidRelaxFormCascadeProctimeWindow_OnWindowJoin(): Unit = { - assumeTrue(!isTwoPhase) + assumeThat(isTwoPhase).isFalse createWindowJoin // a relax form of cascaded proctime window on a window join is unsupported for now, will be translated to group agg @@ -1300,7 +1299,7 @@ class WindowAggregateTest(aggPhaseEnforcer: AggregatePhaseStrategy) extends Tabl } object WindowAggregateTest { - @Parameterized.Parameters(name = "aggPhaseEnforcer={0}") + @Parameters(name = "aggPhaseEnforcer={0}") def parameters(): util.Collection[Array[Any]] = { util.Arrays.asList( Array(AggregatePhaseStrategy.ONE_PHASE), diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/IntervalJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/IntervalJoinTest.scala index c40cc18782b82..4333151b1be58 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/IntervalJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/IntervalJoinTest.scala @@ -24,8 +24,8 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctio import org.apache.flink.table.planner.utils.{StreamTableTestUtil, TableTestBase, TableTestUtil} import org.apache.calcite.rel.logical.LogicalJoin -import org.junit.Assert.assertEquals -import org.junit.Test +import org.assertj.core.api.Assertions.{assertThat, assertThatExceptionOfType, assertThatThrownBy} +import org.junit.jupiter.api.Test class IntervalJoinTest extends TableTestBase { @@ -92,9 +92,6 @@ class IntervalJoinTest extends TableTestBase { /** Both rowtime types in a join condition must be of the same type * */ @Test def testIntervalJoinOnDiffRowTimeType(): Unit = { - expectedException.expectMessage( - "Interval join with rowtime attribute requires same rowtime types," + - " but the types are TIMESTAMP(3) *ROWTIME* and TIMESTAMP_LTZ(3) *ROWTIME*") val sql = """ |SELECT t2.a FROM MyTable2 t1 JOIN MyTable3 t2 ON @@ -102,7 +99,11 @@ class IntervalJoinTest extends TableTestBase { | t1.rowtime > t2.rowtime - INTERVAL '5' SECOND AND | t1.rowtime < t2.rowtime + INTERVAL '5' SECOND """.stripMargin - util.verifyExecPlan(sql) + + assertThatThrownBy(() => util.verifyExecPlan(sql)) + .hasMessageContaining( + "Interval join with rowtime attribute requires same rowtime types," + + " but the types are TIMESTAMP(3) *ROWTIME* and TIMESTAMP_LTZ(3) *ROWTIME*") } /** The time conditions should be an And condition * */ @@ -119,7 +120,7 @@ class IntervalJoinTest extends TableTestBase { } /** Validates that no rowtime attribute is in the output schema * */ - @Test(expected = classOf[TableException]) + @Test def testNoRowtimeAttributeInResult(): Unit = { val sql = """ @@ -128,14 +129,15 @@ class IntervalJoinTest extends TableTestBase { | t1.proctime BETWEEN t2.proctime - INTERVAL '5' SECOND AND t2.proctime """.stripMargin - util.verifyExecPlan(sql) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(sql)) } /** * Currently only the inner join condition can support the Python UDF taking the inputs from the * left table and the right table at the same time. */ - @Test(expected = classOf[TableException]) + @Test def testWindowOuterJoinWithPythonFunctionInCondition(): Unit = { util.addFunction("pyFunc", new PythonScalarFunction("pyFunc")) val sql = @@ -144,7 +146,8 @@ class IntervalJoinTest extends TableTestBase { | t1.a = t2.a AND pyFunc(t1.a, t2.a) = t1.a + t2.a AND | t1.proctime BETWEEN t2.proctime - INTERVAL '1' HOUR AND t2.proctime + INTERVAL '1' HOUR """.stripMargin - util.verifyExecPlan(sql) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(sql)) } @Test @@ -530,9 +533,9 @@ class IntervalJoinTest extends TableTestBase { ) val timeTypeStr = if (windowBounds.get.isEventTime) "rowtime" else "proctime" - assertEquals(expLeftSize, windowBounds.get.getLeftLowerBound) - assertEquals(expRightSize, windowBounds.get.getLeftUpperBound) - assertEquals(expTimeType, timeTypeStr) + assertThat(windowBounds.get.getLeftLowerBound).isEqualTo(expLeftSize) + assertThat(windowBounds.get.getLeftUpperBound).isEqualTo(expRightSize) + assertThat(timeTypeStr).isEqualTo(expTimeType) } private def verifyRemainConditionConvert(sqlQuery: String, expectConditionStr: String): Unit = { @@ -552,7 +555,7 @@ class IntervalJoinTest extends TableTestBase { Thread.currentThread().getContextClassLoader ) val actual: String = remainCondition.getOrElse("").toString - assertEquals(expectConditionStr, actual) + assertThat(actual).isEqualTo(expectConditionStr) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/JoinReorderTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/JoinReorderTest.scala index 9cdacbebe81bc..b4ec8c0c636fa 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/JoinReorderTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/JoinReorderTest.scala @@ -19,12 +19,12 @@ package org.apache.flink.table.planner.plan.stream.sql.join import org.apache.flink.table.planner.plan.common.JoinReorderTestBase import org.apache.flink.table.planner.utils.TableTestUtil +import org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.junit.jupiter.api.extension.ExtendWith /** The plan test for join reorder in stream mode. */ -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class JoinReorderTest(isBushyJoinReorder: Boolean) extends JoinReorderTestBase(isBushyJoinReorder: Boolean) { override protected def getTableTestUtil: TableTestUtil = streamTestUtil() diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/JoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/JoinTest.scala index ac413fc4c70bb..4df4c3de8dd3d 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/JoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/JoinTest.scala @@ -21,7 +21,7 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.{StreamTableTestUtil, TableFunc1, TableTestBase} -import org.junit.Test +import org.junit.jupiter.api.Test class JoinTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/LookupJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/LookupJoinTest.scala index b0f870f72544b..f9e0749a159ee 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/LookupJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/LookupJoinTest.scala @@ -18,7 +18,6 @@ package org.apache.flink.table.planner.plan.stream.sql.join import org.apache.flink.api.scala._ -import org.apache.flink.core.testutils.FlinkMatchers.containsMessage import org.apache.flink.streaming.api.datastream.DataStream import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment import org.apache.flink.table.api._ @@ -35,27 +34,28 @@ import org.apache.flink.table.planner.utils.TableTestUtil.{readFromResource, rep import org.apache.flink.table.sources._ import org.apache.flink.table.types.DataType import org.apache.flink.table.utils.EncodingUtils +import org.apache.flink.testutils.junit.extensions.parameterized.{ParameterizedTestExtension, Parameters} import _root_.java.lang.{Boolean => JBoolean} import _root_.java.sql.Timestamp import _root_.java.util import _root_.java.util.{ArrayList => JArrayList, Collection => JCollection, HashMap => JHashMap, List => JList, Map => JMap} import _root_.scala.collection.JavaConversions._ -import org.junit.{Assume, Before, Test} -import org.junit.Assert.{assertEquals, assertThat, assertTrue, fail} -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import org.assertj.core.api.Assertions.{assertThat, assertThatExceptionOfType, assertThatThrownBy} +import org.assertj.core.api.Assumptions.assumeThat +import org.junit.jupiter.api.{BeforeEach, TestTemplate} +import org.junit.jupiter.api.extension.ExtendWith /** * The physical plans for legacy [[org.apache.flink.table.sources.LookupableTableSource]] and new * [[org.apache.flink.table.connector.source.LookupTableSource]] should be identical. */ -@RunWith(classOf[Parameterized]) +@ExtendWith(Array(classOf[ParameterizedTestExtension])) class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Serializable { private val util = streamTestUtil() - @Before + @BeforeEach def before(): Unit = { util.addDataStream[(Int, String, Long)]( "MyTable", @@ -117,7 +117,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri .set(ExecutionConfigOptions.TABLE_EXEC_RESOURCE_DEFAULT_PARALLELISM, Int.box(4)) } - @Test + @TestTemplate def testJoinInvalidJoinTemporalTable(): Unit = { // must follow a period specification expectExceptionThrown( @@ -143,7 +143,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri ) } - @Test + @TestTemplate def testNotDistinctFromInJoinCondition(): Unit = { // does not support join condition contains `IS NOT DISTINCT` @@ -165,7 +165,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri ) } - @Test + @TestTemplate def testInvalidLookupTableFunction(): Unit = { if (legacyTableSource) { return @@ -262,19 +262,20 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri ) } - @Test + @TestTemplate def testJoinOnDifferentKeyTypes(): Unit = { // Will do implicit type coercion. - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "implicit type conversion between VARCHAR(2147483647) and INTEGER " + + assertThatThrownBy( + () => + util.verifyExecPlan( + "SELECT * FROM MyTable AS T JOIN LookupTable " + + "FOR SYSTEM_TIME AS OF T.proctime AS D ON T.b = D.id")) + .hasMessageContaining("implicit type conversion between VARCHAR(2147483647) and INTEGER " + "is not supported on join's condition now") - util.verifyExecPlan( - "SELECT * FROM MyTable AS T JOIN LookupTable " - + "FOR SYSTEM_TIME AS OF T.proctime AS D ON T.b = D.id") + .isInstanceOf[TableException] } - @Test + @TestTemplate def testJoinTemporalTable(): Unit = { val sql = "SELECT * FROM MyTable AS T JOIN LookupTable " + "FOR SYSTEM_TIME AS OF T.proctime AS D ON T.a = D.id" @@ -282,7 +283,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testLeftJoinTemporalTable(): Unit = { val sql = "SELECT * FROM MyTable AS T LEFT JOIN LookupTable " + "FOR SYSTEM_TIME AS OF T.proctime AS D ON T.a = D.id" @@ -290,7 +291,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithNestedQuery(): Unit = { val sql = "SELECT * FROM " + "(SELECT a, b, proctime FROM MyTable WHERE c > 1000) AS T " + @@ -300,7 +301,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithProjectionPushDown(): Unit = { val sql = """ @@ -313,7 +314,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithFilterPushDown(): Unit = { val sql = """ @@ -326,7 +327,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithCalcPushDown(): Unit = { val sql = """ @@ -339,7 +340,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithMultiIndexColumn(): Unit = { val sql = """ @@ -352,7 +353,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testAvoidAggregatePushDown(): Unit = { val sql1 = """ @@ -379,12 +380,8 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithTrueCondition(): Unit = { - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "Temporal table join requires an equality condition on fields of " + - "table [default_catalog.default_database.LookupTable]") val sql = """ |SELECT * FROM MyTable AS T @@ -393,10 +390,13 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri |WHERE T.c > 1000 """.stripMargin - util.verifyExplain(sql) + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("Temporal table join requires an equality condition on fields of " + + "table [default_catalog.default_database.LookupTable]") + .isInstanceOf[TableException] } - @Test + @TestTemplate def testJoinTemporalTableWithFunctionAndConstantCondition(): Unit = { val sql = @@ -409,7 +409,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithMultiFunctionAndConstantCondition(): Unit = { val sql = @@ -422,7 +422,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithFunctionAndReferenceCondition(): Unit = { val sql = """ @@ -435,7 +435,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithUdfEqualFilter(): Unit = { val sql = """ @@ -449,10 +449,10 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithComputedColumn(): Unit = { // Computed column do not support in legacyTableSource. - Assume.assumeFalse(legacyTableSource) + assumeThat(legacyTableSource).isFalse val sql = """ |SELECT @@ -464,10 +464,10 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithComputedColumnAndPushDown(): Unit = { // Computed column do not support in legacyTableSource. - Assume.assumeFalse(legacyTableSource) + assumeThat(legacyTableSource).isFalse val sql = """ |SELECT @@ -479,7 +479,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithMultiConditionOnSameDimField(): Unit = { val sql = "SELECT * FROM MyTable AS T JOIN LookupTable " + "FOR SYSTEM_TIME AS OF T.proctime AS D ON T.a = D.id and CAST(T.c as INT) = D.id" @@ -487,7 +487,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithCastOnLookupTable(): Unit = { util.addTable(""" |CREATE TABLE LookupTable2 ( @@ -505,14 +505,14 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri |LEFT JOIN LookupTable2 FOR SYSTEM_TIME AS OF MyTable.`proctime` |ON MyTable.a = CAST(LookupTable2.`id` as INT) |""".stripMargin - thrown.expect(classOf[TableException]) - thrown.expectMessage( - "Temporal table join requires an equality condition on fields of " + + + assertThatThrownBy(() => verifyTranslationSuccess(sql)) + .hasMessageContaining("Temporal table join requires an equality condition on fields of " + "table [default_catalog.default_database.LookupTable2]") - verifyTranslationSuccess(sql) + .isInstanceOf[TableException] } - @Test + @TestTemplate def testJoinTemporalTableWithInteroperableCastOnLookupTable(): Unit = { util.addTable(""" |CREATE TABLE LookupTable2 ( @@ -534,7 +534,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri verifyTranslationSuccess(sql) } - @Test + @TestTemplate def testJoinTemporalTableWithCTE(): Unit = { val sql = """ @@ -548,7 +548,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testAggAndAllConstantLookupKeyWithTryResolveMode(): Unit = { // expect lookup join using single parallelism due to all constant lookup key util.tableEnv.getConfig.set( @@ -571,12 +571,11 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri readFromResource( "explain/stream/join/lookup/testAggAndAllConstantLookupKeyWithTryResolveMode_newSource.out") } - assertEquals( - replaceNodeIdInOperator(replaceStreamNodeId(replaceStageId(expected))), - replaceNodeIdInOperator(replaceStreamNodeId(replaceStageId(actual)))) + assertThat(replaceNodeIdInOperator(replaceStreamNodeId(replaceStageId(actual)))) + .isEqualTo(replaceNodeIdInOperator(replaceStreamNodeId(replaceStageId(expected)))) } - @Test + @TestTemplate def testInvalidJoinHint(): Unit = { // lost required hint option 'table' expectExceptionThrown( @@ -727,7 +726,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri ) } - @Test + @TestTemplate def testJoinHintWithTableAlias(): Unit = { val sql = "SELECT /*+ LOOKUP('table'='D', 'retry-predicate'='lookup_miss', 'retry-strategy'='fixed_delay', 'fixed-delay'='10s', 'max-attempts'='3') */ * FROM MyTable AS T JOIN LookupTable " + @@ -735,14 +734,14 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinHintWithTableNameOnly(): Unit = { val sql = "SELECT /*+ LOOKUP('table'='LookupTable') */ * FROM MyTable AS T JOIN LookupTable " + "FOR SYSTEM_TIME AS OF T.proctime ON T.a = LookupTable.id" util.verifyExecPlan(sql) } - @Test + @TestTemplate def testMultipleJoinHintsWithSameTableName(): Unit = { // only the first hint will take effect val sql = @@ -756,7 +755,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testMultipleJoinHintsWithSameTableAlias(): Unit = { // only the first hint will take effect val sql = @@ -770,7 +769,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testMultipleJoinHintsWithDifferentTableName(): Unit = { // both hints on corresponding tables will take effect val sql = @@ -786,7 +785,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testMultipleJoinHintsWithDifferentTableAlias(): Unit = { // both hints on corresponding tables will take effect val sql = @@ -802,7 +801,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinSyncTableWithAsyncHint(): Unit = { val sql = "SELECT /*+ LOOKUP('table'='D', 'async'='true') */ * FROM MyTable AS T JOIN LookupTable " + @@ -810,7 +809,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinAsyncTableWithAsyncHint(): Unit = { val sql = "SELECT /*+ LOOKUP('table'='D', 'async'='true') */ * " + @@ -819,7 +818,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testJoinAsyncTableWithSyncHint(): Unit = { val sql = "SELECT /*+ LOOKUP('table'='D', 'async'='false') */ * " + @@ -828,7 +827,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExecPlan(sql) } - @Test + @TestTemplate def testAggAndLeftJoinAllowUnordered(): Unit = { util.tableEnv.getConfig.set( ExecutionConfigOptions.TABLE_EXEC_ASYNC_LOOKUP_OUTPUT_MODE, @@ -848,9 +847,6 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri } def testAggAndLeftJoinWithTryResolveMode(): Unit = { - thrown.expectMessage("Required sync lookup function by planner, but table") - thrown.expect(classOf[TableException]) - util.tableEnv.getConfig.set( OptimizerConfigOptions.TABLE_OPTIMIZER_NONDETERMINISTIC_UPDATE_STRATEGY, OptimizerConfigOptions.NonDeterministicUpdateStrategy.TRY_RESOLVE) @@ -865,10 +861,12 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri |FOR SYSTEM_TIME AS OF T.proctime AS D ON T.a = D.id |""".stripMargin) - util.verifyExplain(stmt, ExplainDetail.JSON_EXECUTION_PLAN) + assertThatThrownBy(() => util.verifyExplain(stmt, ExplainDetail.JSON_EXECUTION_PLAN)) + .hasMessageContaining("Required sync lookup function by planner, but table") + .isInstanceOf[TableException] } - @Test + @TestTemplate def testAsyncJoinWithDefaultParams(): Unit = { val stmt = util.tableEnv.asInstanceOf[TestingTableEnvironment].createStatementSet() stmt.addInsertSql(""" @@ -882,7 +880,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExplain(stmt, ExplainDetail.JSON_EXECUTION_PLAN) } - @Test + @TestTemplate def testJoinWithAsyncHint(): Unit = { val stmt = util.tableEnv.asInstanceOf[TestingTableEnvironment].createStatementSet() stmt.addInsertSql( @@ -898,7 +896,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExplain(stmt, ExplainDetail.JSON_EXECUTION_PLAN) } - @Test + @TestTemplate def testJoinWithRetryHint(): Unit = { val stmt = util.tableEnv.asInstanceOf[TestingTableEnvironment].createStatementSet() stmt.addInsertSql( @@ -914,7 +912,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExplain(stmt, ExplainDetail.JSON_EXECUTION_PLAN) } - @Test + @TestTemplate def testJoinWithAsyncAndRetryHint(): Unit = { val stmt = util.tableEnv.asInstanceOf[TestingTableEnvironment].createStatementSet() stmt.addInsertSql( @@ -930,7 +928,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri util.verifyExplain(stmt, ExplainDetail.JSON_EXECUTION_PLAN) } - @Test + @TestTemplate def testJoinWithMixedCaseJoinHint(): Unit = { util.verifyExecPlan( """ @@ -946,7 +944,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri ) } - @Test + @TestTemplate def testJoinHintWithNoPropagatingToSubQuery(): Unit = { util.verifyExecPlan( """ @@ -992,14 +990,9 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri sql: String, message: String, clazz: Class[_ <: Throwable] = classOf[ValidationException]): Unit = { - try { - verifyTranslationSuccess(sql) - fail(s"Expected a $clazz, but no exception is thrown.") - } catch { - case e: Throwable => - assertTrue(clazz.isAssignableFrom(e.getClass)) - assertThat(e, containsMessage(message)) - } + assertThatExceptionOfType(clazz) + .isThrownBy(() => verifyTranslationSuccess(sql)) + .withMessageContaining(message) } private def verifyTranslationSuccess(sql: String): Unit = { @@ -1008,7 +1001,7 @@ class LookupJoinTest(legacyTableSource: Boolean) extends TableTestBase with Seri } object LookupJoinTest { - @Parameterized.Parameters(name = "LegacyTableSource={0}") + @Parameters(name = "LegacyTableSource={0}") def parameters(): JCollection[Array[Object]] = { Seq[Array[AnyRef]](Array(JBoolean.TRUE), Array(JBoolean.FALSE)) } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/SemiAntiJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/SemiAntiJoinTest.scala index e8dba6c10913e..f1b8c9d584a7d 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/SemiAntiJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/SemiAntiJoinTest.scala @@ -22,7 +22,7 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedTableFunctions.StringSplit import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test class SemiAntiJoinTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/TemporalFunctionJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/TemporalFunctionJoinTest.scala index 6e522d240a024..3b4ea69d25b5b 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/TemporalFunctionJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/TemporalFunctionJoinTest.scala @@ -21,8 +21,8 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.{StreamTableTestUtil, TableTestBase} -import org.hamcrest.Matchers.containsString -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test import java.sql.Timestamp @@ -128,25 +128,23 @@ class TemporalFunctionJoinTest extends TableTestBase { @Test def testUncorrelatedJoin(): Unit = { - expectedException.expect(classOf[TableException]) - expectedException.expectMessage(containsString("Cannot generate a valid execution plan")) - val sqlQuery = "SELECT " + "o_amount * rate as rate " + "FROM Orders AS o, " + "LATERAL TABLE (Rates(TIMESTAMP '2016-06-27 10:10:42.123')) AS r " + "WHERE currency = o_currency" - util.verifyExplain(sqlQuery) + assertThatThrownBy(() => util.verifyExplain(sqlQuery)) + .hasMessageContaining("Cannot generate a valid execution plan") + .isInstanceOf[TableException] } @Test def testTemporalTableFunctionScan(): Unit = { - expectedException.expect(classOf[TableException]) - expectedException.expectMessage(containsString("Cannot generate a valid execution plan")) - val sqlQuery = "SELECT * FROM LATERAL TABLE (Rates(TIMESTAMP '2016-06-27 10:10:42.123'))" - util.verifyExplain(sqlQuery) + assertThatThrownBy(() => util.verifyExplain(sqlQuery)) + .hasMessageContaining("Cannot generate a valid execution plan") + .isInstanceOf[TableException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/TemporalJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/TemporalJoinTest.scala index c385454a42e5f..29d6521540c9d 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/TemporalJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/TemporalJoinTest.scala @@ -20,15 +20,16 @@ package org.apache.flink.table.planner.plan.stream.sql.join import org.apache.flink.table.api.{ExplainDetail, TableException, ValidationException} import org.apache.flink.table.planner.utils.{StreamTableTestUtil, TableTestBase} -import org.junit.{Before, Test} -import org.junit.Assert.{assertTrue, fail} +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.assertj.core.api.ThrowableAssert.ThrowingCallable +import org.junit.jupiter.api.{BeforeEach, Test} /** Test temporal join in stream mode. */ class TemporalJoinTest extends TableTestBase { val util: StreamTableTestUtil = streamTestUtil() - @Before + @BeforeEach def before(): Unit = { util.addTable(""" |CREATE TABLE Orders ( @@ -618,20 +619,14 @@ class TemporalJoinTest extends TableTestBase { sql: String, keywords: String, clazz: Class[_ <: Throwable] = classOf[ValidationException]): Unit = { - try { - verifyTranslationSuccess(sql) - fail(s"Expected a $clazz, but no exception is thrown.") - } catch { - case e if e.getClass == clazz => - if (keywords != null) { - assertTrue( - s"The actual exception message \n${e.getMessage}\n" + - s"doesn't contain expected keyword \n$keywords\n", - e.getMessage.contains(keywords)) - } - case e: Throwable => - e.printStackTrace() - fail(s"Expected throw ${clazz.getSimpleName}, but is $e.") + val callable: ThrowingCallable = () => verifyTranslationSuccess(sql) + if (keywords != null) { + assertThatExceptionOfType(clazz) + .isThrownBy(callable) + .withMessageContaining(keywords) + } else { + assertThatExceptionOfType(clazz) + .isThrownBy(callable) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/WindowJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/WindowJoinTest.scala index 9fe0b3e7fbfd0..792d2e40df8e0 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/WindowJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/sql/join/WindowJoinTest.scala @@ -20,7 +20,8 @@ package org.apache.flink.table.planner.plan.stream.sql.join import org.apache.flink.table.api.TableException import org.apache.flink.table.planner.utils.{StreamTableTestUtil, TableTestBase} -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test /** Tests for window join. */ class WindowJoinTest extends TableTestBase { @@ -148,9 +149,9 @@ class WindowJoinTest extends TableTestBase { |ON L.window_start = R.window_start AND L.window_end = R.window_end AND L.a = R.a """.stripMargin - thrown.expectMessage("Processing time Window Join is not supported yet.") - thrown.expect(classOf[TableException]) - util.verifyExplain(sql) + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("Processing time Window Join is not supported yet.") + .isInstanceOf[TableException] } @Test @@ -255,9 +256,9 @@ class WindowJoinTest extends TableTestBase { |ON L.window_start = R.window_start AND L.window_end = R.window_end AND L.a = R.a """.stripMargin - thrown.expectMessage("Processing time Window Join is not supported yet.") - thrown.expect(classOf[TableException]) - util.verifyExplain(sql) + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("Processing time Window Join is not supported yet.") + .isInstanceOf[TableException] } @Test @@ -362,9 +363,9 @@ class WindowJoinTest extends TableTestBase { |ON L.window_start = R.window_start AND L.window_end = R.window_end AND L.a = R.a """.stripMargin - thrown.expectMessage("Processing time Window Join is not supported yet.") - thrown.expect(classOf[TableException]) - util.verifyExplain(sql) + assertThatThrownBy(() => util.verifyExplain(sql)) + .hasMessageContaining("Processing time Window Join is not supported yet.") + .isInstanceOf[TableException] } // ---------------------------------------------------------------------------------------- diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/AggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/AggregateTest.scala index 6533785811525..ade4c8b1f031a 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/AggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/AggregateTest.scala @@ -23,7 +23,7 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.WeightedAvg import org.apache.flink.table.planner.utils.{CountMinMax, TableTestBase} -import org.junit.Test +import org.junit.jupiter.api.Test class AggregateTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/CalcTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/CalcTest.scala index 5719ede551062..554eeba0a38af 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/CalcTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/CalcTest.scala @@ -22,7 +22,7 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.expressions.utils.{Func1, Func23, Func24} import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test class CalcTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/ColumnFunctionsTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/ColumnFunctionsTest.scala index 5e636774ad74c..2d5ea2c8634da 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/ColumnFunctionsTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/ColumnFunctionsTest.scala @@ -23,7 +23,7 @@ import org.apache.flink.table.functions.ScalarFunction import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.{CountDistinct, WeightedAvg} import org.apache.flink.table.planner.utils.{CountAggFunction, TableFunc0, TableTestBase} -import org.junit.Test +import org.junit.jupiter.api.Test /** Tests for column functions which includes tests for different column functions. */ class ColumnFunctionsTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/CorrelateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/CorrelateTest.scala index 2198c47046c60..deedcf48680cd 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/CorrelateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/CorrelateTest.scala @@ -26,7 +26,7 @@ import org.apache.flink.table.planner.utils._ import org.apache.calcite.rel.rules.CoreRules import org.apache.calcite.tools.RuleSets -import org.junit.Test +import org.junit.jupiter.api.Test class CorrelateTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/GroupWindowTableAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/GroupWindowTableAggregateTest.scala index c0eb9543b11b6..a20ce81d37436 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/GroupWindowTableAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/GroupWindowTableAggregateTest.scala @@ -21,7 +21,7 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.{EmptyTableAggFunc, TableTestBase} -import org.junit.Test +import org.junit.jupiter.api.Test class GroupWindowTableAggregateTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/GroupWindowTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/GroupWindowTest.scala index 8a826ca9cef1c..49bfe1210e522 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/GroupWindowTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/GroupWindowTest.scala @@ -22,7 +22,7 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.{WeightedAvg, WeightedAvgWithMerge} import org.apache.flink.table.planner.utils.{EmptyTableAggFunc, TableTestBase} -import org.junit.Test +import org.junit.jupiter.api.Test import java.sql.Timestamp diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/JoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/JoinTest.scala index 33a446d2bf1d4..4296b0c8417ff 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/JoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/JoinTest.scala @@ -21,7 +21,7 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test import java.sql.Timestamp diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/LegacyTableSourceTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/LegacyTableSourceTest.scala index 9361ea46c79ce..e3742b206b9a7 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/LegacyTableSourceTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/LegacyTableSourceTest.scala @@ -24,7 +24,7 @@ import org.apache.flink.table.api.internal.TableEnvironmentInternal import org.apache.flink.table.planner.utils.{TableTestBase, TestLegacyProjectableTableSource, TestNestedProjectableTableSource, TestTableSourceWithTime} import org.apache.flink.types.Row -import org.junit.Test +import org.junit.jupiter.api.Test class LegacyTableSourceTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/OverAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/OverAggregateTest.scala index e0850e6764d63..8227a97cac264 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/OverAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/OverAggregateTest.scala @@ -23,7 +23,7 @@ import org.apache.flink.table.planner.expressions.utils.Func1 import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.WeightedAvgWithRetract import org.apache.flink.table.planner.utils.{StreamTableTestUtil, TableTestBase} -import org.junit.Test +import org.junit.jupiter.api.Test class OverAggregateTest extends TableTestBase { private val streamUtil: StreamTableTestUtil = streamTestUtil() diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonAggregateTest.scala index 348339d436519..14d30421fa032 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonAggregateTest.scala @@ -26,8 +26,8 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedAggFunctions. import org.apache.flink.table.planner.utils.TableTestBase import org.apache.flink.table.runtime.dataview.{ListViewSpec, MapViewSpec} -import org.junit.Assert.assertEquals -import org.junit.Test +import org.assertj.core.api.Assertions.{assertThat, assertThatExceptionOfType} +import org.junit.jupiter.api.Test class PythonAggregateTest extends TableTestBase { @@ -92,30 +92,34 @@ class PythonAggregateTest extends TableTestBase { false) ) - assertEquals(expected(0).getClass, specs(0).getClass) - assertEquals(expected(0).getDataType, specs(0).getDataType) - assertEquals(expected(0).getStateId, specs(0).getStateId) - assertEquals(expected(0).getFieldIndex, specs(0).getFieldIndex) - assertEquals(expected(1).getClass, specs(1).getClass) - assertEquals(expected(1).getDataType, specs(1).getDataType) - assertEquals(expected(1).getStateId, specs(1).getStateId) - assertEquals(expected(1).getFieldIndex, specs(1).getFieldIndex) + assertThat(specs(0)).hasSameClassAs(expected(0)) + assertThat(specs(0).getDataType).isEqualTo(expected(0).getDataType) + assertThat(specs(0).getStateId).isEqualTo(expected(0).getStateId) + assertThat(specs(0).getFieldIndex).isEqualTo(expected(0).getFieldIndex) + assertThat(specs(1)).hasSameClassAs(expected(1)) + assertThat(specs(1).getDataType).isEqualTo(expected(1).getDataType) + assertThat(specs(1).getStateId).isEqualTo(expected(1).getStateId) + assertThat(specs(1).getFieldIndex).isEqualTo(expected(1).getFieldIndex) } - @Test(expected = classOf[TableException]) + @Test def testExtractSecondLevelDataViewSpecs(): Unit = { val accType = DataTypes.ROW( DataTypes.FIELD( "f0", DataTypes.ROW(DataTypes.FIELD("f0", ListView.newListViewDataType(DataTypes.STRING()))))) - CommonPythonUtil.extractDataViewSpecs(0, accType) + + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => CommonPythonUtil.extractDataViewSpecs(0, accType)) } - @Test(expected = classOf[TableException]) + @Test def testExtractDataViewSpecsFromStructuredType(): Unit = { val accType = DataTypes.STRUCTURED( classOf[Tuple1[_]], DataTypes.FIELD("f0", ListView.newListViewDataType(DataTypes.STRING()))) - CommonPythonUtil.extractDataViewSpecs(0, accType) + + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => CommonPythonUtil.extractDataViewSpecs(0, accType)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonCalcTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonCalcTest.scala index c3dbfceb3e326..e5e076a534f1b 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonCalcTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonCalcTest.scala @@ -22,12 +22,12 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedScalarFunctions.PythonScalarFunction import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} class PythonCalcTest extends TableTestBase { private val util = streamTestUtil() - @Before + @BeforeEach def setup(): Unit = { util.addTableSource[(Int, Int, Int)]("MyTable", 'a, 'b, 'c) util.addFunction("pyFunc1", new PythonScalarFunction("pyFunc1")) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonGroupWindowAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonGroupWindowAggregateTest.scala index b590a4a801341..a254f337dc53b 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonGroupWindowAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonGroupWindowAggregateTest.scala @@ -23,7 +23,8 @@ import org.apache.flink.table.planner.plan.utils.WindowEmitStrategy.{TABLE_EXEC_ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedAggFunctions.{PandasAggregateFunction, TestPythonAggregateFunction} import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test import java.time.Duration @@ -89,7 +90,7 @@ class PythonGroupWindowAggregateTest extends TableTestBase { util.verifyExecPlan(resultTable) } - @Test(expected = classOf[TableException]) + @Test def testPandasEventTimeSessionGroupWindowOverTime(): Unit = { val util = streamTestUtil() val sourceTable = @@ -100,7 +101,9 @@ class PythonGroupWindowAggregateTest extends TableTestBase { .window(Session.withGap(7.millis).on('rowtime).as('w)) .groupBy('w, 'b) .select('b, 'w.start, 'w.end, func('a, 'c)) - util.verifyExecPlan(windowedTable) + + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(windowedTable)) } @Test @@ -177,7 +180,7 @@ class PythonGroupWindowAggregateTest extends TableTestBase { util.verifyExecPlan(windowedTable) } - @Test(expected = classOf[TableException]) + @Test def testEmitStrategyNotSupported(): Unit = { val util = streamTestUtil() val tableConf = util.getTableEnv.getConfig @@ -193,6 +196,7 @@ class PythonGroupWindowAggregateTest extends TableTestBase { .groupBy('w, 'b) .select('b, 'w.start, 'w.end, func('a, 'c)) - util.verifyExecPlan(resultTable) + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy(() => util.verifyExecPlan(resultTable)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonOverWindowAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonOverWindowAggregateTest.scala index a63faad289380..e297ecf25e465 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonOverWindowAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonOverWindowAggregateTest.scala @@ -22,7 +22,7 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedAggFunctions.PandasAggregateFunction import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test class PythonOverWindowAggregateTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonTableAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonTableAggregateTest.scala index e04970f9b2e0c..834cfa93e1009 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonTableAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/PythonTableAggregateTest.scala @@ -21,7 +21,7 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.{PythonEmptyTableAggFunc, TableTestBase} -import org.junit.Test +import org.junit.jupiter.api.Test class PythonTableAggregateTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/SetOperatorsTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/SetOperatorsTest.scala index 1e0521550d6f7..f5f8e2b81b417 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/SetOperatorsTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/SetOperatorsTest.scala @@ -21,7 +21,7 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.junit.jupiter.api.Test class SetOperatorsTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TableAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TableAggregateTest.scala index 42e173cfb593d..0d566db60d1c7 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TableAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TableAggregateTest.scala @@ -22,7 +22,7 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.expressions.utils.Func0 import org.apache.flink.table.planner.utils.{EmptyTableAggFunc, EmptyTableAggFuncWithIntResultType, TableTestBase} -import org.junit.Test +import org.junit.jupiter.api.Test class TableAggregateTest extends TableTestBase { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TableSourceTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TableSourceTest.scala index 50cc948344d19..91691fc90f3b9 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TableSourceTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TableSourceTest.scala @@ -17,11 +17,11 @@ */ package org.apache.flink.table.planner.plan.stream.table -import org.apache.flink.core.testutils.FlinkMatchers.containsMessage import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test class TableSourceTest extends TableTestBase { @@ -170,10 +170,6 @@ class TableSourceTest extends TableTestBase { @Test def testProctimeOnWatermarkSpec(): Unit = { - thrown.expect(classOf[ValidationException]) - thrown.expect( - containsMessage("A watermark can not be defined for a processing-time attribute.")) - val ddl = s""" |CREATE TABLE T ( @@ -188,10 +184,16 @@ class TableSourceTest extends TableTestBase { | 'bounded' = 'false' |) """.stripMargin - util.tableEnv.executeSql(ddl) - val t = util.tableEnv.from("T").select('ptime) - util.verifyExecPlan(t) + assertThatThrownBy( + () => { + util.tableEnv.executeSql(ddl) + + val t = util.tableEnv.from("T").select('ptime) + util.verifyExecPlan(t) + }) + .hasMessageContaining("A watermark can not be defined for a processing-time attribute.") + .isInstanceOf[ValidationException] } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TemporalTableFunctionJoinTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TemporalTableFunctionJoinTest.scala index 7fc8cf56d3215..af2be420d325b 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TemporalTableFunctionJoinTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TemporalTableFunctionJoinTest.scala @@ -19,15 +19,14 @@ package org.apache.flink.table.planner.plan.stream.table import org.apache.flink.api.scala._ import org.apache.flink.table.api._ -import org.apache.flink.table.expressions.{Expression, FieldReferenceExpression} +import org.apache.flink.table.expressions.FieldReferenceExpression import org.apache.flink.table.functions.{TemporalTableFunction, TemporalTableFunctionImpl} import org.apache.flink.table.planner.utils.{TableTestBase, TableTestUtil} import org.apache.flink.table.types.utils.TypeConversions.fromLegacyInfoToDataType import org.apache.flink.table.typeutils.TimeIndicatorTypeInfo.{PROCTIME_INDICATOR, ROWTIME_INDICATOR} -import org.hamcrest.Matchers.{equalTo, startsWith} -import org.junit.Assert.{assertEquals, assertThat} -import org.junit.Test +import org.assertj.core.api.Assertions.{assertThat, assertThatThrownBy} +import org.junit.jupiter.api.Test import java.sql.Timestamp @@ -143,16 +142,15 @@ class TemporalTableFunctionJoinTest extends TableTestBase { @Test def testUncorrelatedJoin(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage(startsWith("Unsupported argument")) - val result = orders .joinLateral( rates(java.sql.Timestamp.valueOf("2016-06-27 10:10:42.123")), 'o_currency === 'currency) .select($"o_amount" * $"rate") - util.verifyExecPlan(result) + assertThatThrownBy(() => util.verifyExecPlan(result)) + .hasMessageContaining("Unsupported argument") + .isInstanceOf[ValidationException] } @Test @@ -171,9 +169,8 @@ class TemporalTableFunctionJoinTest extends TableTestBase { inputRates: TemporalTableFunction, proctime: Boolean = false): Unit = { val rates = inputRates.asInstanceOf[TemporalTableFunctionImpl] - assertThat( - rates.getPrimaryKey, - equalTo[Expression](new FieldReferenceExpression("currency", DataTypes.STRING(), 0, 0))) + assertThat(rates.getPrimaryKey).isEqualTo( + new FieldReferenceExpression("currency", DataTypes.STRING(), 0, 0)) val (timeFieldName, timeFieldType) = if (proctime) { @@ -182,11 +179,9 @@ class TemporalTableFunctionJoinTest extends TableTestBase { ("rowtime", fromLegacyInfoToDataType(ROWTIME_INDICATOR)) } - assertThat( - rates.getTimeAttribute, - equalTo[Expression](new FieldReferenceExpression(timeFieldName, timeFieldType, 0, 2))) - - assertEquals(expectedSchema.toRowType, rates.getResultType) + assertThat(rates.getTimeAttribute).isEqualTo( + new FieldReferenceExpression(timeFieldName, timeFieldType, 0, 2)) + assertThat(rates.getResultType).isEqualTo(expectedSchema.toRowType) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TwoStageAggregateTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TwoStageAggregateTest.scala index f2538d18948f7..a732907df6c90 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TwoStageAggregateTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/TwoStageAggregateTest.scala @@ -23,14 +23,14 @@ import org.apache.flink.table.api._ import org.apache.flink.table.api.config.{ExecutionConfigOptions, OptimizerConfigOptions} import org.apache.flink.table.planner.utils.{AggregatePhaseStrategy, StreamTableTestUtil, TableTestBase} -import org.junit.{Before, Test} +import org.junit.jupiter.api.{BeforeEach, Test} import java.time.Duration class TwoStageAggregateTest extends TableTestBase { private var util: StreamTableTestUtil = _ - @Before + @BeforeEach def before(): Unit = { util = streamTestUtil() util.tableEnv.getConfig diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/AggregateValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/AggregateValidationTest.scala index 40b920f97d32a..cb8951f1a3d66 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/AggregateValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/AggregateValidationTest.scala @@ -22,55 +22,68 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.{TableFunc0, TableTestBase} import org.apache.flink.types.Row -import org.junit.Assert.{assertTrue, fail} -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.assertj.core.api.ThrowableAssert.ThrowingCallable +import org.junit.jupiter.api.Test class AggregateValidationTest extends TableTestBase { private val util = scalaStreamTestUtil() - @Test(expected = classOf[ValidationException]) + @Test def testGroupingOnNonExistentField(): Unit = { val table = util.addTableSource[(Long, Int, String)]('a, 'b, 'c) - val ds = table - // must fail. '_foo is not a valid field - .groupBy('_foo) - .select('a.avg) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + // must fail. '_foo is not a valid field + .groupBy('foo) + .select('a.avg)) } - @Test(expected = classOf[ValidationException]) + @Test def testGroupingInvalidSelection(): Unit = { val table = util.addTableSource[(Long, Int, String)]('a, 'b, 'c) - table - .groupBy('a, 'b) - // must fail. 'c is not a grouping key or aggregation - .select('c) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + .groupBy('a, 'b) + // must fail. 'c is not a grouping key or aggregation + .select('c)) } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidAggregationInSelection(): Unit = { val table = util.addTableSource[(Long, Int, String)]('a, 'b, 'c) - table - .groupBy('a) - .aggregate('b.sum.as('d)) - // must fail. Cannot use AggregateFunction in select right after aggregate - .select('d.sum) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + .groupBy('a) + .aggregate('b.sum.as('d)) + // must fail. Cannot use AggregateFunction in select right after aggregate + .select('d.sum)) } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidWindowPropertiesInSelection(): Unit = { val table = util.addTableSource[(Long, Int, String)]('a, 'b, 'c) - table - .groupBy('a) - .aggregate('b.sum.as('d)) - // must fail. Cannot use window properties in select right after aggregate - .select('d.start) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + .groupBy('a) + .aggregate('b.sum.as('d)) + // must fail. Cannot use window properties in select right after aggregate + .select('d.start)) } - @Test(expected = classOf[RuntimeException]) + @Test def testTableFunctionInSelection(): Unit = { val table = util.addTableSource[(Long, Int, String)]('a, 'b, 'c) @@ -81,30 +94,37 @@ class AggregateValidationTest extends TableTestBase { // must fail. Cannot use TableFunction in select after aggregate .select(call("func", "abc")) - util.verifyExecPlan(resultTable) + assertThatExceptionOfType(classOf[RuntimeException]) + .isThrownBy(() => util.verifyExecPlan(resultTable)) } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidScalarFunctionInAggregate(): Unit = { val table = util.addTableSource[(Long, Int, String)]('a, 'b, 'c) - table - .groupBy('a) - // must fail. Only AggregateFunction can be used in aggregate - .aggregate('c.upperCase.as('d)) - .select('a, 'd) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + .groupBy('a) + // must fail. Only AggregateFunction can be used in aggregate + .aggregate('c.upperCase.as('d)) + .select('a, 'd)) } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidTableFunctionInAggregate(): Unit = { val table = util.addTableSource[(Long, Int, String)]('a, 'b, 'c) util.addFunction("func", new TableFunc0) - table - .groupBy('a) - // must fail. Only AggregateFunction can be used in aggregate - .aggregate(call("func", $"c").as("d")) - .select('a, 'd) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + .groupBy('a) + // must fail. Only AggregateFunction can be used in aggregate + .aggregate(call("func", $"c").as("d")) + .select('a, 'd)) } @Test @@ -135,17 +155,15 @@ class AggregateValidationTest extends TableTestBase { sql: String, keywords: String, clazz: Class[_ <: Throwable] = classOf[ValidationException]): Unit = { - try { + val callable: ThrowingCallable = () => util.tableEnv.toAppendStream[Row](util.tableEnv.sqlQuery(sql)) - fail(s"Expected a $clazz, but no exception is thrown.") - } catch { - case e if e.getClass == clazz => - if (keywords != null) { - assertTrue( - s"The exception message '${e.getMessage}' doesn't contain keyword '$keywords'", - e.getMessage.contains(keywords)) - } - case e: Throwable => fail(s"Expected throw ${clazz.getSimpleName}, but is $e.") + if (keywords != null) { + assertThatExceptionOfType(clazz) + .isThrownBy(callable) + .withMessageContaining(keywords) + } else { + assertThatExceptionOfType(clazz) + .isThrownBy(callable) } } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/CalcValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/CalcValidationTest.scala index 702ce4a6b5221..661781dd1b88c 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/CalcValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/CalcValidationTest.scala @@ -22,142 +22,186 @@ import org.apache.flink.table.api._ import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.WeightedAvg import org.apache.flink.table.planner.utils.{TableFunc0, TableTestBase} -import org.junit.Test +import org.assertj.core.api.Assertions.{assertThat, assertThatExceptionOfType} +import org.junit.jupiter.api.Test import java.math.BigDecimal class CalcValidationTest extends TableTestBase { - @Test(expected = classOf[ValidationException]) + @Test def testInvalidUseOfRowtime(): Unit = { val util = streamTestUtil() - util - .addDataStream[(Long, Int, Double, Float, BigDecimal, String)]( - "MyTable", - 'rowtime, - 'int, - 'double, - 'float, - 'bigdec, - 'string) - .select('rowtime.rowtime) + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + util + .addDataStream[(Long, Int, Double, Float, BigDecimal, String)]( + "MyTable", + 'rowtime, + 'int, + 'double, + 'float, + 'bigdec, + 'string) + .select('rowtime.rowtime)) } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidUseOfRowtime2(): Unit = { val util = streamTestUtil() - util - .addDataStream[(Long, Int, Double, Float, BigDecimal, String)]( - "MyTable", - 'rowtime, - 'int, - 'double, - 'float, - 'bigdec, - 'string) - .window(Tumble.over(2.millis).on('rowtime).as('w)) - .groupBy('w) - .select('w.end.rowtime, 'int.count.as('int)) // no rowtime on non-window reference + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + util + .addDataStream[(Long, Int, Double, Float, BigDecimal, String)]( + "MyTable", + 'rowtime, + 'int, + 'double, + 'float, + 'bigdec, + 'string) + .window(Tumble.over(2.millis).on('rowtime).as('w)) + .groupBy('w) + // no rowtime on non-window reference + .select('w.end.rowtime, 'int.count.as('int))) } - @Test(expected = classOf[ValidationException]) + @Test def testAddColumnsWithAgg(): Unit = { val util = streamTestUtil() val tab = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - tab.addColumns('a.sum) + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => tab.addColumns('a.sum)) } - @Test(expected = classOf[ValidationException]) + @Test def testAddOrReplaceColumnsWithAgg(): Unit = { val util = streamTestUtil() val tab = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - tab.addOrReplaceColumns('a.sum) + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => tab.addOrReplaceColumns('a.sum)) } - @Test(expected = classOf[ValidationException]) + @Test def testRenameColumnsWithAgg(): Unit = { val util = streamTestUtil() val tab = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - tab.renameColumns('a.sum) + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => tab.renameColumns('a.sum)) } - @Test(expected = classOf[ValidationException]) + @Test def testRenameColumnsWithoutAlias(): Unit = { val util = streamTestUtil() val tab = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - tab.renameColumns('a) + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => tab.renameColumns('a)) } - @Test(expected = classOf[ValidationException]) + @Test def testRenameColumnsWithFunctallCall(): Unit = { val util = streamTestUtil() val tab = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - tab.renameColumns(('a + 1).as('a2)) + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => tab.renameColumns(('a + 1).as('a2))) } - @Test(expected = classOf[ValidationException]) + @Test def testRenameColumnsNotExist(): Unit = { val util = streamTestUtil() val tab = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - tab.renameColumns('e.as('e2)) + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => tab.renameColumns('e.as('e2))) } - @Test(expected = classOf[ValidationException]) + @Test def testDropColumnsWithAgg(): Unit = { val util = streamTestUtil() val tab = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - tab.dropColumns('a.sum) + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => tab.dropColumns('a.sum)) } - @Test(expected = classOf[ValidationException]) + @Test def testDropColumnsNotExist(): Unit = { val util = streamTestUtil() val tab = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - tab.dropColumns('e) + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => tab.dropColumns('e)) } - @Test(expected = classOf[ValidationException]) + @Test def testDropColumnsWithValueLiteral(): Unit = { val util = streamTestUtil() val tab = util.addTableSource[(Int, Long, String)]("Table3", 'a, 'b, 'c) - tab.dropColumns("a") + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => tab.dropColumns("a")) } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidMapFunctionTypeAggregation(): Unit = { val util = streamTestUtil() - util - .addTableSource[(Int)]("MyTable", 'int) - .map('int.sum) // do not support AggregateFunction as input + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + util + .addTableSource[(Int)]("MyTable", 'int) + .map('int.sum)) // do not support AggregateFunction as input } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidMapFunctionTypeUDAGG(): Unit = { val util = streamTestUtil() val weightedAvg = new WeightedAvg - util - .addTableSource[(Int)]("MyTable", 'int) - .map(weightedAvg('int, 'int)) // do not support AggregateFunction as input + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + util + .addTableSource[(Int)]("MyTable", 'int) + .map(weightedAvg('int))) // do not support AggregateFunction as input } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidMapFunctionTypeUDAGG2(): Unit = { val util = streamTestUtil() util.addFunction("weightedAvg", new WeightedAvg) - util - .addTableSource[(Int)]("MyTable", 'int) - .map(call("weightedAvg", $"int", $"int")) // do not support AggregateFunction as input + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + util + .addTableSource[(Int)]("MyTable", 'int) + .map(call("weightedAvg", $"int", $"int"))) // do not support AggregateFunction as input } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidMapFunctionTypeTableFunction(): Unit = { val util = streamTestUtil() util.addFunction("func", new TableFunc0) - util - .addTableSource[(String)]("MyTable", 'string) - .map(call("func", $"string").as("a")) // do not support TableFunction as input + + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + util + .addTableSource[(String)]("MyTable", 'string) + .map(call("func", $"string").as("a"))) // do not support TableFunction as input } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/CorrelateValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/CorrelateValidationTest.scala index 2d8beeef9c139..8c755f9b87b99 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/CorrelateValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/CorrelateValidationTest.scala @@ -23,8 +23,9 @@ import org.apache.flink.table.planner.expressions.utils._ import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.WeightedAvg import org.apache.flink.table.planner.utils.{ObjectTableFunction, TableFunc1, TableFunc2, TableTestBase} -import org.junit.Assert.{assertTrue, fail} -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.assertj.core.api.ThrowableAssert.ThrowingCallable +import org.junit.jupiter.api.Test class CorrelateValidationTest extends TableTestBase { @@ -84,65 +85,84 @@ class CorrelateValidationTest extends TableTestBase { * Due to the improper translation of TableFunction left outer join (see CALCITE-2004), the join * predicate can only be empty or literal true (the restriction should be removed in FLINK-7865). */ - @Test(expected = classOf[ValidationException]) + @Test def testLeftOuterJoinWithPredicates(): Unit = { val util = streamTestUtil() val table = util.addTableSource[(Int, Long, String)]("MyTable", 'a, 'b, 'c) val function = new TableFunc1 util.addFunction("func1", function) - val result = table - .leftOuterJoinLateral(function('c).as('s), 'c === 's) - .select('c, 's) - .where('a > 10) - - util.verifyExecPlan(result) + expectExceptionThrown( + { + val result = table + .leftOuterJoinLateral(function('c).as('s), 'c === 's) + .select('c, 's) + .where('a > 10) + util.verifyExecPlan(result) + }, + null) } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidMapFunctionTypeAggregation(): Unit = { val util = streamTestUtil() - util - .addTableSource[(Int)]("MyTable", 'int) - .flatMap('int.sum) // do not support AggregateFunction as input + expectExceptionThrown( + util + .addTableSource[(Int)]("MyTable", 'int) + // do not support AggregateFunction as input + .flatMap('int.sum), + null) } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidMapFunctionTypeUDAGG(): Unit = { val util = streamTestUtil() - val weightedAvg = new WeightedAvg - util - .addTableSource[(Int)]("MyTable", 'int) - .flatMap(weightedAvg('int, 'int)) // do not support AggregateFunction as input + + expectExceptionThrown( + util + .addTableSource[(Int)]("MyTable", 'int) + // do not support AggregateFunction as input + .flatMap(weightedAvg('int, 'int)), + null) } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidMapFunctionTypeUDAGG2(): Unit = { val util = streamTestUtil() util.addFunction("weightedAvg", new WeightedAvg) - util - .addTableSource[(Int)]("MyTable", 'int) - .flatMap(call("weightedAvg", $"int", $"int")) // do not support AggregateFunction as input + + expectExceptionThrown( + util + .addTableSource[(Int)]("MyTable", 'int) + // do not support AggregateFunction as input + .flatMap(call("weightedAvg", $"int", $"int")), + null) } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidMapFunctionTypeScalarFunction(): Unit = { val util = streamTestUtil() - util - .addTableSource[(String)]("MyTable", 'string) - .flatMap(Func15('string)) // do not support ScalarFunction as input + expectExceptionThrown( + util + .addTableSource[(String)]("MyTable", 'string) + // do not support ScalarFunction as input + .flatMap(Func15('string)), + null) } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidFlatMapFunctionTypeFieldReference(): Unit = { val util = batchTestUtil() - util - .addTableSource[(String)]("MyTable", 'string) - .flatMap('string) // Only TableFunction can be used in flatMap + expectExceptionThrown( + util + .addTableSource[(String)]("MyTable", 'string) + // Only TableFunction can be used in flatMap + .flatMap('string), + null) } // ---------------------------------------------------------------------------------------------- @@ -151,17 +171,14 @@ class CorrelateValidationTest extends TableTestBase { function: => Unit, keywords: String, clazz: Class[_ <: Throwable] = classOf[ValidationException]): Unit = { - try { - function - fail(s"Expected a $clazz, but no exception is thrown.") - } catch { - case e if e.getClass == clazz => - if (keywords != null) { - assertTrue( - s"The exception message '${e.getMessage}' doesn't contain keyword '$keywords'", - e.getMessage.contains(keywords)) - } - case e: Throwable => fail(s"Expected throw ${clazz.getSimpleName}, but is $e.") + val callable: ThrowingCallable = () => function + if (keywords != null) { + assertThatExceptionOfType(clazz) + .isThrownBy(callable) + .withMessageContaining(keywords) + } else { + assertThatExceptionOfType(clazz) + .isThrownBy(callable) } } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/GroupWindowTableAggregateValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/GroupWindowTableAggregateValidationTest.scala index 9a71c6d9ad9c0..4275b411ec948 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/GroupWindowTableAggregateValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/GroupWindowTableAggregateValidationTest.scala @@ -23,7 +23,8 @@ import org.apache.flink.table.planner.plan.utils.WindowEmitStrategy.{TABLE_EXEC_ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedAggFunctions.WeightedAvgWithMerge import org.apache.flink.table.planner.utils.{TableTestBase, Top3} -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test import java.time.Duration @@ -37,36 +38,35 @@ class GroupWindowTableAggregateValidationTest extends TableTestBase { @Test def testTumbleUdAggWithInvalidArgs(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("Invalid function call:\nTop3(BIGINT)") - - table - .window(Slide.over(2.hours).every(30.minutes).on('rowtime).as('w)) - .groupBy('string, 'w) - .flatAggregate(call(top3, 'long)) // invalid args - .select('string, 'f0) + assertThatThrownBy( + () => + table + .window(Slide.over(2.hours).every(30.minutes).on('rowtime).as('w)) + .groupBy('string, 'w) + .flatAggregate(call(top3, 'long)) // invalid args + .select('string, 'f0)) + .hasMessageContaining("Invalid function call:\nTop3(BIGINT)") + .isInstanceOf[ValidationException] } @Test def testInvalidStarInSelection(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("Can not use * for window aggregate!") - val util = streamTestUtil() val table = util.addTableSource[(Long, Int, String)]('long, 'int, 'string, 'proctime.proctime) - table - .window(Tumble.over(2.rows).on('proctime).as('w)) - .groupBy('string, 'w) - .flatAggregate(top3('int)) - .select('*) + assertThatThrownBy( + () => + table + .window(Tumble.over(2.rows).on('proctime).as('w)) + .groupBy('string, 'w) + .flatAggregate(top3('int)) + .select('*)) + .hasMessageContaining("Can not use * for window aggregate!") + .isInstanceOf[ValidationException] } @Test def testEmitStrategyNotSupported(): Unit = { - expectedException.expect(classOf[TableException]) - expectedException.expectMessage("Emit strategy has not been supported for Table Aggregate!") - val util = streamTestUtil() val table = util.addTableSource[(Long, Int, String)]('long, 'int, 'string, 'proctime.proctime) @@ -80,6 +80,8 @@ class GroupWindowTableAggregateValidationTest extends TableTestBase { .flatAggregate(top3('int)) .select('string, 'f0, 'w.start) - util.verifyExecPlan(result) + assertThatThrownBy(() => util.verifyExecPlan(result)) + .hasMessageContaining("Emit strategy has not been supported for Table Aggregate!") + .isInstanceOf[TableException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/GroupWindowValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/GroupWindowValidationTest.scala index 929fb5832399b..d163b7bca7103 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/GroupWindowValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/GroupWindowValidationTest.scala @@ -20,275 +20,294 @@ package org.apache.flink.table.planner.plan.stream.table.validation import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.api.{Session, Slide, Tumble, ValidationException} -import org.apache.flink.table.api.bridge.scala._ import org.apache.flink.table.planner.plan.utils.JavaUserDefinedAggFunctions.WeightedAvgWithMerge import org.apache.flink.table.planner.utils.TableTestBase -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test class GroupWindowValidationTest extends TableTestBase { @Test def testInvalidWindowProperty(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("Window properties can only be used on windowed tables.") - val util = streamTestUtil() val table = util.addTableSource[(Long, Int, String)]('long, 'int, 'string) - table - .groupBy('string) - .select('string, 'string.start) // property in non windowed table + assertThatThrownBy( + () => + table + .groupBy('string) + // property in non windowed table + .select('string, 'string.start)) + .hasMessageContaining("Window properties can only be used on windowed tables.") + .isInstanceOf[ValidationException] } @Test def testGroupByWithoutWindowAlias(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("GroupBy must contain exactly one window alias.") - val util = streamTestUtil() val table = util.addDataStream[(Long, Int, String)]("T1", 'rowtime, 'int, 'string) - table - .window(Tumble.over(5.milli).on('long).as('w)) - .groupBy('string) - .select('string, 'int.count) + assertThatThrownBy( + () => + table + .window(Tumble.over(5.milli).on('long).as('w)) + .groupBy('string) + .select('string, 'int.count)) + .hasMessageContaining("GroupBy must contain exactly one window alias.") + .isInstanceOf[ValidationException] } @Test def testInvalidRowTimeRef(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("Cannot resolve field [int]") - val util = streamTestUtil() val table = util.addDataStream[(Long, Int, String)]("T1", 'rowtime.rowtime, 'int, 'string) - table - .window(Tumble.over(5.milli).on('rowtime).as('w)) - .groupBy('w, 'string) - .select('string, 'int.count) - .window(Slide.over(5.milli).every(1.milli).on('int).as('w2)) // 'Int does not exist in input. - .groupBy('w2) - .select('string) + assertThatThrownBy( + () => + table + .window(Tumble.over(5.milli).on('rowtime).as('w)) + .groupBy('w, 'string) + .select('string, 'int.count) + .window( + Slide.over(5.milli).every(1.milli).on('int).as('w2) + ) // 'Int does not exist in input. + .groupBy('w2) + .select('string)) + .hasMessageContaining("Cannot resolve field [int]") + .isInstanceOf[ValidationException] } @Test def testInvalidTumblingSize(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("A tumble window expects a size value literal") - val util = streamTestUtil() val table = util.addDataStream[(Long, Int, String)]("T1", 'rowtime.rowtime, 'int, 'string) - table - .window(Tumble.over($"WRONG").on($"rowtime").as("w")) // string is not a valid interval - .groupBy('w, 'string) - .select('string, 'int.count) + assertThatThrownBy( + () => + table + .window(Tumble.over($"WRONG").on($"rowtime").as("w")) // string is not a valid interval + .groupBy('w, 'string) + .select('string, 'int.count)) + .hasMessageContaining("A tumble window expects a size value literal") + .isInstanceOf[ValidationException] } @Test def testInvalidTumblingSizeType(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Tumbling window expects a size literal of a day-time interval or BIGINT type.") - val util = streamTestUtil() val table = util.addDataStream[(Long, Int, String)]("T1", 'rowtime.rowtime, 'int, 'string) - table - // row interval is not valid for session windows - .window(Tumble.over(10).on('rowtime).as('w)) - .groupBy('w, 'string) - .select('string, 'int.count) + assertThatThrownBy( + () => + table + // row interval is not valid for session windows + .window(Tumble.over(10).on('rowtime).as('w)) + .groupBy('w, 'string) + .select('string, 'int.count)) + .hasMessageContaining( + "Tumbling window expects a size literal of a day-time interval or BIGINT type.") + .isInstanceOf[ValidationException] } @Test def testTumbleUdAggWithInvalidArgs(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("Invalid function call:\nmyWeightedAvg(STRING, INT)") - val util = streamTestUtil() val weightedAvg = new WeightedAvgWithMerge val table = util.addDataStream[(Long, Int, String)]("T1", 'rowtime.rowtime, 'int, 'string) - table - .window(Tumble.over(2.hours).on('rowtime).as('w)) - .groupBy('w, 'string) - .select('string, call(weightedAvg, 'string, 'int)) // invalid UDAGG args + assertThatThrownBy( + () => + table + .window(Tumble.over(2.hours).on('rowtime).as('w)) + .groupBy('w, 'string) + // invalid UDAGG args + .select('string, call(weightedAvg, 'string, 'int))) + .hasMessageContaining("Invalid function call:\nmyWeightedAvg(STRING, INT)") + .isInstanceOf[ValidationException] } @Test def testInvalidSlidingSize(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("A sliding window expects a size value literal") - val util = streamTestUtil() val table = util.addDataStream[(Long, Int, String)]("T1", 'rowtime.rowtime, 'int, 'string) - table - // field reference is not a valid interval - .window(Slide.over($"WRONG").every($"WRONG").on($"rowtime").as("w")) - .groupBy('w, 'string) - .select('string, 'int.count) + assertThatThrownBy( + () => + table + // field reference is not a valid interval + .window(Slide.over($"WRONG").every($"WRONG").on($"rowtime").as("w")) + .groupBy('w, 'string) + .select('string, 'int.count)) + .hasMessageContaining("A sliding window expects a size value literal") + .isInstanceOf[ValidationException] } @Test def testInvalidSlidingSlide(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("A sliding window expects the same type of size and slide.") - val util = streamTestUtil() val table = util.addDataStream[(Long, Int, String)]("T1", 'rowtime.rowtime, 'int, 'string) - table - // row and time intervals may not be mixed - .window(Slide.over(12.rows).every(1.minute).on('rowtime).as('w)) - .groupBy('w, 'string) - .select('string, 'int.count) + assertThatThrownBy( + () => + table + // row and time intervals may not be mixed + .window(Slide.over(12.rows).every(1.minute).on('rowtime).as('w)) + .groupBy('w, 'string) + .select('string, 'int.count)) + .hasMessageContaining("A sliding window expects the same type of size and slide.") + .isInstanceOf[ValidationException] } @Test def testInvalidSlidingSizeType(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "A sliding window expects a size literal of a day-time interval or BIGINT type.") - val util = streamTestUtil() val table = util.addDataStream[(Long, Int, String)]("T1", 'rowtime.rowtime, 'int, 'string) - table - // row interval is not valid for session windows - .window(Slide.over(10).every(10.milli).on('rowtime).as('w)) - .groupBy('w, 'string) - .select('string, 'int.count) + assertThatThrownBy( + () => + table + // row interval is not valid for session windows + .window(Slide.over(10).every(10.milli).on('rowtime).as('w)) + .groupBy('w, 'string) + .select('string, 'int.count)) + .hasMessageContaining( + "A sliding window expects a size literal of a day-time interval or BIGINT type.") + .isInstanceOf[ValidationException] } @Test def testSlideUdAggWithInvalidArgs(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("Invalid function call:\nmyWeightedAvg(STRING, INT)") - val util = streamTestUtil() val weightedAvg = new WeightedAvgWithMerge val table = util.addDataStream[(Long, Int, String)]("T1", 'rowtime.rowtime, 'int, 'string) - table - .window(Slide.over(2.hours).every(30.minutes).on('rowtime).as('w)) - .groupBy('w, 'string) - .select('string, call(weightedAvg, 'string, 'int)) // invalid UDAGG args + assertThatThrownBy( + () => + table + .window(Slide.over(2.hours).every(30.minutes).on('rowtime).as('w)) + .groupBy('w, 'string) + // invalid UDAGG args + .select('string, call(weightedAvg, 'string, 'int))) + .hasMessageContaining("Invalid function call:\nmyWeightedAvg(STRING, INT)") + .isInstanceOf[ValidationException] } @Test def testInvalidSessionGap(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "A session window expects a gap literal of a day-time interval type.") - val util = streamTestUtil() val table = util.addDataStream[(Long, Int, String)]("T1", 'rowtime.rowtime, 'int, 'string) - table - // row interval is not valid for session windows - .window(Session.withGap(10.rows).on('rowtime).as('w)) - .groupBy('w, 'string) - .select('string, 'int.count) + assertThatThrownBy( + () => + table + // row interval is not valid for session windows + .window(Session.withGap(10.rows).on('rowtime).as('w)) + .groupBy('w, 'string) + .select('string, 'int.count)) + .hasMessageContaining("A session window expects a gap literal of a day-time interval type.") + .isInstanceOf[ValidationException] } @Test def testInvalidSessionGapType(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "A session window expects a gap literal of a day-time interval type.") - val util = streamTestUtil() val table = util.addDataStream[(Long, Int, String)]("T1", 'rowtime.rowtime, 'int, 'string) - table - // row interval is not valid for session windows - .window(Session.withGap(10).on('rowtime).as('w)) - .groupBy('w, 'string) - .select('string, 'int.count) + assertThatThrownBy( + () => + table + // row interval is not valid for session windows + .window(Session.withGap(10).on('rowtime).as('w)) + .groupBy('w, 'string) + .select('string, 'int.count)) + .hasMessageContaining("A session window expects a gap literal of a day-time interval type.") + .isInstanceOf[ValidationException] } @Test def testInvalidWindowAlias1(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Only unresolved reference supported for alias of a " + - "group window.") - val util = streamTestUtil() val table = util.addDataStream[(Long, Int, String)]("T1", 'rowtime, 'int, 'string) - table - // expression instead of a symbol - .window(Session.withGap(100.milli).on('long).as(concat("A", "B"))) - .groupBy(concat("A", "B")) - .select('string, 'int.count) + assertThatThrownBy( + () => + table + // expression instead of a symbol + .window(Session.withGap(100.milli).on('long).as(concat("A", "B"))) + .groupBy(concat("A", "B")) + .select('string, 'int.count)) + .hasMessageContaining("Only unresolved reference supported for alias of a group window.") + .isInstanceOf[ValidationException] } @Test def testInvalidWindowAlias2(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("Cannot resolve field [string]") - val util = streamTestUtil() val table = util.addDataStream[(Long, Int, String)]("T1", 'rowtime.rowtime, 'int, 'string) - table - // field name "string" is already present - .window(Session.withGap(100.milli).on('rowtime).as('string)) - .groupBy('string) - .select('string, 'int.count) + assertThatThrownBy( + () => + table + // field name "string" is already present + .window(Session.withGap(100.milli).on('rowtime).as('string)) + .groupBy('string) + .select('string, 'int.count)) + .hasMessageContaining("Cannot resolve field [string]") + .isInstanceOf[ValidationException] } @Test def testSessionUdAggWithInvalidArgs(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("Invalid function call:\nmyWeightedAvg(STRING, INT)") - val util = streamTestUtil() val weightedAvg = new WeightedAvgWithMerge val table = util.addDataStream[(Long, Int, String)]("T1", 'long, 'int, 'string, 'rowtime.rowtime) - table - .window(Session.withGap(2.hours).on('rowtime).as('w)) - .groupBy('w, 'string) - .select('string, call(weightedAvg, 'string, 'int)) // invalid UDAGG args + assertThatThrownBy( + () => + table + .window(Session.withGap(2.hours).on('rowtime).as('w)) + .groupBy('w, 'string) + // invalid UDAGG args + .select('string, call(weightedAvg, 'string, 'int))) + .hasMessageContaining("Invalid function call:\nmyWeightedAvg(STRING, INT)") + .isInstanceOf[ValidationException] } @Test def testInvalidWindowPropertyOnRowCountsTumblingWindow(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Window start and Window end cannot be selected " + - "for a row-count tumble window.") - val util = streamTestUtil() val table = util.addDataStream[(Long, Int, String)]("T1", 'long, 'int, 'string, 'proctime.proctime) - table - .window(Tumble.over(2.rows).on('proctime).as('w)) - .groupBy('w, 'string) - .select('string, 'w.start, 'w.end) // invalid start/end on rows-count window + assertThatThrownBy( + () => + table + .window(Tumble.over(2.rows).on('proctime).as('w)) + .groupBy('w, 'string) + // invalid start/end on rows-count window + .select('string, 'w.start, 'w.end)) + .hasMessageContaining("Window start and Window end cannot be selected " + + "for a row-count tumble window.") + .isInstanceOf[ValidationException] } @Test def testInvalidWindowPropertyOnRowCountsSlidingWindow(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Window start and Window end cannot be selected for a " + - "row-count slide window.") - val util = streamTestUtil() val table = util.addDataStream[(Long, Int, String)]("T1", 'long, 'int, 'string, 'proctime.proctime) - table - .window(Slide.over(10.rows).every(5.rows).on('proctime).as('w)) - .groupBy('w, 'string) - .select('string, 'w.start, 'w.end) // invalid start/end on rows-count window + assertThatThrownBy( + () => + table + .window(Slide.over(10.rows).every(5.rows).on('proctime).as('w)) + .groupBy('w, 'string) + // invalid start/end on rows-count window + .select('string, 'w.start, 'w.end)) + .hasMessageContaining("Window start and Window end cannot be selected for a " + + "row-count slide window.") + .isInstanceOf[ValidationException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/LegacyTableSinkValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/LegacyTableSinkValidationTest.scala index f92823021f0df..30f0edab0af3e 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/LegacyTableSinkValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/LegacyTableSinkValidationTest.scala @@ -26,27 +26,31 @@ import org.apache.flink.table.planner.runtime.utils.{TestData, TestingAppendSink import org.apache.flink.table.planner.utils.{MemoryTableSourceSinkUtil, TableTestBase, TableTestUtil} import org.apache.flink.types.Row -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class LegacyTableSinkValidationTest extends TableTestBase { - @Test(expected = classOf[ValidationException]) + @Test def testAppendSinkOnUpdatingTable(): Unit = { val env = StreamExecutionEnvironment.getExecutionEnvironment val tEnv = StreamTableEnvironment.create(env, TableTestUtil.STREAM_SETTING) val t = env.fromCollection(TestData.smallTupleData3).toTable(tEnv, 'a, 'b, 'c) - t.groupBy('text) - .select('text, 'id.count, 'num.sum) - .toAppendStream[Row] - .addSink(new TestingAppendSink) - // must fail because table is not append-only - env.execute() + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => { + t.groupBy('text) + .select('text, 'id.count, 'num.sum) + .toAppendStream[Row] + .addSink(new TestingAppendSink) + env.execute() + }) } - @Test(expected = classOf[TableException]) + @Test def testUpsertSinkOnUpdatingTableWithoutFullKey(): Unit = { val env = StreamExecutionEnvironment.getExecutionEnvironment val tEnv = StreamTableEnvironment.create(env, TableTestUtil.STREAM_SETTING) @@ -64,12 +68,17 @@ class LegacyTableSinkValidationTest extends TableTestBase { .select('len, 'id.count, 'num.sum) val schema = result.getSchema sink.configure(schema.getFieldNames, schema.getFieldTypes) - tEnv.asInstanceOf[TableEnvironmentInternal].registerTableSinkInternal("testSink", sink) + // must fail because table is updating table without full key - result.executeInsert("testSink") + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy( + () => { + tEnv.asInstanceOf[TableEnvironmentInternal].registerTableSinkInternal("testSink", sink) + result.executeInsert("testSink") + }) } - @Test(expected = classOf[TableException]) + @Test def testAppendSinkOnLeftJoin(): Unit = { val env = StreamExecutionEnvironment.getExecutionEnvironment val tEnv = StreamTableEnvironment.create(env, TableTestUtil.STREAM_SETTING) @@ -77,26 +86,21 @@ class LegacyTableSinkValidationTest extends TableTestBase { val ds1 = env.fromCollection(TestData.tupleData3).toTable(tEnv, 'a, 'b, 'c) val ds2 = env.fromCollection(TestData.tupleData5).toTable(tEnv, 'd, 'e, 'f, 'g, 'h) - ds1 - .leftOuterJoin(ds2, 'a === 'd && 'b === 'h) - .select('c, 'g) - .toAppendStream[Row] - .addSink(new TestingAppendSink) - // must fail because table is not append-only - env.execute() + assertThatExceptionOfType(classOf[TableException]) + .isThrownBy( + () => { + ds1 + .leftOuterJoin(ds2, 'a === 'd && 'b === 'h) + .select('c, 'g) + .toAppendStream[Row] + .addSink(new TestingAppendSink) + env.execute() + }) } @Test def testValidateSink(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Column types of query result and sink for " + - "'default_catalog.default_database.testSink' do not match.\n" + - "Cause: Incompatible types for sink column 'd' at position 3.\n\n" + - "Query schema: [a: INT, b: BIGINT, c: STRING, d: BIGINT]\n" + - "Sink schema: [a: INT, b: BIGINT, c: STRING, d: INT]") - val env = StreamExecutionEnvironment.getExecutionEnvironment val tEnv = StreamTableEnvironment.create(env, TableTestUtil.STREAM_SETTING) @@ -114,7 +118,14 @@ class LegacyTableSinkValidationTest extends TableTestBase { MemoryTableSourceSinkUtil.createDataTypeOutputFormatTable(tEnv, sinkSchema, "testSink") // must fail because query result table schema is different with sink table schema - resultTable.executeInsert("testSink").await() + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => resultTable.executeInsert("testSink").await()) + .withMessageContaining( + "Column types of query result and sink for " + + "'default_catalog.default_database.testSink' do not match.\n" + + "Cause: Incompatible types for sink column 'd' at position 3.\n\n" + + "Query schema: [a: INT, b: BIGINT, c: STRING, d: BIGINT]\n" + + "Sink schema: [a: INT, b: BIGINT, c: STRING, d: INT]") } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/OverWindowValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/OverWindowValidationTest.scala index 48bfa60ce6494..26cc005fdc888 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/OverWindowValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/OverWindowValidationTest.scala @@ -26,7 +26,8 @@ import org.apache.flink.table.planner.runtime.utils.JavaUserDefinedAggFunctions. import org.apache.flink.table.planner.utils.{StreamTableTestUtil, TableTestBase, TableTestUtil} import org.apache.calcite.rel.RelNode -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.Test class OverWindowValidationTest extends TableTestBase { private val streamUtil: StreamTableTestUtil = streamTestUtil() @@ -34,113 +35,161 @@ class OverWindowValidationTest extends TableTestBase { .addDataStream[(Int, String, Long)]("MyTable", 'a, 'b, 'c, 'proctime.proctime, 'rowtime.rowtime) /** OVER clause is necessary for [[OverAgg0]] window function. */ - @Test(expected = classOf[ValidationException]) + @Test def testInvalidOverAggregation(): Unit = { val util = streamTestUtil() val table = util.addDataStream[(Long, Int, String)]("T1", 'a, 'b, 'c) val overAgg = new OverAgg0 - table.select(overAgg('a, 'b)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => table.select(overAgg('a, 'b))) } /** OVER clause is necessary for [[OverAgg0]] window function. */ - @Test(expected = classOf[ValidationException]) + @Test def testInvalidOverAggregation2(): Unit = { val util = streamTestUtil() - val table = util.addDataStream[(Long, Int, String)]("T1", 'long, 'int, 'string, 'proctime) - val overAgg = new OverAgg0 - table - .window(Tumble.over(2.rows).on('proctime).as('w)) - .groupBy('w, 'string) - .select(overAgg('long, 'int)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => { + val table = util.addDataStream[(Long, Int, String)]("T1", 'long, 'int, 'string, 'proctime) + val overAgg = new OverAgg0 + table + .window(Tumble.over(2.rows).on('proctime).as('w)) + .groupBy('w, 'string) + .select(overAgg('long, 'int)) + }) } - @Test(expected = classOf[ValidationException]) + @Test def testInvalidWindowAlias(): Unit = { - val result = table - .window(Over.partitionBy('c).orderBy('rowtime).preceding(2.rows).as('w)) - .select('c, 'b.count.over('x)) - optimize(TableTestUtil.toRelNode(result)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => { + val result = table + .window(Over.partitionBy('c).orderBy('rowtime).preceding(2.rows).as('w)) + .select('c, 'b.count.over('x)) + optimize(TableTestUtil.toRelNode(result)) + }) } - @Test(expected = classOf[ValidationException]) + @Test def testOrderBy(): Unit = { - val result = table - .window(Over.partitionBy('c).orderBy('abc).preceding(2.rows).as('w)) - .select('c, 'b.count.over('w)) - optimize(TableTestUtil.toRelNode(result)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => { + val result = table + .window(Over.partitionBy('c).orderBy('abc).preceding(2.rows).as('w)) + .select('c, 'b.count.over('w)) + optimize(TableTestUtil.toRelNode(result)) + }) } - @Test(expected = classOf[ValidationException]) + @Test def testPrecedingAndFollowingUsingIsLiteral(): Unit = { - val result = table - .window(Over.partitionBy($"c").orderBy($"rowtime").preceding(2).following($"xx").as($"w")) - .select('c, 'b.count.over('w)) - optimize(TableTestUtil.toRelNode(result)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => { + val result = table + .window( + Over.partitionBy($"c").orderBy($"rowtime").preceding(2).following($"xx").as($"w")) + .select('c, 'b.count.over('w)) + optimize(TableTestUtil.toRelNode(result)) + }) } - @Test(expected = classOf[ValidationException]) + @Test def testPrecedingAndFollowingUsingSameType(): Unit = { - val result = table - .window( - Over.partitionBy('c).orderBy('rowtime).preceding(2.rows).following(CURRENT_RANGE).as('w)) - .select('c, 'b.count.over('w)) - optimize(TableTestUtil.toRelNode(result)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => { + val result = table + .window( + Over + .partitionBy('c) + .orderBy('rowtime) + .preceding(2.rows) + .following(CURRENT_RANGE) + .as('w)) + .select('c, 'b.count.over('w)) + optimize(TableTestUtil.toRelNode(result)) + }) } - @Test(expected = classOf[ValidationException]) + @Test def testPartitionByWithUnresolved(): Unit = { - val result = table - .window(Over.partitionBy('a + 'b).orderBy('rowtime).preceding(2.rows).as('w)) - .select('c, 'b.count.over('w)) - optimize(TableTestUtil.toRelNode(result)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => { + val result = table + .window(Over.partitionBy('a + 'b).orderBy('rowtime).preceding(2.rows).as('w)) + .select('c, 'b.count.over('w)) + optimize(TableTestUtil.toRelNode(result)) + }) } - @Test(expected = classOf[ValidationException]) + @Test def testPartitionByWithNotKeyType(): Unit = { val table2 = streamUtil.addTableSource[(Int, String, Either[Long, String])]("MyTable2", 'a, 'b, 'c) - val result = table2 - .window(Over.partitionBy('c).orderBy('rowtime).preceding(2.rows).as('w)) - .select('c, 'b.count.over('w)) - optimize(TableTestUtil.toRelNode(result)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => { + val result = table2 + .window(Over.partitionBy('c).orderBy('rowtime).preceding(2.rows).as('w)) + .select('c, 'b.count.over('w)) + optimize(TableTestUtil.toRelNode(result)) + }) } - @Test(expected = classOf[ValidationException]) + @Test def testPrecedingValue(): Unit = { - val result = table - .window(Over.orderBy('rowtime).preceding(-1.rows).as('w)) - .select('c, 'b.count.over('w)) - optimize(TableTestUtil.toRelNode(result)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => { + val result = table + .window(Over.orderBy('rowtime).preceding(-1.rows).as('w)) + .select('c, 'b.count.over('w)) + optimize(TableTestUtil.toRelNode(result)) + }) } - @Test(expected = classOf[ValidationException]) + @Test def testFollowingValue(): Unit = { - val result = table - .window(Over.orderBy('rowtime).preceding(1.rows).following(-2.rows).as('w)) - .select('c, 'b.count.over('w)) - optimize(TableTestUtil.toRelNode(result)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => { + val result = table + .window(Over.orderBy('rowtime).preceding(1.rows).following(-2.rows).as('w)) + .select('c, 'b.count.over('w)) + optimize(TableTestUtil.toRelNode(result)) + }) } - @Test(expected = classOf[ValidationException]) + @Test def testUdAggWithInvalidArgs(): Unit = { val weightedAvg = new WeightedAvgWithRetract - val result = table - .window(Over.orderBy('rowtime).preceding(1.minutes).as('w)) - .select('c, weightedAvg('b, 'a).over('w)) - optimize(TableTestUtil.toRelNode(result)) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => { + val result = table + .window(Over.orderBy('rowtime).preceding(1.minutes).as('w)) + .select('c, weightedAvg('b, 'a).over('w)) + optimize(TableTestUtil.toRelNode(result)) + }) } @Test def testAccessesWindowProperties(): Unit = { - thrown.expect(classOf[ValidationException]) - thrown.expectMessage("Window start and end properties are not available for Over windows.") - - table - .window(Over.orderBy('rowtime).preceding(1.minutes).as('w)) - .select('c, 'a.count.over('w), 'w.start + 1, 'w.end) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => + table + .window(Over.orderBy('rowtime).preceding(1.minutes).as('w)) + .select('c, 'a.count.over('w), 'w.start + 1, 'w.end)) + .withMessageContaining("Window start and end properties are not available for Over windows.") } private def optimize(rel: RelNode): RelNode = { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/SetOperatorsValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/SetOperatorsValidationTest.scala index bc4ea4c34f92d..9588ac608b336 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/SetOperatorsValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/SetOperatorsValidationTest.scala @@ -25,12 +25,12 @@ import org.apache.flink.table.planner.runtime.utils.{TestData, TestingAppendSink import org.apache.flink.table.planner.utils.{TableTestBase, TableTestUtil} import org.apache.flink.types.Row -import org.junit.Assert.assertEquals -import org.junit.Test +import org.assertj.core.api.Assertions.{assertThat, assertThatExceptionOfType} +import org.junit.jupiter.api.Test class SetOperatorsValidationTest extends TableTestBase { - @Test(expected = classOf[ValidationException]) + @Test def testUnionFieldsNameNotOverlap1(): Unit = { val env = StreamExecutionEnvironment.getExecutionEnvironment val tEnv = StreamTableEnvironment.create(env, TableTestUtil.STREAM_SETTING) @@ -38,16 +38,19 @@ class SetOperatorsValidationTest extends TableTestBase { val ds1 = env.fromCollection(TestData.smallTupleData3).toTable(tEnv, 'a, 'b, 'c) val ds2 = env.fromCollection(TestData.tupleData5).toTable(tEnv, 'a, 'b, 'd, 'c, 'e) - val unionDs = ds1.unionAll(ds2) - val sink = new TestingAppendSink - unionDs.toAppendStream[Row].addSink(sink) - env.execute() - assertEquals(true, sink.getAppendResults.isEmpty) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => { + val unionDs = ds1.unionAll(ds2) + unionDs.toAppendStream[Row].addSink(sink) + env.execute() + }) + assertThat(sink.getAppendResults.isEmpty).isTrue } - @Test(expected = classOf[ValidationException]) + @Test def testUnionFieldsNameNotOverlap2(): Unit = { val env = StreamExecutionEnvironment.getExecutionEnvironment val tEnv = StreamTableEnvironment.create(env, TableTestUtil.STREAM_SETTING) @@ -58,15 +61,19 @@ class SetOperatorsValidationTest extends TableTestBase { .toTable(tEnv, 'a, 'b, 'c, 'd, 'e) .select('a, 'b, 'c) - val unionDs = ds1.unionAll(ds2) val sink = new TestingAppendSink - unionDs.toAppendStream[Row].addSink(sink) - env.execute() - assertEquals(true, sink.getAppendResults.isEmpty) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy( + () => { + val unionDs = ds1.unionAll(ds2) + unionDs.toAppendStream[Row].addSink(sink) + env.execute() + }) + assertThat(sink.getAppendResults.isEmpty).isTrue } - @Test(expected = classOf[ValidationException]) + @Test def testUnionTablesFromDifferentEnv(): Unit = { val env = StreamExecutionEnvironment.getExecutionEnvironment val tEnv1 = StreamTableEnvironment.create(env, TableTestUtil.STREAM_SETTING) @@ -76,6 +83,7 @@ class SetOperatorsValidationTest extends TableTestBase { val ds2 = env.fromCollection(TestData.smallTupleData3).toTable(tEnv2, 'a, 'b, 'c) // Must fail. Tables are bound to different TableEnvironments. - ds1.unionAll(ds2) + assertThatExceptionOfType(classOf[ValidationException]) + .isThrownBy(() => ds1.unionAll(ds2)) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/TableAggregateValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/TableAggregateValidationTest.scala index 7e67a62b5a4bb..a30aedefb1456 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/TableAggregateValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/TableAggregateValidationTest.scala @@ -21,7 +21,8 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.{EmptyTableAggFunc, TableTestBase} -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test import java.sql.Timestamp @@ -29,118 +30,130 @@ class TableAggregateValidationTest extends TableTestBase { @Test def testInvalidParameterNumber(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Invalid function call:\nEmptyTableAggFunc(BIGINT, INT, STRING)") - val util = streamTestUtil() val table = util.addTableSource[(Long, Int, String)]('a, 'b, 'c) val func = new EmptyTableAggFunc - table - .groupBy('c) - // must fail. func does not take 3 parameters - .flatAggregate(call(func, 'a, 'b, 'c)) - .select('_1, '_2, '_3) + + assertThatThrownBy( + () => + table + .groupBy('c) + // must fail. func does not take 3 parameters + .flatAggregate(call(func, 'a, 'b, 'c)) + .select('_1, '_2, '_3)) + .hasMessageContaining("Invalid function call:\nEmptyTableAggFunc(BIGINT, INT, STRING)") + .isInstanceOf[ValidationException] } @Test def testInvalidParameterType(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("Invalid function call:\nEmptyTableAggFunc(BIGINT, STRING)") - val util = streamTestUtil() val table = util.addTableSource[(Long, Int, String)]('a, 'b, 'c) val func = new EmptyTableAggFunc - table - .groupBy('c) - // must fail. func take 2 parameters of type Long and Timestamp or Long Int - .flatAggregate(call(func, 'a, 'c)) - .select('_1, '_2, '_3) + + assertThatThrownBy( + () => + table + .groupBy('c) + // must fail. func take 2 parameters of type Long and Int + .flatAggregate(call(func, 'a, 'c)) + .select('_1, '_2, '_3)) + .hasMessageContaining("Invalid function call:\nEmptyTableAggFunc(BIGINT, STRING)") + .isInstanceOf[ValidationException] } @Test def testInvalidWithWindowProperties(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("Window properties can only be used on windowed tables.") - val util = streamTestUtil() val table = util.addTableSource[(Long, Int, Timestamp)]('a, 'b, 'c) val func = new EmptyTableAggFunc - table - .groupBy('b) - .flatAggregate(call(func, 'a, 'b).as('x, 'y)) - .select('x.start, 'y) + + assertThatThrownBy( + () => + table + .groupBy('b) + .flatAggregate(call(func, 'a, 'b).as('x, 'y)) + .select('x.start, 'y)) + .hasMessageContaining("Window properties can only be used on windowed tables.") + .isInstanceOf[ValidationException] } @Test def testInvalidWithAggregation(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Aggregate functions are not supported in the " + - "select right after the aggregate or flatAggregate operation.") - val util = streamTestUtil() val table = util.addTableSource[(Long, Int, Timestamp)]('a, 'b, 'c) val func = new EmptyTableAggFunc - table - .groupBy('b) - .flatAggregate(call(func, 'a, 'b).as('x, 'y)) - .select('x.count) + + assertThatThrownBy( + () => + table + .groupBy('b) + .flatAggregate(call(func, 'a, 'b).as('x, 'y)) + .select('x.count)) + .hasMessageContaining("Aggregate functions are not supported in the " + + "select right after the aggregate or flatAggregate operation.") + .isInstanceOf[ValidationException] } @Test def testInvalidParameterWithAgg(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "It's not allowed to use an aggregate function as input of another aggregate function") - val util = streamTestUtil() val table = util.addTableSource[(Long, Int, Timestamp)]('a, 'b, 'c) val func = new EmptyTableAggFunc - table - .groupBy('b) - // must fail. func take agg function as input - .flatAggregate(func('a.sum, 'c)) - .select('_1, '_2, '_3) + + assertThatThrownBy( + () => + table + .groupBy('b) + // must fail. func take agg function as input + .flatAggregate(func('a.sum, 'c)) + .select('_1, '_2, '_3)) + .hasMessageContaining( + "It's not allowed to use an aggregate function as input of another aggregate function") + .isInstanceOf[ValidationException] } @Test def testInvalidAliasWithWrongNumber(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "List of column aliases must have same degree as " + - "table; the returned table of function 'EmptyTableAggFunc' has 2 columns, " + - "whereas alias list has 3 columns") - val util = streamTestUtil() val table = util.addTableSource[(Long, Int, Timestamp)]('a, 'b, 'c) val func = new EmptyTableAggFunc - table - .groupBy('b) - // must fail. alias with wrong number of fields - .flatAggregate(call(func, 'a, 'b).as('a, 'b, 'c)) - .select('*) + + assertThatThrownBy( + () => + table + .groupBy('b) + // must fail. alias with wrong number of fields + .flatAggregate(call(func, 'a, 'b).as('a, 'b, 'c)) + .select('*)) + .hasMessageContaining( + "List of column aliases must have same degree as " + + "table; the returned table of function 'EmptyTableAggFunc' has 2 columns, " + + "whereas alias list has 3 columns") + .isInstanceOf[ValidationException] } @Test def testAliasWithNameConflict(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("Ambiguous column name: b") - val util = streamTestUtil() val table = util.addTableSource[(Long, Int, Timestamp)]('a, 'b, 'c) val func = new EmptyTableAggFunc - table - .groupBy('b) - // must fail. alias with name conflict - .flatAggregate(call(func, 'a, 'b).as('a, 'b)) - .select('*) + + assertThatThrownBy( + () => + table + .groupBy('b) + // must fail. alias with name conflict + .flatAggregate(call(func, 'a, 'b).as('a, 'b)) + .select('*)) + .hasMessageContaining("Ambiguous column name: b") + .isInstanceOf[ValidationException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/TemporalTableJoinValidationTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/TemporalTableJoinValidationTest.scala index 367b6fe42acf6..407d4914ad368 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/TemporalTableJoinValidationTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/stream/table/validation/TemporalTableJoinValidationTest.scala @@ -21,7 +21,8 @@ import org.apache.flink.api.scala._ import org.apache.flink.table.api._ import org.apache.flink.table.planner.utils.{TableTestBase, TableTestUtil} -import org.junit.Test +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test import java.sql.Timestamp @@ -52,26 +53,20 @@ class TemporalTableJoinValidationTest extends TableTestBase { @Test def testInvalidFieldReference(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("Cannot resolve field [foobar]") - - ratesHistory.createTemporalTableFunction('rowtime, 'foobar) + assertThatThrownBy(() => ratesHistory.createTemporalTableFunction('rowtime, 'foobar)) + .hasMessageContaining("Cannot resolve field [foobar]") + .isInstanceOf[ValidationException] } @Test def testInvalidStringFieldReference(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage("Cannot resolve field [foobar]") - - ratesHistory.createTemporalTableFunction($"rowtime", $"foobar") + assertThatThrownBy(() => ratesHistory.createTemporalTableFunction($"rowtime", $"foobar")) + .hasMessageContaining("Cannot resolve field [foobar]") + .isInstanceOf[ValidationException] } @Test def testNonTimeIndicatorOnRightSide(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Non rowtime timeAttribute [TIMESTAMP(3)] used to create TemporalTableFunction") - val rates = ratesHistoryWithoutTimeAttribute.createTemporalTableFunction('rowtime, 'currency) val result = orders @@ -79,15 +74,14 @@ class TemporalTableJoinValidationTest extends TableTestBase { .select($"o_amount" * $"rate") .as("rate") - util.verifyExplain(result) + assertThatThrownBy(() => util.verifyExplain(result)) + .hasMessageContaining( + "Non rowtime timeAttribute [TIMESTAMP(3)] used to create TemporalTableFunction") + .isInstanceOf[ValidationException] } @Test def testNonTimeIndicatorOnLeftSide(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Non rowtime timeAttribute [TIMESTAMP(3)] passed as the argument to TemporalTableFunction") - val rates = ratesHistory.createTemporalTableFunction('rowtime, 'currency) val result = ordersWithoutTimeAttribute @@ -95,16 +89,14 @@ class TemporalTableJoinValidationTest extends TableTestBase { .select($"o_amount" * $"rate") .as("rate") - util.verifyExplain(result) + assertThatThrownBy(() => util.verifyExplain(result)) + .hasMessageContaining( + "Non rowtime timeAttribute [TIMESTAMP(3)] passed as the argument to TemporalTableFunction") + .isInstanceOf[ValidationException] } @Test def testMixedTimeIndicators(): Unit = { - expectedException.expect(classOf[ValidationException]) - expectedException.expectMessage( - "Non rowtime timeAttribute [TIMESTAMP_LTZ(3) *PROCTIME*] passed as the argument " + - "to TemporalTableFunction") - val rates = ratesHistory.createTemporalTableFunction('rowtime, 'currency) val result = ordersProctime @@ -112,6 +104,10 @@ class TemporalTableJoinValidationTest extends TableTestBase { .select($"o_amount" * $"rate") .as("rate") - util.verifyExplain(result) + assertThatThrownBy(() => util.verifyExplain(result)) + .hasMessageContaining( + "Non rowtime timeAttribute [TIMESTAMP_LTZ(3) *PROCTIME*] passed as the argument " + + "to TemporalTableFunction") + .isInstanceOf[ValidationException] } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/NestedProjectionUtilTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/NestedProjectionUtilTest.scala index ec8214f5b91bd..9cd44c9fea71b 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/NestedProjectionUtilTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/NestedProjectionUtilTest.scala @@ -87,12 +87,13 @@ class NestedProjectionUtilTest extends RexNodeTestBase { "$2", "$3", "$4", + "$5", "*($t2, $t3)", "100", - "<($t5, $t6)", + "<($t6, $t7)", "6", - ">($t1, $t8)", - "AND($t7, $t9)"))) + ">($t1, $t9)", + "AND($t8, $t10)"))) val nestedField = NestedProjectionUtil.build(exprs, rexProgram.getInputRowType) val paths = NestedProjectionUtil.convertToIndexArray(nestedField) @@ -101,7 +102,8 @@ class NestedProjectionUtilTest extends RexNodeTestBase { Array(1), Array(2), Array(3), - Array(4) + Array(4), + Array(5) ) assertArray(paths, orderedPaths) val builder = new FlinkRexBuilder(typeFactory) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/RexNodeExtractorTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/RexNodeExtractorTest.scala index 2eb87e35cc89d..bd5f15d3bed06 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/RexNodeExtractorTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/RexNodeExtractorTest.scala @@ -33,6 +33,7 @@ import org.apache.flink.table.planner.utils.{DateTimeTestUtil, IntSumAggFunction import org.apache.flink.table.resource.ResourceManager import org.apache.flink.table.utils.CatalogManagerMocks +import org.apache.calcite.avatica.util.ByteString import org.apache.calcite.rel.`type`.RelDataType import org.apache.calcite.rex.{RexBuilder, RexNode} import org.apache.calcite.sql.`type`.SqlTypeName @@ -145,6 +146,26 @@ class RexNodeExtractorTest extends RexNodeTestBase { assertEquals(0, unconvertedRexNodes.length) } + @Test + def testExtractConditionWithBinaryLiteral(): Unit = { + // blob + val t0 = rexBuilder.makeInputRef(allFieldTypes.get(5), 5) + + // X'616263' + val t1 = rexBuilder.makeBinaryLiteral(ByteString.of("616263", 16)) + + // blob = X'616263' + val a = rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, t0, t1) + + val relBuilder: RexBuilder = new FlinkRexBuilder(typeFactory) + val (convertedExpressions, unconvertedRexNodes) = + extractConjunctiveConditions(a, -1, allFieldNames, relBuilder, functionCatalog) + + val expected: Array[Expression] = Array($"blob" === Array[Byte](97, 98, 99)) + assertExpressionArrayEquals(expected, convertedExpressions) + assertEquals(0, unconvertedRexNodes.length) + } + // ((a AND b) OR c) AND (NOT d) => (a OR c) AND (b OR c) AND (NOT d) @Test def testExtractCnfCondition(): Unit = { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/RexNodeRewriterTest.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/RexNodeRewriterTest.scala index 0cea5c8be699c..57a0edda39ebf 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/RexNodeRewriterTest.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/RexNodeRewriterTest.scala @@ -39,12 +39,13 @@ class RexNodeRewriterTest extends RexNodeTestBase { "$2", "$3", "$4", + "$5", "*($t2, $t3)", "100", - "<($t5, $t6)", + "<($t6, $t7)", "6", - ">($t1, $t8)", - "AND($t7, $t9)"))) + ">($t1, $t9)", + "AND($t8, $t10)"))) // use amount, id, price fields to create a new RexProgram val usedFields = Array(2, 3, 1) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/RexNodeTestBase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/RexNodeTestBase.scala index ec326c2a5403d..1ba968b325390 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/RexNodeTestBase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/utils/RexNodeTestBase.scala @@ -39,14 +39,16 @@ abstract class RexNodeTestBase { val typeFactory: FlinkTypeFactory = new FlinkTypeFactory( Thread.currentThread().getContextClassLoader) - val allFieldNames: java.util.List[String] = List("name", "id", "amount", "price", "flag").asJava + val allFieldNames: java.util.List[String] = + List("name", "id", "amount", "price", "flag", "blob").asJava val allFieldTypes: java.util.List[RelDataType] = List( DataTypes.VARCHAR(100), DataTypes.BIGINT(), DataTypes.INT(), DataTypes.DOUBLE(), - DataTypes.BOOLEAN()) + DataTypes.BOOLEAN(), + DataTypes.BYTES()) .map(LogicalTypeDataTypeConverter.fromDataTypeToLogicalType) .map(typeFactory.createFieldTypeFromLogicalType) .asJava diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/batch/sql/CodeSplitITCase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/batch/sql/CodeSplitITCase.scala index 74384b4cf0df0..ca99525befa81 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/batch/sql/CodeSplitITCase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/batch/sql/CodeSplitITCase.scala @@ -117,7 +117,7 @@ class CodeSplitITCase extends BatchTestBase { for (i <- 0 until 100) { expected.add(s"+I[${Range(0, 100).map(_ => s"$i").mkString(", ")}]") } - assertThatIterable(TestValuesTableFactory.getResults("test_many_values")) + assertThatIterable(TestValuesTableFactory.getResultsAsStrings("test_many_values")) .containsExactlyElementsOf(expected) } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/batch/sql/MiscITCase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/batch/sql/MiscITCase.scala index fd5a5a4343e7f..0f043a307b13a 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/batch/sql/MiscITCase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/batch/sql/MiscITCase.scala @@ -32,6 +32,8 @@ import org.apache.flink.types.Row import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.{BeforeEach, Test} +import java.time.DayOfWeek + /** Misc tests. */ class MiscITCase extends BatchTestBase { @@ -621,4 +623,94 @@ class MiscITCase extends BatchTestBase { )).isInstanceOf(classOf[ValidationException]) } + + @Test + def testEqualAndNotEqual(): Unit = { + // character string + checkQuery( + Seq((null, 2), ("b", 1)), + "SELECT f1 FROM Table1 WHERE f0 <> 'a'", + Seq(Tuple1(1)) + ) + checkQuery( + Seq(("aa", "aa"), ("aa", "bb"), ("aa", null)), + "SELECT * FROM Table1 WHERE SUBSTR(f0, 2, 1) <> SUBSTR(f1, 2, 1)", + Seq(("aa", "bb")) + ) + checkQuery( + Seq(("aa", "aa"), ("aa", "bb"), ("aa", null)), + "SELECT * FROM Table1 WHERE SUBSTR(f0, 2, 1) = SUBSTR(f1, 2, 1)", + Seq(("aa", "aa")) + ) + + // raw + checkQuery( + Seq( + (DayOfWeek.SUNDAY, DayOfWeek.SUNDAY), + (DayOfWeek.SUNDAY, DayOfWeek.MONDAY), + (DayOfWeek.SUNDAY, null)), + "SELECT * FROM Table1 WHERE f0 = f1", + Seq((DayOfWeek.SUNDAY, DayOfWeek.SUNDAY)) + ) + checkQuery( + Seq( + (DayOfWeek.SUNDAY, DayOfWeek.SUNDAY), + (DayOfWeek.SUNDAY, DayOfWeek.MONDAY), + (DayOfWeek.SUNDAY, null)), + "SELECT * FROM Table1 WHERE f0 <> f1", + Seq((DayOfWeek.SUNDAY, DayOfWeek.MONDAY)) + ) + + // multiset + checkQuery( + Seq(("b", 1), ("a", 1), ("b", 1)), + "SELECT t1.ms = t2.ms FROM " + + "(SELECT f1, COLLECT(f0) AS ms FROM Table1 GROUP BY f1) t1 LEFT JOIN " + + "(SELECT f1, COLLECT(f0) AS ms FROM Table1 GROUP BY f1) t2 ON t1.f1 = t2.f1", + Seq(Tuple1("true")) + ) + checkQuery( + Seq(("b", 1), ("a", 1), ("b", 1)), + "SELECT t1.ms = t2.ms FROM " + + "(SELECT f1, COLLECT(f0) AS ms FROM Table1 GROUP BY f1) t1 LEFT JOIN " + + "(SELECT f1, COLLECT(f0) AS ms FROM (SELECT * FROM Table1 LIMIT 2) GROUP BY f1) t2 " + + "ON t1.f1 = t2.f1", + Seq(Tuple1("false")) + ) + checkQuery( + Seq(("b", 1), ("a", 1), ("b", 1)), + "SELECT t1.ms = NULL FROM (SELECT f1, COLLECT(f0) AS ms FROM Table1 GROUP BY f1) t1", + Seq(Tuple1("null")) + ) + checkQuery( + Seq(("b", 1), ("a", 1), ("b", 1)), + "SELECT NULL = t1.ms FROM (SELECT f1, COLLECT(f0) AS ms FROM Table1 GROUP BY f1) t1", + Seq(Tuple1("null")) + ) + checkQuery( + Seq(("b", 1), ("a", 1), ("b", 1)), + "SELECT NOT(t1.ms = t2.ms) FROM " + + "(SELECT f1, COLLECT(f0) AS ms FROM Table1 GROUP BY f1) t1 LEFT JOIN " + + "(SELECT f1, COLLECT(f0) AS ms FROM Table1 GROUP BY f1) t2 ON t1.f1 = t2.f1", + Seq(Tuple1("false")) + ) + checkQuery( + Seq(("b", 1), ("a", 1), ("b", 1)), + "SELECT NOT(t1.ms = t2.ms) FROM " + + "(SELECT f1, COLLECT(f0) AS ms FROM Table1 GROUP BY f1) t1 LEFT JOIN " + + "(SELECT f1, COLLECT(f0) AS ms FROM (SELECT * FROM Table1 LIMIT 2) GROUP BY f1) t2 " + + "ON t1.f1 = t2.f1", + Seq(Tuple1("true")) + ) + checkQuery( + Seq(("b", 1), ("a", 1), ("b", 1)), + "SELECT NOT(t1.ms = NULL) FROM (SELECT f1, COLLECT(f0) AS ms FROM Table1 GROUP BY f1) t1", + Seq(Tuple1("null")) + ) + checkQuery( + Seq(("b", 1), ("a", 1), ("b", 1)), + "SELECT NOT(NULL = t1.ms) FROM (SELECT f1, COLLECT(f0) AS ms FROM Table1 GROUP BY f1) t1", + Seq(Tuple1("null")) + ) + } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/batch/table/TableSinkITCase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/batch/table/TableSinkITCase.scala index e4f757103b26f..b788535a22c3d 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/batch/table/TableSinkITCase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/batch/table/TableSinkITCase.scala @@ -58,7 +58,7 @@ class TableSinkITCase extends BatchTestBase { .select("12345", 55.cast(DataTypes.DECIMAL(10, 0)), "12345".cast(DataTypes.CHAR(5))) table.executeInsert("sink").await() - val result = TestValuesTableFactory.getResults("sink") + val result = TestValuesTableFactory.getResultsAsStrings("sink") val expected = Seq("12345,55,12345") assertThat(result.sorted).isEqualTo(expected.sorted) } @@ -84,7 +84,7 @@ class TableSinkITCase extends BatchTestBase { .select("12345", 55.cast(DataTypes.DECIMAL(10, 0)), "12345".cast(DataTypes.CHAR(5))) table.executeInsert("sink").await() - val result = TestValuesTableFactory.getResults("sink") + val result = TestValuesTableFactory.getResultsAsStrings("sink") val expected = Seq("12345,55,12345") assertThat(result.sorted).isEqualTo(expected.sorted) } @@ -110,7 +110,7 @@ class TableSinkITCase extends BatchTestBase { .select('a, 'b.sum()) table.executeInsert("testSink").await() - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") val expected = List("1,0.1", "2,0.4", "3,1.0", "4,2.2", "5,3.9") assertThat(result.sorted).isEqualTo(expected.sorted) } @@ -135,7 +135,7 @@ class TableSinkITCase extends BatchTestBase { .select('a, 'b.sum()) table.executeInsert("testSink").await() - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") val expected = List("1,0.1", "2,0.4", "3,1.0", "4,2.2", "5,3.9") assertThat(result.sorted).isEqualTo(expected.sorted) } @@ -167,7 +167,7 @@ class TableSinkITCase extends BatchTestBase { .await() val expected = List("1,2021,1,0.1", "2,2021,1,0.4", "3,2021,1,1.0", "4,2021,1,2.2", "5,2021,1,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertThat(result.sorted).isEqualTo(expected.sorted) } @@ -193,7 +193,7 @@ class TableSinkITCase extends BatchTestBase { |""".stripMargin) .await() val expected = List("null,0.1", "null,0.4", "null,1.0", "null,2.2", "null,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertThat(result.sorted).isEqualTo(expected.sorted) } @@ -228,7 +228,7 @@ class TableSinkITCase extends BatchTestBase { "null,2021,1,1.0", "null,2021,1,2.2", "null,2021,1,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertThat(result.sorted).isEqualTo(expected.sorted) } @@ -259,7 +259,7 @@ class TableSinkITCase extends BatchTestBase { .await() val expected = List("1,2021,1,0.1", "2,2021,1,0.4", "3,2021,1,1.0", "4,2021,1,2.2", "5,2021,1,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertThat(result.sorted).isEqualTo(expected.sorted) } @@ -294,7 +294,7 @@ class TableSinkITCase extends BatchTestBase { "null,null,null,1.0", "null,null,null,2.2", "null,null,null,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertThat(result.sorted).isEqualTo(expected.sorted) } @@ -329,7 +329,7 @@ class TableSinkITCase extends BatchTestBase { "null,2021,1,1.0", "null,2021,1,2.2", "null,2021,1,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertThat(result.sorted).isEqualTo(expected.sorted) } @@ -364,7 +364,7 @@ class TableSinkITCase extends BatchTestBase { "null,2021,1,1.0", "null,2021,1,2.2", "null,2021,1,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertThat(result.sorted).isEqualTo(expected.sorted) } @@ -399,7 +399,7 @@ class TableSinkITCase extends BatchTestBase { "null,2021,1,1.0", "null,2021,1,2.2", "null,2021,1,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertThat(result.sorted).isEqualTo(expected.sorted) } @@ -434,7 +434,7 @@ class TableSinkITCase extends BatchTestBase { "null,2021,null,1.0", "null,2021,null,2.2", "null,2021,null,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertThat(result.sorted).isEqualTo(expected.sorted) } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/CalcITCase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/CalcITCase.scala index 301496e37b6e6..84e025d230772 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/CalcITCase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/CalcITCase.scala @@ -659,9 +659,12 @@ class CalcITCase extends StreamingTestBase { } catch { case e: Exception => assertEquals( - "SQL validation failed. From line 1, column 12 to line 1, column 30: " + - "No match found for function signature CURRENT_WATERMARK()", - e.getMessage) + "SQL validation failed. From line 1, column 12 to line 1, column 30: No match found for function signature CURRENT_WATERMARK().\n" + + "Supported signatures are:\n" + + "CURRENT_WATERMARK()\n" + + "CURRENT_WATERMARK()", + e.getMessage + ) } } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/ChangelogSourceITCase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/ChangelogSourceITCase.scala index 0f3af85aac15f..a3621f3482752 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/ChangelogSourceITCase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/ChangelogSourceITCase.scala @@ -115,13 +115,13 @@ class ChangelogSourceITCase( "user1,Tom,tom123@gmail.com,8.10,16.20", "user3,Bailey,bailey@qq.com,9.99,19.98", "user4,Tina,tina@gmail.com,11.30,22.60") - assertEquals(expected.sorted, TestValuesTableFactory.getResults("user_sink").sorted) + assertEquals(expected.sorted, TestValuesTableFactory.getResultsAsStrings("user_sink").sorted) sourceMode match { // verify the update_before messages haven been filtered when scanning changelog source // the CHANGELOG_SOURCE has I,UA,UB,D but no primary key, so we can not omit UB case CHANGELOG_SOURCE_WITH_EVENTS_DUPLICATE => - val rawResult = TestValuesTableFactory.getRawResults("user_sink") + val rawResult = TestValuesTableFactory.getRawResultsAsStrings("user_sink") val hasUB = rawResult.exists(r => r.startsWith("-U")) assertFalse( s"Sink result shouldn't contain UPDATE_BEFORE, but is:\n ${rawResult.mkString("\n")}", @@ -173,7 +173,7 @@ class ChangelogSourceITCase( tEnv.executeSql(dml).await() val expected = Seq("ALL,3,29.39,tom123@gmail.com") - assertEquals(expected.sorted, TestValuesTableFactory.getResults("user_sink").sorted) + assertEquals(expected.sorted, TestValuesTableFactory.getResultsAsStrings("user_sink").sorted) } @Test @@ -202,7 +202,7 @@ class ChangelogSourceITCase( val expected = Seq("16.20,1,tom123@gmail.com", "19.98,1,bailey@qq.com", "22.60,1,tina@gmail.com") - assertEquals(expected.sorted, TestValuesTableFactory.getResults("user_sink").sorted) + assertEquals(expected.sorted, TestValuesTableFactory.getResultsAsStrings("user_sink").sorted) } @Test @@ -233,7 +233,7 @@ class ChangelogSourceITCase( val expected = Seq("user3,Bailey,bailey@qq.com,9.99,19.98", "user4,Tina,tina@gmail.com,11.30,22.60") - assertEquals(expected.sorted, TestValuesTableFactory.getResults("user_sink").sorted) + assertEquals(expected.sorted, TestValuesTableFactory.getResultsAsStrings("user_sink").sorted) } @Test diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/DeduplicateITCase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/DeduplicateITCase.scala index a0f8ab4a47733..ed7e4be891700 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/DeduplicateITCase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/DeduplicateITCase.scala @@ -198,7 +198,7 @@ class DeduplicateITCase(miniBatch: MiniBatchMode, mode: StateBackendMode) """.stripMargin tEnv.executeSql(sql).await() - val rawResult = TestValuesTableFactory.getRawResults("rowtime_sink") + val rawResult = TestValuesTableFactory.getRawResultsAsStrings("rowtime_sink") val expected = List( "+I(1,1,Hi,1970-01-01T00:00:00.001)", @@ -237,7 +237,7 @@ class DeduplicateITCase(miniBatch: MiniBatchMode, mode: StateBackendMode) """.stripMargin tEnv.executeSql(sql).await() - val rawResult = TestValuesTableFactory.getRawResults("rowtime_sink") + val rawResult = TestValuesTableFactory.getRawResultsAsStrings("rowtime_sink") val expected = List( "+I(1,1,Hi,1970-01-01T00:00:00.001)", @@ -279,7 +279,7 @@ class DeduplicateITCase(miniBatch: MiniBatchMode, mode: StateBackendMode) """.stripMargin tEnv.executeSql(sql).await() - val rawResult = TestValuesTableFactory.getResults("rowtime_sink") + val rawResult = TestValuesTableFactory.getResultsAsStrings("rowtime_sink") val expected = List("6") assertEquals(expected.sorted, rawResult.sorted) } @@ -306,7 +306,7 @@ class DeduplicateITCase(miniBatch: MiniBatchMode, mode: StateBackendMode) """.stripMargin tEnv.executeSql(sql).await() - val rawResult = TestValuesTableFactory.getRawResults("rowtime_sink") + val rawResult = TestValuesTableFactory.getRawResultsAsStrings("rowtime_sink") val expected = List( "+I(1,1,Hi,1970-01-01T00:00:00.001)", @@ -349,7 +349,7 @@ class DeduplicateITCase(miniBatch: MiniBatchMode, mode: StateBackendMode) """.stripMargin tEnv.executeSql(sql).await() - val rawResult = TestValuesTableFactory.getRawResults("rowtime_sink") + val rawResult = TestValuesTableFactory.getRawResultsAsStrings("rowtime_sink") val expected = List( "+I(1,1,Hi,1970-01-01T00:00:00.001)", @@ -393,7 +393,7 @@ class DeduplicateITCase(miniBatch: MiniBatchMode, mode: StateBackendMode) """.stripMargin tEnv.executeSql(sql).await() - val rawResult = TestValuesTableFactory.getResults("rowtime_sink") + val rawResult = TestValuesTableFactory.getResultsAsStrings("rowtime_sink") val expected = List("6") assertEquals(expected.sorted, rawResult.sorted) } diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/TableSinkITCase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/TableSinkITCase.scala index 25c0b7c124b1a..e353bb8760a84 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/TableSinkITCase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/TableSinkITCase.scala @@ -114,7 +114,7 @@ class TableSinkITCase(mode: StateBackendMode) extends StreamingWithStateTestBase |""".stripMargin) .await() - val result = TestValuesTableFactory.getResults("JoinDisorderChangeLog") + val result = TestValuesTableFactory.getResultsAsStrings("JoinDisorderChangeLog") val expected = List("+I[jason, 4, 22.5, 22]") assertEquals(expected.sorted, result.sorted) } @@ -139,7 +139,7 @@ class TableSinkITCase(mode: StateBackendMode) extends StreamingWithStateTestBase |""".stripMargin) .await() - val result = TestValuesTableFactory.getResults("SinkDisorderChangeLog") + val result = TestValuesTableFactory.getResultsAsStrings("SinkDisorderChangeLog") val expected = List("+I[jason, 4, 22.5]") assertEquals(expected.sorted, result.sorted) } @@ -168,7 +168,7 @@ class TableSinkITCase(mode: StateBackendMode) extends StreamingWithStateTestBase |""".stripMargin) .await() - val result = TestValuesTableFactory.getResults("SinkRankChangeLog") + val result = TestValuesTableFactory.getResultsAsStrings("SinkRankChangeLog") val expected = List("+I[jason, 4]") assertEquals(expected.sorted, result.sorted) } @@ -197,14 +197,14 @@ class TableSinkITCase(mode: StateBackendMode) extends StreamingWithStateTestBase |""".stripMargin) .await() - val result = TestValuesTableFactory.getResults("sink_with_pk") + val result = TestValuesTableFactory.getResultsAsStrings("sink_with_pk") val expected = List( "+I[user1, Tom, tom123@gmail.com, 8.10]", "+I[user3, Bailey, bailey@qq.com, 9.99]", "+I[user4, Tina, tina@gmail.com, 11.30]") assertEquals(expected.sorted, result.sorted) - val rawResult = TestValuesTableFactory.getRawResults("sink_with_pk") + val rawResult = TestValuesTableFactory.getRawResultsAsStrings("sink_with_pk") val expectedRaw = List( "+I[user1, Tom, tom@gmail.com, 10.02]", "+I[user2, Jack, jack@hotmail.com, 71.20]", @@ -246,7 +246,7 @@ class TableSinkITCase(mode: StateBackendMode) extends StreamingWithStateTestBase |""".stripMargin) .await() - val result = TestValuesTableFactory.getResults("zm_test") + val result = TestValuesTableFactory.getResultsAsStrings("zm_test") val expected = List( "+I[jason, 1, null, null, null, null]", "+I[jason, 1, null, null, null, null]", @@ -272,7 +272,7 @@ class TableSinkITCase(mode: StateBackendMode) extends StreamingWithStateTestBase | src |""".stripMargin) .await() - val actual = TestValuesTableFactory.getResults("MyCtasTable") + val actual = TestValuesTableFactory.getResultsAsStrings("MyCtasTable") val expected = List( "+I[jason, 1]", "+I[jason, 1]", @@ -295,7 +295,7 @@ class TableSinkITCase(mode: StateBackendMode) extends StreamingWithStateTestBase | src |""".stripMargin) statementSet.execute().await() - val actualUseStatement = TestValuesTableFactory.getResults("MyCtasTableUseStatement") + val actualUseStatement = TestValuesTableFactory.getResultsAsStrings("MyCtasTableUseStatement") Assertions.assertThat(actualUseStatement.sorted).isEqualTo(expected.sorted) } @@ -359,7 +359,7 @@ class TableSinkITCase(mode: StateBackendMode) extends StreamingWithStateTestBase |""".stripMargin) .await() - val result = TestValuesTableFactory.getResults("test_sink") + val result = TestValuesTableFactory.getResultsAsStrings("test_sink") val expected = List( "+I[1, jason, 3, null, null]", "+I[2, andy, 2, null, null]", @@ -378,7 +378,7 @@ class TableSinkITCase(mode: StateBackendMode) extends StreamingWithStateTestBase |""".stripMargin) .await() - val result2 = TestValuesTableFactory.getResults("test_sink") + val result2 = TestValuesTableFactory.getResultsAsStrings("test_sink") val expected2 = List("+I[1, jason, 3, X, 43]", "+I[2, andy, 2, Y, 32]", "+I[3, clark, 1, Z, 29]") assertEquals(expected2.sorted, result2.sorted) diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/TemporalJoinITCase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/TemporalJoinITCase.scala index 60aa95184b673..dc97cd48aedc8 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/TemporalJoinITCase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/sql/TemporalJoinITCase.scala @@ -20,7 +20,7 @@ package org.apache.flink.table.planner.runtime.stream.sql import org.apache.flink.table.api.TableException import org.apache.flink.table.api.config.ExecutionConfigOptions import org.apache.flink.table.planner.factories.TestValuesTableFactory -import org.apache.flink.table.planner.factories.TestValuesTableFactory.{getResults, registerData} +import org.apache.flink.table.planner.factories.TestValuesTableFactory.{getResultsAsStrings, registerData} import org.apache.flink.table.planner.runtime.utils.StreamingWithStateTestBase import org.apache.flink.table.planner.runtime.utils.StreamingWithStateTestBase.StateBackendMode import org.apache.flink.table.utils.LegacyRowResource @@ -477,7 +477,7 @@ class TemporalJoinITCase(state: StateBackendMode) extends StreamingWithStateTest "3,RMB,40,2020-08-15T00:03,702,2020-08-15T00:00:04", "4,Euro,14,2020-08-16T00:04,118,2020-08-16T00:01" ) - assertEquals(expected.sorted, getResults("rowtime_default_sink").sorted) + assertEquals(expected.sorted, getResultsAsStrings("rowtime_default_sink").sorted) } @Test @@ -495,7 +495,7 @@ class TemporalJoinITCase(state: StateBackendMode) extends StreamingWithStateTest "3,RMB,40,2020-08-15T00:03,702,2020-08-15T00:00:04", "4,Euro,14,2020-08-16T00:04,118,2020-08-16T00:01" ) - assertEquals(expected.sorted, getResults("rowtime_default_sink").sorted) + assertEquals(expected.sorted, getResultsAsStrings("rowtime_default_sink").sorted) } @Test @@ -513,7 +513,7 @@ class TemporalJoinITCase(state: StateBackendMode) extends StreamingWithStateTest val expected = List( "1,Euro,12,2020-08-15T00:01,114,2020-08-15T00:00:01", "2,US Dollar,18,2020-08-16T00:03,106,2020-08-16T00:02") - assertEquals(expected.sorted, getResults("rowtime_default_sink").sorted) + assertEquals(expected.sorted, getResultsAsStrings("rowtime_default_sink").sorted) } @Test @@ -532,7 +532,7 @@ class TemporalJoinITCase(state: StateBackendMode) extends StreamingWithStateTest "4,Euro,14,2020-08-16T00:04,118,2020-08-16T00:01", "5,RMB,40,2020-08-16T00:03,null,null" ) - assertEquals(expected.sorted, getResults("rowtime_default_sink").sorted) + assertEquals(expected.sorted, getResultsAsStrings("rowtime_default_sink").sorted) } @Test @@ -553,7 +553,7 @@ class TemporalJoinITCase(state: StateBackendMode) extends StreamingWithStateTest "4,Euro,14,2020-08-16T00:04,118,2020-08-16T00:01", "5,RMB,40,2020-08-16T00:03,null,null" ) - assertEquals(expected.sorted, getResults("rowtime_default_sink").sorted) + assertEquals(expected.sorted, getResultsAsStrings("rowtime_default_sink").sorted) } @Test @@ -574,7 +574,7 @@ class TemporalJoinITCase(state: StateBackendMode) extends StreamingWithStateTest "4,Euro,14,2020-08-16T00:04,118,2020-08-16T00:01", "5,RMB,40,2020-08-16T00:03,null,null" ) - assertEquals(expected.sorted, getResults("rowtime_default_sink").sorted) + assertEquals(expected.sorted, getResultsAsStrings("rowtime_default_sink").sorted) } @Test @@ -592,7 +592,7 @@ class TemporalJoinITCase(state: StateBackendMode) extends StreamingWithStateTest "3,RMB,40,2020-08-15T00:03,702,2020-08-15T00:00:04", "4,Euro,14,2020-08-16T00:04,118,2020-08-16T00:01" ) - assertEquals(expected.sorted, getResults("rowtime_default_sink").sorted) + assertEquals(expected.sorted, getResultsAsStrings("rowtime_default_sink").sorted) } @Test @@ -609,7 +609,7 @@ class TemporalJoinITCase(state: StateBackendMode) extends StreamingWithStateTest "2,US Dollar,18,2020-08-16T00:03,106,2020-08-16T00:02", "4,Euro,14,2020-08-16T00:04,118,2020-08-16T00:01" ) - assertEquals(expected.sorted, getResults("rowtime_default_sink").sorted) + assertEquals(expected.sorted, getResultsAsStrings("rowtime_default_sink").sorted) } @Test @@ -625,7 +625,7 @@ class TemporalJoinITCase(state: StateBackendMode) extends StreamingWithStateTest "1,Euro,12,2020-08-15T00:01,114,2020-08-15T00:00:01", "4,Euro,14,2020-08-16T00:04,118,2020-08-16T00:01" ) - assertEquals(expected.sorted, getResults("rowtime_default_sink").sorted) + assertEquals(expected.sorted, getResultsAsStrings("rowtime_default_sink").sorted) } @Test @@ -663,7 +663,7 @@ class TemporalJoinITCase(state: StateBackendMode) extends StreamingWithStateTest "4,Euro,14,2020-08-16T00:04,118,2020-08-16T00:01,118,2020-08-16T00:01", "5,RMB,40,2020-08-16T00:03,null,null,null,null" ) - assertEquals(expected.sorted, getResults("rowtime_sink1").sorted) + assertEquals(expected.sorted, getResultsAsStrings("rowtime_sink1").sorted) } @Test @@ -683,7 +683,7 @@ class TemporalJoinITCase(state: StateBackendMode) extends StreamingWithStateTest "4,Euro,14,2020-08-16T00:04,114,2020-08-15T00:00:01", "5,RMB,40,2020-08-16T00:03,702,2020-08-15T00:00:04" ) - assertEquals(expected.sorted, getResults("rowtime_default_sink").sorted) + assertEquals(expected.sorted, getResultsAsStrings("rowtime_default_sink").sorted) } @Test @@ -703,7 +703,7 @@ class TemporalJoinITCase(state: StateBackendMode) extends StreamingWithStateTest "4,Euro,14,2020-08-16T00:04,118,2020-08-16T00:01", "5,RMB,40,2020-08-16T00:03,702,2020-08-15T00:00:04" ) - assertEquals(expected.sorted, getResults("rowtime_default_sink").sorted) + assertEquals(expected.sorted, getResultsAsStrings("rowtime_default_sink").sorted) } @Test @@ -723,7 +723,7 @@ class TemporalJoinITCase(state: StateBackendMode) extends StreamingWithStateTest "4,Euro,14,2020-08-16T00:04,null,null", "5,RMB,40,2020-08-16T00:03,null,null" ) - assertEquals(expected.sorted, getResults("rowtime_default_sink").sorted) + assertEquals(expected.sorted, getResultsAsStrings("rowtime_default_sink").sorted) } @Test @@ -750,7 +750,7 @@ class TemporalJoinITCase(state: StateBackendMode) extends StreamingWithStateTest "4,Euro,14,2020-08-16T00:04,118,2020-08-16T00:01", "5,RMB,40,2020-08-16T00:03,702,2020-08-15T00:00:04" ) - assertEquals(expected.sorted, getResults("rowtime_default_sink").sorted) + assertEquals(expected.sorted, getResultsAsStrings("rowtime_default_sink").sorted) } private def createSinkTable(tableName: String, columns: Option[String]): Unit = { diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/table/TableSinkITCase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/table/TableSinkITCase.scala index 3423bf74fab89..640a0dd411308 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/table/TableSinkITCase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/runtime/stream/table/TableSinkITCase.scala @@ -75,7 +75,7 @@ class TableSinkITCase extends StreamingTestBase { .select('w.end.as('t), 'id.count.as('icnt), 'num.sum.as('nsum)) table.executeInsert("appendSink").await() - val result = TestValuesTableFactory.getResults("appendSink") + val result = TestValuesTableFactory.getResultsAsStrings("appendSink") val expected = List( "1970-01-01T00:00:00.005,4,8", "1970-01-01T00:00:00.010,5,18", @@ -108,7 +108,7 @@ class TableSinkITCase extends StreamingTestBase { "INSERT INTO appendSink /*+ OPTIONS('sink.parallelism' = '1') */(t, num, text) SELECT id, num, text FROM src") .await() - val result = TestValuesTableFactory.getResults("appendSink") + val result = TestValuesTableFactory.getResultsAsStrings("appendSink") val expected = List("1,1,Hi", "2,2,Hello", "3,2,Hello world") assertEquals(expected.sorted, result.sorted) } @@ -131,7 +131,7 @@ class TableSinkITCase extends StreamingTestBase { |""".stripMargin) tEnv.executeSql("INSERT INTO appendSink SELECT id, ROW(num, text) FROM src").await() - val result = TestValuesTableFactory.getResults("appendSink") + val result = TestValuesTableFactory.getResultsAsStrings("appendSink") val expected = List("1,1,Hi", "2,2,Hello", "3,2,Hello world") assertEquals(expected.sorted, result.sorted) } @@ -157,7 +157,7 @@ class TableSinkITCase extends StreamingTestBase { .select('c, 'g) table.executeInsert("appendSink").await() - val result = TestValuesTableFactory.getResults("appendSink") + val result = TestValuesTableFactory.getResultsAsStrings("appendSink") val expected = List("Hi,Hallo", "Hello,Hallo Welt", "Hello world,Hallo Welt") assertEquals(expected.sorted, result.sorted) } @@ -186,7 +186,7 @@ class TableSinkITCase extends StreamingTestBase { .select('len, 'id.count.as('icnt), 'num.sum.as('nsum)) table.executeInsert("retractSink").await() - val result = TestValuesTableFactory.getResults("retractSink") + val result = TestValuesTableFactory.getResultsAsStrings("retractSink") val expected = List("2,1,1", "5,1,2", "11,1,2", "25,1,3", "10,7,39", "14,1,3", "9,9,41") assertEquals(expected.sorted, result.sorted) @@ -216,13 +216,13 @@ class TableSinkITCase extends StreamingTestBase { .select('w.end.as('t), 'id.count.as('icnt), 'num.sum.as('nsum)) table.executeInsert("retractSink").await() - val rawResult = TestValuesTableFactory.getRawResults("retractSink") + val rawResult = TestValuesTableFactory.getRawResultsAsStrings("retractSink") assertFalse( "Received retraction messages for append only table", rawResult.exists(_.startsWith("-")) ) // maybe -U or -D - val result = TestValuesTableFactory.getResults("retractSink") + val result = TestValuesTableFactory.getResultsAsStrings("retractSink") val expected = List( "1970-01-01T00:00:00.005,4,8", "1970-01-01T00:00:00.010,5,18", @@ -261,10 +261,10 @@ class TableSinkITCase extends StreamingTestBase { .select('count, 'len.count.as('lencnt), 'cTrue) table.executeInsert("upsertSink").await() - val rawResult = TestValuesTableFactory.getRawResults("upsertSink") + val rawResult = TestValuesTableFactory.getRawResultsAsStrings("upsertSink") assertTrue("Results must include delete messages", rawResult.exists(_.startsWith("-D("))) - val result = TestValuesTableFactory.getResults("upsertSink") + val result = TestValuesTableFactory.getResultsAsStrings("upsertSink") val expected = List("1,5,true", "7,1,true", "9,1,true") assertEquals(expected.sorted, result.sorted) } @@ -295,13 +295,13 @@ class TableSinkITCase extends StreamingTestBase { .select('num, 'w.end.as('window_end), 'id.count.as('icnt)) table.executeInsert("upsertSink").await() - val rawResult = TestValuesTableFactory.getRawResults("upsertSink") + val rawResult = TestValuesTableFactory.getRawResultsAsStrings("upsertSink") assertFalse( "Received retraction messages for append only table", rawResult.exists(_.startsWith("-")) ) // maybe -D or -U - val result = TestValuesTableFactory.getResults("upsertSink") + val result = TestValuesTableFactory.getResultsAsStrings("upsertSink") val expected = List( "1,1970-01-01T00:00:00.005,1", "2,1970-01-01T00:00:00.005,2", @@ -341,7 +341,7 @@ class TableSinkITCase extends StreamingTestBase { .select('w.end.as('wend), 'id.count.as('cnt)) table.executeInsert("upsertSink").await() - val rawResult = TestValuesTableFactory.getRawResults("upsertSink") + val rawResult = TestValuesTableFactory.getRawResultsAsStrings("upsertSink") assertFalse( "Received retraction messages for append only table", rawResult.exists(_.startsWith("-")) @@ -386,7 +386,7 @@ class TableSinkITCase extends StreamingTestBase { .select('num, 'id.count.as('cnt)) table.executeInsert("upsertSink").await() - val rawResult = TestValuesTableFactory.getRawResults("upsertSink") + val rawResult = TestValuesTableFactory.getRawResultsAsStrings("upsertSink") assertFalse( "Received retraction messages for append only table", rawResult.exists(_.startsWith("-")) @@ -438,7 +438,7 @@ class TableSinkITCase extends StreamingTestBase { .where('cnt <= 3) table.executeInsert("upsertSink").await() - val result = TestValuesTableFactory.getResults("upsertSink") + val result = TestValuesTableFactory.getResultsAsStrings("upsertSink") val expected = List("1,1", "2,2", "3,3") assertEquals(expected.sorted, result.sorted) } @@ -502,7 +502,7 @@ class TableSinkITCase extends StreamingTestBase { .select('num, 'w.rowtime.as('rowtime1), 'w.rowtime.as('rowtime2)) table.executeInsert("sink").await() - val result = TestValuesTableFactory.getResults("sink") + val result = TestValuesTableFactory.getResultsAsStrings("sink") assertEquals(result.size(), 10) // clean up @@ -532,7 +532,7 @@ class TableSinkITCase extends StreamingTestBase { .select("12345", 55.cast(DataTypes.DECIMAL(10, 0)), "12345".cast(DataTypes.CHAR(5))) table.executeInsert("sink").await() - val result = TestValuesTableFactory.getResults("sink") + val result = TestValuesTableFactory.getResultsAsStrings("sink") val expected = Seq("12345,55,12345") assertEquals(expected.sorted, result.sorted) } @@ -558,7 +558,7 @@ class TableSinkITCase extends StreamingTestBase { .select("12345", 55.cast(DataTypes.DECIMAL(10, 0)), "12345".cast(DataTypes.CHAR(5))) table.executeInsert("sink").await() - val result = TestValuesTableFactory.getResults("sink") + val result = TestValuesTableFactory.getResultsAsStrings("sink") val expected = Seq("12345,55,12345") assertEquals(expected.sorted, result.sorted) } @@ -608,7 +608,7 @@ class TableSinkITCase extends StreamingTestBase { |""".stripMargin) .await() - val rawResult = TestValuesTableFactory.getRawResults("changelog_sink") + val rawResult = TestValuesTableFactory.getRawResultsAsStrings("changelog_sink") val expected = List( "+I(1,user2,71.20)", "+I(1,user1,10.02)", @@ -659,7 +659,7 @@ class TableSinkITCase extends StreamingTestBase { |GROUP BY user_name |""".stripMargin) .await() - val finalResult = TestValuesTableFactory.getResults("final_sink") + val finalResult = TestValuesTableFactory.getResultsAsStrings("final_sink") val finalExpected = List("user1,28.12", "user2,71.20", "user3,32.33", "user4,9.99") assertEquals(finalExpected.sorted, finalResult.sorted) } @@ -701,7 +701,7 @@ class TableSinkITCase extends StreamingTestBase { |""".stripMargin) .await() - val result = TestValuesTableFactory.getResults("MetadataSink") + val result = TestValuesTableFactory.getResultsAsStrings("MetadataSink") val expected = List("1,book,12", "2,book,null", "3,fruit,44", "4,book,11", "4,fruit,null", "5,fruit,null") assertEquals(expected.sorted, result.sorted) @@ -815,7 +815,7 @@ class TableSinkITCase extends StreamingTestBase { |""".stripMargin) .await() val expected = List("null,0.1", "null,0.4", "null,1.0", "null,2.2", "null,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertEquals(expected.sorted, result.sorted) } @@ -851,7 +851,7 @@ class TableSinkITCase extends StreamingTestBase { "null,2021,1,1.0", "null,2021,1,2.2", "null,2021,1,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertEquals(expected.sorted, result.sorted) } @@ -883,7 +883,7 @@ class TableSinkITCase extends StreamingTestBase { .await() val expected = List("1,2021,1,0.1", "2,2021,1,0.4", "3,2021,1,1.0", "4,2021,1,2.2", "5,2021,1,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertEquals(expected.sorted, result.sorted) } @@ -919,7 +919,7 @@ class TableSinkITCase extends StreamingTestBase { "null,null,null,1.0", "null,null,null,2.2", "null,null,null,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertEquals(expected.sorted, result.sorted) } @@ -958,7 +958,7 @@ class TableSinkITCase extends StreamingTestBase { "1,c,c1,c2,33333,12,1.0", "1,c,c1,c2,33333,12,2.2", "1,c,c1,c2,33333,12,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertEquals(expected.sorted, result.sorted) } @@ -995,7 +995,7 @@ class TableSinkITCase extends StreamingTestBase { "1,c,c1,c2,33333,12,1.0", "1,c,c1,c2,33333,12,2.2", "1,c,c1,c2,33333,12,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertEquals(expected.sorted, result.sorted) } @@ -1031,7 +1031,7 @@ class TableSinkITCase extends StreamingTestBase { "null,2021,1,1.0", "null,2021,1,2.2", "null,2021,1,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertEquals(expected.sorted, result.sorted) } @@ -1068,7 +1068,7 @@ class TableSinkITCase extends StreamingTestBase { "null,2021,1,1.0", "null,2021,1,2.2", "null,2021,1,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertEquals(expected.sorted, result.sorted) } @@ -1104,7 +1104,7 @@ class TableSinkITCase extends StreamingTestBase { "null,2021,1,1.0", "null,2021,1,2.2", "null,2021,1,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertEquals(expected.sorted, result.sorted) } @@ -1140,7 +1140,7 @@ class TableSinkITCase extends StreamingTestBase { "null,2021,null,1.0", "null,2021,null,2.2", "null,2021,null,3.9") - val result = TestValuesTableFactory.getResults("testSink") + val result = TestValuesTableFactory.getResultsAsStrings("testSink") assertEquals(expected.sorted, result.sorted) } @@ -1235,7 +1235,7 @@ class TableSinkITCase extends StreamingTestBase { .build()) .build()) .await() - assertEquals(Seq("+I(42)"), TestValuesTableFactory.getOnlyRawResults.toList) + assertEquals(Seq("+I(42)"), TestValuesTableFactory.getOnlyRawResultsAsStrings.toList) // Derived schema @@ -1247,7 +1247,7 @@ class TableSinkITCase extends StreamingTestBase { .from("T2") .executeInsert(TableDescriptor.forConnector("values").build()) .await() - assertEquals(Seq("+I(42)"), TestValuesTableFactory.getOnlyRawResults.toList) + assertEquals(Seq("+I(42)"), TestValuesTableFactory.getOnlyRawResultsAsStrings.toList) // Enriched schema @@ -1269,7 +1269,7 @@ class TableSinkITCase extends StreamingTestBase { .build()) .build()) .await() - assertEquals(Seq("+I(42)"), TestValuesTableFactory.getOnlyRawResults.toList) + assertEquals(Seq("+I(42)"), TestValuesTableFactory.getOnlyRawResultsAsStrings.toList) TestValuesTableFactory.clearAllData() } @@ -1303,7 +1303,7 @@ class TableSinkITCase extends StreamingTestBase { ) .execute() .await() - assertEquals(Seq("+I(42)"), TestValuesTableFactory.getOnlyRawResults.toList) + assertEquals(Seq("+I(42)"), TestValuesTableFactory.getOnlyRawResultsAsStrings.toList) // Derived schema @@ -1319,7 +1319,7 @@ class TableSinkITCase extends StreamingTestBase { ) .execute() .await() - assertEquals(Seq("+I(42)"), TestValuesTableFactory.getOnlyRawResults.toList) + assertEquals(Seq("+I(42)"), TestValuesTableFactory.getOnlyRawResultsAsStrings.toList) // Enriched schema @@ -1344,7 +1344,7 @@ class TableSinkITCase extends StreamingTestBase { ) .execute() .await() - assertEquals(Seq("+I(42)"), TestValuesTableFactory.getOnlyRawResults.toList) + assertEquals(Seq("+I(42)"), TestValuesTableFactory.getOnlyRawResultsAsStrings.toList) } @Test @@ -1375,7 +1375,7 @@ class TableSinkITCase extends StreamingTestBase { .select('w.end.as('t), 'id.count.as('icnt), 'num.sum.as('nsum)) table.executeInsert("sink").await() - val result = TestValuesTableFactory.getResults("sink") + val result = TestValuesTableFactory.getResultsAsStrings("sink") val expected = List( "1970-01-01T00:00:00.005,4,8", "1970-01-01T00:00:00.010,5,18", diff --git a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/utils/TableTestBase.scala b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/utils/TableTestBase.scala index 7fc23d8281f6f..d8920bc62c06c 100644 --- a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/utils/TableTestBase.scala +++ b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/utils/TableTestBase.scala @@ -22,7 +22,6 @@ import org.apache.flink.api.common.typeinfo.{AtomicType, TypeInformation} import org.apache.flink.api.java.typeutils.{PojoTypeInfo, RowTypeInfo, TupleTypeInfo} import org.apache.flink.api.scala.typeutils.CaseClassTypeInfo import org.apache.flink.configuration.BatchExecutionOptions -import org.apache.flink.core.testutils.FlinkMatchers.containsMessage import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.core.JsonParseException import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.JsonNode import org.apache.flink.streaming.api.{environment, TimeCharacteristic} @@ -67,6 +66,7 @@ import org.apache.flink.table.sources.{StreamTableSource, TableSource} import org.apache.flink.table.types.logical.LogicalType import org.apache.flink.table.types.utils.TypeConversions import org.apache.flink.table.typeutils.FieldInfoUtils +import org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension import org.apache.flink.types.Row import org.apache.flink.util.{FlinkUserCodeClassLoaders, MutableURLClassLoader} import org.apache.flink.util.jackson.JacksonMapperFactory @@ -79,35 +79,27 @@ import org.apache.calcite.avatica.util.TimeUnit import org.apache.calcite.rel.RelNode import org.apache.calcite.sql.{SqlExplainLevel, SqlIntervalQualifier} import org.apache.calcite.sql.parser.SqlParserPos -import org.junit.Assert.{assertEquals, assertThat, assertTrue, fail} -import org.junit.Rule -import org.junit.rules.{ExpectedException, TemporaryFolder, TestName} +import org.assertj.core.api.Assertions.{assertThat, assertThatExceptionOfType, fail} +import org.junit.jupiter.api.Assertions.{assertEquals, assertTrue} +import org.junit.jupiter.api.extension.{BeforeEachCallback, ExtendWith, ExtensionContext, RegisterExtension} +import org.junit.jupiter.api.io.TempDir +import org.junit.platform.commons.support.AnnotationSupport import java.io.{File, IOException} import java.net.URL -import java.nio.file.{Files, Paths} +import java.nio.file.{Files, Path, Paths} import java.time.Duration import java.util.Collections /** Test base for testing Table API / SQL plans. */ abstract class TableTestBase { - // used for accurate exception information checking. - val expectedException: ExpectedException = ExpectedException.none() - // used for get test case method name + @RegisterExtension val testName: TestName = new TestName - val _tempFolder = new TemporaryFolder - - @Rule - def tempFolder: TemporaryFolder = _tempFolder - - @Rule - def thrown: ExpectedException = expectedException - - @Rule - def name: TestName = testName + @TempDir + var tempFolder: Path = _ def streamTestUtil(tableConfig: TableConfig = TableConfig.getDefault): StreamTableTestUtil = StreamTableTestUtil(this, tableConfig = tableConfig) @@ -127,9 +119,36 @@ abstract class TableTestBase { val expectedString = FlinkRelOptUtil.toString(TableTestUtil.toRelNode(expected)) val actualString = FlinkRelOptUtil.toString(TableTestUtil.toRelNode(actual)) assertEquals( - "Logical plans do not match", LogicalPlanFormatUtils.formatTempTableId(expectedString), - LogicalPlanFormatUtils.formatTempTableId(actualString)) + LogicalPlanFormatUtils.formatTempTableId(actualString), + "Logical plans do not match") + } +} + +class TestName extends BeforeEachCallback { + + private val bracketsRegex = """\[.*\]""".r + + private var methodName: String = _ + + def getMethodName: String = methodName + + override def beforeEach(context: ExtensionContext): Unit = { + if (hasParameterizedTestExtension(context)) { + val displayName = context.getDisplayName match { + case bracketsRegex(_*) => context.getDisplayName + case _ => s"[${context.getDisplayName}]" + } + methodName = s"${context.getTestMethod.get().getName}$displayName" + } else { + methodName = context.getTestMethod.get().getName + } + } + + private def hasParameterizedTestExtension(context: ExtensionContext): Boolean = { + Option(AnnotationSupport.findAnnotation(context.getTestClass, classOf[ExtendWith]).orElse(null)) + .map(_.value) + .exists(_.contains(classOf[ParameterizedTestExtension])) } } @@ -622,7 +641,7 @@ abstract class TableTestUtilBase(test: TableTestBase, isStreamingMode: Boolean) val optimizedPlan = getOptimizedRelPlan(Array(optimizedRel), Array.empty, withRowType = false) val result = notExpected.forall(!optimizedPlan.contains(_)) val message = s"\nactual plan:\n$optimizedPlan\nnot expected:\n${notExpected.mkString(", ")}" - assertTrue(message, result) + assertTrue(result, message) } /** @@ -752,14 +771,9 @@ abstract class TableTestUtilBase(test: TableTestBase, isStreamingMode: Boolean) sql: String, message: String, clazz: Class[_ <: Throwable] = classOf[ValidationException]): Unit = { - try { - verifyExplain(sql) - fail(s"Expected a $clazz, but no exception is thrown.") - } catch { - case e: Throwable => - assertTrue(clazz.isAssignableFrom(e.getClass)) - assertThat(e, containsMessage(message)) - } + assertThatExceptionOfType(clazz) + .isThrownBy(() => verifyExplain(sql)) + .withMessageContaining(message) } /** @@ -841,22 +855,20 @@ abstract class TableTestUtilBase(test: TableTestBase, isStreamingMode: Boolean) fail(s"$testMethodFileName regenerated.") } else { val expected = String.join("\n", Files.readAllLines(path)) - assertEquals( - TableTestUtil.replaceExecNodeId(TableTestUtil.getPrettyJson(expected)), - TableTestUtil.replaceExecNodeId(TableTestUtil.getPrettyJson(jsonPlanWithoutFlinkVersion)) - ) + assertThat( + TableTestUtil.replaceExecNodeId(TableTestUtil.getPrettyJson(jsonPlanWithoutFlinkVersion))) + .isEqualTo(TableTestUtil.replaceExecNodeId(TableTestUtil.getPrettyJson(expected))) // check json serde round trip as well val expectedWithFlinkVersion = JsonTestUtils.writeToString( JsonTestUtils .setFlinkVersion(JsonTestUtils.readFromString(expected), FlinkVersion.current())) - assertEquals( - TableTestUtil.replaceExecNodeId(TableTestUtil.getFormattedJson(expectedWithFlinkVersion)), + assertThat( TableTestUtil.replaceExecNodeId( - TableTestUtil.getFormattedJson( - getPlanner - .loadPlan(PlanReference.fromJsonString(expectedWithFlinkVersion)) - .asJsonString())) - ) + TableTestUtil.getFormattedJson(getPlanner + .loadPlan(PlanReference.fromJsonString(expectedWithFlinkVersion)) + .asJsonString()))) + .isEqualTo( + TableTestUtil.replaceExecNodeId(TableTestUtil.getFormattedJson(expectedWithFlinkVersion))) } } @@ -1127,16 +1139,16 @@ abstract class TableTestUtilBase(test: TableTestBase, isStreamingMode: Boolean) def assertEqualsOrExpand(tag: String, actual: String, expand: Boolean = true): Unit = { val expected = s"$${$tag}" if (!expand) { - diffRepository.assertEquals(test.name.getMethodName, tag, expected, actual) + diffRepository.assertEquals(test.testName.getMethodName, tag, expected, actual) return } - val expanded = diffRepository.expand(test.name.getMethodName, tag, expected) + val expanded = diffRepository.expand(test.testName.getMethodName, tag, expected) if (expanded != null && !expanded.equals(expected)) { // expected does exist, check result - diffRepository.assertEquals(test.name.getMethodName, tag, expected, actual) + diffRepository.assertEquals(test.testName.getMethodName, tag, expected, actual) } else { // expected does not exist, update - diffRepository.expand(test.name.getMethodName, tag, actual) + diffRepository.expand(test.testName.getMethodName, tag, actual) } } } diff --git a/flink-test-utils-parent/flink-test-utils/src/main/java/org/apache/flink/streaming/util/TestStreamEnvironment.java b/flink-test-utils-parent/flink-test-utils/src/main/java/org/apache/flink/streaming/util/TestStreamEnvironment.java index 86da4b2a66fd1..747fbae4fcac6 100644 --- a/flink-test-utils-parent/flink-test-utils/src/main/java/org/apache/flink/streaming/util/TestStreamEnvironment.java +++ b/flink-test-utils-parent/flink-test-utils/src/main/java/org/apache/flink/streaming/util/TestStreamEnvironment.java @@ -18,6 +18,7 @@ package org.apache.flink.streaming.util; +import org.apache.flink.configuration.CheckpointingOptions; import org.apache.flink.configuration.Configuration; import org.apache.flink.configuration.ReadableConfig; import org.apache.flink.configuration.StateChangelogOptions; @@ -118,6 +119,7 @@ private static void randomizeConfiguration(MiniCluster miniCluster, Configuratio Duration.ofSeconds(0), Duration.ofMillis(100), Duration.ofSeconds(2)); + randomize(conf, CheckpointingOptions.CLEANER_PARALLEL_MODE, true, false); } // randomize ITTests for enabling state change log diff --git a/flink-tests/src/test/java/org/apache/flink/test/checkpointing/AutoRescalingITCase.java b/flink-tests/src/test/java/org/apache/flink/test/checkpointing/AutoRescalingITCase.java new file mode 100644 index 0000000000000..403449a388b52 --- /dev/null +++ b/flink-tests/src/test/java/org/apache/flink/test/checkpointing/AutoRescalingITCase.java @@ -0,0 +1,968 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.test.checkpointing; + +import org.apache.flink.api.common.JobID; +import org.apache.flink.api.common.functions.RichFlatMapFunction; +import org.apache.flink.api.common.functions.RuntimeContext; +import org.apache.flink.api.common.restartstrategy.RestartStrategies; +import org.apache.flink.api.common.state.ListState; +import org.apache.flink.api.common.state.ListStateDescriptor; +import org.apache.flink.api.common.state.ValueState; +import org.apache.flink.api.common.state.ValueStateDescriptor; +import org.apache.flink.api.common.time.Deadline; +import org.apache.flink.api.common.typeutils.base.IntSerializer; +import org.apache.flink.api.java.functions.KeySelector; +import org.apache.flink.api.java.tuple.Tuple2; +import org.apache.flink.client.program.ClusterClient; +import org.apache.flink.client.program.rest.RestClusterClient; +import org.apache.flink.configuration.CheckpointingOptions; +import org.apache.flink.configuration.Configuration; +import org.apache.flink.configuration.JobManagerOptions; +import org.apache.flink.configuration.NettyShuffleEnvironmentOptions; +import org.apache.flink.configuration.StateBackendOptions; +import org.apache.flink.configuration.WebOptions; +import org.apache.flink.core.testutils.OneShotLatch; +import org.apache.flink.runtime.client.JobExecutionException; +import org.apache.flink.runtime.jobgraph.JobGraph; +import org.apache.flink.runtime.jobgraph.JobResourceRequirements; +import org.apache.flink.runtime.jobgraph.JobVertex; +import org.apache.flink.runtime.state.FunctionInitializationContext; +import org.apache.flink.runtime.state.FunctionSnapshotContext; +import org.apache.flink.runtime.state.KeyGroupRangeAssignment; +import org.apache.flink.runtime.testutils.MiniClusterResourceConfiguration; +import org.apache.flink.streaming.api.CheckpointingMode; +import org.apache.flink.streaming.api.checkpoint.CheckpointedFunction; +import org.apache.flink.streaming.api.checkpoint.ListCheckpointed; +import org.apache.flink.streaming.api.datastream.DataStream; +import org.apache.flink.streaming.api.environment.CheckpointConfig; +import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; +import org.apache.flink.streaming.api.functions.sink.SinkFunction; +import org.apache.flink.streaming.api.functions.sink.v2.DiscardingSink; +import org.apache.flink.streaming.api.functions.source.RichParallelSourceFunction; +import org.apache.flink.streaming.api.functions.source.SourceFunction; +import org.apache.flink.test.util.MiniClusterWithClientResource; +import org.apache.flink.testutils.TestingUtils; +import org.apache.flink.testutils.executor.TestExecutorResource; +import org.apache.flink.util.Collector; +import org.apache.flink.util.TestLogger; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.File; +import java.time.Duration; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import static org.apache.flink.runtime.testutils.CommonTestUtils.waitForAllTaskRunning; +import static org.apache.flink.runtime.testutils.CommonTestUtils.waitForOneMoreCheckpoint; +import static org.apache.flink.test.scheduling.UpdateJobResourceRequirementsITCase.waitForAvailableSlots; +import static org.apache.flink.test.scheduling.UpdateJobResourceRequirementsITCase.waitForRunningTasks; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Test checkpoint rescaling under changing resource requirements. This test is mostly a variant of + * {@link RescalingITCase} with two main differences: (1) We rescale from checkpoints instead of + * savepoints and (2) rescaling without cancel/restart but triggered by changing resource + * requirements. + */ +@RunWith(Parameterized.class) +public class AutoRescalingITCase extends TestLogger { + + @ClassRule + public static final TestExecutorResource EXECUTOR_RESOURCE = + TestingUtils.defaultExecutorResource(); + + private static final int numTaskManagers = 2; + private static final int slotsPerTaskManager = 2; + private static final int totalSlots = numTaskManagers * slotsPerTaskManager; + + @Parameterized.Parameters(name = "backend = {0}, buffersPerChannel = {1}") + public static Collection data() { + return Arrays.asList( + new Object[][] { + {"rocksdb", 0}, {"rocksdb", 2}, {"filesystem", 0}, {"filesystem", 2} + }); + } + + public AutoRescalingITCase(String backend, int buffersPerChannel) { + this.backend = backend; + this.buffersPerChannel = buffersPerChannel; + } + + private final String backend; + + private final int buffersPerChannel; + + private String currentBackend = null; + + enum OperatorCheckpointMethod { + NON_PARTITIONED, + CHECKPOINTED_FUNCTION, + CHECKPOINTED_FUNCTION_BROADCAST, + LIST_CHECKPOINTED + } + + private static MiniClusterWithClientResource cluster; + private static RestClusterClient restClusterClient; + + @ClassRule public static TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Before + public void setup() throws Exception { + // detect parameter change + if (!Objects.equals(currentBackend, backend)) { + shutDownExistingCluster(); + + currentBackend = backend; + + Configuration config = new Configuration(); + + final File checkpointDir = temporaryFolder.newFolder(); + final File savepointDir = temporaryFolder.newFolder(); + + config.setString(StateBackendOptions.STATE_BACKEND, currentBackend); + config.setBoolean(CheckpointingOptions.INCREMENTAL_CHECKPOINTS, true); + config.setBoolean(CheckpointingOptions.LOCAL_RECOVERY, true); + config.setString( + CheckpointingOptions.CHECKPOINTS_DIRECTORY, checkpointDir.toURI().toString()); + config.setString( + CheckpointingOptions.SAVEPOINT_DIRECTORY, savepointDir.toURI().toString()); + config.setInteger( + NettyShuffleEnvironmentOptions.NETWORK_BUFFERS_PER_CHANNEL, buffersPerChannel); + + config.set(JobManagerOptions.SCHEDULER, JobManagerOptions.SchedulerType.Adaptive); + + // speed the test suite up + // - lower refresh interval -> controls how fast we invalidate ExecutionGraphCache + // - lower slot idle timeout -> controls how fast we return idle slots to TM + config.set(WebOptions.REFRESH_INTERVAL, 50L); + config.set(JobManagerOptions.SLOT_IDLE_TIMEOUT, 50L); + + cluster = + new MiniClusterWithClientResource( + new MiniClusterResourceConfiguration.Builder() + .setConfiguration(config) + .setNumberTaskManagers(numTaskManagers) + .setNumberSlotsPerTaskManager(slotsPerTaskManager) + .build()); + cluster.before(); + restClusterClient = cluster.getRestClusterClient(); + } + } + + @AfterClass + public static void shutDownExistingCluster() { + if (cluster != null) { + cluster.after(); + cluster = null; + } + } + + @Test + public void testCheckpointRescalingInKeyedState() throws Exception { + testCheckpointRescalingKeyedState(false); + } + + @Test + public void testCheckpointRescalingOutKeyedState() throws Exception { + testCheckpointRescalingKeyedState(true); + } + + /** + * Tests that a job with purely keyed state can be restarted from a checkpoint with a different + * parallelism. + */ + public void testCheckpointRescalingKeyedState(boolean scaleOut) throws Exception { + final int numberKeys = 42; + final int numberElements = 1000; + final int parallelism = scaleOut ? totalSlots / 2 : totalSlots; + final int parallelism2 = scaleOut ? totalSlots : totalSlots / 2; + final int maxParallelism = 13; + + Duration timeout = Duration.ofMinutes(3); + Deadline deadline = Deadline.now().plus(timeout); + + ClusterClient client = cluster.getClusterClient(); + + try { + + JobGraph jobGraph = + createJobGraphWithKeyedState( + new Configuration(), + parallelism, + maxParallelism, + numberKeys, + numberElements); + + final JobID jobID = jobGraph.getJobID(); + + client.submitJob(jobGraph).get(); + + SubtaskIndexSource.SOURCE_LATCH.trigger(); + + // wait til the sources have emitted numberElements for each key and completed a + // checkpoint + assertTrue( + SubtaskIndexFlatMapper.workCompletedLatch.await( + deadline.timeLeft().toMillis(), TimeUnit.MILLISECONDS)); + + // verify the current state + + Set> actualResult = CollectionSink.getElementsSet(); + + Set> expectedResult = new HashSet<>(); + + for (int key = 0; key < numberKeys; key++) { + int keyGroupIndex = KeyGroupRangeAssignment.assignToKeyGroup(key, maxParallelism); + + expectedResult.add( + Tuple2.of( + KeyGroupRangeAssignment.computeOperatorIndexForKeyGroup( + maxParallelism, parallelism, keyGroupIndex), + numberElements * key)); + } + + assertEquals(expectedResult, actualResult); + + // clear the CollectionSink set for the restarted job + CollectionSink.clearElementsSet(); + + waitForAllTaskRunning(cluster.getMiniCluster(), jobGraph.getJobID(), false); + + waitForOneMoreCheckpoint(jobID, cluster.getMiniCluster()); + + SubtaskIndexSource.SOURCE_LATCH.reset(); + + JobResourceRequirements.Builder builder = JobResourceRequirements.newBuilder(); + for (JobVertex vertex : jobGraph.getVertices()) { + builder.setParallelismForJobVertex(vertex.getID(), parallelism2, parallelism2); + } + + restClusterClient.updateJobResourceRequirements(jobID, builder.build()).join(); + + waitForRunningTasks(restClusterClient, jobID, 2 * parallelism2); + waitForAvailableSlots(restClusterClient, totalSlots - parallelism2); + + SubtaskIndexSource.SOURCE_LATCH.trigger(); + + client.requestJobResult(jobID).get(); + + Set> actualResult2 = CollectionSink.getElementsSet(); + + Set> expectedResult2 = new HashSet<>(); + + for (int key = 0; key < numberKeys; key++) { + int keyGroupIndex = KeyGroupRangeAssignment.assignToKeyGroup(key, maxParallelism); + expectedResult2.add( + Tuple2.of( + KeyGroupRangeAssignment.computeOperatorIndexForKeyGroup( + maxParallelism, parallelism2, keyGroupIndex), + key * 2 * numberElements)); + } + + assertEquals(expectedResult2, actualResult2); + + } finally { + // clear the CollectionSink set for the restarted job + CollectionSink.clearElementsSet(); + } + } + + /** + * Tests that a job cannot be restarted from a checkpoint with a different parallelism if the + * rescaled operator has non-partitioned state. + */ + @Test + public void testCheckpointRescalingNonPartitionedStateCausesException() throws Exception { + final int parallelism = totalSlots / 2; + final int parallelism2 = totalSlots; + final int maxParallelism = 13; + + ClusterClient client = cluster.getClusterClient(); + + try { + JobGraph jobGraph = + createJobGraphWithOperatorState( + parallelism, maxParallelism, OperatorCheckpointMethod.NON_PARTITIONED); + // make sure the job does not finish before we take a checkpoint + StateSourceBase.canFinishLatch = new CountDownLatch(1); + + final JobID jobID = jobGraph.getJobID(); + + client.submitJob(jobGraph).get(); + + // wait until the operator is started + waitForAllTaskRunning(cluster.getMiniCluster(), jobGraph.getJobID(), false); + // wait until the operator handles some data + StateSourceBase.workStartedLatch.await(); + + waitForOneMoreCheckpoint(jobID, cluster.getMiniCluster()); + + JobResourceRequirements.Builder builder = JobResourceRequirements.newBuilder(); + for (JobVertex vertex : jobGraph.getVertices()) { + builder.setParallelismForJobVertex(vertex.getID(), parallelism2, parallelism2); + } + + restClusterClient.updateJobResourceRequirements(jobID, builder.build()).join(); + + waitForRunningTasks(restClusterClient, jobID, parallelism2); + waitForAvailableSlots(restClusterClient, totalSlots - parallelism2); + + StateSourceBase.canFinishLatch.countDown(); + + client.requestJobResult(jobID).get(); + } catch (JobExecutionException exception) { + if (!(exception.getCause() instanceof IllegalStateException)) { + throw exception; + } + } + } + + /** + * Tests that a job with non partitioned state can be restarted from a checkpoint with a + * different parallelism if the operator with non-partitioned state are not rescaled. + */ + @Test + public void testCheckpointRescalingWithKeyedAndNonPartitionedState() throws Exception { + int numberKeys = 42; + int numberElements = 1000; + int parallelism = totalSlots / 2; + int parallelism2 = totalSlots; + int maxParallelism = 13; + + Duration timeout = Duration.ofMinutes(3); + Deadline deadline = Deadline.now().plus(timeout); + + ClusterClient client = cluster.getClusterClient(); + + try { + + JobGraph jobGraph = + createJobGraphWithKeyedAndNonPartitionedOperatorState( + parallelism, + maxParallelism, + parallelism, + numberKeys, + numberElements, + numberElements); + + final JobID jobID = jobGraph.getJobID(); + + client.submitJob(jobGraph).get(); + + SubtaskIndexSource.SOURCE_LATCH.trigger(); + + // wait til the sources have emitted numberElements for each key and completed a + // checkpoint + assertTrue( + SubtaskIndexFlatMapper.workCompletedLatch.await( + deadline.timeLeft().toMillis(), TimeUnit.MILLISECONDS)); + + // verify the current state + + Set> actualResult = CollectionSink.getElementsSet(); + + Set> expectedResult = new HashSet<>(); + + for (int key = 0; key < numberKeys; key++) { + int keyGroupIndex = KeyGroupRangeAssignment.assignToKeyGroup(key, maxParallelism); + + expectedResult.add( + Tuple2.of( + KeyGroupRangeAssignment.computeOperatorIndexForKeyGroup( + maxParallelism, parallelism, keyGroupIndex), + numberElements * key)); + } + + assertEquals(expectedResult, actualResult); + + // clear the CollectionSink set for the restarted job + CollectionSink.clearElementsSet(); + + waitForOneMoreCheckpoint(jobID, cluster.getMiniCluster()); + + SubtaskIndexSource.SOURCE_LATCH.reset(); + + JobResourceRequirements.Builder builder = JobResourceRequirements.newBuilder(); + for (JobVertex vertex : jobGraph.getVertices()) { + if (vertex.getMaxParallelism() >= parallelism2) { + builder.setParallelismForJobVertex(vertex.getID(), parallelism2, parallelism2); + } else { + builder.setParallelismForJobVertex( + vertex.getID(), vertex.getMaxParallelism(), vertex.getMaxParallelism()); + } + } + + restClusterClient.updateJobResourceRequirements(jobID, builder.build()).join(); + + waitForRunningTasks(restClusterClient, jobID, parallelism2); + waitForAvailableSlots(restClusterClient, totalSlots - parallelism2); + + SubtaskIndexSource.SOURCE_LATCH.trigger(); + + client.requestJobResult(jobID).get(); + + Set> actualResult2 = CollectionSink.getElementsSet(); + + Set> expectedResult2 = new HashSet<>(); + + for (int key = 0; key < numberKeys; key++) { + int keyGroupIndex = KeyGroupRangeAssignment.assignToKeyGroup(key, maxParallelism); + expectedResult2.add( + Tuple2.of( + KeyGroupRangeAssignment.computeOperatorIndexForKeyGroup( + maxParallelism, parallelism2, keyGroupIndex), + key * 2 * numberElements)); + } + + assertEquals(expectedResult2, actualResult2); + + } finally { + // clear the CollectionSink set for the restarted job + CollectionSink.clearElementsSet(); + } + } + + @Test + public void testCheckpointRescalingInPartitionedOperatorState() throws Exception { + testCheckpointRescalingPartitionedOperatorState( + false, OperatorCheckpointMethod.CHECKPOINTED_FUNCTION); + } + + @Test + public void testCheckpointRescalingOutPartitionedOperatorState() throws Exception { + testCheckpointRescalingPartitionedOperatorState( + true, OperatorCheckpointMethod.CHECKPOINTED_FUNCTION); + } + + @Test + public void testCheckpointRescalingInBroadcastOperatorState() throws Exception { + testCheckpointRescalingPartitionedOperatorState( + false, OperatorCheckpointMethod.CHECKPOINTED_FUNCTION_BROADCAST); + } + + @Test + public void testCheckpointRescalingOutBroadcastOperatorState() throws Exception { + testCheckpointRescalingPartitionedOperatorState( + true, OperatorCheckpointMethod.CHECKPOINTED_FUNCTION_BROADCAST); + } + + /** Tests rescaling of partitioned operator state. */ + public void testCheckpointRescalingPartitionedOperatorState( + boolean scaleOut, OperatorCheckpointMethod checkpointMethod) throws Exception { + final int parallelism = scaleOut ? totalSlots : totalSlots / 2; + final int parallelism2 = scaleOut ? totalSlots / 2 : totalSlots; + final int maxParallelism = 13; + + ClusterClient client = cluster.getClusterClient(); + + int counterSize = Math.max(parallelism, parallelism2); + + if (checkpointMethod == OperatorCheckpointMethod.CHECKPOINTED_FUNCTION + || checkpointMethod == OperatorCheckpointMethod.CHECKPOINTED_FUNCTION_BROADCAST) { + PartitionedStateSource.checkCorrectSnapshot = new int[counterSize]; + PartitionedStateSource.checkCorrectRestore = new int[counterSize]; + PartitionedStateSource.checkCorrectSnapshots.clear(); + } else { + throw new UnsupportedOperationException("Unsupported method:" + checkpointMethod); + } + + JobGraph jobGraph = + createJobGraphWithOperatorState(parallelism, maxParallelism, checkpointMethod); + // make sure the job does not finish before we take the checkpoint + StateSourceBase.canFinishLatch = new CountDownLatch(1); + + final JobID jobID = jobGraph.getJobID(); + + client.submitJob(jobGraph).get(); + + // wait until the operator is started + waitForAllTaskRunning(cluster.getMiniCluster(), jobGraph.getJobID(), false); + // wait until the operator handles some data + StateSourceBase.workStartedLatch.await(); + + waitForOneMoreCheckpoint(jobID, cluster.getMiniCluster()); + + JobResourceRequirements.Builder builder = JobResourceRequirements.newBuilder(); + for (JobVertex vertex : jobGraph.getVertices()) { + builder.setParallelismForJobVertex(vertex.getID(), parallelism2, parallelism2); + } + + restClusterClient.updateJobResourceRequirements(jobID, builder.build()).join(); + + waitForRunningTasks(restClusterClient, jobID, 2 * parallelism2); + waitForAvailableSlots(restClusterClient, totalSlots - parallelism2); + + StateSourceBase.canFinishLatch.countDown(); + + client.requestJobResult(jobID).get(); + + int sumExp = 0; + int sumAct = 0; + + if (checkpointMethod == OperatorCheckpointMethod.CHECKPOINTED_FUNCTION) { + for (int c : PartitionedStateSource.checkCorrectSnapshot) { + sumExp += c; + } + + for (int c : PartitionedStateSource.checkCorrectRestore) { + sumAct += c; + } + } else { + for (int c : PartitionedStateSource.checkCorrectSnapshot) { + sumExp += c; + } + + for (int c : PartitionedStateSource.checkCorrectRestore) { + sumAct += c; + } + + sumExp *= parallelism2; + } + + assertEquals(sumExp, sumAct); + } + + // ------------------------------------------------------------------------------------------------------------------ + + private static void configureCheckpointing(CheckpointConfig config) { + config.setCheckpointInterval(100); + config.setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE); + config.enableUnalignedCheckpoints(true); + } + + private static JobGraph createJobGraphWithOperatorState( + int parallelism, int maxParallelism, OperatorCheckpointMethod checkpointMethod) { + + StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); + configureCheckpointing(env.getCheckpointConfig()); + env.setParallelism(parallelism); + env.getConfig().setMaxParallelism(maxParallelism); + env.setRestartStrategy(RestartStrategies.noRestart()); + + StateSourceBase.workStartedLatch = new CountDownLatch(parallelism); + + SourceFunction src; + + switch (checkpointMethod) { + case CHECKPOINTED_FUNCTION: + src = new PartitionedStateSource(false); + break; + case CHECKPOINTED_FUNCTION_BROADCAST: + src = new PartitionedStateSource(true); + break; + case NON_PARTITIONED: + src = new NonPartitionedStateSource(); + break; + default: + throw new IllegalArgumentException(checkpointMethod.name()); + } + + DataStream input = env.addSource(src); + + input.sinkTo(new DiscardingSink<>()); + + return env.getStreamGraph().getJobGraph(); + } + + public static JobGraph createJobGraphWithKeyedState( + Configuration configuration, + int parallelism, + int maxParallelism, + int numberKeys, + int numberElements) { + StreamExecutionEnvironment env = + StreamExecutionEnvironment.getExecutionEnvironment(configuration); + env.setParallelism(parallelism); + if (0 < maxParallelism) { + env.getConfig().setMaxParallelism(maxParallelism); + } + + configureCheckpointing(env.getCheckpointConfig()); + env.setRestartStrategy(RestartStrategies.noRestart()); + env.getConfig().setUseSnapshotCompression(true); + + DataStream input = + env.addSource(new SubtaskIndexSource(numberKeys, numberElements, parallelism)) + .keyBy( + new KeySelector() { + private static final long serialVersionUID = + -7952298871120320940L; + + @Override + public Integer getKey(Integer value) { + return value; + } + }); + + SubtaskIndexFlatMapper.workCompletedLatch = new CountDownLatch(numberKeys); + + DataStream> result = + input.flatMap(new SubtaskIndexFlatMapper(numberElements)); + + result.addSink(new CollectionSink<>()); + + return env.getStreamGraph().getJobGraph(); + } + + private static JobGraph createJobGraphWithKeyedAndNonPartitionedOperatorState( + int parallelism, + int maxParallelism, + int fixedParallelism, + int numberKeys, + int numberElements, + int numberElementsAfterRestart) { + + StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); + env.setParallelism(parallelism); + env.getConfig().setMaxParallelism(maxParallelism); + configureCheckpointing(env.getCheckpointConfig()); + env.setRestartStrategy(RestartStrategies.noRestart()); + + DataStream input = + env.addSource( + new SubtaskIndexNonPartitionedStateSource( + numberKeys, + numberElements, + numberElementsAfterRestart, + parallelism)) + .setParallelism(fixedParallelism) + .setMaxParallelism(fixedParallelism) + .keyBy( + new KeySelector() { + private static final long serialVersionUID = + -7952298871120320940L; + + @Override + public Integer getKey(Integer value) { + return value; + } + }); + + SubtaskIndexFlatMapper.workCompletedLatch = new CountDownLatch(numberKeys); + + DataStream> result = + input.flatMap(new SubtaskIndexFlatMapper(numberElements)); + + result.addSink(new CollectionSink<>()); + + return env.getStreamGraph().getJobGraph(); + } + + private static class SubtaskIndexSource extends RichParallelSourceFunction { + + private static final long serialVersionUID = -400066323594122516L; + + private final int numberKeys; + + private final int originalParallelism; + protected int numberElements; + + protected int counter = 0; + + private boolean running = true; + + private static final OneShotLatch SOURCE_LATCH = new OneShotLatch(); + + SubtaskIndexSource(int numberKeys, int numberElements, int originalParallelism) { + this.numberKeys = numberKeys; + this.numberElements = numberElements; + this.originalParallelism = originalParallelism; + } + + @Override + public void run(SourceContext ctx) throws Exception { + RuntimeContext runtimeContext = getRuntimeContext(); + final int subtaskIndex = runtimeContext.getIndexOfThisSubtask(); + + boolean isRestartedOrRescaled = + runtimeContext.getNumberOfParallelSubtasks() != originalParallelism + || runtimeContext.getAttemptNumber() > 0; + while (running) { + SOURCE_LATCH.await(); + if (counter < numberElements) { + synchronized (ctx.getCheckpointLock()) { + for (int value = subtaskIndex; + value < numberKeys; + value += runtimeContext.getNumberOfParallelSubtasks()) { + ctx.collect(value); + } + + counter++; + } + } else { + if (isRestartedOrRescaled) { + running = false; + } else { + Thread.sleep(100); + } + } + } + } + + @Override + public void cancel() { + running = false; + } + } + + private static class SubtaskIndexNonPartitionedStateSource extends SubtaskIndexSource + implements ListCheckpointed { + + private static final long serialVersionUID = 8388073059042040203L; + private final int numElementsAfterRestart; + + SubtaskIndexNonPartitionedStateSource( + int numberKeys, + int numberElements, + int numElementsAfterRestart, + int originalParallelism) { + super(numberKeys, numberElements, originalParallelism); + this.numElementsAfterRestart = numElementsAfterRestart; + } + + @Override + public List snapshotState(long checkpointId, long timestamp) { + return Collections.singletonList(this.counter); + } + + @Override + public void restoreState(List state) { + if (state.size() != 1) { + throw new RuntimeException( + "Test failed due to unexpected recovered state size " + state.size()); + } + this.counter = state.get(0); + this.numberElements += numElementsAfterRestart; + } + } + + private static class SubtaskIndexFlatMapper + extends RichFlatMapFunction> + implements CheckpointedFunction { + + private static final long serialVersionUID = 5273172591283191348L; + + private static CountDownLatch workCompletedLatch = new CountDownLatch(1); + + private transient ValueState counter; + private transient ValueState sum; + + private final int numberElements; + + SubtaskIndexFlatMapper(int numberElements) { + this.numberElements = numberElements; + } + + @Override + public void flatMap(Integer value, Collector> out) + throws Exception { + + int count = counter.value() + 1; + counter.update(count); + + int s = sum.value() + value; + sum.update(s); + + if (count % numberElements == 0) { + out.collect(Tuple2.of(getRuntimeContext().getIndexOfThisSubtask(), s)); + workCompletedLatch.countDown(); + } + } + + @Override + public void snapshotState(FunctionSnapshotContext context) { + // all managed, nothing to do. + } + + @Override + public void initializeState(FunctionInitializationContext context) { + counter = + context.getKeyedStateStore() + .getState(new ValueStateDescriptor<>("counter", Integer.class, 0)); + sum = + context.getKeyedStateStore() + .getState(new ValueStateDescriptor<>("sum", Integer.class, 0)); + } + } + + private static class CollectionSink implements SinkFunction { + + private static final Set elements = + Collections.newSetFromMap(new ConcurrentHashMap<>()); + + private static final long serialVersionUID = -1652452958040267745L; + + public static Set getElementsSet() { + return (Set) elements; + } + + public static void clearElementsSet() { + elements.clear(); + } + + @Override + public void invoke(IN value) { + elements.add(value); + } + } + + private static class StateSourceBase extends RichParallelSourceFunction { + + private static final long serialVersionUID = 7512206069681177940L; + private static CountDownLatch workStartedLatch = new CountDownLatch(1); + private static CountDownLatch canFinishLatch = new CountDownLatch(0); + + protected volatile int counter = 0; + protected volatile boolean running = true; + + @Override + public void run(SourceContext ctx) throws Exception { + while (running) { + synchronized (ctx.getCheckpointLock()) { + ++counter; + ctx.collect(1); + } + + Thread.sleep(2); + + if (counter == 10) { + workStartedLatch.countDown(); + } + + if (counter >= 500) { + break; + } + } + + canFinishLatch.await(); + } + + @Override + public void cancel() { + running = false; + } + } + + private static class NonPartitionedStateSource extends StateSourceBase + implements ListCheckpointed { + + private static final long serialVersionUID = -8108185918123186841L; + + @Override + public List snapshotState(long checkpointId, long timestamp) { + return Collections.singletonList(this.counter); + } + + @Override + public void restoreState(List state) { + if (!state.isEmpty()) { + this.counter = state.get(0); + } + } + } + + private static class PartitionedStateSource extends StateSourceBase + implements CheckpointedFunction { + + private static final long serialVersionUID = -359715965103593462L; + private static final int NUM_PARTITIONS = 7; + + private transient ListState counterPartitions; + private final boolean broadcast; + + private static final ConcurrentHashMap checkCorrectSnapshots = + new ConcurrentHashMap<>(); + private static int[] checkCorrectSnapshot; + private static int[] checkCorrectRestore; + + public PartitionedStateSource(boolean broadcast) { + this.broadcast = broadcast; + } + + @Override + public void snapshotState(FunctionSnapshotContext context) throws Exception { + + if (getRuntimeContext().getAttemptNumber() == 0) { + int[] snapshot = + checkCorrectSnapshots.computeIfAbsent( + context.getCheckpointId(), + (x) -> new int[checkCorrectRestore.length]); + snapshot[getRuntimeContext().getIndexOfThisSubtask()] = counter; + } + + counterPartitions.clear(); + + int div = counter / NUM_PARTITIONS; + int mod = counter % NUM_PARTITIONS; + + for (int i = 0; i < NUM_PARTITIONS; ++i) { + int partitionValue = div; + if (mod > 0) { + --mod; + ++partitionValue; + } + counterPartitions.add(partitionValue); + } + } + + @Override + public void initializeState(FunctionInitializationContext context) throws Exception { + if (broadcast) { + this.counterPartitions = + context.getOperatorStateStore() + .getUnionListState( + new ListStateDescriptor<>( + "counter_partitions", IntSerializer.INSTANCE)); + } else { + this.counterPartitions = + context.getOperatorStateStore() + .getListState( + new ListStateDescriptor<>( + "counter_partitions", IntSerializer.INSTANCE)); + } + + if (context.isRestored()) { + for (int v : counterPartitions.get()) { + counter += v; + } + checkCorrectRestore[getRuntimeContext().getIndexOfThisSubtask()] = counter; + context.getRestoredCheckpointId() + .ifPresent((id) -> checkCorrectSnapshot = checkCorrectSnapshots.get(id)); + } + } + } +} diff --git a/flink-tests/src/test/java/org/apache/flink/test/scheduling/UpdateJobResourceRequirementsITCase.java b/flink-tests/src/test/java/org/apache/flink/test/scheduling/UpdateJobResourceRequirementsITCase.java index 74829f34caf1e..e0a88aaab2cd2 100644 --- a/flink-tests/src/test/java/org/apache/flink/test/scheduling/UpdateJobResourceRequirementsITCase.java +++ b/flink-tests/src/test/java/org/apache/flink/test/scheduling/UpdateJobResourceRequirementsITCase.java @@ -202,7 +202,7 @@ private void runRescalingTest( } } - private static int getNumberRunningTasks(RestClusterClient restClusterClient, JobID jobId) { + public static int getNumberRunningTasks(RestClusterClient restClusterClient, JobID jobId) { final JobDetailsInfo jobDetailsInfo = restClusterClient.getJobDetails(jobId).join(); return jobDetailsInfo.getJobVertexInfos().stream() .map(JobDetailsInfo.JobVertexDetailsInfo::getTasksPerState) diff --git a/flink-tests/src/test/java/org/apache/flink/test/streaming/runtime/SinkV2ITCase.java b/flink-tests/src/test/java/org/apache/flink/test/streaming/runtime/SinkV2ITCase.java new file mode 100644 index 0000000000000..70d9400474883 --- /dev/null +++ b/flink-tests/src/test/java/org/apache/flink/test/streaming/runtime/SinkV2ITCase.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.test.streaming.runtime; + +import org.apache.flink.api.common.RuntimeExecutionMode; +import org.apache.flink.api.common.typeinfo.IntegerTypeInfo; +import org.apache.flink.api.connector.sink2.Committer; +import org.apache.flink.api.java.tuple.Tuple3; +import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; +import org.apache.flink.streaming.runtime.operators.sink.TestSinkV2; +import org.apache.flink.streaming.util.FiniteTestSource; +import org.apache.flink.test.util.AbstractTestBase; + +import org.junit.Before; +import org.junit.Test; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.function.BooleanSupplier; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; + +/** + * Integration test for {@link org.apache.flink.api.connector.sink.Sink} run time implementation. + */ +public class SinkV2ITCase extends AbstractTestBase { + static final List SOURCE_DATA = + Arrays.asList( + 895, 127, 148, 161, 148, 662, 822, 491, 275, 122, 850, 630, 682, 765, 434, 970, + 714, 795, 288, 422); + + // source send data two times + static final int STREAMING_SOURCE_SEND_ELEMENTS_NUM = SOURCE_DATA.size() * 2; + + static final List EXPECTED_COMMITTED_DATA_IN_STREAMING_MODE = + SOURCE_DATA.stream() + // source send data two times + .flatMap( + x -> + Collections.nCopies( + 2, Tuple3.of(x, null, Long.MIN_VALUE).toString()) + .stream()) + .collect(Collectors.toList()); + + static final List EXPECTED_COMMITTED_DATA_IN_BATCH_MODE = + SOURCE_DATA.stream() + .map(x -> Tuple3.of(x, null, Long.MIN_VALUE).toString()) + .collect(Collectors.toList()); + + static final Queue> COMMIT_QUEUE = + new ConcurrentLinkedQueue<>(); + + static final BooleanSupplier COMMIT_QUEUE_RECEIVE_ALL_DATA = + (BooleanSupplier & Serializable) + () -> COMMIT_QUEUE.size() == STREAMING_SOURCE_SEND_ELEMENTS_NUM; + + @Before + public void init() { + COMMIT_QUEUE.clear(); + } + + @Test + public void writerAndCommitterExecuteInStreamingMode() throws Exception { + final StreamExecutionEnvironment env = buildStreamEnv(); + final FiniteTestSource source = + new FiniteTestSource<>(COMMIT_QUEUE_RECEIVE_ALL_DATA, SOURCE_DATA); + + env.addSource(source, IntegerTypeInfo.INT_TYPE_INFO) + .sinkTo( + TestSinkV2.newBuilder() + .setDefaultCommitter( + (Supplier>> + & Serializable) + () -> COMMIT_QUEUE) + .build()); + env.execute(); + assertThat( + COMMIT_QUEUE.stream() + .map(Committer.CommitRequest::getCommittable) + .collect(Collectors.toList()), + containsInAnyOrder(EXPECTED_COMMITTED_DATA_IN_STREAMING_MODE.toArray())); + } + + @Test + public void writerAndCommitterExecuteInBatchMode() throws Exception { + final StreamExecutionEnvironment env = buildBatchEnv(); + + env.fromCollection(SOURCE_DATA) + .sinkTo( + TestSinkV2.newBuilder() + .setDefaultCommitter( + (Supplier>> + & Serializable) + () -> COMMIT_QUEUE) + .build()); + env.execute(); + assertThat( + COMMIT_QUEUE.stream() + .map(Committer.CommitRequest::getCommittable) + .collect(Collectors.toList()), + containsInAnyOrder(EXPECTED_COMMITTED_DATA_IN_BATCH_MODE.toArray())); + } + + private StreamExecutionEnvironment buildStreamEnv() { + final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); + env.setRuntimeMode(RuntimeExecutionMode.STREAMING); + env.enableCheckpointing(100); + return env; + } + + private StreamExecutionEnvironment buildBatchEnv() { + final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); + env.setRuntimeMode(RuntimeExecutionMode.BATCH); + return env; + } +} diff --git a/flink-tests/src/test/java/org/apache/flink/test/streaming/runtime/SinkV2MetricsITCase.java b/flink-tests/src/test/java/org/apache/flink/test/streaming/runtime/SinkV2MetricsITCase.java new file mode 100644 index 0000000000000..76fcc7b66cc38 --- /dev/null +++ b/flink-tests/src/test/java/org/apache/flink/test/streaming/runtime/SinkV2MetricsITCase.java @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.test.streaming.runtime; + +import org.apache.flink.api.common.JobID; +import org.apache.flink.api.common.typeinfo.BasicTypeInfo; +import org.apache.flink.api.connector.sink2.Sink; +import org.apache.flink.configuration.Configuration; +import org.apache.flink.core.execution.JobClient; +import org.apache.flink.metrics.Metric; +import org.apache.flink.metrics.groups.OperatorMetricGroup; +import org.apache.flink.metrics.groups.SinkWriterMetricGroup; +import org.apache.flink.runtime.metrics.MetricNames; +import org.apache.flink.runtime.testutils.InMemoryReporter; +import org.apache.flink.runtime.testutils.MiniClusterResourceConfiguration; +import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; +import org.apache.flink.streaming.runtime.operators.sink.TestSinkV2; +import org.apache.flink.test.util.MiniClusterWithClientResource; +import org.apache.flink.testutils.junit.SharedObjects; +import org.apache.flink.testutils.junit.SharedReference; +import org.apache.flink.util.TestLogger; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CyclicBarrier; +import java.util.stream.LongStream; + +import static org.apache.flink.metrics.testutils.MetricAssertions.assertThatCounter; +import static org.apache.flink.metrics.testutils.MetricAssertions.assertThatGauge; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; + +/** Tests whether all provided metrics of a {@link Sink} are of the expected values (FLIP-33). */ +public class SinkV2MetricsITCase extends TestLogger { + + private static final String TEST_SINK_NAME = "MetricTestSink"; + // please refer to SinkTransformationTranslator#WRITER_NAME + private static final String DEFAULT_WRITER_NAME = "Writer"; + private static final int DEFAULT_PARALLELISM = 4; + + @Rule public final SharedObjects sharedObjects = SharedObjects.create(); + private static final InMemoryReporter reporter = InMemoryReporter.createWithRetainedMetrics(); + + @ClassRule + public static final MiniClusterWithClientResource MINI_CLUSTER_RESOURCE = + new MiniClusterWithClientResource( + new MiniClusterResourceConfiguration.Builder() + .setNumberTaskManagers(1) + .setNumberSlotsPerTaskManager(DEFAULT_PARALLELISM) + .setConfiguration(reporter.addToConfiguration(new Configuration())) + .build()); + + @Test + public void testMetrics() throws Exception { + StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); + int numSplits = Math.max(1, env.getParallelism() - 2); + + int numRecordsPerSplit = 10; + + // make sure all parallel instances have processed the same amount of records before + // validating metrics + SharedReference beforeBarrier = + sharedObjects.add(new CyclicBarrier(numSplits + 1)); + SharedReference afterBarrier = + sharedObjects.add(new CyclicBarrier(numSplits + 1)); + int stopAtRecord1 = 4; + int stopAtRecord2 = numRecordsPerSplit - 1; + + env.fromSequence(0, numSplits - 1) + .flatMap( + (split, collector) -> + LongStream.range(0, numRecordsPerSplit).forEach(collector::collect)) + .returns(BasicTypeInfo.LONG_TYPE_INFO) + .map( + i -> { + if (i % numRecordsPerSplit == stopAtRecord1 + || i % numRecordsPerSplit == stopAtRecord2) { + beforeBarrier.get().await(); + afterBarrier.get().await(); + } + return i; + }) + .sinkTo(TestSinkV2.newBuilder().setWriter(new MetricWriter()).build()) + .name(TEST_SINK_NAME); + JobClient jobClient = env.executeAsync(); + final JobID jobId = jobClient.getJobID(); + + beforeBarrier.get().await(); + assertSinkMetrics(jobId, stopAtRecord1, env.getParallelism(), numSplits); + afterBarrier.get().await(); + + beforeBarrier.get().await(); + assertSinkMetrics(jobId, stopAtRecord2, env.getParallelism(), numSplits); + afterBarrier.get().await(); + + jobClient.getJobExecutionResult().get(); + } + + @SuppressWarnings("checkstyle:WhitespaceAfter") + private void assertSinkMetrics( + JobID jobId, long processedRecordsPerSubtask, int parallelism, int numSplits) { + List groups = + reporter.findOperatorMetricGroups( + jobId, TEST_SINK_NAME + ": " + DEFAULT_WRITER_NAME); + assertThat(groups, hasSize(parallelism)); + + int subtaskWithMetrics = 0; + for (OperatorMetricGroup group : groups) { + Map metrics = reporter.getMetricsByGroup(group); + // There are only 2 splits assigned; so two groups will not update metrics. + if (group.getIOMetricGroup().getNumRecordsOutCounter().getCount() == 0) { + continue; + } + subtaskWithMetrics++; + + // SinkWriterMetricGroup metrics + assertThatCounter(metrics.get(MetricNames.IO_NUM_RECORDS_OUT)) + .isEqualTo(processedRecordsPerSubtask); + assertThatCounter(metrics.get(MetricNames.IO_NUM_BYTES_OUT)) + .isEqualTo(processedRecordsPerSubtask * MetricWriter.RECORD_SIZE_IN_BYTES); + // MetricWriter is just incrementing errors every even record + assertThatCounter(metrics.get(MetricNames.NUM_RECORDS_OUT_ERRORS)) + .isEqualTo((processedRecordsPerSubtask + 1) / 2); + + // Test "send" metric series has the same value as "out" metric series. + assertThatCounter(metrics.get(MetricNames.NUM_RECORDS_SEND)) + .isEqualTo(processedRecordsPerSubtask); + assertThatCounter(metrics.get(MetricNames.NUM_BYTES_SEND)) + .isEqualTo(processedRecordsPerSubtask * MetricWriter.RECORD_SIZE_IN_BYTES); + assertThatCounter(metrics.get(MetricNames.NUM_RECORDS_SEND_ERRORS)) + .isEqualTo((processedRecordsPerSubtask + 1) / 2); + + // check if the latest send time is fetched + assertThatGauge(metrics.get(MetricNames.CURRENT_SEND_TIME)) + .isEqualTo((processedRecordsPerSubtask - 1) * MetricWriter.BASE_SEND_TIME); + } + assertThat(subtaskWithMetrics, equalTo(numSplits)); + } + + private static class MetricWriter extends TestSinkV2.DefaultSinkWriter { + static final long BASE_SEND_TIME = 100; + static final long RECORD_SIZE_IN_BYTES = 10; + private SinkWriterMetricGroup metricGroup; + private long sendTime; + + @Override + public void init(Sink.InitContext context) { + this.metricGroup = context.metricGroup(); + metricGroup.setCurrentSendTimeGauge(() -> sendTime); + } + + @Override + public void write(Long element, Context context) { + super.write(element, context); + sendTime = element * BASE_SEND_TIME; + metricGroup.getIOMetricGroup().getNumRecordsOutCounter().inc(); + if (element % 2 == 0) { + metricGroup.getNumRecordsOutErrorsCounter().inc(); + } + metricGroup.getIOMetricGroup().getNumBytesOutCounter().inc(RECORD_SIZE_IN_BYTES); + } + } +} diff --git a/pom.xml b/pom.xml index 1812449bdf15f..66ceba2669c4b 100644 --- a/pom.xml +++ b/pom.xml @@ -144,7 +144,7 @@ under the License. 5.4.0 - 1.11.1 + 1.11.3 2.14.3 1.2.0 @@ -160,7 +160,7 @@ under the License. 2.43.0 3.21.7 3.14.9 - 1.18.3 + 1.19.1 1.8.0 false validate @@ -720,7 +720,7 @@ under the License. org.apache.commons commons-compress - 1.21 + 1.24.0 @@ -1124,6 +1124,35 @@ under the License. + + java21 + + [21,) + + + + + 2.12.18 + + + + + + + com.diffplug.spotless + spotless-maven-plugin + + + true + + + + + + + fast @@ -1170,7 +1199,7 @@ under the License. - io.github.zentol.japicmp + com.github.siom79.japicmp japicmp-maven-plugin true @@ -1365,11 +1394,10 @@ under the License. - - 3.8.6 - + + - 1.8.0 + [1.8.0,1.8.1) @@ -1442,7 +1470,7 @@ under the License. - io.github.zentol.japicmp + com.github.siom79.japicmp japicmp-maven-plugin @@ -1561,6 +1589,7 @@ under the License. flink-table/flink-table-planner/src/test/resources/**/*.out flink-table/flink-table-planner/src/test/resources/json/*.json flink-table/flink-table-planner/src/test/resources/jsonplan/*.json + flink-table/flink-table-planner/src/test/resources/restore-tests/** flink-yarn/src/test/resources/krb5.keytab flink-end-to-end-tests/test-scripts/test-data/** flink-end-to-end-tests/test-scripts/docker-hadoop-secure-cluster/hadoop/config/keystore.jks @@ -1767,8 +1796,7 @@ under the License. - - [3.1.1,) + [3.8.6] ${target.java.version} @@ -2212,9 +2240,9 @@ under the License. - io.github.zentol.japicmp + com.github.siom79.japicmp japicmp-maven-plugin - 0.17.1.1_m325 + 0.17.1 diff --git a/tools/azure-pipelines/build-apache-repo.yml b/tools/azure-pipelines/build-apache-repo.yml index 2375fbe984a49..892d2a519dd58 100644 --- a/tools/azure-pipelines/build-apache-repo.yml +++ b/tools/azure-pipelines/build-apache-repo.yml @@ -146,7 +146,7 @@ stages: name: Default e2e_pool_definition: vmImage: 'ubuntu-20.04' - environment: PROFILE="-Dflink.hadoop.version=2.10.2 -Dscala-2.12 -Djdk11 -Djdk17" + environment: PROFILE="-Dflink.hadoop.version=2.10.2 -Dscala-2.12 -Djdk11 -Djdk17 -Pjava17-target" run_end_to_end: true container: flink-build-container jdk: 17