This is an automated email from the ASF dual-hosted git repository.

lzljs3620320 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink-table-store.git


The following commit(s) were added to refs/heads/master by this push:
     new ebb4e38  [FLINK-27264] Add IT case for concurrent batch overwrite and 
streaming insert
ebb4e38 is described below

commit ebb4e388ee585367c2336094c304a91c24a92723
Author: Jane Chan <[email protected]>
AuthorDate: Sat Apr 16 14:54:06 2022 +0800

    [FLINK-27264] Add IT case for concurrent batch overwrite and streaming 
insert
    
    This closes #91
---
 .../store/connector/ReadWriteTableTestBase.java    |  12 +-
 .../store/connector/StreamingWarehouseITCase.java  | 252 +++++++++++++++++++++
 2 files changed, 262 insertions(+), 2 deletions(-)

diff --git 
a/flink-table-store-connector/src/test/java/org/apache/flink/table/store/connector/ReadWriteTableTestBase.java
 
b/flink-table-store-connector/src/test/java/org/apache/flink/table/store/connector/ReadWriteTableTestBase.java
index 5c47b7a..8b73c2c 100644
--- 
a/flink-table-store-connector/src/test/java/org/apache/flink/table/store/connector/ReadWriteTableTestBase.java
+++ 
b/flink-table-store-connector/src/test/java/org/apache/flink/table/store/connector/ReadWriteTableTestBase.java
@@ -421,17 +421,25 @@ public class ReadWriteTableTestBase extends 
KafkaTableTestBase {
     }
 
     protected static StreamExecutionEnvironment buildStreamEnv() {
+        return buildStreamEnv(2);
+    }
+
+    protected static StreamExecutionEnvironment buildStreamEnv(int 
parallelism) {
         final StreamExecutionEnvironment env = 
StreamExecutionEnvironment.getExecutionEnvironment();
         env.setRuntimeMode(RuntimeExecutionMode.STREAMING);
         env.enableCheckpointing(100);
-        env.setParallelism(2);
+        env.setParallelism(parallelism);
         return env;
     }
 
     protected static StreamExecutionEnvironment buildBatchEnv() {
+        return buildBatchEnv(2);
+    }
+
+    protected static StreamExecutionEnvironment buildBatchEnv(int parallelism) 
{
         final StreamExecutionEnvironment env = 
StreamExecutionEnvironment.getExecutionEnvironment();
         env.setRuntimeMode(RuntimeExecutionMode.BATCH);
-        env.setParallelism(2);
+        env.setParallelism(parallelism);
         return env;
     }
 
diff --git 
a/flink-table-store-connector/src/test/java/org/apache/flink/table/store/connector/StreamingWarehouseITCase.java
 
