1 /*
2 * Copyright (c) 2021 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
16 #include "kvdb_manager.h"
17 #include "log_print.h"
18 #include "db_common.h"
19 #include "db_dfx_adapter.h"
20 #include "runtime_context.h"
21 #include "schema_object.h"
22 #include "default_factory.h"
23 #include "generic_kvdb.h"
24 #include "db_constant.h"
25 #include "res_finalizer.h"
26
27 namespace DistributedDB {
28 const std::string KvDBManager::PROCESS_LABEL_CONNECTOR = "-";
29 std::atomic<KvDBManager *> KvDBManager::instance_{nullptr};
30 std::mutex KvDBManager::kvDBLock_;
31 std::mutex KvDBManager::instanceLock_;
32 std::map<std::string, OS::FileHandle *> KvDBManager::locks_;
33 std::mutex KvDBManager::fileHandleMutex_;
34
35 namespace {
36 DefaultFactory g_defaultFactory;
37
38 static const KvDBType g_dbTypeArr[] = {
39 #ifndef OMIT_MULTI_VER
40 LOCAL_KVDB_SQLITE,
41 #endif // OMIT_MULTI_VER
42 SINGER_VER_KVDB_SQLITE,
43 #ifndef OMIT_MULTI_VER
44 MULTI_VER_KVDB_SQLITE,
45 #endif // OMIT_MULTI_VER
46 SINGLE_VER_KVDB_RD
47 };
48
CreateDataBaseInstance(const KvDBProperties & property,IKvDB * & kvDB)49 int CreateDataBaseInstance(const KvDBProperties &property, IKvDB *&kvDB)
50 {
51 IKvDBFactory *factory = IKvDBFactory::GetCurrent();
52 if (factory == nullptr) {
53 return -E_OUT_OF_MEMORY;
54 }
55 int errCode = E_OK;
56 int databaseType = property.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE_SQLITE);
57 if (databaseType == KvDBProperties::LOCAL_TYPE_SQLITE) {
58 #ifndef OMIT_MULTI_VER
59 kvDB = factory->CreateKvDb(LOCAL_KVDB_SQLITE, errCode);
60 if (kvDB != nullptr) {
61 kvDB->EnableAutonomicUpgrade();
62 }
63 #else
64 return -E_NOT_SUPPORT;
65 #endif // OMIT_MULTI_VER
66 } else if (databaseType == KvDBProperties::SINGLE_VER_TYPE_SQLITE) {
67 kvDB = factory->CreateKvDb(SINGER_VER_KVDB_SQLITE, errCode);
68 } else if (databaseType == KvDBProperties::SINGLE_VER_TYPE_RD_KERNAL) {
69 kvDB = factory->CreateKvDb(SINGLE_VER_KVDB_RD, errCode);
70 } else {
71 #ifndef OMIT_MULTI_VER
72 kvDB = factory->CreateKvDb(MULTI_VER_KVDB_SQLITE, errCode);
73 #else
74 return -E_NOT_SUPPORT;
75 #endif // OMIT_MULTI_VER
76 }
77 return errCode;
78 }
79
CreateRemoveStateFlagFile(const KvDBProperties & properties)80 int CreateRemoveStateFlagFile(const KvDBProperties &properties)
81 {
82 std::string dataDir = properties.GetStringProp(KvDBProperties::DATA_DIR, "");
83 std::string identifier = properties.GetStringProp(KvDBProperties::IDENTIFIER_DATA, "");
84 std::string identifierName = DBCommon::TransferStringToHex(identifier);
85 std::string storeDir = dataDir + "/" + identifierName + DBConstant::DELETE_KVSTORE_REMOVING;
86 if (OS::CheckPathExistence(storeDir)) {
87 return E_OK;
88 }
89 // create the pre flag file.
90 int errCode = OS::CreateFileByFileName(storeDir);
91 if (errCode != E_OK) {
92 LOGE("Create remove state flag file failed:%d.", errCode);
93 }
94 return errCode;
95 }
96 }
97
CheckRemoveStateAndRetry(const KvDBProperties & property)98 int KvDBManager::CheckRemoveStateAndRetry(const KvDBProperties &property)
99 {
100 std::string dataDir = property.GetStringProp(KvDBProperties::DATA_DIR, "");
101 std::string identifier = property.GetStringProp(KvDBProperties::IDENTIFIER_DATA, "");
102 std::string identifierName = DBCommon::TransferStringToHex(identifier);
103 std::string storeDir = dataDir + "/" + identifierName + DBConstant::DELETE_KVSTORE_REMOVING;
104
105 if (OS::CheckPathExistence(storeDir)) {
106 KvDBManager::ExecuteRemoveDatabase(property);
107 }
108 // Re-detection deleted had been finish
109 if (OS::CheckPathExistence(storeDir)) {
110 LOGD("Deletekvstore unfinished, can not create new same identifier kvstore!");
111 return -E_REMOVE_FILE;
112 }
113 return E_OK;
114 }
115
ExecuteRemoveDatabase(const KvDBProperties & properties)116 int KvDBManager::ExecuteRemoveDatabase(const KvDBProperties &properties)
117 {
118 int errCode = CheckDatabaseFileStatus(properties);
119 if (errCode != E_OK) {
120 return errCode;
121 }
122 IKvDBFactory *factory = IKvDBFactory::GetCurrent();
123 if (factory == nullptr) {
124 return -E_INVALID_DB;
125 }
126
127 errCode = CreateRemoveStateFlagFile(properties);
128 if (errCode != E_OK) {
129 LOGE("create ctrl file failed:%d.", errCode);
130 return errCode;
131 }
132
133 errCode = -E_NOT_FOUND;
134 for (KvDBType kvDbType : g_dbTypeArr) {
135 int innerErrCode = E_OK;
136 IKvDB *kvdb = factory->CreateKvDb(kvDbType, innerErrCode);
137 if (innerErrCode != E_OK) {
138 return innerErrCode;
139 }
140 innerErrCode = kvdb->RemoveKvDB(properties);
141 RefObject::DecObjRef(kvdb);
142 if (innerErrCode != -E_NOT_FOUND) {
143 if (innerErrCode != E_OK) {
144 return innerErrCode;
145 }
146 errCode = E_OK;
147 }
148 }
149
150 if (errCode == -E_NOT_FOUND) {
151 LOGE("DataBase file Not exist! return NOT_FOUND.");
152 }
153
154 RemoveDBDirectory(properties);
155 return errCode;
156 }
157
RemoveDBDirectory(const KvDBProperties & properties)158 void KvDBManager::RemoveDBDirectory(const KvDBProperties &properties)
159 {
160 std::string dataDir = properties.GetStringProp(KvDBProperties::DATA_DIR, "");
161 std::string identifier = properties.GetStringProp(KvDBProperties::IDENTIFIER_DATA, "");
162 std::string identifierName = DBCommon::TransferStringToHex(identifier);
163 std::string storeDir = dataDir + "/" + identifierName;
164 std::string removingFlag = dataDir + "/" + identifierName + DBConstant::DELETE_KVSTORE_REMOVING;
165 (void)OS::RemoveDBDirectory(storeDir);
166
167 std::string storeId = properties.GetStringProp(KvDBProperties::STORE_ID, "");
168 identifier = DBCommon::TransferHashString(storeId);
169 identifierName = DBCommon::TransferStringToHex(identifier);
170 storeDir = dataDir + "/" + identifierName;
171 (void)OS::RemoveDBDirectory(storeDir);
172
173 (void)OS::RemoveFile(removingFlag);
174 }
175
176 // Used to open a kvdb with the given property
OpenDatabase(const KvDBProperties & property,int & errCode)177 IKvDB *KvDBManager::OpenDatabase(const KvDBProperties &property, int &errCode)
178 {
179 KvDBManager *manager = GetInstance();
180 if (manager == nullptr) {
181 errCode = -E_OUT_OF_MEMORY;
182 return nullptr;
183 }
184 return manager->GetDataBase(property, errCode, true);
185 }
186
EnterDBOpenCloseProcess(const std::string & identifier)187 void KvDBManager::EnterDBOpenCloseProcess(const std::string &identifier)
188 {
189 std::unique_lock<std::mutex> lock(kvDBOpenMutex_);
190 kvDBOpenCondition_.wait(lock, [this, &identifier]() {
191 return this->kvDBOpenSet_.count(identifier) == 0;
192 });
193 (void)kvDBOpenSet_.insert(identifier);
194 }
195
ExitDBOpenCloseProcess(const std::string & identifier)196 void KvDBManager::ExitDBOpenCloseProcess(const std::string &identifier)
197 {
198 std::unique_lock<std::mutex> lock(kvDBOpenMutex_);
199 (void)kvDBOpenSet_.erase(identifier);
200 kvDBOpenCondition_.notify_all();
201 }
202
203 // one time 100ms
204 // In order to prevent long-term blocking of the process, a retry method is used
205 // The dimensions of the lock by appid-userid-storeid
TryLockDB(const KvDBProperties & kvDBProp,int retryTimes)206 int KvDBManager::TryLockDB(const KvDBProperties &kvDBProp, int retryTimes)
207 {
208 std::string dataDir = kvDBProp.GetStringProp(KvDBProperties::DATA_DIR, "");
209 bool isMemoryDb = kvDBProp.GetBoolProp(KvDBProperties::MEMORY_MODE, false);
210 std::string id = KvDBManager::GenerateKvDBIdentifier(kvDBProp);
211 if (dataDir.back() != '/') {
212 dataDir += "/";
213 }
214
215 if (isMemoryDb) {
216 LOGI("MemoryDb not need lock!");
217 return E_OK;
218 }
219
220 {
221 std::lock_guard<std::mutex> autoLock(fileHandleMutex_);
222 if (locks_.count(id) != 0) {
223 LOGI("db has been locked!");
224 return E_OK;
225 }
226 }
227
228 std::string hexHashId = DBCommon::TransferStringToHex((id));
229 OS::FileHandle *handle = nullptr;
230 int errCode = OS::OpenFile(dataDir + hexHashId + DBConstant::DB_LOCK_POSTFIX, handle);
231 if (errCode != E_OK) {
232 LOGE("Open lock file fail errCode = [%d], errno:%d", errCode, errno);
233 return errCode;
234 }
235
236 while (retryTimes-- > 0) {
237 errCode = OS::FileLock(handle, false); // not block process
238 if (errCode == E_OK) {
239 LOGI("[%s]locked!", STR_MASK(DBCommon::TransferStringToHex(KvDBManager::GenerateKvDBIdentifier(kvDBProp))));
240 std::lock_guard<std::mutex> autoLock(fileHandleMutex_);
241 locks_[id] = handle;
242 return errCode;
243 } else if (errCode == -E_BUSY) {
244 LOGD("DB already held by process lock!");
245 std::this_thread::sleep_for(std::chrono::milliseconds(100)); // wait for 100ms
246 continue;
247 } else {
248 LOGE("Try lock db failed, errCode = [%d] errno:%d", errCode, errno);
249 OS::CloseFile(handle);
250 return errCode;
251 }
252 }
253 OS::CloseFile(handle);
254 return -E_BUSY;
255 }
256
UnlockDB(const KvDBProperties & kvDBProp)257 int KvDBManager::UnlockDB(const KvDBProperties &kvDBProp)
258 {
259 bool isMemoryDb = kvDBProp.GetBoolProp(KvDBProperties::MEMORY_MODE, false);
260 if (isMemoryDb) {
261 return E_OK;
262 }
263 std::string identifierDir = KvDBManager::GenerateKvDBIdentifier(kvDBProp);
264 OS::FileHandle *handle = nullptr;
265 {
266 std::lock_guard<std::mutex> autoLock(fileHandleMutex_);
267 if (locks_.count(identifierDir) == 0) {
268 return E_OK;
269 }
270 handle = locks_[identifierDir];
271 }
272 int errCode = OS::FileUnlock(handle);
273 if (errCode != E_OK) {
274 LOGE("DB unlocked! errCode = [%d]", errCode);
275 return errCode;
276 }
277 errCode = OS::CloseFile(handle);
278 if (errCode != E_OK) {
279 LOGE("DB closed! errCode = [%d]", errCode);
280 return errCode;
281 }
282 std::lock_guard<std::mutex> autoLock(fileHandleMutex_);
283 locks_.erase(identifierDir);
284 return E_OK;
285 }
286
CheckOpenDBOptionWithCached(const KvDBProperties & properties,IKvDB * kvDB)287 bool KvDBManager::CheckOpenDBOptionWithCached(const KvDBProperties &properties, IKvDB *kvDB)
288 {
289 bool isMemoryDb = properties.GetBoolProp(KvDBProperties::MEMORY_MODE, false);
290 std::string canonicalDir = properties.GetStringProp(KvDBProperties::DATA_DIR, "");
291 if (!isMemoryDb && (canonicalDir.empty() || canonicalDir != kvDB->GetStorePath())) {
292 LOGE("Failed to check store path, the input path does not match with cached store.");
293 return false;
294 }
295
296 bool compressOnSyncUser = properties.GetBoolProp(KvDBProperties::COMPRESS_ON_SYNC, false);
297 bool compressOnSyncGet = kvDB->GetMyProperties().GetBoolProp(KvDBProperties::COMPRESS_ON_SYNC, false);
298 if (compressOnSyncUser != compressOnSyncGet) {
299 LOGE("Failed to check compress option, the input %d not match with cached %d.", compressOnSyncUser,
300 compressOnSyncGet);
301 return false;
302 }
303 if (compressOnSyncUser) {
304 int compressRateUser = properties.GetIntProp(KvDBProperties::COMPRESSION_RATE, 0);
305 int compressRateGet = kvDB->GetMyProperties().GetIntProp(KvDBProperties::COMPRESSION_RATE, 0);
306 if (compressRateUser != compressRateGet) {
307 LOGE("Failed to check compress rate, the input %d not match with cached %d.", compressRateUser,
308 compressRateGet);
309 return false;
310 }
311 }
312 return true;
313 }
314
315 // Used to open a kvdb with the given property
GetDatabaseConnection(const KvDBProperties & properties,int & errCode,bool isNeedIfOpened)316 IKvDBConnection *KvDBManager::GetDatabaseConnection(const KvDBProperties &properties, int &errCode,
317 bool isNeedIfOpened)
318 {
319 auto manager = GetInstance();
320 if (manager == nullptr) {
321 errCode = -E_OUT_OF_MEMORY;
322 return nullptr;
323 }
324 IKvDBConnection *connection = nullptr;
325 std::string identifier = properties.GetStringProp(KvDBProperties::IDENTIFIER_DATA, "");
326 LOGD("Begin to get [%s] database connection.", STR_MASK(DBCommon::TransferStringToHex(identifier)));
327 manager->EnterDBOpenCloseProcess(identifier);
328
329 IKvDB *kvDB = manager->GetDataBase(properties, errCode, isNeedIfOpened);
330 if (kvDB == nullptr) {
331 if (isNeedIfOpened) {
332 DBDfxAdapter::ReportBehavior(
333 {__func__, Scene::OPEN_CONN, State::END, Stage::GET_DB, StageResult::FAIL, errCode});
334 LOGE("Failed to open the db:%d", errCode);
335 }
336 } else {
337 if (!CheckOpenDBOptionWithCached(properties, kvDB)) {
338 LOGE("Failed to check open db option");
339 errCode = -E_INVALID_ARGS;
340 DBDfxAdapter::ReportBehavior(
341 {__func__, Scene::OPEN_CONN, State::END, Stage::CHECK_OPT, StageResult::FAIL, errCode});
342 } else {
343 connection = kvDB->GetDBConnection(errCode);
344 if (connection == nullptr) { // not kill kvdb, Other operations like import may be used concurrently
345 DBDfxAdapter::ReportBehavior(
346 {__func__, Scene::OPEN_CONN, State::END, Stage::GET_DB_CONN, StageResult::FAIL, errCode});
347 LOGE("Failed to get the db connect for delegate:%d", errCode);
348 }
349 }
350 RefObject::DecObjRef(kvDB); // restore the reference increased by the cache.
351 kvDB = nullptr;
352 }
353
354 manager->ExitDBOpenCloseProcess(identifier);
355 if (errCode == -E_INVALID_PASSWD_OR_CORRUPTED_DB) {
356 std::string appId = properties.GetStringProp(KvDBProperties::APP_ID, "");
357 std::string userId = properties.GetStringProp(KvDBProperties::USER_ID, "");
358 std::string storeId = properties.GetStringProp(KvDBProperties::STORE_ID, "");
359 manager->DataBaseCorruptNotify(appId, userId, storeId);
360 LOGE("Database [%s] is corrupted or authentication failed:%d",
361 STR_MASK(DBCommon::TransferStringToHex(identifier)), errCode);
362 }
363
364 return connection;
365 }
366
ReleaseDatabaseConnection(IKvDBConnection * connection)367 int KvDBManager::ReleaseDatabaseConnection(IKvDBConnection *connection)
368 {
369 if (connection == nullptr) {
370 return -E_INVALID_DB;
371 }
372
373 std::string identifier = connection->GetIdentifier();
374 auto manager = GetInstance();
375 if (manager == nullptr) {
376 return -E_OUT_OF_MEMORY;
377 }
378 manager->EnterDBOpenCloseProcess(identifier);
379 int errCode = connection->Close();
380 manager->ExitDBOpenCloseProcess(identifier);
381
382 if (errCode != E_OK) {
383 LOGE("[KvDBManager] Release db connection:%d", errCode);
384 }
385 LOGI("[Connection] db[%s] conn Close", STR_MASK(DBCommon::TransferStringToHex(identifier)));
386 return errCode;
387 }
388
CreateDataBase(const KvDBProperties & property,int & errCode)389 IKvDB *KvDBManager::CreateDataBase(const KvDBProperties &property, int &errCode)
390 {
391 IKvDB *kvDB = OpenNewDatabase(property, errCode);
392 int databaseType = property.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE_SQLITE);
393 if (kvDB == nullptr) {
394 LOGE("Failed to open the new database.");
395 bool isReadOnly = property.GetBoolProp(KvDBProperties::READ_ONLY_MODE, false);
396 if (errCode == -E_INVALID_PASSWD_OR_CORRUPTED_DB &&
397 databaseType == KvDBProperties::SINGLE_VER_TYPE_RD_KERNAL && isReadOnly) {
398 LOGI("readOnly process can not remove database");
399 return kvDB;
400 }
401 if (errCode == -E_INVALID_PASSWD_OR_CORRUPTED_DB &&
402 property.GetBoolProp(KvDBProperties::RM_CORRUPTED_DB, false)) {
403 LOGI("Remove the corrupted database while open");
404 ExecuteRemoveDatabase(property);
405 kvDB = OpenNewDatabase(property, errCode);
406 if (kvDB != nullptr) {
407 kvDB->MarkRebuild();
408 }
409 }
410 return kvDB;
411 }
412 bool rebuild = false;
413 if (property.GetBoolProp(KvDBProperties::CHECK_INTEGRITY, false) &&
414 databaseType != KvDBProperties::SINGLE_VER_TYPE_RD_KERNAL) {
415 int integrityStatus = kvDB->CheckIntegrity();
416 if (integrityStatus == -E_INVALID_PASSWD_OR_CORRUPTED_DB) {
417 RemoveKvDBFromCache(kvDB);
418 RefObject::KillAndDecObjRef(kvDB);
419 kvDB = nullptr;
420 errCode = -E_INVALID_PASSWD_OR_CORRUPTED_DB;
421 if (property.GetBoolProp(KvDBProperties::RM_CORRUPTED_DB, false)) {
422 LOGI("Remove the corrupted database for the integrity check");
423 ExecuteRemoveDatabase(property);
424 kvDB = OpenNewDatabase(property, errCode);
425 rebuild = true;
426 }
427 }
428 }
429 if (kvDB != nullptr && rebuild) {
430 kvDB->MarkRebuild();
431 }
432 return kvDB;
433 }
434
GetDataBase(const KvDBProperties & property,int & errCode,bool isNeedIfOpened)435 IKvDB *KvDBManager::GetDataBase(const KvDBProperties &property, int &errCode, bool isNeedIfOpened)
436 {
437 bool isMemoryDb = property.GetBoolProp(KvDBProperties::MEMORY_MODE, false);
438 bool isCreateNecessary = property.GetBoolProp(KvDBProperties::CREATE_IF_NECESSARY, true);
439 IKvDB *kvDB = FindAndGetKvDBFromCache(property, errCode);
440 if (kvDB != nullptr) {
441 if (!isNeedIfOpened) {
442 LOGI("[KvDBManager] Database has already been opened.");
443 RefObject::DecObjRef(kvDB);
444 errCode = -E_ALREADY_OPENED;
445 kvDB = nullptr;
446 }
447 return kvDB;
448 }
449 if (isMemoryDb && !isCreateNecessary) {
450 LOGI("IsCreateNecessary is false, Not need create database");
451 return nullptr;
452 }
453 if (errCode != -E_NOT_FOUND) {
454 return nullptr;
455 }
456
457 // Taking into account the compatibility of version delivery,
458 // temporarily use isNeedIntegrityCheck this field to avoid multi-process concurrency
459 bool isNeedIntegrityCheck = property.GetBoolProp(KvDBProperties::CHECK_INTEGRITY, false);
460 int databaseType = property.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE_SQLITE);
461 if (isNeedIntegrityCheck || databaseType == KvDBProperties::SINGLE_VER_TYPE_RD_KERNAL) {
462 LOGI("db need lock, need check integrity is [%d]", isNeedIntegrityCheck);
463 errCode = KvDBManager::TryLockDB(property, 10); // default 10 times retry
464 if (errCode != E_OK) {
465 return nullptr;
466 }
467 }
468
469 ResFinalizer unlock([&errCode, &property, &kvDB]() {
470 int err = KvDBManager::UnlockDB(property);
471 if (err != E_OK) {
472 LOGE("GetDataBase unlock failed! err [%d] errCode [%d]", err, errCode);
473 errCode = err;
474 RefObject::KillAndDecObjRef(kvDB);
475 kvDB = nullptr;
476 }
477 });
478
479 kvDB = CreateDataBase(property, errCode);
480 if (errCode != E_OK) {
481 LOGE("Create database failed, errCode = [%d]", errCode);
482 }
483 return kvDB;
484 }
485
IsOpenMemoryDb(const KvDBProperties & properties,const std::map<std::string,IKvDB * > & cache) const486 bool KvDBManager::IsOpenMemoryDb(const KvDBProperties &properties, const std::map<std::string, IKvDB *> &cache) const
487 {
488 std::string identifier = GenerateKvDBIdentifier(properties);
489 auto iter = cache.find(identifier);
490 if (iter != cache.end()) {
491 IKvDB *kvDB = iter->second;
492 if (kvDB != nullptr && kvDB->GetMyProperties().GetBoolProp(KvDBProperties::MEMORY_MODE, false)) {
493 return true;
494 }
495 }
496 return false;
497 }
498
499 // used to get kvdb size with the given property.
CalculateKvStoreSize(const KvDBProperties & properties,uint64_t & size)500 int KvDBManager::CalculateKvStoreSize(const KvDBProperties &properties, uint64_t &size)
501 {
502 KvDBManager *manager = GetInstance();
503 if (manager == nullptr) {
504 LOGE("Failed to get KvDBManager instance!");
505 return -E_OUT_OF_MEMORY;
506 }
507
508 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
509 if (manager->IsOpenMemoryDb(properties, manager->singleVerNaturalStores_)) {
510 size = 0;
511 return E_OK;
512 }
513
514 IKvDBFactory *factory = IKvDBFactory::GetCurrent();
515 if (factory == nullptr) {
516 return -E_INVALID_DB;
517 }
518
519 uint64_t totalSize = 0;
520 for (KvDBType kvDbType : g_dbTypeArr) {
521 if (kvDbType == SINGLE_VER_KVDB_RD) {
522 continue;
523 }
524 int innerErrCode = E_OK;
525 IKvDB *kvDB = factory->CreateKvDb(kvDbType, innerErrCode);
526 if (innerErrCode != E_OK) {
527 return innerErrCode;
528 }
529 uint64_t dbSize = 0;
530 innerErrCode = kvDB->GetKvDBSize(properties, dbSize);
531 RefObject::DecObjRef(kvDB);
532 if (innerErrCode != E_OK && innerErrCode != -E_NOT_FOUND) {
533 return innerErrCode;
534 }
535 LOGD("DB type [%u], size[%" PRIu64 "]", static_cast<unsigned>(kvDbType), dbSize);
536 totalSize = totalSize + dbSize;
537 }
538 // This represent Db file size(Unit is byte), It is small than max size(max uint64_t represent 2^64B)
539 if (totalSize != 0ULL) {
540 size = totalSize;
541 return E_OK;
542 }
543 return -E_NOT_FOUND;
544 }
545
GetKvDBFromCacheByIdentify(const std::string & identifier,const std::map<std::string,IKvDB * > & cache) const546 IKvDB *KvDBManager::GetKvDBFromCacheByIdentify(const std::string &identifier,
547 const std::map<std::string, IKvDB *> &cache) const
548 {
549 auto iter = cache.find(identifier);
550 if (iter != cache.end()) {
551 IKvDB *kvDB = iter->second;
552 if (kvDB == nullptr) {
553 LOGE("Kvstore cache is nullptr, there may be a logic error");
554 return nullptr;
555 }
556 return kvDB;
557 }
558 return nullptr;
559 }
560
CheckDatabaseFileStatus(const KvDBProperties & properties)561 int KvDBManager::CheckDatabaseFileStatus(const KvDBProperties &properties)
562 {
563 KvDBManager *manager = GetInstance();
564 if (manager == nullptr) {
565 LOGE("Failed to get KvDBManager instance!");
566 return -E_OUT_OF_MEMORY;
567 }
568
569 std::string identifier = GenerateKvDBIdentifier(properties);
570 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
571 IKvDB *kvDB = manager->GetKvDBFromCacheByIdentify(identifier, manager->localKvDBs_);
572 if (kvDB != nullptr) {
573 LOGE("The local KvDB is busy!");
574 return -E_BUSY;
575 }
576
577 kvDB = manager->GetKvDBFromCacheByIdentify(identifier, manager->multiVerNaturalStores_);
578 if (kvDB != nullptr) {
579 LOGE("The multi ver natural store is busy!");
580 return -E_BUSY;
581 }
582
583 kvDB = manager->GetKvDBFromCacheByIdentify(identifier, manager->singleVerNaturalStores_);
584 if (kvDB != nullptr) {
585 LOGE("The single version natural store is busy!");
586 return -E_BUSY;
587 }
588 return E_OK;
589 }
590
OpenNewDatabase(const KvDBProperties & property,int & errCode)591 IKvDB *KvDBManager::OpenNewDatabase(const KvDBProperties &property, int &errCode)
592 {
593 errCode = CheckRemoveStateAndRetry(property);
594 if (errCode != E_OK) {
595 LOGE("Failed to open IKvDB! Because delete kvstore unfinished err:%d", errCode);
596 return nullptr;
597 }
598
599 IKvDB *kvDB = nullptr;
600 errCode = CreateDataBaseInstance(property, kvDB);
601 if (errCode != E_OK) {
602 LOGE("Failed to get IKvDB! err:%d", errCode);
603 return nullptr;
604 }
605
606 errCode = kvDB->Open(property);
607 if (errCode != E_OK) {
608 LOGE("Failed to open IKvDB! err:%d", errCode);
609 RefObject::KillAndDecObjRef(kvDB);
610 kvDB = nullptr;
611 return nullptr;
612 }
613 auto identifier = DBCommon::TransferStringToHex(property.GetStringProp(KvDBProperties::IDENTIFIER_DATA, ""));
614 auto dbDir = property.GetStringProp(KvDBProperties::IDENTIFIER_DIR, "");
615 LOGI("Database identifier:%.6s, dir:%.6s", identifier.c_str(), dbDir.c_str());
616 // Register the callback function when the database is closed, triggered when kvdb free
617 kvDB->OnClose([kvDB, this]() {
618 this->RemoveKvDBFromCache(kvDB);
619 });
620
621 IKvDB *kvDBTmp = SaveKvDBToCache(kvDB);
622 if (kvDBTmp != kvDB) {
623 RefObject::KillAndDecObjRef(kvDB);
624 kvDB = nullptr;
625 return kvDBTmp;
626 }
627 return kvDB;
628 }
629
630 // used to delete a kvdb with the given property.
631 // return BUSY if in use
RemoveDatabase(const KvDBProperties & properties)632 int KvDBManager::RemoveDatabase(const KvDBProperties &properties)
633 {
634 KvDBManager *manager = GetInstance();
635 if (manager == nullptr) {
636 LOGE("Failed to get kvdb manager while removing the db!");
637 return -E_OUT_OF_MEMORY;
638 }
639 std::string identifier = GenerateKvDBIdentifier(properties);
640 manager->EnterDBOpenCloseProcess(identifier);
641
642 LOGI("KvDBManager::RemoveDatabase begin try lock the database!");
643 std::string lockFile = properties.GetStringProp(KvDBProperties::DATA_DIR, "") + "/" +
644 DBCommon::TransferStringToHex(identifier) + DBConstant::DB_LOCK_POSTFIX;
645 int errCode = E_OK;
646 if (OS::CheckPathExistence(lockFile)) {
647 errCode = KvDBManager::TryLockDB(properties, 10); // default 10 times retry
648 if (errCode != E_OK) {
649 manager->ExitDBOpenCloseProcess(identifier);
650 return errCode;
651 }
652 }
653
654 errCode = ExecuteRemoveDatabase(properties);
655 if (errCode != E_OK) {
656 LOGE("[KvDBManager] Remove database failed:%d", errCode);
657 }
658 int err = KvDBManager::UnlockDB(properties); // unlock and delete lock file before delete dir
659 if (err != E_OK) {
660 LOGE("[KvDBManager][RemoveDatabase] UnlockDB failed:%d, errno:%d", err, errno);
661 errCode = err;
662 }
663
664 manager->ExitDBOpenCloseProcess(identifier);
665 return errCode;
666 }
667
GenerateKvDBIdentifier(const KvDBProperties & property)668 std::string KvDBManager::GenerateKvDBIdentifier(const KvDBProperties &property)
669 {
670 return property.GetStringProp(KvDBProperties::IDENTIFIER_DATA, "");
671 }
672
GetInstance()673 KvDBManager *KvDBManager::GetInstance()
674 {
675 // For Double-Checked Locking, we need check instance_ twice
676 if (instance_ == nullptr) {
677 std::lock_guard<std::mutex> lockGuard(instanceLock_);
678 if (instance_ == nullptr) {
679 instance_ = new (std::nothrow) KvDBManager();
680 if (instance_ == nullptr) {
681 LOGE("failed to new KvDBManager!");
682 return nullptr;
683 }
684 }
685 }
686 if (IKvDBFactory::GetCurrent() == nullptr) {
687 IKvDBFactory::Register(&g_defaultFactory);
688 }
689 return instance_;
690 }
691
692 // Save to IKvDB to the global map
SaveKvDBToCache(IKvDB * kvDB)693 IKvDB *KvDBManager::SaveKvDBToCache(IKvDB *kvDB)
694 {
695 if (kvDB == nullptr) {
696 return nullptr;
697 }
698
699 {
700 KvDBProperties properties = kvDB->GetMyProperties();
701 std::string identifier = GenerateKvDBIdentifier(properties);
702 int databaseType = properties.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE_SQLITE);
703 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
704 int errCode = E_OK;
705 if (databaseType == KvDBProperties::LOCAL_TYPE_SQLITE) {
706 IKvDB *kvDBTmp = FindKvDBFromCache(properties, localKvDBs_, true, errCode);
707 if (kvDBTmp != nullptr) {
708 kvDBTmp->IncObjRef(kvDBTmp);
709 return kvDBTmp;
710 }
711 localKvDBs_.insert(std::pair<std::string, IKvDB *>(identifier, kvDB));
712 } else if (databaseType == KvDBProperties::MULTI_VER_TYPE_SQLITE) {
713 IKvDB *kvDBTmp = FindKvDBFromCache(properties, multiVerNaturalStores_, true, errCode);
714 if (kvDBTmp != nullptr) {
715 kvDBTmp->IncObjRef(kvDBTmp);
716 return kvDBTmp;
717 }
718 kvDB->WakeUpSyncer();
719 multiVerNaturalStores_.insert(std::pair<std::string, IKvDB *>(identifier, kvDB));
720 } else {
721 IKvDB *kvDBTmp = FindKvDBFromCache(properties, singleVerNaturalStores_, true, errCode);
722 if (kvDBTmp != nullptr) {
723 kvDBTmp->IncObjRef(kvDBTmp);
724 return kvDBTmp;
725 }
726 kvDB->WakeUpSyncer();
727 singleVerNaturalStores_.insert(std::pair<std::string, IKvDB *>(identifier, kvDB));
728 }
729 }
730 kvDB->SetCorruptHandler([kvDB, this]() {
731 std::string appId = kvDB->GetMyProperties().GetStringProp(KvDBProperties::APP_ID, "");
732 std::string userId = kvDB->GetMyProperties().GetStringProp(KvDBProperties::USER_ID, "");
733 std::string storeId = kvDB->GetMyProperties().GetStringProp(KvDBProperties::STORE_ID, "");
734 this->DataBaseCorruptNotifyAsync(appId, userId, storeId);
735 });
736 return kvDB;
737 }
738
739 // Save to IKvDB to the global map
RemoveKvDBFromCache(const IKvDB * kvDB)740 void KvDBManager::RemoveKvDBFromCache(const IKvDB *kvDB)
741 {
742 const KvDBProperties &properties = kvDB->GetMyProperties();
743 std::string identifier = GenerateKvDBIdentifier(properties);
744 int databaseType = properties.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE_SQLITE);
745 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
746 if (databaseType == KvDBProperties::LOCAL_TYPE_SQLITE) {
747 localKvDBs_.erase(identifier);
748 } else if (databaseType == KvDBProperties::MULTI_VER_TYPE_SQLITE) {
749 multiVerNaturalStores_.erase(identifier);
750 } else {
751 singleVerNaturalStores_.erase(identifier);
752 }
753 }
754
755 // Get IKvDB from the global map
FindAndGetKvDBFromCache(const KvDBProperties & properties,int & errCode) const756 IKvDB *KvDBManager::FindAndGetKvDBFromCache(const KvDBProperties &properties, int &errCode) const
757 {
758 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
759
760 IKvDB *kvDB = FindKvDBFromCache(properties, localKvDBs_, true, errCode);
761 if (kvDB != nullptr) {
762 kvDB->IncObjRef(kvDB);
763 return kvDB;
764 }
765 if (errCode != -E_NOT_FOUND) {
766 return nullptr;
767 }
768
769 kvDB = FindKvDBFromCache(properties, multiVerNaturalStores_, true, errCode);
770 if (kvDB != nullptr) {
771 kvDB->IncObjRef(kvDB);
772 return kvDB;
773 }
774 if (errCode != -E_NOT_FOUND) {
775 return nullptr;
776 }
777
778 kvDB = FindKvDBFromCache(properties, singleVerNaturalStores_, true, errCode);
779 if (kvDB != nullptr) {
780 kvDB->IncObjRef(kvDB);
781 return kvDB;
782 }
783 return nullptr;
784 }
785
FindKvDBFromCache(const KvDBProperties & properties,const std::map<std::string,IKvDB * > & cache,bool isNeedCheckPasswd,int & errCode) const786 IKvDB *KvDBManager::FindKvDBFromCache(const KvDBProperties &properties, const std::map<std::string, IKvDB *> &cache,
787 bool isNeedCheckPasswd, int &errCode) const
788 {
789 errCode = E_OK;
790 std::string identifier = GenerateKvDBIdentifier(properties);
791 auto iter = cache.find(identifier);
792 if (iter != cache.end()) {
793 IKvDB *kvDB = iter->second;
794 if (kvDB == nullptr) {
795 LOGE("KVSTORE cache is nullptr, there may be a logic error");
796 errCode = -E_INTERNAL_ERROR;
797 return nullptr;
798 }
799 int newType = properties.GetIntProp(KvDBProperties::DATABASE_TYPE, KvDBProperties::LOCAL_TYPE_SQLITE);
800 int oldType = kvDB->GetMyProperties().GetIntProp(KvDBProperties::DATABASE_TYPE,
801 KvDBProperties::LOCAL_TYPE_SQLITE);
802 if (oldType == newType) {
803 errCode = CheckKvDBProperties(kvDB, properties, isNeedCheckPasswd);
804 if (errCode != E_OK) {
805 return nullptr;
806 }
807 return kvDB;
808 } else {
809 errCode = -E_INVALID_ARGS;
810 LOGE("Database [%s] type not matched, type [%d] vs [%d]",
811 STR_MASK(DBCommon::TransferStringToHex(identifier)), newType, oldType);
812 return nullptr;
813 }
814 }
815
816 errCode = -E_NOT_FOUND;
817 return nullptr;
818 }
819
SetProcessLabel(const std::string & appId,const std::string & userId)820 int KvDBManager::SetProcessLabel(const std::string &appId, const std::string &userId)
821 {
822 std::string label = appId + PROCESS_LABEL_CONNECTOR + userId;
823 RuntimeContext::GetInstance()->SetProcessLabel(label);
824 return E_OK;
825 }
826
RestoreSyncableKvStore()827 void KvDBManager::RestoreSyncableKvStore()
828 {
829 KvDBManager *manager = GetInstance();
830 if (manager == nullptr) {
831 return;
832 }
833
834 manager->RestoreSyncerOfAllKvStore();
835 }
836
SetDatabaseCorruptionHandler(const KvStoreCorruptionHandler & handler)837 void KvDBManager::SetDatabaseCorruptionHandler(const KvStoreCorruptionHandler &handler)
838 {
839 KvDBManager *manager = GetInstance();
840 if (manager == nullptr) {
841 return;
842 }
843
844 manager->SetAllDatabaseCorruptionHander(handler);
845 }
846
SetAllDatabaseCorruptionHander(const KvStoreCorruptionHandler & handler)847 void KvDBManager::SetAllDatabaseCorruptionHander(const KvStoreCorruptionHandler &handler)
848 {
849 {
850 std::lock_guard<std::mutex> lock(corruptMutex_);
851 corruptHandler_ = handler;
852 }
853 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
854 SetCorruptHandlerForDatabases(singleVerNaturalStores_);
855 SetCorruptHandlerForDatabases(localKvDBs_);
856 SetCorruptHandlerForDatabases(multiVerNaturalStores_);
857 }
858
DataBaseCorruptNotify(const std::string & appId,const std::string & userId,const std::string & storeId)859 void KvDBManager::DataBaseCorruptNotify(const std::string &appId, const std::string &userId, const std::string &storeId)
860 {
861 KvStoreCorruptionHandler corruptHandler = nullptr;
862 {
863 std::lock_guard<std::mutex> lock(corruptMutex_);
864 corruptHandler = corruptHandler_;
865 }
866
867 if (corruptHandler != nullptr) {
868 corruptHandler(appId, userId, storeId);
869 }
870 }
871
DataBaseCorruptNotifyAsync(const std::string & appId,const std::string & userId,const std::string & storeId)872 void KvDBManager::DataBaseCorruptNotifyAsync(const std::string &appId, const std::string &userId,
873 const std::string &storeId)
874 {
875 int errCode = RuntimeContext::GetInstance()->ScheduleTask(
876 [this, appId, userId, storeId] { DataBaseCorruptNotify(appId, userId, storeId); });
877 if (errCode != E_OK) {
878 LOGE("[KvDBManager][CorruptNotify] ScheduleTask failed, errCode = %d.", errCode);
879 return;
880 }
881 }
882
SetCorruptHandlerForDatabases(const std::map<std::string,IKvDB * > & dbMaps)883 void KvDBManager::SetCorruptHandlerForDatabases(const std::map<std::string, IKvDB *> &dbMaps)
884 {
885 for (const auto &item : dbMaps) {
886 if (item.second == nullptr) {
887 continue;
888 }
889
890 item.second->SetCorruptHandler([item, this]() {
891 std::string appId = item.second->GetMyProperties().GetStringProp(KvDBProperties::APP_ID, "");
892 std::string userId = item.second->GetMyProperties().GetStringProp(KvDBProperties::USER_ID, "");
893 std::string storeId = item.second->GetMyProperties().GetStringProp(KvDBProperties::STORE_ID, "");
894 this->DataBaseCorruptNotifyAsync(appId, userId, storeId);
895 });
896 }
897 }
898
RestoreSyncerOfAllKvStore()899 void KvDBManager::RestoreSyncerOfAllKvStore()
900 {
901 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
902 for (auto &item : singleVerNaturalStores_) {
903 if (item.second != nullptr) {
904 item.second->WakeUpSyncer();
905 }
906 }
907
908 for (auto &item : multiVerNaturalStores_) {
909 if (item.second != nullptr) {
910 item.second->WakeUpSyncer();
911 }
912 }
913 }
914
CompareSchemaObject(const SchemaObject & newSchema,const SchemaObject & oldSchema)915 bool KvDBManager::CompareSchemaObject(const SchemaObject &newSchema, const SchemaObject &oldSchema)
916 {
917 if (!newSchema.IsSchemaValid() && !oldSchema.IsSchemaValid()) { // LCOV_EXCL_BR_LINE
918 return true;
919 }
920 if (!newSchema.IsSchemaValid() || !oldSchema.IsSchemaValid()) { // LCOV_EXCL_BR_LINE
921 return false;
922 }
923 return (oldSchema.CompareAgainstSchemaObject(newSchema) == -E_SCHEMA_EQUAL_EXACTLY);
924 }
925
CheckSchema(const IKvDB * kvDB,const KvDBProperties & properties)926 int KvDBManager::CheckSchema(const IKvDB *kvDB, const KvDBProperties &properties)
927 {
928 if (kvDB == nullptr) {
929 LOGE("input kvdb is nullptr");
930 return -E_INVALID_ARGS;
931 }
932 SchemaObject inputSchema = properties.GetSchema();
933 SchemaObject cacheSchema = kvDB->GetMyProperties().GetSchema();
934 bool isFirstOpenReadOnly =
935 kvDB->GetMyProperties().GetBoolProp(KvDBProperties::FIRST_OPEN_IS_READ_ONLY, false);
936 if (isFirstOpenReadOnly) {
937 if (inputSchema.IsSchemaValid()) {
938 LOGE("schema not matched");
939 return -E_SCHEMA_MISMATCH;
940 } else {
941 return E_OK;
942 }
943 }
944 if (!CompareSchemaObject(inputSchema, cacheSchema)) {
945 LOGE("schema not matched");
946 return -E_SCHEMA_MISMATCH;
947 }
948 return E_OK;
949 }
950
951 namespace {
CheckSecOptions(const KvDBProperties & input,const KvDBProperties & existed)952 bool CheckSecOptions(const KvDBProperties &input, const KvDBProperties &existed)
953 {
954 // If any has NO_SET, skip the check and using the existed option.
955 if (input.GetIntProp(KvDBProperties::SECURITY_LABEL, 0) != 0 &&
956 existed.GetIntProp(KvDBProperties::SECURITY_LABEL, 0) != 0) {
957 if (existed.GetIntProp(KvDBProperties::SECURITY_LABEL, 0) !=
958 input.GetIntProp(KvDBProperties::SECURITY_LABEL, 0)) {
959 LOGE("Security label mismatch: existed[%d] vs input[%d]",
960 existed.GetIntProp(KvDBProperties::SECURITY_LABEL, 0),
961 input.GetIntProp(KvDBProperties::SECURITY_LABEL, 0));
962 return false;
963 }
964 if (existed.GetIntProp(KvDBProperties::SECURITY_FLAG, 0) !=
965 input.GetIntProp(KvDBProperties::SECURITY_FLAG, 0)) {
966 LOGE("Security flag mismatch: existed[%d] vs input[%d]",
967 existed.GetIntProp(KvDBProperties::SECURITY_FLAG, 0),
968 input.GetIntProp(KvDBProperties::SECURITY_FLAG, 0));
969 return false;
970 }
971 }
972 return true;
973 }
974 }
975
CheckKvDBProperties(const IKvDB * kvDB,const KvDBProperties & properties,bool isNeedCheckPasswd) const976 int KvDBManager::CheckKvDBProperties(const IKvDB *kvDB, const KvDBProperties &properties,
977 bool isNeedCheckPasswd) const
978 {
979 // if get from cache is not memoryDb, do not support open or create memory DB
980 bool isMemoryDb = properties.GetBoolProp(KvDBProperties::MEMORY_MODE, false);
981 if (isMemoryDb != kvDB->GetMyProperties().GetBoolProp(KvDBProperties::MEMORY_MODE, false)) { // LCOV_EXCL_BR_LINE
982 LOGE("Already open same id physical DB, so do not support open or create memory DB");
983 return -E_INVALID_ARGS;
984 }
985
986 if (kvDB->GetMyProperties().GetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, false) !=
987 properties.GetBoolProp(KvDBProperties::CREATE_DIR_BY_STORE_ID_ONLY, false)) { // LCOV_EXCL_BR_LINE
988 LOGE("Different ways to create dir.");
989 return -E_INVALID_ARGS;
990 }
991
992 if (kvDB->GetMyProperties().GetIntProp(KvDBProperties::CONFLICT_RESOLVE_POLICY, 0) !=
993 properties.GetIntProp(KvDBProperties::CONFLICT_RESOLVE_POLICY, 0)) { // LCOV_EXCL_BR_LINE
994 LOGE("Different conflict resolve policy.");
995 return -E_INVALID_ARGS;
996 }
997
998 if (kvDB->GetMyProperties().GetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, false) !=
999 properties.GetBoolProp(KvDBProperties::SYNC_DUAL_TUPLE_MODE, false)) { // LCOV_EXCL_BR_LINE
1000 LOGE("Different dual tuple sync mode");
1001 return -E_MODE_MISMATCH;
1002 }
1003
1004 if (kvDB->GetMyProperties().GetBoolProp(KvDBProperties::LOCAL_ONLY, false) !=
1005 properties.GetBoolProp(KvDBProperties::LOCAL_ONLY, false)) { // LCOV_EXCL_BR_LINE
1006 LOGE("Different local only mode");
1007 return -E_INVALID_ARGS;
1008 }
1009
1010 if (!CheckSecOptions(properties, kvDB->GetMyProperties())) { // LCOV_EXCL_BR_LINE
1011 return -E_INVALID_ARGS;
1012 }
1013
1014 CipherType cacheType;
1015 CipherType inputType;
1016 CipherPassword cachePasswd;
1017 CipherPassword inputPasswd;
1018 kvDB->GetMyProperties().GetPassword(cacheType, cachePasswd);
1019 properties.GetPassword(inputType, inputPasswd);
1020 if (isNeedCheckPasswd && (cachePasswd != inputPasswd ||
1021 !DBCommon::IsSameCipher(cacheType, inputType))) { // LCOV_EXCL_BR_LINE
1022 LOGE("Identification not matched");
1023 return -E_INVALID_PASSWD_OR_CORRUPTED_DB;
1024 }
1025
1026 return CheckSchema(kvDB, properties);
1027 }
1028
1029 // Attention. After call FindKvDB and kvdb is not null, you need to call DecObjRef.
FindKvDB(const std::string & identifier) const1030 IKvDB* KvDBManager::FindKvDB(const std::string &identifier) const
1031 {
1032 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
1033 auto kvdb = singleVerNaturalStores_.find(identifier);
1034 if (kvdb != singleVerNaturalStores_.end()) {
1035 // Increase ref counter here.
1036 RefObject::IncObjRef(kvdb->second);
1037 return kvdb->second;
1038 }
1039 return nullptr;
1040 }
1041
Dump(int fd)1042 void KvDBManager::Dump(int fd)
1043 {
1044 std::lock_guard<std::mutex> lockGuard(kvDBLock_);
1045 for (const auto &entry : singleVerNaturalStores_) {
1046 RefObject::IncObjRef(entry.second);
1047 entry.second->Dump(fd);
1048 RefObject::DecObjRef(entry.second);
1049 }
1050 }
1051 } // namespace DistributedDB
1052