diff --git a/cachelib/cachebench/cache/Cache-inl.h b/cachelib/cachebench/cache/Cache-inl.h index 5ac6ad40ab..d27a0d5e77 100644 --- a/cachelib/cachebench/cache/Cache-inl.h +++ b/cachelib/cachebench/cache/Cache-inl.h @@ -327,6 +327,7 @@ template void Cache::enableConsistencyCheck( const std::vector& keys) { XDCHECK(valueTracker_ == nullptr); + XDCHECK(!valueValidatingEnabled()); valueTracker_ = std::make_unique(ValueTracker::wrapStrings(keys)); for (const std::string& key : keys) { @@ -334,6 +335,14 @@ void Cache::enableConsistencyCheck( } } +template +void Cache::enableValueValidating( + const std::string &expectedValue) { + XDCHECK(!valueValidatingEnabled()); + XDCHECK(!consistencyCheckEnabled()); + this->expectedValue_ = expectedValue; +} + template typename Cache::RemoveRes Cache::remove(Key key) { if (!consistencyCheckEnabled()) { @@ -426,6 +435,20 @@ typename Cache::ItemHandle Cache::insertOrReplace( return rv; } +template +void Cache::validateValue(const ItemHandle &it) const { + XDCHECK(valueValidatingEnabled()); + + const auto &expected = expectedValue_.value(); + + auto ptr = reinterpret_cast(getMemory(it)); + auto cmp = std::memcmp(ptr, expected.data(), std::min(expected.size(), + getSize(it))); + if (cmp != 0) { + throw std::runtime_error("Value does not match!"); + } +} + template typename Cache::ItemHandle Cache::find(Key key, AccessMode mode) { @@ -441,9 +464,15 @@ typename Cache::ItemHandle Cache::find(Key key, }; if (!consistencyCheckEnabled()) { - return findFn(); + auto it = findFn(); + if (valueValidatingEnabled()) { + validateValue(it); + } + return it; } + XDCHECK(!valueValidatingEnabled()); + auto opId = valueTracker_->beginGet(key); auto it = findFn(); if (checkGet(opId, it)) { diff --git a/cachelib/cachebench/cache/Cache.h b/cachelib/cachebench/cache/Cache.h index da376689ea..2fc7760463 100644 --- a/cachelib/cachebench/cache/Cache.h +++ b/cachelib/cachebench/cache/Cache.h @@ -163,6 +163,9 @@ class Cache { return getSize(item.get()); } + // checks if values stored in it matches expectedValue_. + void validateValue(const ItemHandle &it) const; + // returns the size of the item, taking into account ItemRecords could be // enabled. uint32_t getSize(const Item* item) const noexcept; @@ -220,9 +223,15 @@ class Cache { // @param keys list of keys that the stressor uses for the workload. void enableConsistencyCheck(const std::vector& keys); + // enables validating all values on find. Each value is compared to + // expected Value. + void enableValueValidating(const std::string &expectedValue); + // returns true if the consistency checking is enabled. bool consistencyCheckEnabled() const { return valueTracker_ != nullptr; } + bool valueValidatingEnabled() const { return expectedValue_.has_value(); } + // return true if the key was previously detected to be inconsistent. This // is useful only when consistency checking is enabled by calling // enableConsistencyCheck() @@ -345,6 +354,9 @@ class Cache { // tracker for consistency monitoring. std::unique_ptr valueTracker_; + // exceptected value of all items in Cache. + std::optional expectedValue_; + // reading of the nand bytes written for the benchmark if enabled. const uint64_t nandBytesBegin_{0}; diff --git a/cachelib/cachebench/runner/CacheStressor.h b/cachelib/cachebench/runner/CacheStressor.h index af4e9de2d2..84b10634a8 100644 --- a/cachelib/cachebench/runner/CacheStressor.h +++ b/cachelib/cachebench/runner/CacheStressor.h @@ -110,6 +110,9 @@ class CacheStressor : public Stressor { if (config_.checkConsistency) { cache_->enableConsistencyCheck(wg_->getAllKeys()); } + if (config_.validateValue) { + cache_->enableValueValidating(hardcodedString_); + } if (config_.opRatePerSec > 0) { rateLimiter_ = std::make_unique>( config_.opRatePerSec, config_.opRatePerSec); diff --git a/cachelib/cachebench/util/Config.cpp b/cachelib/cachebench/util/Config.cpp index 6bea18115f..2166fe5e47 100644 --- a/cachelib/cachebench/util/Config.cpp +++ b/cachelib/cachebench/util/Config.cpp @@ -34,6 +34,7 @@ StressorConfig::StressorConfig(const folly::dynamic& configJson) { JSONSetVal(configJson, samplingIntervalMs); JSONSetVal(configJson, checkConsistency); + JSONSetVal(configJson, validateValue); JSONSetVal(configJson, numOps); JSONSetVal(configJson, numThreads); diff --git a/cachelib/cachebench/util/Config.h b/cachelib/cachebench/util/Config.h index 9ab89e2f83..1a35c61b67 100644 --- a/cachelib/cachebench/util/Config.h +++ b/cachelib/cachebench/util/Config.h @@ -188,8 +188,13 @@ struct StressorConfig : public JSONConfig { uint64_t samplingIntervalMs{1000}; // If enabled, stressor will verify operations' results are consistent. + // Mutually exclusive with validateValue bool checkConsistency{false}; + // If enable, stressos will verify if value read is equal to value written. + // Mutually exclusive with checkConsistency + bool validateValue{false}; + uint64_t numOps{0}; // operation per thread uint64_t numThreads{0}; // number of threads that will run uint64_t numKeys{0}; // number of keys that will be used