b/flink-table-store-connector/src/test/java/org/apache/flink/table/store/connector/StreamingWarehouseITCase.java
new file mode 100644
index 0000000..48dc2ab
--- /dev/null
+++ 
b/flink-table-store-connector/src/test/java/org/apache/flink/table/store/connector/StreamingWarehouseITCase.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.store.connector;
+
+import org.apache.flink.api.common.JobStatus;
+import org.apache.flink.core.execution.JobClient;
+import org.apache.flink.table.api.EnvironmentSettings;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+import org.apache.flink.table.store.file.utils.BlockingIterator;
+import org.apache.flink.types.Row;
+
+import org.junit.Test;
+
+import java.time.LocalDateTime;
+import java.util.function.Function;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/** Table store IT case to test concurrent batch overwrite and streaming 
insert into. */
+public class StreamingWarehouseITCase extends ReadWriteTableTestBase {
+
+    private final StreamTableEnvironment streamTableEnv =
+            StreamTableEnvironment.create(buildStreamEnv(1));
+    private final StreamTableEnvironment batchTableEnv =
+            StreamTableEnvironment.create(buildBatchEnv(1), 
EnvironmentSettings.inBatchMode());
+
+    @Test
+    public void testUserStory() throws Exception {
+        // Step1: define trade order table schema
+        rootPath = TEMPORARY_FOLDER.newFolder().getPath();
+        String orderSource =
+                "CREATE TABLE IF NOT EXISTS trade_orders (\n"
+                        + "    order_id BIGINT NOT NULL,\n"
+                        + "    order_timestamp AS LOCALTIMESTAMP,\n"
+                        + "    buyer_id STRING,\n"
+                        + "    order_amount DOUBLE,\n"
+                        + "    loyalty_discount DOUBLE,\n"
+                        + "    shipping_fee DOUBLE,\n"
+                        + "    order_verified BOOLEAN,\n"
+                        + "    PRIMARY KEY (order_id) NOT ENFORCED\n"
+                        + "  )\n"
+                        + "WITH (\n"
+                        + "    'connector' = 'datagen',\n"
+                        + "    'rows-per-second' = '10',\n"
+                        + "    'fields.order_id.kind' = 'random',\n"
+                        + "    'fields.order_id.min' = '1',\n"
+                        + "    'fields.buyer_id.kind' = 'random',\n"
+                        + "    'fields.buyer_id.length' = '3',\n"
+                        + "    'fields.order_amount.min' = '10',\n"
+                        + "    'fields.order_amount.max' = '1000',\n"
+                        + "    'fields.loyalty_discount.min' = '0',\n"
+                        + "    'fields.loyalty_discount.max' = '10',\n"
+                        + "    'fields.shipping_fee.min' = '5',\n"
+                        + "    'fields.shipping_fee.max' = '20'\n"
+                        + "  );";
+
+        String cleanedOrders =
+                String.format(
+                        "CREATE TABLE IF NOT EXISTS cleaned_trade_order (\n"
+                                + "    order_id BIGINT NOT NULL,\n"
+                                + "    order_timestamp TIMESTAMP (3),\n"
+                                + "    buyer_id STRING,\n"
+                                + "    order_amount DOUBLE,\n"
+                                + "    loyalty_discount DOUBLE,\n"
+                                + "    shipping_fee DOUBLE,\n"
+                                + "    order_verified BOOLEAN,\n"
+                                + "    actual_gmv DOUBLE,\n"
+                                + "    dt STRING,\n"
+                                + "    PRIMARY KEY (dt, order_id) NOT 
ENFORCED\n"
+                                + "  )\n"
+                                + "PARTITIONED BY (dt)\n"
+                                + "WITH (\n"
+                                + "    'path' = '%s',\n"
+                                + "    'log.system' = 'kafka', "
+                                + "    'log.kafka.bootstrap.servers' = '%s');",
+                        rootPath, getBootstrapServers());
+        streamTableEnv.executeSql(orderSource);
+        streamTableEnv.executeSql(cleanedOrders);
+        batchTableEnv.executeSql(orderSource);
+        batchTableEnv.executeSql(cleanedOrders);
+
+        // Step2: batch write some corrupted historical data
+        String corruptedHistoricalData =
+                "INSERT INTO cleaned_trade_order\n"
+                        + "PARTITION (dt = '2022-04-14')\n"
+                        + "SELECT order_id,\n"
+                        + "  TIMESTAMPADD (\n"
+                        + "    HOUR,\n"
+                        + "    RAND_INTEGER (24),\n"
+                        + "    TO_TIMESTAMP ('2022-04-14', 'yyyy-MM-dd')\n"
+                        + "  ) AS order_timestamp,\n"
+                        + "  IF (\n"
+                        + "    order_verified\n"
+                        + "    AND order_id % 2 = 1,\n"
+                        + "    '404NotFound',\n"
+                        + "    buyer_id\n"
+                        + "  ) AS buyer_id,\n" // corrupt data conditionally
+                        + "  order_amount,\n"
+                        + "  loyalty_discount,\n"
+                        + "  shipping_fee,\n"
+                        + "  order_verified,\n"
+                        + "  IF (\n"
+                        + "    order_verified\n"
+                        + "    AND order_id % 2 = 1,\n"
+                        + "    -1,\n"
+                        + "    order_amount + shipping_fee - 
loyalty_discount\n"
+                        + "  ) AS actual_gmv\n" // corrupt data conditionally
+                        + "FROM\n"
+                        + "  trade_orders\n"
+                        + "  /*+ OPTIONS ('number-of-rows' = '50')  */";
+        batchTableEnv.executeSql(corruptedHistoricalData).await();
+
+        // Step3: start downstream streaming task to read
+        String streamingRead = "SELECT * FROM cleaned_trade_order";
+        BlockingIterator<Row, CleanedTradeOrder> streamIter =
+                BlockingIterator.of(
+                        streamTableEnv.executeSql(streamingRead).collect(), 
ORDER_CONVERTER);
+        // verify historical data is corrupted
+        streamIter.collect(50).stream()
+                .filter(order -> order.orderVerified && order.orderId % 2 == 1)
+                .forEach(
+                        order -> {
+                            assertThat(order.buyerId).isEqualTo("404NotFound");
+                            assertThat(order.actualGmv).isEqualTo(-1);
+                            assertThat(order.dt).isEqualTo("2022-04-14");
+                        });
+
+        // Step4: prepare day-to-day streaming sync task
+        String streamingWrite =
+                "INSERT INTO cleaned_trade_order\n"
+                        + "SELECT order_id,\n"
+                        + "  order_timestamp,\n"
+                        + "  buyer_id,\n"
+                        + "  order_amount,\n"
+                        + "  loyalty_discount,\n"
+                        + "  shipping_fee,\n"
+                        + "  order_verified,\n"
+                        + "  order_amount + shipping_fee - loyalty_discount AS 
actual_gmv,\n"
+                        + "  DATE_FORMAT (order_timestamp, 'yyyy-MM-dd') AS 
dt\n"
+                        + "FROM\n"
+                        + "  trade_orders";
+        JobClient dailyTaskHandler = 
streamTableEnv.executeSql(streamingWrite).getJobClient().get();
+        while (true) {
+            if (dailyTaskHandler.getJobStatus().get() == JobStatus.RUNNING) {
+                break;
+            }
+        }
+
+        // Step5: prepare back-fill task to correct historical data
+        String backFillOverwrite =
+                "INSERT OVERWRITE cleaned_trade_order\n"
+                        + "SELECT order_id,\n"
+                        + "  order_timestamp,\n"
+                        + "  IF (buyer_id = '404NotFound', '_ANONYMOUS_USER_', 
buyer_id) AS buyer_id,\n"
+                        + "  order_amount,\n"
+                        + "  loyalty_discount,\n"
+                        + "  shipping_fee,\n"
+                        + "  order_verified,\n"
+                        + "  IF (\n"
+                        + "    actual_gmv = -1,\n"
+                        + "    order_amount + shipping_fee - 
loyalty_discount,\n"
+                        + "    actual_gmv\n"
+                        + "  ) AS actual_gmv,\n"
+                        + "  dt\n"
+                        + "FROM\n"
+                        + "  cleaned_trade_order\n"
+                        + "WHERE\n"
+                        + "  dt = '2022-04-14';";
+
+        // wait for back-fill task to finish
+        batchTableEnv.executeSql(backFillOverwrite).await();
+
+        // Step6: check streaming read does not achieve any changelog
+        int checkSize = 200;
+        while (checkSize > 0) {
+            Thread.sleep(1000L);
+            streamIter
+                    .collect(10) // rows-per-second is 10
+                    .forEach(order -> 
assertThat(order.dt).isGreaterThan("2022-04-14"));
+            checkSize -= 10;
+        }
+
+        // verify corrupted historical data is corrected
+        BlockingIterator<Row, CleanedTradeOrder> batchIter =
+                BlockingIterator.of(
+                        batchTableEnv
+                                .executeSql(
+                                        "SELECT * FROM cleaned_trade_order 
WHERE dt ='2022-04-14'")
+                                .collect(),
+                        ORDER_CONVERTER);
+        batchIter.collect(50).stream()
+                .filter(order -> order.orderVerified && order.orderId % 2 == 1)
+                .forEach(
+                        order -> {
+                            
assertThat(order.buyerId).isEqualTo("_ANONYMOUS_USER_");
+                            assertThat(order.actualGmv)
+                                    .isEqualTo(
+                                            order.orderAmount
+                                                    + order.shippingFee
+                                                    - order.loyaltyDiscount);
+                            assertThat(order.dt).isEqualTo("2022-04-14");
+                        });
+
+        streamIter.close();
+        dailyTaskHandler.cancel().get();
+    }
+
+    private static final Function<Row, CleanedTradeOrder> ORDER_CONVERTER =
+            (row) -> {
+                assert row != null && row.getArity() == 9;
+                CleanedTradeOrder order = new CleanedTradeOrder();
+                order.orderId = (Long) row.getField(0);
+                order.orderTimestamp = (LocalDateTime) row.getField(1);
+                order.buyerId = (String) row.getField(2);
+                order.orderAmount = (Double) row.getField(3);
+                order.loyaltyDiscount = (Double) row.getField(4);
+                order.shippingFee = (Double) row.getField(5);
+                order.orderVerified = (Boolean) row.getField(6);
+                order.actualGmv = (Double) row.getField(7);
+                order.dt = (String) row.getField(8);
+                return order;
+            };
+
+    /** A test POJO. */
+    private static class CleanedTradeOrder {
+        protected Long orderId;
+        protected LocalDateTime orderTimestamp;
+        protected String buyerId;
+        protected Double orderAmount;
+        protected Double loyaltyDiscount;
+        protected Double shippingFee;
+        protected Boolean orderVerified;
+        protected Double actualGmv;
+        protected String dt;
+    }
+}

Reply via email to