Skip to content

Commit 1a3548e

Browse files
authored
Add cache for single measurement validation (#17899)
* Add cache * Add cache config * Add a log when resource is missing in snapshot * Ignore measurement cache performance IT * Only warn when resource not exist in the snapshot * remove unused exception * add import * spotless
1 parent 283bce2 commit 1a3548e

10 files changed

Lines changed: 320 additions & 4 deletions

File tree

‎integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppCommonConfig.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,13 @@ public CommonConfig setTagAttributeTotalSize(int tagAttributeTotalSize) {
523523
return this;
524524
}
525525

526+
@Override
527+
public CommonConfig setSingleMeasurementCheckCacheSize(int singleMeasurementCheckCacheSize) {
528+
setProperty(
529+
"single_measurement_check_cache_size", String.valueOf(singleMeasurementCheckCacheSize));
530+
return this;
531+
}
532+
526533
@Override
527534
public CommonConfig setDnConnectionTimeoutMs(int connectionTimeoutMs) {
528535
setProperty("dn_connection_timeout_ms", String.valueOf(connectionTimeoutMs));

‎integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppSharedCommonConfig.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,13 @@ public CommonConfig setTagAttributeTotalSize(int tagAttributeTotalSize) {
542542
return this;
543543
}
544544

545+
@Override
546+
public CommonConfig setSingleMeasurementCheckCacheSize(int singleMeasurementCheckCacheSize) {
547+
dnConfig.setSingleMeasurementCheckCacheSize(singleMeasurementCheckCacheSize);
548+
cnConfig.setSingleMeasurementCheckCacheSize(singleMeasurementCheckCacheSize);
549+
return this;
550+
}
551+
545552
@Override
546553
public CommonConfig setDnConnectionTimeoutMs(int connectionTimeoutMs) {
547554
dnConfig.setDnConnectionTimeoutMs(connectionTimeoutMs);

‎integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteCommonConfig.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,11 @@ public CommonConfig setTagAttributeTotalSize(int tagAttributeTotalSize) {
383383
return this;
384384
}
385385

386+
@Override
387+
public CommonConfig setSingleMeasurementCheckCacheSize(int singleMeasurementCheckCacheSize) {
388+
return this;
389+
}
390+
386391
@Override
387392
public CommonConfig setDnConnectionTimeoutMs(int connectionTimeoutMs) {
388393
return this;

‎integration-test/src/main/java/org/apache/iotdb/itbase/env/CommonConfig.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ CommonConfig setEnableAutoLeaderBalanceForIoTConsensus(
168168

169169
CommonConfig setTagAttributeTotalSize(int tagAttributeTotalSize);
170170

171+
CommonConfig setSingleMeasurementCheckCacheSize(int singleMeasurementCheckCacheSize);
172+
171173
CommonConfig setDnConnectionTimeoutMs(int connectionTimeoutMs);
172174

173175
CommonConfig setPipeHeartbeatIntervalSecondsForCollectingPipeMeta(
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iotdb.db.it.performance;
21+
22+
import org.apache.iotdb.isession.ISession;
23+
import org.apache.iotdb.it.env.EnvFactory;
24+
import org.apache.iotdb.it.framework.IoTDBTestRunner;
25+
import org.apache.iotdb.itbase.category.LocalStandaloneIT;
26+
import org.apache.iotdb.rpc.IoTDBConnectionException;
27+
import org.apache.iotdb.rpc.StatementExecutionException;
28+
29+
import org.apache.tsfile.enums.TSDataType;
30+
import org.junit.Assert;
31+
import org.junit.Ignore;
32+
import org.junit.Test;
33+
import org.junit.experimental.categories.Category;
34+
import org.junit.runner.RunWith;
35+
import org.slf4j.Logger;
36+
import org.slf4j.LoggerFactory;
37+
38+
import java.util.ArrayList;
39+
import java.util.List;
40+
41+
@Ignore
42+
@RunWith(IoTDBTestRunner.class)
43+
@Category({LocalStandaloneIT.class})
44+
public class IoTDBSingleMeasurementCheckCachePerformanceIT {
45+
46+
private static final Logger LOGGER =
47+
LoggerFactory.getLogger(IoTDBSingleMeasurementCheckCachePerformanceIT.class);
48+
49+
private static final ExperimentGroup[] EXPERIMENT_GROUPS = {
50+
new ExperimentGroup(1_000, 500),
51+
new ExperimentGroup(2_000, 2_000),
52+
new ExperimentGroup(4_000, 8_000)
53+
};
54+
private static final int CACHE_DISABLED_SIZE = 0;
55+
private static final int REPEAT_COUNT = 3;
56+
private static final int BATCH_COUNT = 800;
57+
private static final int ROWS_PER_BATCH = 100;
58+
private static final String DEVICE = "root.sg_cache_perf.d1";
59+
60+
@Test
61+
public void testEndToEndWritePerformanceWithDifferentSingleMeasurementCheckCacheSizes()
62+
throws Exception {
63+
for (ExperimentGroup experimentGroup : EXPERIMENT_GROUPS) {
64+
long totalDisabledCost = 0;
65+
long totalEnabledCost = 0;
66+
for (int repeatIndex = 0; repeatIndex < REPEAT_COUNT; repeatIndex++) {
67+
long disabledCost =
68+
runWritePerformanceExperiment(
69+
new Experiment(experimentGroup.measurementCount, CACHE_DISABLED_SIZE));
70+
long enabledCost =
71+
runWritePerformanceExperiment(
72+
new Experiment(experimentGroup.measurementCount, experimentGroup.cacheSize));
73+
totalDisabledCost += disabledCost;
74+
totalEnabledCost += enabledCost;
75+
LOGGER.info(
76+
"End-to-end write cost repeat {}/{} with measurementCount={}, cache disabled: {} ms, "
77+
+ "cacheSize={} (cacheSize {} measurementCount): {} ms, enabled/disabled ratio: {}",
78+
repeatIndex + 1,
79+
REPEAT_COUNT,
80+
experimentGroup.measurementCount,
81+
disabledCost / 1_000_000,
82+
experimentGroup.cacheSize,
83+
experimentGroup.cacheSizeRelation(),
84+
enabledCost / 1_000_000,
85+
String.format("%.3f", (double) enabledCost / disabledCost));
86+
}
87+
long averageDisabledCost = totalDisabledCost / REPEAT_COUNT;
88+
long averageEnabledCost = totalEnabledCost / REPEAT_COUNT;
89+
LOGGER.info(
90+
"Average end-to-end write cost after {} repeats with measurementCount={}, cache disabled: "
91+
+ "{} ms, cacheSize={} (cacheSize {} measurementCount): {} ms, enabled/disabled "
92+
+ "ratio: {}",
93+
REPEAT_COUNT,
94+
experimentGroup.measurementCount,
95+
averageDisabledCost / 1_000_000,
96+
experimentGroup.cacheSize,
97+
experimentGroup.cacheSizeRelation(),
98+
averageEnabledCost / 1_000_000,
99+
String.format("%.3f", (double) averageEnabledCost / averageDisabledCost));
100+
Assert.assertTrue(totalDisabledCost > 0);
101+
Assert.assertTrue(totalEnabledCost > 0);
102+
}
103+
}
104+
105+
private long runWritePerformanceExperiment(Experiment experiment) throws Exception {
106+
EnvFactory.getEnv()
107+
.getConfig()
108+
.getCommonConfig()
109+
.setSingleMeasurementCheckCacheSize(experiment.cacheSize)
110+
.setAutoCreateSchemaEnabled(false);
111+
EnvFactory.getEnv().initClusterEnvironment();
112+
try {
113+
try (ISession session = EnvFactory.getEnv().getSessionConnection()) {
114+
createTimeseries(session, experiment.measurementCount);
115+
long cost = executeWriteWorkload(session, experiment.measurementCount);
116+
assertRowCount(session);
117+
return cost;
118+
}
119+
} finally {
120+
EnvFactory.getEnv().cleanClusterEnvironment();
121+
}
122+
}
123+
124+
private void createTimeseries(ISession session, int measurementCount)
125+
throws IoTDBConnectionException, StatementExecutionException {
126+
session.executeNonQueryStatement("CREATE DATABASE root.sg_cache_perf");
127+
for (int i = 0; i < measurementCount; i++) {
128+
session.executeNonQueryStatement(
129+
"CREATE TIMESERIES "
130+
+ DEVICE
131+
+ ".`sensor+"
132+
+ i
133+
+ "` WITH DATATYPE=INT64, ENCODING=PLAIN");
134+
}
135+
}
136+
137+
private long executeWriteWorkload(ISession session, int measurementCount)
138+
throws IoTDBConnectionException, StatementExecutionException {
139+
List<String> measurements = new ArrayList<>(measurementCount);
140+
List<TSDataType> types = new ArrayList<>(measurementCount);
141+
List<Object> values = new ArrayList<>(measurementCount);
142+
for (int i = 0; i < measurementCount; i++) {
143+
measurements.add("`sensor+" + i + "`");
144+
types.add(TSDataType.INT64);
145+
values.add((long) i);
146+
}
147+
148+
long startTime = System.nanoTime();
149+
for (int batchIndex = 0; batchIndex < BATCH_COUNT; batchIndex++) {
150+
List<Long> timestamps = new ArrayList<>(ROWS_PER_BATCH);
151+
List<List<String>> measurementsList = new ArrayList<>(ROWS_PER_BATCH);
152+
List<List<TSDataType>> typesList = new ArrayList<>(ROWS_PER_BATCH);
153+
List<List<Object>> valuesList = new ArrayList<>(ROWS_PER_BATCH);
154+
for (int rowIndex = 0; rowIndex < ROWS_PER_BATCH; rowIndex++) {
155+
timestamps.add((long) batchIndex * ROWS_PER_BATCH + rowIndex);
156+
measurementsList.add(measurements);
157+
typesList.add(types);
158+
valuesList.add(values);
159+
}
160+
session.insertRecordsOfOneDevice(DEVICE, timestamps, measurementsList, typesList, valuesList);
161+
}
162+
return System.nanoTime() - startTime;
163+
}
164+
165+
private void assertRowCount(ISession session)
166+
throws IoTDBConnectionException, StatementExecutionException {
167+
try (org.apache.iotdb.isession.SessionDataSet dataSet =
168+
session.executeQueryStatement("SELECT COUNT(`sensor+0`) FROM " + DEVICE)) {
169+
Assert.assertTrue(dataSet.hasNext());
170+
Assert.assertEquals(
171+
(long) BATCH_COUNT * ROWS_PER_BATCH, dataSet.next().getFields().get(0).getLongV());
172+
Assert.assertFalse(dataSet.hasNext());
173+
}
174+
}
175+
176+
private static class ExperimentGroup {
177+
178+
private final int measurementCount;
179+
private final int cacheSize;
180+
181+
private ExperimentGroup(int measurementCount, int cacheSize) {
182+
this.measurementCount = measurementCount;
183+
this.cacheSize = cacheSize;
184+
}
185+
186+
private String cacheSizeRelation() {
187+
if (cacheSize < measurementCount) {
188+
return "<";
189+
}
190+
if (cacheSize == measurementCount) {
191+
return "=";
192+
}
193+
return ">";
194+
}
195+
}
196+
197+
private static class Experiment {
198+
199+
private final int measurementCount;
200+
private final int cacheSize;
201+
202+
private Experiment(int measurementCount, int cacheSize) {
203+
this.measurementCount = measurementCount;
204+
this.cacheSize = cacheSize;
205+
}
206+
}
207+
}

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/SnapshotLoader.java‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
import org.apache.iotdb.db.storageengine.StorageEngine;
2929
import org.apache.iotdb.db.storageengine.dataregion.DataRegion;
3030
import org.apache.iotdb.db.storageengine.dataregion.flush.CompressionRatio;
31+
import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource;
3132

33+
import org.apache.tsfile.common.constant.TsFileConstant;
3234
import org.apache.tsfile.external.commons.io.FileUtils;
3335
import org.slf4j.Logger;
3436
import org.slf4j.LoggerFactory;
@@ -360,6 +362,8 @@ private void createLinksFromSnapshotToSourceDir(
360362
String targetSuffix, File[] files, FolderManager folderManager) throws IOException {
361363
Map<String, String> fileTarget = new HashMap<>();
362364
for (File file : files) {
365+
checkTsFileResourceExists(file);
366+
363367
String fileKey = file.getName().split("\\.")[0];
364368
String dataDir = fileTarget.get(fileKey);
365369

@@ -394,6 +398,17 @@ private void createLinksFromSnapshotToSourceDir(
394398
}
395399
}
396400

401+
private void checkTsFileResourceExists(File file) {
402+
if (!file.getName().endsWith(TsFileConstant.TSFILE_SUFFIX)) {
403+
return;
404+
}
405+
406+
String resourceFileName = file.getAbsolutePath() + TsFileResource.RESOURCE_SUFFIX;
407+
if (!new File(resourceFileName).exists()) {
408+
LOGGER.warn("The associated resource file of {} is not found in the snapshot", file);
409+
}
410+
}
411+
397412
private void createLinksFromSnapshotDirToDataDirWithLog() throws IOException {
398413
String snapshotId = logAnalyzer.getSnapshotId();
399414
int loggedFileNum = logAnalyzer.getTotalFileCountInSnapshot();

‎iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,12 @@ tag_attribute_flush_interval=1000
909909
# Datatype: int
910910
tag_attribute_total_size=700
911911

912+
# The maximum number of single measurement check results cached by PathUtils.
913+
# Set to 0 to disable this cache.
914+
# effectiveMode: restart
915+
# Datatype: int
916+
single_measurement_check_cache_size=10000
917+
912918
# max measurement num of internal request
913919
# When creating timeseries with Session.createMultiTimeseries, the user input plan, the timeseries num of
914920
# which exceeds this num, will be split to several plans with timeseries no more than this num.

‎iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,8 @@ public class CommonConfig {
431431
// Max size for tag and attribute of one time series
432432
private int tagAttributeTotalSize = 700;
433433

434+
private int singleMeasurementCheckCacheSize = 10_000;
435+
434436
// maximum number of Cluster Databases allowed
435437
private int databaseLimitThreshold = -1;
436438

@@ -2783,6 +2785,14 @@ public void setTagAttributeTotalSize(int tagAttributeTotalSize) {
27832785
this.tagAttributeTotalSize = tagAttributeTotalSize;
27842786
}
27852787

2788+
public int getSingleMeasurementCheckCacheSize() {
2789+
return singleMeasurementCheckCacheSize;
2790+
}
2791+
2792+
public void setSingleMeasurementCheckCacheSize(int singleMeasurementCheckCacheSize) {
2793+
this.singleMeasurementCheckCacheSize = singleMeasurementCheckCacheSize;
2794+
}
2795+
27862796
public int getDatabaseLimitThreshold() {
27872797
return databaseLimitThreshold;
27882798
}

‎iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonDescriptor.java‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,15 @@ public void loadCommonProps(TrimProperties properties) throws IOException {
276276
properties.getProperty(
277277
"tag_attribute_total_size", String.valueOf(config.getTagAttributeTotalSize()))));
278278

279+
int singleMeasurementCheckCacheSize =
280+
Integer.parseInt(
281+
properties.getProperty(
282+
"single_measurement_check_cache_size",
283+
String.valueOf(config.getSingleMeasurementCheckCacheSize())));
284+
if (singleMeasurementCheckCacheSize >= 0) {
285+
config.setSingleMeasurementCheckCacheSize(singleMeasurementCheckCacheSize);
286+
}
287+
279288
config.setTimePartitionOrigin(
280289
Long.parseLong(
281290
properties.getProperty(

0 commit comments

Comments
 (0)