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

yiguolei pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 0ca0d60d5b7 [regression-test](profile) Refine test for profile #35213
0ca0d60d5b7 is described below

commit 0ca0d60d5b767a0f0c8410fceba35aff6f506c1b
Author: zhiqiang <[email protected]>
AuthorDate: Wed May 22 20:50:25 2024 +0800

    [regression-test](profile) Refine test for profile #35213
---
 .../apache/doris/common/util/ProfileManager.java   |   8 +-
 .../data/query_profile/s3_load_profile_test.out    |   4 +
 .../query_profile/s3_load_profile_test.groovy      | 250 +++++++++++++++++++++
 .../suites/query_profile/test_profile.groovy       | 144 +++---------
 4 files changed, 285 insertions(+), 121 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/common/util/ProfileManager.java 
b/fe/fe-core/src/main/java/org/apache/doris/common/util/ProfileManager.java
index eaeb3635c58..6d5724665af 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/util/ProfileManager.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/util/ProfileManager.java
@@ -355,11 +355,16 @@ public class ProfileManager {
             try {
                 loadJobId = Long.parseLong(id);
             } catch (Exception e) {
-                return futures;
+                throw new IllegalArgumentException("Invalid profile id: " + 
id);
             }
 
             LoadJob loadJob = 
Env.getCurrentEnv().getLoadManager().getLoadJob(loadJobId);
+            if (loadJob == null) {
+                throw new RuntimeException("Profile " + id + " not found");
+            }
+
             if (loadJob.getLoadTaskIds() == null) {
+                LOG.warn("Load job {} has no task ids", loadJobId);
                 return futures;
             }
 
@@ -393,7 +398,6 @@ public class ProfileManager {
 
     public String getProfile(String id) {
         List<Future<TGetRealtimeExecStatusResponse>> futures = 
createFetchRealTimeProfileTasks(id);
-
         // beAddr of reportExecStatus of QeProcessorImpl is meaningless, so 
assign a dummy address
         // to avoid compile failing.
         TNetworkAddress dummyAddr = new TNetworkAddress();
diff --git a/regression-test/data/query_profile/s3_load_profile_test.out 
b/regression-test/data/query_profile/s3_load_profile_test.out
new file mode 100644
index 00000000000..2029fd38720
--- /dev/null
+++ b/regression-test/data/query_profile/s3_load_profile_test.out
@@ -0,0 +1,4 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !select --
+20
+
diff --git a/regression-test/suites/query_profile/s3_load_profile_test.groovy 
b/regression-test/suites/query_profile/s3_load_profile_test.groovy
new file mode 100644
index 00000000000..f525c74fba4
--- /dev/null
+++ b/regression-test/suites/query_profile/s3_load_profile_test.groovy
@@ -0,0 +1,250 @@
+// 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.
+
+import groovy.json.JsonSlurper
+
+def getProfile = { id ->
+        def dst = 'http://' + context.config.feHttpAddress
+        def conn = new URL(dst + "/rest/v1/query_profile/$id").openConnection()
+        conn.setRequestMethod("GET")
+        def encoding = 
Base64.getEncoder().encodeToString((context.config.feHttpUser + ":" + 
+                (context.config.feHttpPassword == null ? "" : 
context.config.feHttpPassword)).getBytes("UTF-8"))
+        conn.setRequestProperty("Authorization", "Basic ${encoding}")
+        return conn.getInputStream().getText()
+    }
+
+// ref 
https://github.com/apache/doris/blob/3525a03815814f66ec78aa2ad6bbd9225b0e7a6b/regression-test/suites/load_p0/broker_load/test_s3_load.groovy
+suite('s3_load_profile_test') {
+    sql "drop table if exists dup_tbl_basic;"
+    sql """
+    CREATE TABLE dup_tbl_basic
+(
+    k00 INT             NOT NULL,
+    k01 DATE            NOT NULL,
+    k02 BOOLEAN         NULL,
+    k03 TINYINT         NULL,
+    k04 SMALLINT        NULL,
+    k05 INT             NULL,
+    k06 BIGINT          NULL,
+    k07 LARGEINT        NULL,
+    k08 FLOAT           NULL,
+    k09 DOUBLE          NULL,
+    k10 DECIMAL(9,1)           NULL,
+    k11 DECIMALV3(9,1)         NULL,
+    k12 DATETIME        NULL,
+    k13 DATEV2          NULL,
+    k14 DATETIMEV2      NULL,
+    k15 CHAR            NULL,
+    k16 VARCHAR         NULL,
+    k17 STRING          NULL,
+    k18 JSON            NULL,
+    kd01 BOOLEAN         NOT NULL DEFAULT "TRUE",
+    kd02 TINYINT         NOT NULL DEFAULT "1",
+    kd03 SMALLINT        NOT NULL DEFAULT "2",
+    kd04 INT             NOT NULL DEFAULT "3",
+    kd05 BIGINT          NOT NULL DEFAULT "4",
+    kd06 LARGEINT        NOT NULL DEFAULT "5",
+    kd07 FLOAT           NOT NULL DEFAULT "6.0",
+    kd08 DOUBLE          NOT NULL DEFAULT "7.0",
+    kd09 DECIMAL         NOT NULL DEFAULT "888888888",
+    kd10 DECIMALV3       NOT NULL DEFAULT "999999999",
+    kd11 DATE            NOT NULL DEFAULT "2023-08-24",
+    kd12 DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    kd13 DATEV2          NOT NULL DEFAULT "2023-08-24",
+    kd14 DATETIMEV2      NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    kd15 CHAR(255)       NOT NULL DEFAULT "我能吞下玻璃而不伤身体",
+    kd16 VARCHAR(300)    NOT NULL DEFAULT "我能吞下玻璃而不伤身体",
+    kd17 STRING          NOT NULL DEFAULT "我能吞下玻璃而不伤身体",
+    kd18 JSON            NULL,
+
+    INDEX idx_inverted_k104 (`k05`) USING INVERTED,
+    INDEX idx_inverted_k110 (`k11`) USING INVERTED,
+    INDEX idx_inverted_k113 (`k13`) USING INVERTED,
+    INDEX idx_inverted_k114 (`k14`) USING INVERTED,
+    INDEX idx_inverted_k117 (`k17`) USING INVERTED PROPERTIES("parser" = 
"english"),
+    INDEX idx_ngrambf_k115 (`k15`) USING NGRAM_BF PROPERTIES("gram_size"="3", 
"bf_size"="256"),
+    INDEX idx_ngrambf_k116 (`k16`) USING NGRAM_BF PROPERTIES("gram_size"="3", 
"bf_size"="256"),
+    INDEX idx_ngrambf_k117 (`k17`) USING NGRAM_BF PROPERTIES("gram_size"="3", 
"bf_size"="256"),
+
+    INDEX idx_bitmap_k104 (`k02`) USING BITMAP,
+    INDEX idx_bitmap_k110 (`kd01`) USING BITMAP
+
+)
+    DUPLICATE KEY(k00)
+PARTITION BY RANGE(k01)
+(
+    PARTITION p1 VALUES [('2023-08-01'), ('2023-08-11')),
+    PARTITION p2 VALUES [('2023-08-11'), ('2023-08-21')),
+    PARTITION p3 VALUES [('2023-08-21'), ('2023-09-01'))
+)
+DISTRIBUTED BY HASH(k00) BUCKETS 32
+PROPERTIES (
+    "bloom_filter_columns"="k05",
+    "replication_num" = "1"
+);
+"""
+    def loadAttribute =new 
LoadAttributes("s3://doris-build-1308700295/regression/load/data/basic_data.csv",
+                "dup_tbl_basic", "LINES TERMINATED BY \"\n\"", "COLUMNS 
TERMINATED BY \"|\"", "FORMAT AS \"CSV\"", 
"(k00,k01,k02,k03,k04,k05,k06,k07,k08,k09,k10,k11,k12,k13,k14,k15,k16,k17,k18)",
+                "", "", "", "", "")
+
+    def ak = getS3AK()
+    def sk = getS3SK()
+
+    sql "set enable_profile=true;"    
+    
+    def label = "test_s3_load_" + UUID.randomUUID().toString().replace("-", 
"_")
+    loadAttribute.label = label
+    def prop = loadAttribute.getPropertiesStr()
+
+    def sql_str = """
+        LOAD LABEL $label (
+            $loadAttribute.dataDesc.mergeType
+            DATA INFILE("$loadAttribute.dataDesc.path")
+            INTO TABLE $loadAttribute.dataDesc.tableName
+            $loadAttribute.dataDesc.columnTermClause
+            $loadAttribute.dataDesc.lineTermClause
+            $loadAttribute.dataDesc.formatClause
+            $loadAttribute.dataDesc.columns
+            $loadAttribute.dataDesc.columnsFromPathClause
+            $loadAttribute.dataDesc.columnMappingClause
+            $loadAttribute.dataDesc.precedingFilterClause
+            $loadAttribute.dataDesc.orderByClause
+            $loadAttribute.dataDesc.whereExpr
+        )
+        WITH S3 (
+            "AWS_ACCESS_KEY" = "$ak",
+            "AWS_SECRET_KEY" = "$sk",
+            "AWS_ENDPOINT" = "cos.ap-beijing.myqcloud.com",
+            "AWS_REGION" = "ap-beijing",
+            "use_path_style" = "$loadAttribute.usePathStyle"
+        )
+        ${prop}
+        """
+    logger.info("submit sql: ${sql_str}");
+    sql """${sql_str}"""
+    logger.info("Submit load with lable: $label, table: 
$loadAttribute.dataDesc.tableName, path: $loadAttribute.dataDesc.path")
+
+    def max_try_milli_secs = 600000
+    def jobId = -1
+    while (max_try_milli_secs > 0) {
+        String[][] result = sql """ show load where 
label="$loadAttribute.label" order by createtime desc limit 1; """
+        if (result[0][2].equals("FINISHED")) {
+            if (loadAttribute.isExceptFailed) {
+                assertTrue(false, "load should be failed but was success: 
$result")
+            }
+            jobId = result[0][0].toInteger()
+            logger.info("Load FINISHED " + loadAttribute.label + ": $result" + 
" loadId: $jobId")
+            break
+        }
+        if (result[0][2].equals("CANCELLED")) {
+            if (loadAttribute.isExceptFailed) {
+                logger.info("Load FINISHED " + loadAttribute.label)
+                break
+            }
+            assertTrue(false, "load failed: $result")
+            break
+        }
+        Thread.sleep(1000)
+        max_try_milli_secs -= 1000
+        if (max_try_milli_secs <= 0) {
+            assertTrue(false, "load Timeout: $loadAttribute.label")
+        }
+    }
+
+    qt_select """ select count(*) from $loadAttribute.dataDesc.tableName """
+
+    def profileString = getProfile(jobId)
+    profileJson = new JsonSlurper().parseText(profileString)
+    assertEquals(0, profileJson.code)
+    profileDataString = profileJson.data
+    def taskStateIdx = profileDataString.indexOf("- Task State: FINISHED")
+    assertFalse(taskStateIdx == -1)
+    def fragmentIdx = profileDataString.indexOf(" Fragment 0:")
+    assertFalse(fragmentIdx == -1)
+    def executionProfileIdx = profileDataString.indexOf("Execution Profile")
+    assertFalse(executionProfileIdx == -1)
+    def pattern = ~/Active:\s*([1-9]\d*|0\.\d+|[1-9]\d*\.\d*)ms/
+    def matcher = pattern.matcher(profileDataString)
+    assertTrue(matcher.find())
+}
+
+class DataDesc {
+    public String mergeType = ""
+    public String path
+    public String tableName
+    public String lineTermClause
+    public String columnTermClause
+    public String formatClause
+    public String columns
+    public String columnsFromPathClause
+    public String precedingFilterClause
+    public String columnMappingClause
+    public String whereExpr
+    public String orderByClause
+}
+
+class LoadAttributes {
+    LoadAttributes(String path, String tableName, String lineTermClause, 
String columnTermClause, String formatClause,
+                   String columns, String columnsFromPathClause, String 
precedingFilterClause, String columnMappingClause, String whereExpr, String 
orderByClause, boolean isExceptFailed = false) {
+        this.dataDesc = new DataDesc()
+        this.dataDesc.path = path
+        this.dataDesc.tableName = tableName
+        this.dataDesc.lineTermClause = lineTermClause
+        this.dataDesc.columnTermClause = columnTermClause
+        this.dataDesc.formatClause = formatClause
+        this.dataDesc.columns = columns
+        this.dataDesc.columnsFromPathClause = columnsFromPathClause
+        this.dataDesc.precedingFilterClause = precedingFilterClause
+        this.dataDesc.columnMappingClause = columnMappingClause
+        this.dataDesc.whereExpr = whereExpr
+        this.dataDesc.orderByClause = orderByClause
+
+        this.isExceptFailed = isExceptFailed
+
+        properties = new HashMap<>()
+        properties.put("use_new_load_scan_node", "true")
+    }
+
+    LoadAttributes addProperties(String k, String v) {
+        properties.put(k, v)
+        return this
+    }
+
+    String getPropertiesStr() {
+        if (properties.isEmpty()) {
+            return ""
+        }
+        String prop = "PROPERTIES ("
+        properties.forEach (k, v) -> {
+            prop += "\"${k}\" = \"${v}\","
+        }
+        prop = prop.substring(0, prop.size() - 1)
+        prop += ")"
+        return prop
+    }
+
+    LoadAttributes withPathStyle() {
+        usePathStyle = "true"
+        return this
+    }
+
+    public DataDesc dataDesc
+    public Map<String, String> properties
+    public String label
+    public String usePathStyle = "false"
+    public boolean isExceptFailed
+}
diff --git a/regression-test/suites/query_profile/test_profile.groovy 
b/regression-test/suites/query_profile/test_profile.groovy
index 37d5993b876..fd442726e1d 100644
--- a/regression-test/suites/query_profile/test_profile.groovy
+++ b/regression-test/suites/query_profile/test_profile.groovy
@@ -18,132 +18,38 @@
 import groovy.json.JsonOutput
 import groovy.json.JsonSlurper
 
-/**
-*   @Params url is "/xxx"
-*   @Return response body
-*/
-def http_get(url) {
+def getProfileList = {
     def dst = 'http://' + context.config.feHttpAddress
-    def conn = new URL(dst + url).openConnection()
+    def conn = new URL(dst + "/rest/v1/query_profile").openConnection()
     conn.setRequestMethod("GET")
-    //token for root
-    conn.setRequestProperty("Authorization","Basic cm9vdDo=")
+    def encoding = 
Base64.getEncoder().encodeToString((context.config.feHttpUser + ":" + 
+            (context.config.feHttpPassword == null ? "" : 
context.config.feHttpPassword)).getBytes("UTF-8"))
+    conn.setRequestProperty("Authorization", "Basic ${encoding}")
     return conn.getInputStream().getText()
 }
 
-def SUCCESS_MSG = 'success'
-def SUCCESS_CODE = 0
-def QUERY_NUM = 5
-
-random = new Random()
-
-def getRandomNumber(int num){
-    return random.nextInt(num)
-}
-
-suite('test_profile') {
-
-    // nereids not return same profile with legacy planner, fallback to legacy 
planner.
-    sql """set enable_nereids_planner=false"""
-
-    def table = 'test_profile_table'
-    def id_data = [1,2,3,4,5,6,7]
-    def value_data = [1,2,3,4,5,6,7]
-    def len = id_data.size
-
-    assertEquals(id_data.size, value_data.size)
-
-    sql """ DROP TABLE IF EXISTS ${table} """
-
-    sql """
-        CREATE TABLE IF NOT EXISTS ${table}(
-            `id` INT,
-            `cost` INT
-        )
-        DISTRIBUTED BY HASH(id) BUCKETS 1
-        PROPERTIES (
-            "replication_num" = "1"
-        )
-    """
-    
-    sql """ SET enable_profile = true """
-
-    //———————— test for insert stmt ——————————
-    for(int i = 0; i < len; i++){
-        sql """ INSERT INTO ${table} values (${id_data[i]}, 
"${value_data[i]}") """
+def getProfile = { id ->
+        def dst = 'http://' + context.config.feHttpAddress
+        def conn = new URL(dst + "/rest/v1/query_profile/$id").openConnection()
+        conn.setRequestMethod("GET")
+        def encoding = 
Base64.getEncoder().encodeToString((context.config.feHttpUser + ":" + 
+                (context.config.feHttpPassword == null ? "" : 
context.config.feHttpPassword)).getBytes("UTF-8"))
+        conn.setRequestProperty("Authorization", "Basic ${encoding}")
+        return conn.getInputStream().getText()
     }
 
-    //———————— test for insert stmt (SQL) ——————————
-    log.info("test HTTP API interface for insert profile")
-    def url = '/rest/v1/query_profile/'
-    def query_list_result = http_get(url)
-
-    def obj = new JsonSlurper().parseText(query_list_result)
-    assertEquals(obj.msg, SUCCESS_MSG)
-    assertEquals(obj.code, SUCCESS_CODE)
-
-    for(int i = 0 ; i < len ; i++){
-        def insert_order = len - i - 1
-        def stmt_query_info = obj.data.rows[i]
-        
-        assertNotNull(stmt_query_info["Profile ID"])
-        assertNotEquals(stmt_query_info["Profile ID"], "N/A")
-        
-        assertEquals(stmt_query_info['Sql Statement'].toString(), 
-            """ INSERT INTO ${table} values (${id_data[insert_order]}, 
"${value_data[insert_order]}") """.toString())
-    }
-
-    //———————— test for select stmt ———————————
-    def op_data = ["<", ">", "=", "<=", ">="]
-
-    def ops = []
-    def nums = []
-    
-    for(int i = 0 ; i < QUERY_NUM ; i++){
-        ops.add(op_data[getRandomNumber(5)])
-        nums.add(getRandomNumber(len + 1))
-        sql """ SELECT * FROM ${table} WHERE cost ${ops[i]} ${nums[i]} """
-    }
-    
-    
-    /*  test for `show query profile` stmt
-        query profile header
-        
JobID|QueryId|User|DefaultDb|SQL|QueryType|StartTime|EndTime|TotalTime|QueryState
 */
-    //———————— test for select stmt (SQL) ———————————
-    log.info("test for show query profile stmt")
-    List<List<Object>> show_query_profile_obj = sql """ show query profile "/" 
"""
-    log.info("found ${show_query_profile_obj.size} profile data".toString())
-    assertTrue(show_query_profile_obj.size >= QUERY_NUM)
-
-    for(int i = 0 ; i < QUERY_NUM ; i++){
-        def insert_order = QUERY_NUM - i - 1
-        def current_obj = show_query_profile_obj[i]
-        def stmt_query_info = current_obj[9]
-        assertNotEquals(current_obj[1].toString(), "N/A".toString())
-        assertEquals(stmt_query_info.toString(),  """ SELECT * FROM ${table} 
WHERE cost ${ops[insert_order]} ${nums[insert_order]} """.toString())
-    }
-
-    //———————— test for select stmt (HTTP)————————
-    log.info("test HTTP API interface for query profile")
-    url = '/rest/v1/query_profile/'
-    query_list_result = http_get(url)
-
-    obj = new JsonSlurper().parseText(query_list_result)
-    assertEquals(obj.msg, SUCCESS_MSG)
-    assertEquals(obj.code, SUCCESS_CODE)
+suite('test_profile') {
+    // TODO: more test for normal situation
 
-    for(int i = 0 ; i < QUERY_NUM ; i++){
-        def insert_order = QUERY_NUM - i - 1
-        def stmt_query_info = obj.data.rows[i]
-        
-        assertNotNull(stmt_query_info["Profile ID"])
-        assertNotEquals(stmt_query_info["Profile ID"].toString(), 
"N/A".toString())
-        
-        assertEquals(stmt_query_info['Sql Statement'].toString(), 
-           """ SELECT * FROM ${table} WHERE cost ${ops[insert_order]} 
${nums[insert_order]} """.toString())
-    }
+    // invalidProfileId
+    def invalidProfileString = getProfile("ABCD")
+    logger.info("invalidProfileString:{}", invalidProfileString);
+    def json = new JsonSlurper().parseText(invalidProfileString)
+    assertEquals(500, json.code)
     
-    //———————— clean table and disable profile ————————
-    sql """ SET enable_profile = false """
-    sql """ DROP TABLE IF EXISTS ${table} """
+    // notExistingProfileId
+    def notExistingProfileString = getProfile("-100")
+    logger.info("notExistingProfileString:{}", notExistingProfileString)
+    def json2 = new JsonSlurper().parseText(notExistingProfileString)
+    assertEquals("ID -100 does not exist", json2.data)
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to