persistent_cache_test.h 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. // Copyright (c) 2013, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. //
  6. // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  7. // Use of this source code is governed by a BSD-style license that can be
  8. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  9. #pragma once
  10. #ifndef ROCKSDB_LITE
  11. #include <functional>
  12. #include <limits>
  13. #include <list>
  14. #include <memory>
  15. #include <string>
  16. #include <thread>
  17. #include <vector>
  18. #include "db/db_test_util.h"
  19. #include "memory/arena.h"
  20. #include "port/port.h"
  21. #include "rocksdb/cache.h"
  22. #include "table/block_based/block_builder.h"
  23. #include "test_util/testharness.h"
  24. #include "utilities/persistent_cache/volatile_tier_impl.h"
  25. namespace ROCKSDB_NAMESPACE {
  26. //
  27. // Unit tests for testing PersistentCacheTier
  28. //
  29. class PersistentCacheTierTest : public testing::Test {
  30. public:
  31. PersistentCacheTierTest();
  32. virtual ~PersistentCacheTierTest() {
  33. if (cache_) {
  34. Status s = cache_->Close();
  35. assert(s.ok());
  36. }
  37. }
  38. protected:
  39. // Flush cache
  40. void Flush() {
  41. if (cache_) {
  42. cache_->TEST_Flush();
  43. }
  44. }
  45. // create threaded workload
  46. template <class T>
  47. std::list<port::Thread> SpawnThreads(const size_t n, const T& fn) {
  48. std::list<port::Thread> threads;
  49. for (size_t i = 0; i < n; i++) {
  50. port::Thread th(fn);
  51. threads.push_back(std::move(th));
  52. }
  53. return threads;
  54. }
  55. // Wait for threads to join
  56. void Join(std::list<port::Thread>&& threads) {
  57. for (auto& th : threads) {
  58. th.join();
  59. }
  60. threads.clear();
  61. }
  62. // Run insert workload in threads
  63. void Insert(const size_t nthreads, const size_t max_keys) {
  64. key_ = 0;
  65. max_keys_ = max_keys;
  66. // spawn threads
  67. auto fn = std::bind(&PersistentCacheTierTest::InsertImpl, this);
  68. auto threads = SpawnThreads(nthreads, fn);
  69. // join with threads
  70. Join(std::move(threads));
  71. // Flush cache
  72. Flush();
  73. }
  74. // Run verification on the cache
  75. void Verify(const size_t nthreads = 1, const bool eviction_enabled = false) {
  76. stats_verify_hits_ = 0;
  77. stats_verify_missed_ = 0;
  78. key_ = 0;
  79. // spawn threads
  80. auto fn =
  81. std::bind(&PersistentCacheTierTest::VerifyImpl, this, eviction_enabled);
  82. auto threads = SpawnThreads(nthreads, fn);
  83. // join with threads
  84. Join(std::move(threads));
  85. }
  86. // pad 0 to numbers
  87. std::string PaddedNumber(const size_t data, const size_t pad_size) {
  88. assert(pad_size);
  89. char* ret = new char[pad_size];
  90. int pos = static_cast<int>(pad_size) - 1;
  91. size_t count = 0;
  92. size_t t = data;
  93. // copy numbers
  94. while (t) {
  95. count++;
  96. ret[pos--] = '0' + t % 10;
  97. t = t / 10;
  98. }
  99. // copy 0s
  100. while (pos >= 0) {
  101. ret[pos--] = '0';
  102. }
  103. // post condition
  104. assert(count <= pad_size);
  105. assert(pos == -1);
  106. std::string result(ret, pad_size);
  107. delete[] ret;
  108. return result;
  109. }
  110. // Insert workload implementation
  111. void InsertImpl() {
  112. const std::string prefix = "key_prefix_";
  113. while (true) {
  114. size_t i = key_++;
  115. if (i >= max_keys_) {
  116. break;
  117. }
  118. char data[4 * 1024];
  119. memset(data, '0' + (i % 10), sizeof(data));
  120. auto k = prefix + PaddedNumber(i, /*count=*/8);
  121. Slice key(k);
  122. while (true) {
  123. Status status = cache_->Insert(key, data, sizeof(data));
  124. if (status.ok()) {
  125. break;
  126. }
  127. ASSERT_TRUE(status.IsTryAgain());
  128. Env::Default()->SleepForMicroseconds(1 * 1000 * 1000);
  129. }
  130. }
  131. }
  132. // Verification implementation
  133. void VerifyImpl(const bool eviction_enabled = false) {
  134. const std::string prefix = "key_prefix_";
  135. while (true) {
  136. size_t i = key_++;
  137. if (i >= max_keys_) {
  138. break;
  139. }
  140. char edata[4 * 1024];
  141. memset(edata, '0' + (i % 10), sizeof(edata));
  142. auto k = prefix + PaddedNumber(i, /*count=*/8);
  143. Slice key(k);
  144. std::unique_ptr<char[]> block;
  145. size_t block_size;
  146. if (eviction_enabled) {
  147. if (!cache_->Lookup(key, &block, &block_size).ok()) {
  148. // assume that the key is evicted
  149. stats_verify_missed_++;
  150. continue;
  151. }
  152. }
  153. ASSERT_OK(cache_->Lookup(key, &block, &block_size));
  154. ASSERT_EQ(block_size, sizeof(edata));
  155. ASSERT_EQ(memcmp(edata, block.get(), sizeof(edata)), 0);
  156. stats_verify_hits_++;
  157. }
  158. }
  159. // template for insert test
  160. void RunInsertTest(const size_t nthreads, const size_t max_keys) {
  161. Insert(nthreads, max_keys);
  162. Verify(nthreads);
  163. ASSERT_EQ(stats_verify_hits_, max_keys);
  164. ASSERT_EQ(stats_verify_missed_, 0);
  165. cache_->Close();
  166. cache_.reset();
  167. }
  168. // template for negative insert test
  169. void RunNegativeInsertTest(const size_t nthreads, const size_t max_keys) {
  170. Insert(nthreads, max_keys);
  171. Verify(nthreads, /*eviction_enabled=*/true);
  172. ASSERT_LT(stats_verify_hits_, max_keys);
  173. ASSERT_GT(stats_verify_missed_, 0);
  174. cache_->Close();
  175. cache_.reset();
  176. }
  177. // template for insert with eviction test
  178. void RunInsertTestWithEviction(const size_t nthreads, const size_t max_keys) {
  179. Insert(nthreads, max_keys);
  180. Verify(nthreads, /*eviction_enabled=*/true);
  181. ASSERT_EQ(stats_verify_hits_ + stats_verify_missed_, max_keys);
  182. ASSERT_GT(stats_verify_hits_, 0);
  183. ASSERT_GT(stats_verify_missed_, 0);
  184. cache_->Close();
  185. cache_.reset();
  186. }
  187. const std::string path_;
  188. std::shared_ptr<Logger> log_;
  189. std::shared_ptr<PersistentCacheTier> cache_;
  190. std::atomic<size_t> key_{0};
  191. size_t max_keys_ = 0;
  192. std::atomic<size_t> stats_verify_hits_{0};
  193. std::atomic<size_t> stats_verify_missed_{0};
  194. };
  195. //
  196. // RocksDB tests
  197. //
  198. class PersistentCacheDBTest : public DBTestBase {
  199. public:
  200. PersistentCacheDBTest();
  201. static uint64_t TestGetTickerCount(const Options& options,
  202. Tickers ticker_type) {
  203. return static_cast<uint32_t>(
  204. options.statistics->getTickerCount(ticker_type));
  205. }
  206. // insert data to table
  207. void Insert(const Options& options,
  208. const BlockBasedTableOptions& /*table_options*/,
  209. const int num_iter, std::vector<std::string>* values) {
  210. CreateAndReopenWithCF({"pikachu"}, options);
  211. // default column family doesn't have block cache
  212. Options no_block_cache_opts;
  213. no_block_cache_opts.statistics = options.statistics;
  214. no_block_cache_opts = CurrentOptions(no_block_cache_opts);
  215. BlockBasedTableOptions table_options_no_bc;
  216. table_options_no_bc.no_block_cache = true;
  217. no_block_cache_opts.table_factory.reset(
  218. NewBlockBasedTableFactory(table_options_no_bc));
  219. ReopenWithColumnFamilies(
  220. {"default", "pikachu"},
  221. std::vector<Options>({no_block_cache_opts, options}));
  222. Random rnd(301);
  223. // Write 8MB (80 values, each 100K)
  224. ASSERT_EQ(NumTableFilesAtLevel(0, 1), 0);
  225. std::string str;
  226. for (int i = 0; i < num_iter; i++) {
  227. if (i % 4 == 0) { // high compression ratio
  228. str = RandomString(&rnd, 1000);
  229. }
  230. values->push_back(str);
  231. ASSERT_OK(Put(1, Key(i), (*values)[i]));
  232. }
  233. // flush all data from memtable so that reads are from block cache
  234. ASSERT_OK(Flush(1));
  235. }
  236. // verify data
  237. void Verify(const int num_iter, const std::vector<std::string>& values) {
  238. for (int j = 0; j < 2; ++j) {
  239. for (int i = 0; i < num_iter; i++) {
  240. ASSERT_EQ(Get(1, Key(i)), values[i]);
  241. }
  242. }
  243. }
  244. // test template
  245. void RunTest(const std::function<std::shared_ptr<PersistentCacheTier>(bool)>&
  246. new_pcache,
  247. const size_t max_keys, const size_t max_usecase);
  248. };
  249. } // namespace ROCKSDB_NAMESPACE
  250. #endif