persistent_cache_test.cc 14 KB

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