Skip to content

Commit 8c7360b

Browse files
author
Andrei Popescu
authored
Do not remove protected keys in DefaultCache. (#1193)
When user calls DefaultCache::Remove() and DefaultCache::RemoveKeysWithPrefix() we should not remove protected keys. This is now implemented and tested. Relates-To: OLPEDGE-2521 Signed-off-by: Andrei Popescu <[email protected]>
1 parent b0d21fe commit 8c7360b

File tree

7 files changed

+353
-65
lines changed

7 files changed

+353
-65
lines changed

olp-cpp-sdk-core/include/olp/core/cache/DefaultCache.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2019-2020 HERE Europe B.V.
2+
* Copyright (C) 2019-2021 HERE Europe B.V.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -277,4 +277,4 @@ class CORE_API DefaultCache : public KeyValueCache {
277277
} // namespace cache
278278
} // namespace olp
279279

280-
#endif // OLP_SDK_ENABLE_DEFAULT_CACHE
280+
#endif // OLP_SDK_ENABLE_DEFAULT_CACHE

olp-cpp-sdk-core/src/cache/DefaultCacheImpl.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -346,15 +346,25 @@ KeyValueCache::ValueTypePtr DefaultCacheImpl::Get(const std::string& key) {
346346

347347
return value;
348348
}
349-
350349
return nullptr;
351350
}
352351

