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

jt2594838 pushed a commit to branch dev/1.3
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/dev/1.3 by this push:
     new 8e402ea5f72 [Performance] Optimize tablet RPC deserialization (#18248) 
(#18268)
8e402ea5f72 is described below

commit 8e402ea5f722e8f668d1543650a1c0969207c0e5
Author: Caideyipi <[email protected]>
AuthorDate: Wed Jul 22 11:55:30 2026 +0800

    [Performance] Optimize tablet RPC deserialization (#18248) (#18268)
    
    * [Performance] Optimize tablet RPC deserialization
    
    * [Performance] Add tablet RPC deserialization benchmarks
    
    * Address tablet deserialization review comments
---
 .../protocol/thrift/impl/ClientRPCServiceImpl.java |  6 +-
 .../plan/parser/StatementGenerator.java            | 45 +++++++------
 .../org/apache/iotdb/commons/utils/PathUtils.java  | 68 ++++++++++++++++++-
 .../apache/iotdb/commons/utils/PathUtilsTest.java  | 77 ++++++++++++++++++++++
 4 files changed, 166 insertions(+), 30 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
index 3fe0e5e5267..c4bfd21a7a4 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
@@ -2058,9 +2058,7 @@ public class ClientRPCServiceImpl implements 
IClientRPCServiceWithHandler {
       if (!SESSION_MANAGER.checkLogin(clientSession)) {
         return getNotLoggedInStatus();
       }
-
-      req.setMeasurementsList(
-          
PathUtils.checkIsLegalSingleMeasurementListsAndUpdate(req.getMeasurementsList()));
+      
PathUtils.checkIsLegalSingleMeasurementListsAndUpdateInPlace(req.getMeasurementsList());
 
       // Step 1: transfer from TSInsertTabletsReq to Statement
       InsertMultiTabletsStatement statement = 
StatementGenerator.createStatement(req);
@@ -2119,7 +2117,7 @@ public class ClientRPCServiceImpl implements 
IClientRPCServiceWithHandler {
       }
 
       // check whether measurement is legal according to syntax convention
-      
req.setMeasurements(PathUtils.checkIsLegalSingleMeasurementsAndUpdate(req.getMeasurements()));
+      
PathUtils.checkIsLegalSingleMeasurementsAndUpdateInPlace(req.getMeasurements());
       // Step 1: transfer from TSInsertTabletReq to Statement
       InsertTabletStatement statement = 
StatementGenerator.createStatement(req);
       // return success when this statement is empty because server doesn't 
need to execute it
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGenerator.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGenerator.java
index 9f149abd531..9e19889ee2b 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGenerator.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGenerator.java
@@ -313,6 +313,7 @@ public class StatementGenerator {
     insertStatement.setDevicePath(
         DEVICE_PATH_CACHE.getPartialPath(insertTabletReq.getPrefixPath()));
     
insertStatement.setMeasurements(insertTabletReq.getMeasurements().toArray(new 
String[0]));
+    TSDataType[] dataTypes = deserializeDataTypes(insertTabletReq.types);
     long[] timestamps =
         QueryDataSetUtils.readTimesFromBuffer(insertTabletReq.timestamps, 
insertTabletReq.size);
     if (timestamps.length != 0) {
@@ -321,19 +322,12 @@ public class StatementGenerator {
     insertStatement.setTimes(timestamps);
     insertStatement.setColumns(
         QueryDataSetUtils.readTabletValuesFromBuffer(
-            insertTabletReq.values,
-            insertTabletReq.types,
-            insertTabletReq.types.size(),
-            insertTabletReq.size));
+            insertTabletReq.values, dataTypes, dataTypes.length, 
insertTabletReq.size));
     insertStatement.setBitMaps(
         QueryDataSetUtils.readBitMapsFromBuffer(
-                insertTabletReq.values, insertTabletReq.types.size(), 
insertTabletReq.size)
+                insertTabletReq.values, dataTypes.length, insertTabletReq.size)
             .orElse(null));
     insertStatement.setRowCount(insertTabletReq.size);
-    TSDataType[] dataTypes = new TSDataType[insertTabletReq.types.size()];
-    for (int i = 0; i < insertTabletReq.types.size(); i++) {
-      dataTypes[i] = TSDataType.deserialize((byte) 
insertTabletReq.types.get(i).intValue());
-    }
     insertStatement.setDataTypes(dataTypes);
     insertStatement.setAligned(insertTabletReq.isAligned);
     PERFORMANCE_OVERVIEW_METRICS.recordParseCost(System.nanoTime() - 
startTime);
@@ -345,32 +339,29 @@ public class StatementGenerator {
     final long startTime = System.nanoTime();
     // construct insert statement
     InsertMultiTabletsStatement insertStatement = new 
InsertMultiTabletsStatement();
-    List<InsertTabletStatement> insertTabletStatementList = new ArrayList<>();
-    for (int i = 0; i < req.prefixPaths.size(); i++) {
+    int tabletCount = req.prefixPaths.size();
+    List<InsertTabletStatement> insertTabletStatementList = new 
ArrayList<>(tabletCount);
+    for (int i = 0; i < tabletCount; i++) {
+      List<String> measurements = req.measurementsList.get(i);
+      TSDataType[] dataTypes = deserializeDataTypes(req.typesList.get(i));
+      int rowCount = req.sizeList.get(i);
       InsertTabletStatement insertTabletStatement = new 
InsertTabletStatement();
       
insertTabletStatement.setDevicePath(DEVICE_PATH_CACHE.getPartialPath(req.prefixPaths.get(i)));
-      
insertTabletStatement.setMeasurements(req.measurementsList.get(i).toArray(new 
String[0]));
+      insertTabletStatement.setMeasurements(measurements.toArray(new 
String[0]));
       long[] timestamps =
-          QueryDataSetUtils.readTimesFromBuffer(req.timestampsList.get(i), 
req.sizeList.get(i));
+          QueryDataSetUtils.readTimesFromBuffer(req.timestampsList.get(i), 
rowCount);
       if (timestamps.length != 0) {
         
TimestampPrecisionUtils.checkTimestampPrecision(timestamps[timestamps.length - 
1]);
       }
       insertTabletStatement.setTimes(timestamps);
       insertTabletStatement.setColumns(
           QueryDataSetUtils.readTabletValuesFromBuffer(
-              req.valuesList.get(i),
-              req.typesList.get(i),
-              req.measurementsList.get(i).size(),
-              req.sizeList.get(i)));
+              req.valuesList.get(i), dataTypes, measurements.size(), 
rowCount));
       insertTabletStatement.setBitMaps(
           QueryDataSetUtils.readBitMapsFromBuffer(
-                  req.valuesList.get(i), req.measurementsList.get(i).size(), 
req.sizeList.get(i))
+                  req.valuesList.get(i), measurements.size(), rowCount)
               .orElse(null));
-      insertTabletStatement.setRowCount(req.sizeList.get(i));
-      TSDataType[] dataTypes = new TSDataType[req.typesList.get(i).size()];
-      for (int j = 0; j < dataTypes.length; j++) {
-        dataTypes[j] = TSDataType.deserialize((byte) 
req.typesList.get(i).get(j).intValue());
-      }
+      insertTabletStatement.setRowCount(rowCount);
       insertTabletStatement.setDataTypes(dataTypes);
       insertTabletStatement.setAligned(req.isAligned);
       // skip empty tablet
@@ -384,6 +375,14 @@ public class StatementGenerator {
     return insertStatement;
   }
 
+  private static TSDataType[] deserializeDataTypes(List<Integer> 
serializedDataTypes) {
+    TSDataType[] dataTypes = new TSDataType[serializedDataTypes.size()];
+    for (int i = 0; i < dataTypes.length; i++) {
+      dataTypes[i] = TSDataType.deserialize((byte) 
serializedDataTypes.get(i).intValue());
+    }
+    return dataTypes;
+  }
+
   public static InsertRowsStatement createStatement(TSInsertRecordsReq req)
       throws IllegalPathException, QueryProcessException {
     final long startTime = System.nanoTime();
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/PathUtils.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/PathUtils.java
index e3af7aa5eb9..41a77d3be31 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/PathUtils.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/PathUtils.java
@@ -69,7 +69,7 @@ public class PathUtils {
     }
     // skip checking duplicated measurements
     Map<String, String> checkedMeasurements = new HashMap<>();
-    List<List<String>> res = new ArrayList<>();
+    List<List<String>> res = new ArrayList<>(measurementLists.size());
     for (List<String> measurements : measurementLists) {
       res.add(checkLegalSingleMeasurementsAndSkipDuplicate(measurements, 
checkedMeasurements));
     }
@@ -85,7 +85,7 @@ public class PathUtils {
     if (measurements == null) {
       return null;
     }
-    List<String> res = new ArrayList<>();
+    List<String> res = new ArrayList<>(measurements.size());
     for (String measurement : measurements) {
       if (measurement == null) {
         res.add(null);
@@ -110,7 +110,7 @@ public class PathUtils {
     if (measurements == null) {
       return null;
     }
-    List<String> res = new ArrayList<>();
+    List<String> res = new ArrayList<>(measurements.size());
     for (String measurement : measurements) {
       if (measurement == null) {
         continue;
@@ -120,6 +120,68 @@ public class PathUtils {
     return res;
   }
 
+  /**
+   * Check and canonicalize single measurements in place. This avoids 
allocating another list when
+   * the input is a mutable list created by Thrift.
+   */
+  public static void 
checkIsLegalSingleMeasurementsAndUpdateInPlace(List<String> measurements)
+      throws MetadataException {
+    if (measurements == null) {
+      return;
+    }
+    for (int i = 0; i < measurements.size(); ) {
+      String measurement = measurements.get(i);
+      if (measurement == null) {
+        measurements.remove(i);
+      } else {
+        measurements.set(i, checkAndReturnSingleMeasurement(measurement));
+        i++;
+      }
+    }
+  }
+
+  /**
+   * Check and canonicalize lists of single measurements in place. Duplicate 
measurements in one
+   * request are checked only once.
+   */
+  public static void checkIsLegalSingleMeasurementListsAndUpdateInPlace(
+      List<List<String>> measurementLists) throws MetadataException {
+    if (measurementLists == null || measurementLists.isEmpty()) {
+      return;
+    }
+    if (measurementLists.size() == 1) {
+      List<String> measurements = measurementLists.get(0);
+      if (measurements == null) {
+        return;
+      }
+      for (int i = 0; i < measurements.size(); i++) {
+        String measurement = measurements.get(i);
+        if (measurement != null) {
+          measurements.set(i, checkAndReturnSingleMeasurement(measurement));
+        }
+      }
+      return;
+    }
+    Map<String, String> checkedMeasurements = new HashMap<>();
+    for (List<String> measurements : measurementLists) {
+      if (measurements == null) {
+        continue;
+      }
+      for (int i = 0; i < measurements.size(); i++) {
+        String measurement = measurements.get(i);
+        if (measurement == null) {
+          continue;
+        }
+        String checked = checkedMeasurements.get(measurement);
+        if (checked == null) {
+          checked = checkAndReturnSingleMeasurement(measurement);
+          checkedMeasurements.put(measurement, checked);
+        }
+        measurements.set(i, checked);
+      }
+    }
+  }
+
   /**
    * check whether measurement is legal according to syntax convention 
measurement could be like a.b
    * (more than one node name), in template?
diff --git 
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/PathUtilsTest.java
 
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/PathUtilsTest.java
new file mode 100644
index 00000000000..1119e54737f
--- /dev/null
+++ 
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/PathUtilsTest.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.iotdb.commons.utils;
+
+import org.apache.iotdb.commons.exception.MetadataException;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+public class PathUtilsTest {
+
+  @Test
+  public void testCheckSingleMeasurementsInPlace() throws MetadataException {
+    List<String> measurements =
+        new ArrayList<>(Arrays.asList("path_utils_test_s1", 
"`path_utils_test_s2`", null));
+
+    PathUtils.checkIsLegalSingleMeasurementsAndUpdateInPlace(measurements);
+
+    assertEquals("path_utils_test_s1", measurements.get(0));
+    assertEquals("path_utils_test_s2", measurements.get(1));
+    assertEquals(2, measurements.size());
+  }
+
+  @Test
+  public void testCheckSingleMeasurementListsInPlaceReusesStrings() throws 
MetadataException {
+    List<List<String>> measurementLists = new ArrayList<>();
+    measurementLists.add(
+        new ArrayList<>(
+            Arrays.asList(new String("path_utils_batch_s1"), new 
String("`path_utils_batch_s2`"))));
+    measurementLists.add(
+        new ArrayList<>(
+            Arrays.asList(new String("path_utils_batch_s1"), new 
String("`path_utils_batch_s2`"))));
+
+    
PathUtils.checkIsLegalSingleMeasurementListsAndUpdateInPlace(measurementLists);
+
+    assertSame(measurementLists.get(0).get(0), measurementLists.get(1).get(0));
+    assertSame(measurementLists.get(0).get(1), measurementLists.get(1).get(1));
+    assertEquals("path_utils_batch_s2", measurementLists.get(0).get(1));
+  }
+
+  @Test
+  public void testCheckSingleMeasurementListInPlace() throws MetadataException 
{
+    List<String> measurements =
+        new ArrayList<>(Arrays.asList("path_utils_batch_s1", 
"`path_utils_batch_s2`", null));
+
+    PathUtils.checkIsLegalSingleMeasurementListsAndUpdateInPlace(
+        Collections.singletonList(measurements));
+
+    assertEquals("path_utils_batch_s1", measurements.get(0));
+    assertEquals("path_utils_batch_s2", measurements.get(1));
+    assertNull(measurements.get(2));
+  }
+}

Reply via email to