1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #define LOG_TAG "SingleStorePerfPhoneTest"
16 #include "distributed_kv_data_manager.h"
17 #include <gtest/gtest.h>
18 #include "log_print.h"
19 #include <random>
20 #include <sys/time.h>
21 #include <thread>
22 #include <vector>
23 #include "store_manager.h"
24 #include "types.h"
25 #include "process_system_api_adapter_impl.h"
26
27 #ifdef DB_DEBUG_ENV
28
29 #include "system_time.h"
30
31 #endif // DB_DEBUG_ENV
32
33 using namespace testing::ext;
34 using namespace OHOS::DistributedKv;
35 using namespace std;
36 namespace OHOS::Test {
37 // define some variables to init a KvStoreDelegateManager object.
38 std::shared_ptr<SingleKvStore> store1;
39 std::shared_ptr<SingleKvStore> store2;
40 std::shared_ptr<SingleKvStore> store3;
41
42 class SingleStorePerfPhoneTest : public testing::Test {
43 public:
44 struct DataConfig {
45 int batch;
46 int count;
47 int size;
48 int ratio;
49 };
50
51 struct PresetResult {
52 std::string firstKey;
53 std::string lastKey;
54 };
55 std::shared_ptr<SingleKvStore> CreateKVStore(std::string storeIdTest, KvStoreType type, bool encrypt, bool backup);
56 static void SetUpTestCase();
57 static void TearDownTestCase();
58 void SetUp() override;
59 void TearDown() override;
60 };
61
GenerateBytes(const string & str)62 OHOS::DistributedKv::Key GenerateBytes(const string &str)
63 {
64 DistributedDB::Key bytes;
65 const char *buffer = str.c_str();
66 for (uint32_t i = 0; i < str.size(); i++) {
67 bytes.emplace_back(buffer[i]);
68 }
69 return bytes;
70 }
71
GenerateRandomString(long length)72 string GenerateRandomString(long length)
73 {
74 std::string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
75 std::string out;
76 std::random_device rd;
77 std::mt19937 gen(rd());
78 std::uniform_int_distribution<> dis(0, chars.size() - 1);
79 for (int i = 0; i < length; i++) {
80 out += chars[dis(gen)];
81 }
82 return out;
83 }
84
GetDate(char * cType,int iDay)85 string GetDate(char *cType, int iDay)
86 {
87 char cTimePARTNUMBER[256] = "";
88 time_t rawtime;
89 struct tm timeinfo{};
90 rawtime = time(nullptr) + iDay * 24 * 3600; // 24 hours a day, 3600 seconds per hour
91 localtime_r(&rawtime, &timeinfo);
92 char buffer1[256];
93 size_t n = strftime(cTimePARTNUMBER, sizeof(buffer1), cType, &timeinfo); //20180623
94 if (n != 0) {
95 string strTimePARTNUMBER = cTimePARTNUMBER;
96 return strTimePARTNUMBER;
97 } else {
98 return "";
99 }
100 }
101
102 /**
103 * Presets data to a specified single-key value store.
104 * @param store Smart pointer of a single key-value store.
105 * @param batch The number of batches, representing the batch in which the data was inserted.
106 * @param count Number of pictures on a screen.
107 * @param size Size of the value in kilobytes for each key-value pair.
108 * @param ratio Ratio of compression
109 * @param firstKey An output parameter that receives the first key in the first batch
110 * @param lastKey An output parameter that receives the last key in the last batch
111 * @details This function is responsible for writing a preset set of data to a specified
112 * single-key value store based on a given batch, quantity, size, and ratio.
113 */
PresetData(std::shared_ptr<SingleKvStore> & store,SingleStorePerfPhoneTest::DataConfig & config,SingleStorePerfPhoneTest::PresetResult & result)114 void PresetData(std::shared_ptr<SingleKvStore> &store,
115 SingleStorePerfPhoneTest::DataConfig &config, SingleStorePerfPhoneTest::PresetResult &result)
116 {
117 std::ostringstream s;
118 s << std::setw(16) << std::setfill('0') << 0; // 16 bytes
119 string strDate;
120 // The batches are iterated backward to generate data for different dates
121 string ss = to_string(config.size) + "K";
122 for (int i = config.batch; i >= 1; i--) {
123 strDate = GetDate(const_cast<char*>("%Y%m%d"), -i);
124 for (int index = 1; index <= config.count; index++) {
125 // Produces a unique key string containing date, size, and index information
126 string tmp =
127 s.str() + "_" + ss + "_" + strDate + string(3 - to_string(index).length(), '0') + to_string(index);
128 string val;
129 // Generate random value string based on ratio If the ratio is 0, an empty string is generated
130 if (config.ratio != 0) {
131 val = GenerateRandomString(static_cast<long>(config.size * 1024) / config.ratio); // 1024 bytes per 1K
132 } else {
133 val = GenerateRandomString(0);
134 }
135 const DistributedDB::Key key = GenerateBytes(tmp);
136 const DistributedDB::Value value = GenerateBytes(val);
137 ASSERT_EQ(store->Put(key, value), SUCCESS);
138 if (i == config.batch && index == 1) {
139 // Convert the contents of the key to std::string and assign it to the firstKey
140 result.firstKey = std::string(key.begin(), key.end());
141 }
142 if (i == 1 && index == config.count) {
143 result.lastKey = std::string(key.begin(), key.end());
144 }
145 }
146 }
147 cout << "put success!" << endl;
148 }
149
150 /**
151 * @brief Calculates the duration of a RangeResultSet.
152 * @param store Smart pointer of a single key-value store.
153 * @param batch The number of batches, representing the batch in which the data was inserted.
154 * @param size Size of the value in kilobytes for each key-value pair.
155 * @param count Number of pictures on a screen.
156 * @param firstKey An output parameter that receives the first key in the first batch
157 * @param lastKey An output parameter that receives the last key in the last batch
158 * @details This function is used to calculate the duration for obtaining data on a screen.
159 * Data is obtained through multiple cycles and the average time is calculated.
160 */
CalcRangeResultSetDuration(std::shared_ptr<SingleKvStore> & store,SingleStorePerfPhoneTest::DataConfig & config,SingleStorePerfPhoneTest::PresetResult & result)161 void CalcRangeResultSetDuration(std::shared_ptr<SingleKvStore> &store,
162 SingleStorePerfPhoneTest::DataConfig &config, SingleStorePerfPhoneTest::PresetResult &result)
163 {
164 // batch = totolCnt / (448/ 112) size(4 8) count (448 112)
165 double dur = 0.0;
166 double totalTime = 0.0;
167 double avrTime = 0.0;
168 int failCount = 0;
169 // The outer loop is run 100 times to improve the accuracy of the test results
170 for (int n = 0; n < 100; ++n) { // 100 times
171 DataQuery query;
172 // The sequential query is carried out according to the actual business scenario
173 query.Between("", result.lastKey);
174 std::shared_ptr<KvStoreResultSet> readResultSet;
175 ASSERT_EQ(store->GetResultSet(query, readResultSet), SUCCESS);
176 ASSERT_TRUE(readResultSet != nullptr);
177 for (int ind = config.batch; ind >= 1; ind--) {
178 struct timeval startTime{};
179 struct timeval endTime{};
180 (void) gettimeofday(&startTime, nullptr);
181 for (int i = 0; i < config.count; ++i) { // Loop through a screen of data
182 readResultSet->MoveToNext(); // Move the read position to the next row.
183 Entry entry; // Data is organized by entry definition.
184 readResultSet->GetEntry(entry);
185 }
186 (void) gettimeofday(&endTime, nullptr);
187 double startUsec = static_cast<double>((startTime.tv_sec * 1000 * 1000) + startTime.tv_usec);
188 double endUsec = static_cast<double>((endTime.tv_sec * 1000 * 1000) + endTime.tv_usec);
189 dur = endUsec - startUsec;
190 totalTime += dur;
191 avrTime = (dur / 1000); // 1000 is to convert ms
192 if (avrTime >= 3.0) { // 3.0 ms is upper bound on performance
193 failCount += 1;
194 }
195 }
196 EXPECT_EQ(store->CloseResultSet(readResultSet), SUCCESS);
197 readResultSet = nullptr;
198 }
199 if (config.batch != 0) {
200 // 100 is for unit conversion
201 avrTime = (((totalTime / config.batch) / 100) / 1000); // 1000 is to convert ms
202 cout << "Scan Range ResultSet avg cost = " << avrTime << " ms." << endl;
203 cout << "failCount: " << failCount << endl;
204 EXPECT_LT(avrTime, 3.0); // 3.0 ms is upper bound on performance
205 } else {
206 cout << "Error: Division by zero." << endl;
207 }
208 }
209
SetUpTestCase()210 void SingleStorePerfPhoneTest::SetUpTestCase()
211 {
212 std::string baseDir = "/data/service/el1/public/database/SingleStorePerfPhoneTest";
213 mkdir(baseDir.c_str(), (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH));
214 }
215
TearDownTestCase()216 void SingleStorePerfPhoneTest::TearDownTestCase()
217 {
218 std::string baseDir = "/data/service/el1/public/database/SingleStorePerfPhoneTest";
219 StoreManager::GetInstance().Delete({ "SingleStorePerfPhoneTest" }, { "SingleKVStore" }, baseDir);
220
221 (void)remove("/data/service/el1/public/database/SingleStorePerfPhoneTest/key");
222 (void)remove("/data/service/el1/public/database/SingleStorePerfPhoneTest/kvdb");
223 (void)remove("/data/service/el1/public/database/SingleStorePerfPhoneTest");
224 }
225
SetUp()226 void SingleStorePerfPhoneTest::SetUp()
227 {
228 store1 = CreateKVStore("SingleKVStore1", LOCAL_ONLY, false, false);
229 if (store1 == nullptr) {
230 store1 = CreateKVStore("SingleKVStore1", LOCAL_ONLY, false, false);
231 }
232 ASSERT_NE(store1, nullptr);
233
234 store2 = CreateKVStore("SingleKVStore2", LOCAL_ONLY, false, false);
235 if (store2 == nullptr) {
236 store2 = CreateKVStore("SingleKVStore2", LOCAL_ONLY, false, false);
237 }
238 ASSERT_NE(store2, nullptr);
239
240 store3 = CreateKVStore("SingleKVStore3", LOCAL_ONLY, false, false);
241 if (store3 == nullptr) {
242 store3 = CreateKVStore("SingleKVStore3", LOCAL_ONLY, false, false);
243 }
244 ASSERT_NE(store3, nullptr);
245 }
246
TearDown()247 void SingleStorePerfPhoneTest::TearDown()
248 {
249 AppId appId = { "SingleStorePerfPhoneTest" };
250 StoreId storeId1 = { "SingleKVStore1" };
251 StoreId storeId2 = { "SingleKVStore2" };
252 StoreId storeId3 = { "SingleKVStore3" };
253 store1 = nullptr;
254 store2 = nullptr;
255 store3 = nullptr;
256 auto status = StoreManager::GetInstance().CloseKVStore(appId, storeId1);
257 ASSERT_EQ(status, SUCCESS);
258 status = StoreManager::GetInstance().CloseKVStore(appId, storeId2);
259 ASSERT_EQ(status, SUCCESS);
260 status = StoreManager::GetInstance().CloseKVStore(appId, storeId3);
261 ASSERT_EQ(status, SUCCESS);
262 auto baseDir = "/data/service/el1/public/database/SingleStorePerfPhoneTest";
263 status = StoreManager::GetInstance().Delete(appId, storeId1, baseDir);
264 ASSERT_EQ(status, SUCCESS);
265 status = StoreManager::GetInstance().Delete(appId, storeId2, baseDir);
266 ASSERT_EQ(status, SUCCESS);
267 status = StoreManager::GetInstance().Delete(appId, storeId3, baseDir);
268 ASSERT_EQ(status, SUCCESS);
269 }
270
CreateKVStore(std::string storeIdTest,KvStoreType type,bool encrypt,bool backup)271 std::shared_ptr<SingleKvStore> SingleStorePerfPhoneTest::CreateKVStore(std::string storeIdTest, KvStoreType type,
272 bool encrypt, bool backup)
273 {
274 Options options;
275 options.kvStoreType = type;
276 options.securityLevel = S1;
277 options.encrypt = encrypt;
278 options.area = EL1;
279 options.backup = backup;
280 options.baseDir = "/data/service/el1/public/database/SingleStorePerfPhoneTest";
281
282 AppId appId = { "SingleStorePerfPhoneTest" };
283 StoreId storeId = { storeIdTest };
284 Status status = StoreManager::GetInstance().Delete(appId, storeId, options.baseDir);
285 return StoreManager::GetInstance().GetKVStore(appId, storeId, options, status);
286 }
287
288 /**
289 * @tc.name: Gallery1WThumbnailsKVStoreBetweenTest
290 * @tc.desc: Gallery 10,000 thumbnails High-performance KV database Native interface Between
291 * query performance less than 3 ms
292 * @tc.type: PERF
293 * @tc.require:
294 * @tc.author: Gang Wang
295 */
296 HWTEST_F(SingleStorePerfPhoneTest, Gallery1WThumbnailsKVStoreBetweenTest, TestSize.Level0)
297 {
298 cout << "monthly start" << endl;
299 SingleStorePerfPhoneTest::DataConfig monthlyConfig = {
300 .batch = static_cast<int>(10000 / 112),
301 .count = 112,
302 .size = 8,
303 .ratio = 1
304 };
305 SingleStorePerfPhoneTest::PresetResult result1;
306 PresetData(store1, monthlyConfig, result1);
307 cout << "first key: " << result1.firstKey << ", last key: " << result1.lastKey << endl;
308 CalcRangeResultSetDuration(store1, monthlyConfig, result1);
309
310 cout << "annually start" << endl;
311 SingleStorePerfPhoneTest::DataConfig annuallyConfig = {
312 .batch = static_cast<int>(10000 / 448),
313 .count = 448,
314 .size = 4,
315 .ratio = 1
316 };
317 SingleStorePerfPhoneTest::PresetResult result2;
318 PresetData(store2, annuallyConfig, result2);
319 cout << "first key: " << result2.firstKey << ", last key: " << result2.lastKey << endl;
320 CalcRangeResultSetDuration(store2, annuallyConfig, result2);
321 }
322
323 /**
324 * @tc.name: Gallery5WThumbnailsKVStoreBetweenTest
325 * @tc.desc: Gallery 50,000 thumbnails High-performance KV database Native interface Between
326 * query performance less than 3 ms
327 * @tc.type: PERF
328 * @tc.require:
329 * @tc.author: Gang Wang
330 */
331 HWTEST_F(SingleStorePerfPhoneTest, Gallery5WThumbnailsKVStoreBetweenTest, TestSize.Level0)
332 {
333 cout << "monthly start" << endl;
334 SingleStorePerfPhoneTest::DataConfig monthlyConfig = {
335 .batch = static_cast<int>(50000 / 112),
336 .count = 112,
337 .size = 8,
338 .ratio = 1
339 };
340 SingleStorePerfPhoneTest::PresetResult result1;
341 PresetData(store1, monthlyConfig, result1);
342 cout << "first key: " << result1.firstKey << ", last key: " << result1.lastKey << endl;
343 CalcRangeResultSetDuration(store1, monthlyConfig, result1);
344
345 cout << "annually start" << endl;
346 SingleStorePerfPhoneTest::DataConfig annuallyConfig = {
347 .batch = static_cast<int>(50000 / 448),
348 .count = 448,
349 .size = 4,
350 .ratio = 1
351 };
352 SingleStorePerfPhoneTest::PresetResult result2;
353 PresetData(store2, annuallyConfig, result2);
354 cout << "first key: " << result2.firstKey << ", last key: " << result2.lastKey << endl;
355 CalcRangeResultSetDuration(store2, annuallyConfig, result2);
356 }
357 } // namespace OHOS::Test