353352
bool DefaultCacheImpl::Remove(const std::string& key) {
354353
std::lock_guard<std::mutex> lock(cache_lock_);
354+
355355
if (!is_open_) {
356356
return false;
357357
}
358+
359+
// In case the key is protected do not remove it
360+
if (protected_keys_.IsProtected(key)) {
361+
OLP_SDK_LOG_INFO_F(kLogTag,
362+
"Remove() called on a protected key, ignoring, key='%s'",
363+
key.c_str());
364+
365+
return false;
366+
}
367+
358368
// protected data could be removed by user
359369
if (memory_cache_) {
360370
memory_cache_->Remove(key);
@@ -374,19 +384,27 @@ bool DefaultCacheImpl::Remove(const std::string& key) {
374384

375385
bool DefaultCacheImpl::RemoveKeysWithPrefix(const std::string& key) {
376386
std::lock_guard<std::mutex> lock(cache_lock_);
387+
377388
if (!is_open_) {
378389
return false;
379390
}
380391

392+
auto filter = [&](const std::string& cache_key) {
393+
return protected_keys_.IsProtected(cache_key);
394+
};
395+
381396
if (memory_cache_) {
382-
memory_cache_->RemoveKeysWithPrefix(key);
397+
memory_cache_->RemoveKeysWithPrefix(key, filter);
383398
}
384399

400+
// No need to check here for protected key as these are not added to LRU from
401+
// the start
385402
RemoveKeysWithPrefixLru(key);
386403

387404
if (mutable_cache_) {
388405
uint64_t removed_data_size = 0;
389-
auto result = mutable_cache_->RemoveKeysWithPrefix(key, removed_data_size);
406+
auto result =
407+
mutable_cache_->RemoveKeysWithPrefix(key, removed_data_size, filter);
390408
mutable_cache_data_size_ -= removed_data_size;
391409
return result;
392410
}
@@ -1037,5 +1055,6 @@ uint64_t DefaultCacheImpl::Size(uint64_t new_size) {
10371055
mutable_cache_->Compact();
10381056
return evicted;
10391057
}
1058+
10401059
} // namespace cache
10411060
} // namespace olp

olp-cpp-sdk-core/src/cache/DiskCache.cpp

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -384,34 +384,46 @@ DiskCache::OperationOutcome DiskCache::ApplyBatch(
384384
}
385385

386386
bool DiskCache::RemoveKeysWithPrefix(const std::string& prefix,
387-
uint64_t& removed_data_size) {
387+
uint64_t& removed_data_size,
388+
const RemoveFilterFunc& filter) {
388389
uint64_t data_size = 0u;
389-
if (!database_) {
390-
removed_data_size = 0u;
391-
OLP_SDK_LOG_ERROR(kLogTag,
392-
"RemoveKeysWithPrefix: Database is uninitialized");
393-
return false;
394-
}
395-
396-
auto batch = std::make_unique<leveldb::WriteBatch>();
390+
removed_data_size = 0u;
397391

392+
// As we remove data probably it is not wise to flood the leveldb memory cache
398393
leveldb::ReadOptions opts;
399394
opts.verify_checksums = check_crc_;
400-
opts.fill_cache = true;
395+
opts.fill_cache = false;
401396
auto iterator = NewIterator(opts);
397+
if (!iterator) {
398+
OLP_SDK_LOG_WARNING(kLogTag,
399+
"RemoveKeysWithPrefix: Database is uninitialized");
400+
return false;
401+
}
402402

403-
if (prefix.empty()) {
404-
for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
405-
batch->Delete(iterator->key());
406-
data_size += iterator->value().size() + iterator->key().size();
407-
}
403+
auto batch = std::make_unique<leveldb::WriteBatch>();
404+
auto prefix_slice = leveldb::Slice(prefix);
405+
const bool prefix_empty = prefix_slice.empty();
406+
407+
if (prefix_empty) {
408+
iterator->SeekToFirst();
408409
} else {
409-
for (iterator->Seek(prefix);
410-
iterator->Valid() && iterator->key().starts_with(prefix);
411-
iterator->Next()) {
412-
batch->Delete(iterator->key());
413-
data_size += iterator->value().size() + iterator->key().size();
410+
iterator->Seek(prefix_slice);
411+
}
412+
413+
auto condition = [&](const leveldb::Iterator& it) {
414+
return it.Valid() && (prefix_empty || it.key().starts_with(prefix_slice));
415+
};
416+
417+
for (; condition(*iterator); iterator->Next()) {
418+
auto key = iterator->key();
419+
420+
// Do not delete if protected
421+
if (filter && filter(key.ToString())) {
422+
continue;
414423
}
424+
425+
batch->Delete(key);
426+
data_size += iterator->value().size() + key.size();
415427
}
416428

417429
auto result = ApplyBatch(std::move(batch));

olp-cpp-sdk-core/src/cache/DiskCache.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <olp/core/cache/CacheSettings.h>
3939
#include <olp/core/cache/KeyValueCache.h>
4040
#include <olp/core/client/ApiError.h>
41+
#include <olp/core/client/ApiNoResult.h>
4142
#include <olp/core/client/ApiResponse.h>
4243

4344
namespace leveldb {
@@ -91,11 +92,14 @@ class DiskCache {
9192
static constexpr uint64_t kSizeMax = std::numeric_limits<uint64_t>::max();
9293

9394
/// No error type
94-
struct NoError {};
95+
using NoError = client::ApiNoResult;
9596

9697
/// Operation result type
9798
using OperationOutcome = client::ApiResponse<NoError, client::ApiError>;
9899

100+
/// Will be used to filter out keys to be removed in case they are protected.
101+
using RemoveFilterFunc = std::function<bool(const std::string&)>;
102+
99103
/// Logger that forwards leveldb log messages to our logging framework.
100104
class LevelDBLogger : public leveldb::Logger {
101105
void Logv(const char* format, va_list ap) override;
@@ -140,7 +144,8 @@ class DiskCache {
140144

141145
/// Empty prefix deleted everything from DB. Returns size of removed data.
142146
bool RemoveKeysWithPrefix(const std::string& prefix,
143-
uint64_t& removed_data_size);
147+
uint64_t& removed_data_size,
148+
const RemoveFilterFunc& filter = nullptr);
144149

145150
/// Check if cache contains data with the key.
146151
bool Contains(const std::string& key);

olp-cpp-sdk-core/src/cache/InMemoryCache.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,18 @@ bool InMemoryCache::Remove(const std::string& key) {
9393
return item_tuples_.Erase(key);
9494
}
9595

96-
void InMemoryCache::RemoveKeysWithPrefix(const std::string& key_prefix) {
96+
void InMemoryCache::RemoveKeysWithPrefix(const std::string& key_prefix,
97+
const RemoveFilterFunc& filter) {
9798
std::lock_guard<std::mutex> lock{mutex_};
99+
98100
for (auto it = item_tuples_.begin(); it != item_tuples_.end();) {
99101
if (it->key().substr(0, key_prefix.length()) == key_prefix) {
102+
// Check if this key is not protected, and if it is do not remove
103+
if (filter && filter(it->key())) {
104+
++it;
105+
continue;
106+
}
107+
100108
// we allow concurrent modifications.
101109
it = item_tuples_.Erase(it);
102110
} else {

olp-cpp-sdk-core/src/cache/InMemoryCache.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ class InMemoryCache {
4747
using TimeProvider = std::function<time_t()>;
4848
using ModelCacheCostFunc = std::function<std::size_t(const ItemTuple&)>;
4949

50+
/// Will be used to filter out keys to be removed in case they are protected.
51+
using RemoveFilterFunc = std::function<bool(const std::string&)>;
52+
5053
/// Default cache cost based on size.
5154
struct DefaultCacheCost {
5255
std::size_t operator()(const ItemTuple& value) const {
@@ -75,7 +78,8 @@ class InMemoryCache {
7578
void Clear();
7679

7780
bool Remove(const std::string& key);
78-
void RemoveKeysWithPrefix(const std::string& key_prefix);
81+
void RemoveKeysWithPrefix(const std::string& key_prefix,
82+
const RemoveFilterFunc& filter = nullptr);
7983
bool Contains(const std::string& key) const;
8084

8185
protected:

0 commit comments

Comments
 (0)