persistent_cache_test.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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. // GetUniqueIdFromFile is not implemented on Windows. Persistent cache
  10. // breaks when that function is not implemented
  11. #if !defined(ROCKSDB_LITE) && !defined(OS_WIN)
  12. #include "utilities/persistent_cache/persistent_cache_test.h"
  13. #include <functional>
  14. #include <memory>
  15. #include <thread>
  16. #include "utilities/persistent_cache/block_cache_tier.h"
  17. namespace ROCKSDB_NAMESPACE {
  18. static const double kStressFactor = .125;
  19. #ifdef OS_LINUX
  20. static void OnOpenForRead(void* arg) {
  21. int* val = static_cast<int*>(arg);
  22. *val &= ~O_DIRECT;
  23. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  24. "NewRandomAccessFile:O_DIRECT",
  25. std::bind(OnOpenForRead, std::placeholders::_1));
  26. }
  27. static void OnOpenForWrite(void* arg) {
  28. int* val = static_cast<int*>(arg);
  29. *val &= ~O_DIRECT;
  30. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  31. "NewWritableFile:O_DIRECT",
  32. std::bind(OnOpenForWrite, std::placeholders::_1));
  33. }
  34. #endif
  35. static void RemoveDirectory(const std::string& folder) {
  36. std::vector<std::string> files;
  37. Status status = Env::Default()->GetChildren(folder, &files);
  38. if (!status.ok()) {
  39. // we assume the directory does not exist
  40. return;
  41. }
  42. // cleanup files with the patter :digi:.rc
  43. for (auto file : files) {
  44. if (file == "." || file == "..") {
  45. continue;
  46. }
  47. status = Env::Default()->DeleteFile(folder + "/" + file);
  48. assert(status.ok());
  49. }
  50. status = Env::Default()->DeleteDir(folder);
  51. assert(status.ok());
  52. }
  53. static void OnDeleteDir(void* arg) {
  54. char* dir = static_cast<char*>(arg);
  55. RemoveDirectory(std::string(dir));
  56. }
  57. //
  58. // Simple logger that prints message on stdout
  59. //
  60. class ConsoleLogger : public Logger {
  61. public:
  62. using Logger::Logv;
  63. ConsoleLogger() : Logger(InfoLogLevel::ERROR_LEVEL) {}
  64. void Logv(const char* format, va_list ap) override {
  65. MutexLock _(&lock_);
  66. vprintf(format, ap);
  67. printf("\n");
  68. }
  69. port::Mutex lock_;
  70. };
  71. // construct a tiered RAM+Block cache
  72. std::unique_ptr<PersistentTieredCache> NewTieredCache(
  73. const size_t mem_size, const PersistentCacheConfig& opt) {
  74. std::unique_ptr<PersistentTieredCache> tcache(new PersistentTieredCache());
  75. // create primary tier
  76. assert(mem_size);
  77. auto pcache = std::shared_ptr<PersistentCacheTier>(new VolatileCacheTier(
  78. /*is_compressed*/ true, mem_size));
  79. tcache->AddTier(pcache);
  80. // create secondary tier
  81. auto scache = std::shared_ptr<PersistentCacheTier>(new BlockCacheTier(opt));
  82. tcache->AddTier(scache);
  83. Status s = tcache->Open();
  84. assert(s.ok());
  85. return tcache;
  86. }
  87. // create block cache
  88. std::unique_ptr<PersistentCacheTier> NewBlockCache(
  89. Env* env, const std::string& path,
  90. const uint64_t max_size = std::numeric_limits<uint64_t>::max(),
  91. const bool enable_direct_writes = false) {
  92. const uint32_t max_file_size = static_cast<uint32_t>(12 * 1024 * 1024 * kStressFactor);
  93. auto log = std::make_shared<ConsoleLogger>();
  94. PersistentCacheConfig opt(env, path, max_size, log);
  95. opt.cache_file_size = max_file_size;
  96. opt.max_write_pipeline_backlog_size = std::numeric_limits<uint64_t>::max();
  97. opt.enable_direct_writes = enable_direct_writes;
  98. std::unique_ptr<PersistentCacheTier> scache(new BlockCacheTier(opt));
  99. Status s = scache->Open();
  100. assert(s.ok());
  101. return scache;
  102. }
  103. // create a new cache tier
  104. std::unique_ptr<PersistentTieredCache> NewTieredCache(
  105. Env* env, const std::string& path, const uint64_t max_volatile_cache_size,
  106. const uint64_t max_block_cache_size =
  107. std::numeric_limits<uint64_t>::max()) {
  108. const uint32_t max_file_size = static_cast<uint32_t>(12 * 1024 * 1024 * kStressFactor);
  109. auto log = std::make_shared<ConsoleLogger>();
  110. auto opt = PersistentCacheConfig(env, path, max_block_cache_size, log);
  111. opt.cache_file_size = max_file_size;
  112. opt.max_write_pipeline_backlog_size = std::numeric_limits<uint64_t>::max();
  113. // create tier out of the two caches
  114. auto cache = NewTieredCache(max_volatile_cache_size, opt);
  115. return cache;
  116. }
  117. PersistentCacheTierTest::PersistentCacheTierTest()
  118. : path_(test::PerThreadDBPath("cache_test")) {
  119. #ifdef OS_LINUX
  120. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  121. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  122. "NewRandomAccessFile:O_DIRECT", OnOpenForRead);
  123. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  124. "NewWritableFile:O_DIRECT", OnOpenForWrite);
  125. #endif
  126. }
  127. // Block cache tests
  128. TEST_F(PersistentCacheTierTest, DISABLED_BlockCacheInsertWithFileCreateError) {
  129. cache_ = NewBlockCache(Env::Default(), path_,
  130. /*size=*/std::numeric_limits<uint64_t>::max(),
  131. /*direct_writes=*/ false);
  132. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  133. "BlockCacheTier::NewCacheFile:DeleteDir", OnDeleteDir);
  134. RunNegativeInsertTest(/*nthreads=*/ 1,
  135. /*max_keys*/
  136. static_cast<size_t>(10 * 1024 * kStressFactor));
  137. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
  138. }
  139. #if defined(TRAVIS) || defined(ROCKSDB_VALGRIND_RUN)
  140. // Travis is unable to handle the normal version of the tests running out of
  141. // fds, out of space and timeouts. This is an easier version of the test
  142. // specifically written for Travis
  143. TEST_F(PersistentCacheTierTest, BasicTest) {
  144. cache_ = std::make_shared<VolatileCacheTier>();
  145. RunInsertTest(/*nthreads=*/1, /*max_keys=*/1024);
  146. cache_ = NewBlockCache(Env::Default(), path_,
  147. /*size=*/std::numeric_limits<uint64_t>::max(),
  148. /*direct_writes=*/true);
  149. RunInsertTest(/*nthreads=*/1, /*max_keys=*/1024);
  150. cache_ = NewTieredCache(Env::Default(), path_,
  151. /*memory_size=*/static_cast<size_t>(1 * 1024 * 1024));
  152. RunInsertTest(/*nthreads=*/1, /*max_keys=*/1024);
  153. }
  154. #else
  155. // Volatile cache tests
  156. TEST_F(PersistentCacheTierTest, VolatileCacheInsert) {
  157. for (auto nthreads : {1, 5}) {
  158. for (auto max_keys :
  159. {10 * 1024 * kStressFactor, 1 * 1024 * 1024 * kStressFactor}) {
  160. cache_ = std::make_shared<VolatileCacheTier>();
  161. RunInsertTest(nthreads, static_cast<size_t>(max_keys));
  162. }
  163. }
  164. }
  165. TEST_F(PersistentCacheTierTest, VolatileCacheInsertWithEviction) {
  166. for (auto nthreads : {1, 5}) {
  167. for (auto max_keys : {1 * 1024 * 1024 * kStressFactor}) {
  168. cache_ = std::make_shared<VolatileCacheTier>(
  169. /*compressed=*/true, /*size=*/static_cast<size_t>(1 * 1024 * 1024 * kStressFactor));
  170. RunInsertTestWithEviction(nthreads, static_cast<size_t>(max_keys));
  171. }
  172. }
  173. }
  174. // Block cache tests
  175. TEST_F(PersistentCacheTierTest, BlockCacheInsert) {
  176. for (auto direct_writes : {true, false}) {
  177. for (auto nthreads : {1, 5}) {
  178. for (auto max_keys :
  179. {10 * 1024 * kStressFactor, 1 * 1024 * 1024 * kStressFactor}) {
  180. cache_ = NewBlockCache(Env::Default(), path_,
  181. /*size=*/std::numeric_limits<uint64_t>::max(),
  182. direct_writes);
  183. RunInsertTest(nthreads, static_cast<size_t>(max_keys));
  184. }
  185. }
  186. }
  187. }
  188. TEST_F(PersistentCacheTierTest, BlockCacheInsertWithEviction) {
  189. for (auto nthreads : {1, 5}) {
  190. for (auto max_keys : {1 * 1024 * 1024 * kStressFactor}) {
  191. cache_ = NewBlockCache(Env::Default(), path_,
  192. /*max_size=*/static_cast<size_t>(200 * 1024 * 1024 * kStressFactor));
  193. RunInsertTestWithEviction(nthreads, static_cast<size_t>(max_keys));
  194. }
  195. }
  196. }
  197. // Tiered cache tests
  198. TEST_F(PersistentCacheTierTest, TieredCacheInsert) {
  199. for (auto nthreads : {1, 5}) {
  200. for (auto max_keys :
  201. {10 * 1024 * kStressFactor, 1 * 1024 * 1024 * kStressFactor}) {
  202. cache_ = NewTieredCache(Env::Default(), path_,
  203. /*memory_size=*/static_cast<size_t>(1 * 1024 * 1024 * kStressFactor));
  204. RunInsertTest(nthreads, static_cast<size_t>(max_keys));
  205. }
  206. }
  207. }
  208. // the tests causes a lot of file deletions which Travis limited testing
  209. // environment cannot handle
  210. TEST_F(PersistentCacheTierTest, TieredCacheInsertWithEviction) {
  211. for (auto nthreads : {1, 5}) {
  212. for (auto max_keys : {1 * 1024 * 1024 * kStressFactor}) {
  213. cache_ = NewTieredCache(
  214. Env::Default(), path_,
  215. /*memory_size=*/static_cast<size_t>(1 * 1024 * 1024 * kStressFactor),
  216. /*block_cache_size*/ static_cast<size_t>(200 * 1024 * 1024 * kStressFactor));
  217. RunInsertTestWithEviction(nthreads, static_cast<size_t>(max_keys));
  218. }
  219. }
  220. }
  221. #endif
  222. std::shared_ptr<PersistentCacheTier> MakeVolatileCache(
  223. const std::string& /*dbname*/) {
  224. return std::make_shared<VolatileCacheTier>();
  225. }
  226. std::shared_ptr<PersistentCacheTier> MakeBlockCache(const std::string& dbname) {
  227. return NewBlockCache(Env::Default(), dbname);
  228. }
  229. std::shared_ptr<PersistentCacheTier> MakeTieredCache(
  230. const std::string& dbname) {
  231. const auto memory_size = 1 * 1024 * 1024 * kStressFactor;
  232. return NewTieredCache(Env::Default(), dbname, static_cast<size_t>(memory_size));
  233. }
  234. #ifdef OS_LINUX
  235. static void UniqueIdCallback(void* arg) {
  236. int* result = reinterpret_cast<int*>(arg);
  237. if (*result == -1) {
  238. *result = 0;
  239. }
  240. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearTrace();
  241. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  242. "GetUniqueIdFromFile:FS_IOC_GETVERSION", UniqueIdCallback);
  243. }
  244. #endif
  245. TEST_F(PersistentCacheTierTest, FactoryTest) {
  246. for (auto nvm_opt : {true, false}) {
  247. ASSERT_FALSE(cache_);
  248. auto log = std::make_shared<ConsoleLogger>();
  249. std::shared_ptr<PersistentCache> cache;
  250. ASSERT_OK(NewPersistentCache(Env::Default(), path_,
  251. /*size=*/1 * 1024 * 1024 * 1024, log, nvm_opt,
  252. &cache));
  253. ASSERT_TRUE(cache);
  254. ASSERT_EQ(cache->Stats().size(), 1);
  255. ASSERT_TRUE(cache->Stats()[0].size());
  256. cache.reset();
  257. }
  258. }
  259. PersistentCacheDBTest::PersistentCacheDBTest() : DBTestBase("/cache_test") {
  260. #ifdef OS_LINUX
  261. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  262. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  263. "GetUniqueIdFromFile:FS_IOC_GETVERSION", UniqueIdCallback);
  264. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  265. "NewRandomAccessFile:O_DIRECT", OnOpenForRead);
  266. #endif
  267. }
  268. // test template
  269. void PersistentCacheDBTest::RunTest(
  270. const std::function<std::shared_ptr<PersistentCacheTier>(bool)>& new_pcache,
  271. const size_t max_keys = 100 * 1024, const size_t max_usecase = 5) {
  272. if (!Snappy_Supported()) {
  273. return;
  274. }
  275. // number of insertion interations
  276. int num_iter = static_cast<int>(max_keys * kStressFactor);
  277. for (size_t iter = 0; iter < max_usecase; iter++) {
  278. Options options;
  279. options.write_buffer_size =
  280. static_cast<size_t>(64 * 1024 * kStressFactor); // small write buffer
  281. options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
  282. options = CurrentOptions(options);
  283. // setup page cache
  284. std::shared_ptr<PersistentCacheTier> pcache;
  285. BlockBasedTableOptions table_options;
  286. table_options.cache_index_and_filter_blocks = true;
  287. const size_t size_max = std::numeric_limits<size_t>::max();
  288. switch (iter) {
  289. case 0:
  290. // page cache, block cache, no-compressed cache
  291. pcache = new_pcache(/*is_compressed=*/true);
  292. table_options.persistent_cache = pcache;
  293. table_options.block_cache = NewLRUCache(size_max);
  294. table_options.block_cache_compressed = nullptr;
  295. options.table_factory.reset(NewBlockBasedTableFactory(table_options));
  296. break;
  297. case 1:
  298. // page cache, block cache, compressed cache
  299. pcache = new_pcache(/*is_compressed=*/true);
  300. table_options.persistent_cache = pcache;
  301. table_options.block_cache = NewLRUCache(size_max);
  302. table_options.block_cache_compressed = NewLRUCache(size_max);
  303. options.table_factory.reset(NewBlockBasedTableFactory(table_options));
  304. break;
  305. case 2:
  306. // page cache, block cache, compressed cache + KNoCompression
  307. // both block cache and compressed cache, but DB is not compressed
  308. // also, make block cache sizes bigger, to trigger block cache hits
  309. pcache = new_pcache(/*is_compressed=*/true);
  310. table_options.persistent_cache = pcache;
  311. table_options.block_cache = NewLRUCache(size_max);
  312. table_options.block_cache_compressed = NewLRUCache(size_max);
  313. options.table_factory.reset(NewBlockBasedTableFactory(table_options));
  314. options.compression = kNoCompression;
  315. break;
  316. case 3:
  317. // page cache, no block cache, no compressed cache
  318. pcache = new_pcache(/*is_compressed=*/false);
  319. table_options.persistent_cache = pcache;
  320. table_options.block_cache = nullptr;
  321. table_options.block_cache_compressed = nullptr;
  322. options.table_factory.reset(NewBlockBasedTableFactory(table_options));
  323. break;
  324. case 4:
  325. // page cache, no block cache, no compressed cache
  326. // Page cache caches compressed blocks
  327. pcache = new_pcache(/*is_compressed=*/true);
  328. table_options.persistent_cache = pcache;
  329. table_options.block_cache = nullptr;
  330. table_options.block_cache_compressed = nullptr;
  331. options.table_factory.reset(NewBlockBasedTableFactory(table_options));
  332. break;
  333. default:
  334. FAIL();
  335. }
  336. std::vector<std::string> values;
  337. // insert data
  338. Insert(options, table_options, num_iter, &values);
  339. // flush all data in cache to device
  340. pcache->TEST_Flush();
  341. // verify data
  342. Verify(num_iter, values);
  343. auto block_miss = TestGetTickerCount(options, BLOCK_CACHE_MISS);
  344. auto compressed_block_hit =
  345. TestGetTickerCount(options, BLOCK_CACHE_COMPRESSED_HIT);
  346. auto compressed_block_miss =
  347. TestGetTickerCount(options, BLOCK_CACHE_COMPRESSED_MISS);
  348. auto page_hit = TestGetTickerCount(options, PERSISTENT_CACHE_HIT);
  349. auto page_miss = TestGetTickerCount(options, PERSISTENT_CACHE_MISS);
  350. // check that we triggered the appropriate code paths in the cache
  351. switch (iter) {
  352. case 0:
  353. // page cache, block cache, no-compressed cache
  354. ASSERT_GT(page_miss, 0);
  355. ASSERT_GT(page_hit, 0);
  356. ASSERT_GT(block_miss, 0);
  357. ASSERT_EQ(compressed_block_miss, 0);
  358. ASSERT_EQ(compressed_block_hit, 0);
  359. break;
  360. case 1:
  361. // page cache, block cache, compressed cache
  362. ASSERT_GT(page_miss, 0);
  363. ASSERT_GT(block_miss, 0);
  364. ASSERT_GT(compressed_block_miss, 0);
  365. break;
  366. case 2:
  367. // page cache, block cache, compressed cache + KNoCompression
  368. ASSERT_GT(page_miss, 0);
  369. ASSERT_GT(page_hit, 0);
  370. ASSERT_GT(block_miss, 0);
  371. ASSERT_GT(compressed_block_miss, 0);
  372. // remember kNoCompression
  373. ASSERT_EQ(compressed_block_hit, 0);
  374. break;
  375. case 3:
  376. case 4:
  377. // page cache, no block cache, no compressed cache
  378. ASSERT_GT(page_miss, 0);
  379. ASSERT_GT(page_hit, 0);
  380. ASSERT_EQ(compressed_block_hit, 0);
  381. ASSERT_EQ(compressed_block_miss, 0);
  382. break;
  383. default:
  384. FAIL();
  385. }
  386. options.create_if_missing = true;
  387. DestroyAndReopen(options);
  388. pcache->Close();
  389. }
  390. }
  391. #if defined(TRAVIS) || defined(ROCKSDB_VALGRIND_RUN)
  392. // Travis is unable to handle the normal version of the tests running out of
  393. // fds, out of space and timeouts. This is an easier version of the test
  394. // specifically written for Travis
  395. TEST_F(PersistentCacheDBTest, BasicTest) {
  396. RunTest(std::bind(&MakeBlockCache, dbname_), /*max_keys=*/1024,
  397. /*max_usecase=*/1);
  398. }
  399. #else
  400. // test table with block page cache
  401. TEST_F(PersistentCacheDBTest, BlockCacheTest) {
  402. RunTest(std::bind(&MakeBlockCache, dbname_));
  403. }
  404. // test table with volatile page cache
  405. TEST_F(PersistentCacheDBTest, VolatileCacheTest) {
  406. RunTest(std::bind(&MakeVolatileCache, dbname_));
  407. }
  408. // test table with tiered page cache
  409. TEST_F(PersistentCacheDBTest, TieredCacheTest) {
  410. RunTest(std::bind(&MakeTieredCache, dbname_));
  411. }
  412. #endif
  413. } // namespace ROCKSDB_NAMESPACE
  414. int main(int argc, char** argv) {
  415. ::testing::InitGoogleTest(&argc, argv);
  416. return RUN_ALL_TESTS();
  417. }
  418. #else // !defined(ROCKSDB_LITE) && !defined(OS_WIN)
  419. int main() { return 0; }
  420. #endif // !defined(ROCKSDB_LITE) && !defined(OS_WIN)