ttl_test.cc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934
  1. // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
  2. // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  3. // Use of this source code is governed by a BSD-style license that can be
  4. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  5. #include <map>
  6. #include <memory>
  7. #include "rocksdb/compaction_filter.h"
  8. #include "rocksdb/convenience.h"
  9. #include "rocksdb/merge_operator.h"
  10. #include "rocksdb/utilities/db_ttl.h"
  11. #include "rocksdb/utilities/object_registry.h"
  12. #include "test_util/testharness.h"
  13. #include "util/string_util.h"
  14. #include "utilities/merge_operators/bytesxor.h"
  15. #include "utilities/ttl/db_ttl_impl.h"
  16. #ifndef OS_WIN
  17. #include <unistd.h>
  18. #endif
  19. namespace ROCKSDB_NAMESPACE {
  20. namespace {
  21. using KVMap = std::map<std::string, std::string>;
  22. enum BatchOperation { OP_PUT = 0, OP_DELETE = 1 };
  23. } // namespace
  24. class SpecialTimeEnv : public EnvWrapper {
  25. public:
  26. explicit SpecialTimeEnv(Env* base) : EnvWrapper(base) {
  27. EXPECT_OK(base->GetCurrentTime(&current_time_));
  28. }
  29. const char* Name() const override { return "SpecialTimeEnv"; }
  30. void Sleep(int64_t sleep_time) { current_time_ += sleep_time; }
  31. Status GetCurrentTime(int64_t* current_time) override {
  32. *current_time = current_time_;
  33. return Status::OK();
  34. }
  35. private:
  36. int64_t current_time_ = 0;
  37. };
  38. class TtlTest : public testing::Test {
  39. public:
  40. TtlTest() {
  41. env_.reset(new SpecialTimeEnv(Env::Default()));
  42. dbname_ = test::PerThreadDBPath("db_ttl");
  43. options_.create_if_missing = true;
  44. options_.env = env_.get();
  45. // ensure that compaction is kicked in to always strip timestamp from kvs
  46. options_.max_compaction_bytes = 1;
  47. // compaction should take place always from level0 for determinism
  48. db_ttl_ = nullptr;
  49. EXPECT_OK(DestroyDB(dbname_, Options()));
  50. }
  51. ~TtlTest() override {
  52. CloseTtl();
  53. EXPECT_OK(DestroyDB(dbname_, Options()));
  54. }
  55. // Open database with TTL support when TTL not provided with db_ttl_ pointer
  56. void OpenTtl() {
  57. ASSERT_TRUE(db_ttl_ ==
  58. nullptr); // db should be closed before opening again
  59. ASSERT_OK(DBWithTTL::Open(options_, dbname_, &db_ttl_));
  60. }
  61. // Open database with TTL support when TTL provided with db_ttl_ pointer
  62. void OpenTtl(int32_t ttl) {
  63. ASSERT_TRUE(db_ttl_ == nullptr);
  64. ASSERT_OK(DBWithTTL::Open(options_, dbname_, &db_ttl_, ttl));
  65. }
  66. // Open with TestFilter compaction filter
  67. void OpenTtlWithTestCompaction(int32_t ttl) {
  68. options_.compaction_filter_factory =
  69. std::shared_ptr<CompactionFilterFactory>(
  70. new TestFilterFactory(kSampleSize_, kNewValue_));
  71. OpenTtl(ttl);
  72. }
  73. // Open database with TTL support in read_only mode
  74. void OpenReadOnlyTtl(int32_t ttl) {
  75. ASSERT_TRUE(db_ttl_ == nullptr);
  76. ASSERT_OK(DBWithTTL::Open(options_, dbname_, &db_ttl_, ttl, true));
  77. }
  78. // Call db_ttl_->Close() before delete db_ttl_
  79. void CloseTtl() { CloseTtlHelper(true); }
  80. // No db_ttl_->Close() before delete db_ttl_
  81. void CloseTtlNoDBClose() { CloseTtlHelper(false); }
  82. void CloseTtlHelper(bool close_db) {
  83. if (db_ttl_ != nullptr) {
  84. if (close_db) {
  85. EXPECT_OK(db_ttl_->Close());
  86. }
  87. delete db_ttl_;
  88. db_ttl_ = nullptr;
  89. }
  90. }
  91. // Populates and returns a kv-map
  92. void MakeKVMap(int64_t num_entries) {
  93. kvmap_.clear();
  94. int digits = 1;
  95. for (int64_t dummy = num_entries; dummy /= 10; ++digits) {
  96. }
  97. int digits_in_i = 1;
  98. for (int64_t i = 0; i < num_entries; i++) {
  99. std::string key = "key";
  100. std::string value = "value";
  101. if (i % 10 == 0) {
  102. digits_in_i++;
  103. }
  104. for (int j = digits_in_i; j < digits; j++) {
  105. key.append("0");
  106. value.append("0");
  107. }
  108. AppendNumberTo(&key, i);
  109. AppendNumberTo(&value, i);
  110. kvmap_[key] = value;
  111. }
  112. ASSERT_EQ(static_cast<int64_t>(kvmap_.size()),
  113. num_entries); // check all insertions done
  114. }
  115. // Makes a write-batch with key-vals from kvmap_ and 'Write''s it
  116. void MakePutWriteBatch(const BatchOperation* batch_ops, int64_t num_ops) {
  117. ASSERT_LE(num_ops, static_cast<int64_t>(kvmap_.size()));
  118. static WriteOptions wopts;
  119. static FlushOptions flush_opts;
  120. WriteBatch batch;
  121. kv_it_ = kvmap_.begin();
  122. for (int64_t i = 0; i < num_ops && kv_it_ != kvmap_.end(); i++, ++kv_it_) {
  123. switch (batch_ops[i]) {
  124. case OP_PUT:
  125. ASSERT_OK(batch.Put(kv_it_->first, kv_it_->second));
  126. break;
  127. case OP_DELETE:
  128. ASSERT_OK(batch.Delete(kv_it_->first));
  129. break;
  130. default:
  131. FAIL();
  132. }
  133. }
  134. ASSERT_OK(db_ttl_->Write(wopts, &batch));
  135. ASSERT_OK(db_ttl_->Flush(flush_opts));
  136. }
  137. // Puts num_entries starting from start_pos_map from kvmap_ into the database
  138. void PutValues(int64_t start_pos_map, int64_t num_entries, bool flush = true,
  139. ColumnFamilyHandle* cf = nullptr) {
  140. ASSERT_TRUE(db_ttl_);
  141. ASSERT_LE(start_pos_map + num_entries, static_cast<int64_t>(kvmap_.size()));
  142. static WriteOptions wopts;
  143. static FlushOptions flush_opts;
  144. kv_it_ = kvmap_.begin();
  145. advance(kv_it_, start_pos_map);
  146. for (int64_t i = 0; kv_it_ != kvmap_.end() && i < num_entries;
  147. i++, ++kv_it_) {
  148. ASSERT_OK(cf == nullptr
  149. ? db_ttl_->Put(wopts, kv_it_->first, kv_it_->second)
  150. : db_ttl_->Put(wopts, cf, kv_it_->first, kv_it_->second));
  151. }
  152. // Put a mock kv at the end because CompactionFilter doesn't delete last key
  153. ASSERT_OK(cf == nullptr ? db_ttl_->Put(wopts, "keymock", "valuemock")
  154. : db_ttl_->Put(wopts, cf, "keymock", "valuemock"));
  155. if (flush) {
  156. if (cf == nullptr) {
  157. ASSERT_OK(db_ttl_->Flush(flush_opts));
  158. } else {
  159. ASSERT_OK(db_ttl_->Flush(flush_opts, cf));
  160. }
  161. }
  162. }
  163. // Runs a manual compaction
  164. Status ManualCompact(ColumnFamilyHandle* cf = nullptr) {
  165. assert(db_ttl_);
  166. if (cf == nullptr) {
  167. return db_ttl_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
  168. } else {
  169. return db_ttl_->CompactRange(CompactRangeOptions(), cf, nullptr, nullptr);
  170. }
  171. }
  172. // Runs a DeleteRange
  173. void MakeDeleteRange(std::string start, std::string end,
  174. ColumnFamilyHandle* cf = nullptr) {
  175. ASSERT_TRUE(db_ttl_);
  176. static WriteOptions wops;
  177. WriteBatch wb;
  178. ASSERT_OK(cf == nullptr
  179. ? wb.DeleteRange(db_ttl_->DefaultColumnFamily(), start, end)
  180. : wb.DeleteRange(cf, start, end));
  181. ASSERT_OK(db_ttl_->Write(wops, &wb));
  182. }
  183. // checks the whole kvmap_ to return correct values using KeyMayExist
  184. void SimpleKeyMayExistCheck() {
  185. static ReadOptions ropts;
  186. bool value_found;
  187. std::string val;
  188. for (auto& kv : kvmap_) {
  189. bool ret = db_ttl_->KeyMayExist(ropts, kv.first, &val, &value_found);
  190. if (ret == false || value_found == false) {
  191. fprintf(stderr,
  192. "KeyMayExist could not find key=%s in the database but"
  193. " should have\n",
  194. kv.first.c_str());
  195. FAIL();
  196. } else if (val.compare(kv.second) != 0) {
  197. fprintf(stderr,
  198. " value for key=%s present in database is %s but"
  199. " should be %s\n",
  200. kv.first.c_str(), val.c_str(), kv.second.c_str());
  201. FAIL();
  202. }
  203. }
  204. }
  205. // checks the whole kvmap_ to return correct values using MultiGet
  206. void SimpleMultiGetTest() {
  207. static ReadOptions ropts;
  208. std::vector<Slice> keys;
  209. std::vector<std::string> values;
  210. for (auto& kv : kvmap_) {
  211. keys.emplace_back(kv.first);
  212. }
  213. auto statuses = db_ttl_->MultiGet(ropts, keys, &values);
  214. size_t i = 0;
  215. for (auto& kv : kvmap_) {
  216. ASSERT_OK(statuses[i]);
  217. ASSERT_EQ(values[i], kv.second);
  218. ++i;
  219. }
  220. }
  221. void CompactCheck(int64_t st_pos, int64_t span, bool check = true,
  222. bool test_compaction_change = false,
  223. ColumnFamilyHandle* cf = nullptr) {
  224. static ReadOptions ropts;
  225. kv_it_ = kvmap_.begin();
  226. advance(kv_it_, st_pos);
  227. std::string v;
  228. for (int64_t i = 0; kv_it_ != kvmap_.end() && i < span; i++, ++kv_it_) {
  229. Status s = (cf == nullptr) ? db_ttl_->Get(ropts, kv_it_->first, &v)
  230. : db_ttl_->Get(ropts, cf, kv_it_->first, &v);
  231. if (s.ok() != check) {
  232. fprintf(stderr, "key=%s ", kv_it_->first.c_str());
  233. if (!s.ok()) {
  234. fprintf(stderr, "is absent from db but was expected to be present\n");
  235. } else {
  236. fprintf(stderr, "is present in db but was expected to be absent\n");
  237. }
  238. FAIL();
  239. } else if (s.ok()) {
  240. if (test_compaction_change && v.compare(kNewValue_) != 0) {
  241. fprintf(stderr,
  242. " value for key=%s present in database is %s but "
  243. " should be %s\n",
  244. kv_it_->first.c_str(), v.c_str(), kNewValue_.c_str());
  245. FAIL();
  246. } else if (!test_compaction_change && v.compare(kv_it_->second) != 0) {
  247. fprintf(stderr,
  248. " value for key=%s present in database is %s but "
  249. " should be %s\n",
  250. kv_it_->first.c_str(), v.c_str(), kv_it_->second.c_str());
  251. FAIL();
  252. }
  253. }
  254. }
  255. }
  256. // Sleeps for slp_tim then runs a manual compaction
  257. // Checks span starting from st_pos from kvmap_ in the db and
  258. // Gets should return true if check is true and false otherwise
  259. // Also checks that value that we got is the same as inserted; and =kNewValue
  260. // if test_compaction_change is true
  261. void SleepCompactCheck(int slp_tim, int64_t st_pos, int64_t span,
  262. bool check = true, bool test_compaction_change = false,
  263. ColumnFamilyHandle* cf = nullptr) {
  264. ASSERT_TRUE(db_ttl_);
  265. env_->Sleep(slp_tim);
  266. ASSERT_OK(ManualCompact(cf));
  267. CompactCheck(st_pos, span, check, test_compaction_change, cf);
  268. }
  269. // Similar as SleepCompactCheck but uses TtlIterator to read from db
  270. void SleepCompactCheckIter(int slp, int st_pos, int64_t span,
  271. bool check = true) {
  272. ASSERT_TRUE(db_ttl_);
  273. env_->Sleep(slp);
  274. ASSERT_OK(ManualCompact());
  275. static ReadOptions ropts;
  276. Iterator* dbiter = db_ttl_->NewIterator(ropts);
  277. kv_it_ = kvmap_.begin();
  278. advance(kv_it_, st_pos);
  279. dbiter->Seek(kv_it_->first);
  280. if (!check) {
  281. if (dbiter->Valid()) {
  282. ASSERT_NE(dbiter->value().compare(kv_it_->second), 0);
  283. }
  284. } else { // dbiter should have found out kvmap_[st_pos]
  285. for (int64_t i = st_pos; kv_it_ != kvmap_.end() && i < st_pos + span;
  286. i++, ++kv_it_) {
  287. ASSERT_TRUE(dbiter->Valid());
  288. ASSERT_EQ(dbiter->value().compare(kv_it_->second), 0);
  289. dbiter->Next();
  290. }
  291. }
  292. ASSERT_OK(dbiter->status());
  293. delete dbiter;
  294. }
  295. // Set ttl on open db
  296. void SetTtl(int32_t ttl, ColumnFamilyHandle* cf = nullptr) {
  297. ASSERT_TRUE(db_ttl_);
  298. cf == nullptr ? db_ttl_->SetTtl(ttl) : db_ttl_->SetTtl(cf, ttl);
  299. }
  300. class TestFilter : public CompactionFilter {
  301. public:
  302. TestFilter(const int64_t kSampleSize, const std::string& kNewValue)
  303. : kSampleSize_(kSampleSize), kNewValue_(kNewValue) {}
  304. // Works on keys of the form "key<number>"
  305. // Drops key if number at the end of key is in [0, kSampleSize_/3),
  306. // Keeps key if it is in [kSampleSize_/3, 2*kSampleSize_/3),
  307. // Change value if it is in [2*kSampleSize_/3, kSampleSize_)
  308. // Eg. kSampleSize_=6. Drop:key0-1...Keep:key2-3...Change:key4-5...
  309. bool Filter(int /*level*/, const Slice& key, const Slice& /*value*/,
  310. std::string* new_value, bool* value_changed) const override {
  311. assert(new_value != nullptr);
  312. std::string search_str = "0123456789";
  313. std::string key_string = key.ToString();
  314. size_t pos = key_string.find_first_of(search_str);
  315. int num_key_end;
  316. if (pos != std::string::npos) {
  317. auto key_substr = key_string.substr(pos, key.size() - pos);
  318. #ifndef CYGWIN
  319. num_key_end = std::stoi(key_substr);
  320. #else
  321. num_key_end = std::strtol(key_substr.c_str(), 0, 10);
  322. #endif
  323. } else {
  324. return false; // Keep keys not matching the format "key<NUMBER>"
  325. }
  326. int64_t partition = kSampleSize_ / 3;
  327. if (num_key_end < partition) {
  328. return true;
  329. } else if (num_key_end < partition * 2) {
  330. return false;
  331. } else {
  332. *new_value = kNewValue_;
  333. *value_changed = true;
  334. return false;
  335. }
  336. }
  337. const char* Name() const override { return "TestFilter"; }
  338. private:
  339. const int64_t kSampleSize_;
  340. const std::string kNewValue_;
  341. };
  342. class TestFilterFactory : public CompactionFilterFactory {
  343. public:
  344. TestFilterFactory(const int64_t kSampleSize, const std::string& kNewValue)
  345. : kSampleSize_(kSampleSize), kNewValue_(kNewValue) {}
  346. std::unique_ptr<CompactionFilter> CreateCompactionFilter(
  347. const CompactionFilter::Context& /*context*/) override {
  348. return std::unique_ptr<CompactionFilter>(
  349. new TestFilter(kSampleSize_, kNewValue_));
  350. }
  351. const char* Name() const override { return "TestFilterFactory"; }
  352. private:
  353. const int64_t kSampleSize_;
  354. const std::string kNewValue_;
  355. };
  356. // Choose carefully so that Put, Gets & Compaction complete in 1 second buffer
  357. static const int64_t kSampleSize_ = 100;
  358. std::string dbname_;
  359. DBWithTTL* db_ttl_;
  360. std::unique_ptr<SpecialTimeEnv> env_;
  361. protected:
  362. Options options_;
  363. private:
  364. KVMap kvmap_;
  365. KVMap::iterator kv_it_;
  366. const std::string kNewValue_ = "new_value";
  367. std::unique_ptr<CompactionFilter> test_comp_filter_;
  368. }; // class TtlTest
  369. // If TTL is non positive or not provided, the behaviour is TTL = infinity
  370. // This test opens the db 3 times with such default behavior and inserts a
  371. // bunch of kvs each time. All kvs should accumulate in the db till the end
  372. // Partitions the sample-size provided into 3 sets over boundary1 and boundary2
  373. TEST_F(TtlTest, NoEffect) {
  374. MakeKVMap(kSampleSize_);
  375. int64_t boundary1 = kSampleSize_ / 3;
  376. int64_t boundary2 = 2 * boundary1;
  377. OpenTtl();
  378. PutValues(0, boundary1); // T=0: Set1 never deleted
  379. SleepCompactCheck(1, 0, boundary1); // T=1: Set1 still there
  380. CloseTtl();
  381. OpenTtl(0);
  382. PutValues(boundary1, boundary2 - boundary1); // T=1: Set2 never deleted
  383. SleepCompactCheck(1, 0, boundary2); // T=2: Sets1 & 2 still there
  384. CloseTtl();
  385. OpenTtl(-1);
  386. PutValues(boundary2, kSampleSize_ - boundary2); // T=3: Set3 never deleted
  387. SleepCompactCheck(1, 0, kSampleSize_, true); // T=4: Sets 1,2,3 still there
  388. CloseTtl();
  389. }
  390. // Rerun the NoEffect test with a different version of CloseTtl
  391. // function, where db is directly deleted without close.
  392. TEST_F(TtlTest, DestructWithoutClose) {
  393. MakeKVMap(kSampleSize_);
  394. int64_t boundary1 = kSampleSize_ / 3;
  395. int64_t boundary2 = 2 * boundary1;
  396. OpenTtl();
  397. PutValues(0, boundary1); // T=0: Set1 never deleted
  398. SleepCompactCheck(1, 0, boundary1); // T=1: Set1 still there
  399. CloseTtlNoDBClose();
  400. OpenTtl(0);
  401. PutValues(boundary1, boundary2 - boundary1); // T=1: Set2 never deleted
  402. SleepCompactCheck(1, 0, boundary2); // T=2: Sets1 & 2 still there
  403. CloseTtlNoDBClose();
  404. OpenTtl(-1);
  405. PutValues(boundary2, kSampleSize_ - boundary2); // T=3: Set3 never deleted
  406. SleepCompactCheck(1, 0, kSampleSize_, true); // T=4: Sets 1,2,3 still there
  407. CloseTtlNoDBClose();
  408. }
  409. // Puts a set of values and checks its presence using Get during ttl
  410. TEST_F(TtlTest, PresentDuringTTL) {
  411. MakeKVMap(kSampleSize_);
  412. OpenTtl(2); // T=0:Open the db with ttl = 2
  413. PutValues(0, kSampleSize_); // T=0:Insert Set1. Delete at t=2
  414. SleepCompactCheck(1, 0, kSampleSize_,
  415. true); // T=1:Set1 should still be there
  416. CloseTtl();
  417. }
  418. // Puts a set of values and checks its absence using Get after ttl
  419. TEST_F(TtlTest, AbsentAfterTTL) {
  420. MakeKVMap(kSampleSize_);
  421. OpenTtl(1); // T=0:Open the db with ttl = 2
  422. PutValues(0, kSampleSize_); // T=0:Insert Set1. Delete at t=2
  423. SleepCompactCheck(2, 0, kSampleSize_, false); // T=2:Set1 should not be there
  424. CloseTtl();
  425. }
  426. // Resets the timestamp of a set of kvs by updating them and checks that they
  427. // are not deleted according to the old timestamp
  428. TEST_F(TtlTest, ResetTimestamp) {
  429. MakeKVMap(kSampleSize_);
  430. OpenTtl(3);
  431. PutValues(0, kSampleSize_); // T=0: Insert Set1. Delete at t=3
  432. env_->Sleep(2); // T=2
  433. PutValues(0, kSampleSize_); // T=2: Insert Set1. Delete at t=5
  434. SleepCompactCheck(2, 0, kSampleSize_); // T=4: Set1 should still be there
  435. CloseTtl();
  436. }
  437. // Similar to PresentDuringTTL but uses Iterator
  438. TEST_F(TtlTest, IterPresentDuringTTL) {
  439. MakeKVMap(kSampleSize_);
  440. OpenTtl(2);
  441. PutValues(0, kSampleSize_); // T=0: Insert. Delete at t=2
  442. SleepCompactCheckIter(1, 0, kSampleSize_); // T=1: Set should be there
  443. CloseTtl();
  444. }
  445. // Similar to AbsentAfterTTL but uses Iterator
  446. TEST_F(TtlTest, IterAbsentAfterTTL) {
  447. MakeKVMap(kSampleSize_);
  448. OpenTtl(1);
  449. PutValues(0, kSampleSize_); // T=0: Insert. Delete at t=1
  450. SleepCompactCheckIter(2, 0, kSampleSize_, false); // T=2: Should not be there
  451. CloseTtl();
  452. }
  453. // Checks presence while opening the same db more than once with the same ttl
  454. // Note: The second open will open the same db
  455. TEST_F(TtlTest, MultiOpenSamePresent) {
  456. MakeKVMap(kSampleSize_);
  457. OpenTtl(2);
  458. PutValues(0, kSampleSize_); // T=0: Insert. Delete at t=2
  459. CloseTtl();
  460. OpenTtl(2); // T=0. Delete at t=2
  461. SleepCompactCheck(1, 0, kSampleSize_); // T=1: Set should be there
  462. CloseTtl();
  463. }
  464. // Checks absence while opening the same db more than once with the same ttl
  465. // Note: The second open will open the same db
  466. TEST_F(TtlTest, MultiOpenSameAbsent) {
  467. MakeKVMap(kSampleSize_);
  468. OpenTtl(1);
  469. PutValues(0, kSampleSize_); // T=0: Insert. Delete at t=1
  470. CloseTtl();
  471. OpenTtl(1); // T=0.Delete at t=1
  472. SleepCompactCheck(2, 0, kSampleSize_, false); // T=2: Set should not be there
  473. CloseTtl();
  474. }
  475. // Checks presence while opening the same db more than once with bigger ttl
  476. TEST_F(TtlTest, MultiOpenDifferent) {
  477. MakeKVMap(kSampleSize_);
  478. OpenTtl(1);
  479. PutValues(0, kSampleSize_); // T=0: Insert. Delete at t=1
  480. CloseTtl();
  481. OpenTtl(3); // T=0: Set deleted at t=3
  482. SleepCompactCheck(2, 0, kSampleSize_); // T=2: Set should be there
  483. CloseTtl();
  484. }
  485. // Checks presence during ttl in read_only mode
  486. TEST_F(TtlTest, ReadOnlyPresentForever) {
  487. MakeKVMap(kSampleSize_);
  488. OpenTtl(1); // T=0:Open the db normally
  489. PutValues(0, kSampleSize_); // T=0:Insert Set1. Delete at t=1
  490. CloseTtl();
  491. OpenReadOnlyTtl(1);
  492. ASSERT_TRUE(db_ttl_);
  493. env_->Sleep(2);
  494. Status s = ManualCompact(); // T=2:Set1 should still be there
  495. ASSERT_TRUE(s.IsNotSupported());
  496. CompactCheck(0, kSampleSize_);
  497. CloseTtl();
  498. }
  499. // Checks whether WriteBatch works well with TTL
  500. // Puts all kvs in kvmap_ in a batch and writes first, then deletes first half
  501. TEST_F(TtlTest, WriteBatchTest) {
  502. MakeKVMap(kSampleSize_);
  503. BatchOperation batch_ops[kSampleSize_];
  504. for (int i = 0; i < kSampleSize_; i++) {
  505. batch_ops[i] = OP_PUT;
  506. }
  507. OpenTtl(2);
  508. MakePutWriteBatch(batch_ops, kSampleSize_);
  509. for (int i = 0; i < kSampleSize_ / 2; i++) {
  510. batch_ops[i] = OP_DELETE;
  511. }
  512. MakePutWriteBatch(batch_ops, kSampleSize_ / 2);
  513. SleepCompactCheck(0, 0, kSampleSize_ / 2, false);
  514. SleepCompactCheck(0, kSampleSize_ / 2, kSampleSize_ - kSampleSize_ / 2);
  515. CloseTtl();
  516. }
  517. // Checks user's compaction filter for correctness with TTL logic
  518. TEST_F(TtlTest, CompactionFilter) {
  519. MakeKVMap(kSampleSize_);
  520. OpenTtlWithTestCompaction(1);
  521. PutValues(0, kSampleSize_); // T=0:Insert Set1. Delete at t=1
  522. // T=2: TTL logic takes precedence over TestFilter:-Set1 should not be there
  523. SleepCompactCheck(2, 0, kSampleSize_, false);
  524. CloseTtl();
  525. OpenTtlWithTestCompaction(3);
  526. PutValues(0, kSampleSize_); // T=0:Insert Set1.
  527. int64_t partition = kSampleSize_ / 3;
  528. SleepCompactCheck(1, 0, partition, false); // Part dropped
  529. SleepCompactCheck(0, partition, partition); // Part kept
  530. SleepCompactCheck(0, 2 * partition, partition, true, true); // Part changed
  531. CloseTtl();
  532. }
  533. TEST_F(TtlTest, UnregisteredMergeOperator) {
  534. class UnregisteredMergeOperator : public MergeOperator {
  535. public:
  536. const char* Name() const override { return "UnregisteredMergeOperator"; }
  537. };
  538. options_.merge_operator = std::make_shared<UnregisteredMergeOperator>();
  539. OpenTtl();
  540. CloseTtl();
  541. }
  542. // Insert some key-values which KeyMayExist should be able to get and check that
  543. // values returned are fine
  544. TEST_F(TtlTest, KeyMayExist) {
  545. MakeKVMap(kSampleSize_);
  546. OpenTtl();
  547. PutValues(0, kSampleSize_, false);
  548. SimpleKeyMayExistCheck();
  549. CloseTtl();
  550. }
  551. TEST_F(TtlTest, MultiGetTest) {
  552. MakeKVMap(kSampleSize_);
  553. OpenTtl();
  554. PutValues(0, kSampleSize_, false);
  555. SimpleMultiGetTest();
  556. CloseTtl();
  557. }
  558. TEST_F(TtlTest, TtlFiftenYears) {
  559. MakeKVMap(kSampleSize_);
  560. // 15 year will lead int32_t overflow from now
  561. const int kFifteenYearSeconds = 86400 * 365 * 15;
  562. OpenTtl(kFifteenYearSeconds);
  563. PutValues(0, kSampleSize_, true);
  564. // trigger the compaction
  565. SleepCompactCheck(1, 0, kSampleSize_);
  566. CloseTtl();
  567. }
  568. TEST_F(TtlTest, ColumnFamiliesTest) {
  569. DB* db;
  570. Options options;
  571. options.create_if_missing = true;
  572. options.env = env_.get();
  573. ASSERT_OK(DB::Open(options, dbname_, &db));
  574. ColumnFamilyHandle* handle;
  575. ASSERT_OK(db->CreateColumnFamily(ColumnFamilyOptions(options),
  576. "ttl_column_family", &handle));
  577. delete handle;
  578. delete db;
  579. std::vector<ColumnFamilyDescriptor> column_families;
  580. column_families.emplace_back(kDefaultColumnFamilyName,
  581. ColumnFamilyOptions(options));
  582. column_families.emplace_back("ttl_column_family",
  583. ColumnFamilyOptions(options));
  584. std::vector<ColumnFamilyHandle*> handles;
  585. ASSERT_OK(DBWithTTL::Open(DBOptions(options), dbname_, column_families,
  586. &handles, &db_ttl_, {3, 5}, false));
  587. ASSERT_EQ(handles.size(), 2U);
  588. ColumnFamilyHandle* new_handle;
  589. ASSERT_OK(db_ttl_->CreateColumnFamilyWithTtl(options, "ttl_column_family_2",
  590. &new_handle, 2));
  591. handles.push_back(new_handle);
  592. MakeKVMap(kSampleSize_);
  593. PutValues(0, kSampleSize_, false, handles[0]);
  594. PutValues(0, kSampleSize_, false, handles[1]);
  595. PutValues(0, kSampleSize_, false, handles[2]);
  596. // everything should be there after 1 second
  597. SleepCompactCheck(1, 0, kSampleSize_, true, false, handles[0]);
  598. SleepCompactCheck(0, 0, kSampleSize_, true, false, handles[1]);
  599. SleepCompactCheck(0, 0, kSampleSize_, true, false, handles[2]);
  600. // only column family 1 should be alive after 4 seconds
  601. SleepCompactCheck(3, 0, kSampleSize_, false, false, handles[0]);
  602. SleepCompactCheck(0, 0, kSampleSize_, true, false, handles[1]);
  603. SleepCompactCheck(0, 0, kSampleSize_, false, false, handles[2]);
  604. // nothing should be there after 6 seconds
  605. SleepCompactCheck(2, 0, kSampleSize_, false, false, handles[0]);
  606. SleepCompactCheck(0, 0, kSampleSize_, false, false, handles[1]);
  607. SleepCompactCheck(0, 0, kSampleSize_, false, false, handles[2]);
  608. for (auto h : handles) {
  609. delete h;
  610. }
  611. delete db_ttl_;
  612. db_ttl_ = nullptr;
  613. }
  614. // Puts a set of values and checks its absence using Get after ttl
  615. TEST_F(TtlTest, ChangeTtlOnOpenDb) {
  616. MakeKVMap(kSampleSize_);
  617. OpenTtl(1); // T=0:Open the db with ttl = 2
  618. SetTtl(3);
  619. int32_t ttl = 0;
  620. ASSERT_OK(db_ttl_->GetTtl(db_ttl_->DefaultColumnFamily(), &ttl));
  621. ASSERT_EQ(ttl, 3);
  622. PutValues(0, kSampleSize_); // T=0:Insert Set1. Delete at t=2
  623. SleepCompactCheck(2, 0, kSampleSize_, true); // T=2:Set1 should be there
  624. CloseTtl();
  625. }
  626. // Test DeleteRange for DBWithTtl
  627. TEST_F(TtlTest, DeleteRangeTest) {
  628. OpenTtl();
  629. ASSERT_OK(db_ttl_->Put(WriteOptions(), "a", "val"));
  630. MakeDeleteRange("a", "b");
  631. ASSERT_OK(db_ttl_->Put(WriteOptions(), "c", "val"));
  632. MakeDeleteRange("b", "d");
  633. ASSERT_OK(db_ttl_->Put(WriteOptions(), "e", "val"));
  634. MakeDeleteRange("d", "e");
  635. // first iteration verifies query correctness in memtable, second verifies
  636. // query correctness for a single SST file
  637. for (int i = 0; i < 2; i++) {
  638. if (i > 0) {
  639. ASSERT_OK(db_ttl_->Flush(FlushOptions()));
  640. }
  641. std::string value;
  642. ASSERT_TRUE(db_ttl_->Get(ReadOptions(), "a", &value).IsNotFound());
  643. ASSERT_TRUE(db_ttl_->Get(ReadOptions(), "c", &value).IsNotFound());
  644. ASSERT_OK(db_ttl_->Get(ReadOptions(), "e", &value));
  645. }
  646. CloseTtl();
  647. }
  648. class DummyFilter : public CompactionFilter {
  649. public:
  650. bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
  651. std::string* /*new_value*/,
  652. bool* /*value_changed*/) const override {
  653. return false;
  654. }
  655. const char* Name() const override { return kClassName(); }
  656. static const char* kClassName() { return "DummyFilter"; }
  657. };
  658. class DummyFilterFactory : public CompactionFilterFactory {
  659. public:
  660. const char* Name() const override { return kClassName(); }
  661. static const char* kClassName() { return "DummyFilterFactory"; }
  662. std::unique_ptr<CompactionFilter> CreateCompactionFilter(
  663. const CompactionFilter::Context&) override {
  664. std::unique_ptr<CompactionFilter> f(new DummyFilter());
  665. return f;
  666. }
  667. };
  668. static int RegisterTestObjects(ObjectLibrary& library,
  669. const std::string& /*arg*/) {
  670. library.AddFactory<CompactionFilter>(
  671. "DummyFilter", [](const std::string& /*uri*/,
  672. std::unique_ptr<CompactionFilter>* /*guard*/,
  673. std::string* /* errmsg */) {
  674. static DummyFilter dummy;
  675. return &dummy;
  676. });
  677. library.AddFactory<CompactionFilterFactory>(
  678. "DummyFilterFactory", [](const std::string& /*uri*/,
  679. std::unique_ptr<CompactionFilterFactory>* guard,
  680. std::string* /* errmsg */) {
  681. guard->reset(new DummyFilterFactory());
  682. return guard->get();
  683. });
  684. return 2;
  685. }
  686. class TtlOptionsTest : public testing::Test {
  687. public:
  688. TtlOptionsTest() {
  689. config_options_.registry->AddLibrary("RegisterTtlObjects",
  690. RegisterTtlObjects, "");
  691. config_options_.registry->AddLibrary("RegisterTtlTestObjects",
  692. RegisterTestObjects, "");
  693. }
  694. ConfigOptions config_options_;
  695. };
  696. TEST_F(TtlOptionsTest, LoadTtlCompactionFilter) {
  697. const CompactionFilter* filter = nullptr;
  698. ASSERT_OK(CompactionFilter::CreateFromString(
  699. config_options_, TtlCompactionFilter::kClassName(), &filter));
  700. ASSERT_NE(filter, nullptr);
  701. ASSERT_STREQ(filter->Name(), TtlCompactionFilter::kClassName());
  702. auto ttl = filter->GetOptions<int32_t>("TTL");
  703. ASSERT_NE(ttl, nullptr);
  704. ASSERT_EQ(*ttl, 0);
  705. ASSERT_OK(filter->ValidateOptions(DBOptions(), ColumnFamilyOptions()));
  706. delete filter;
  707. filter = nullptr;
  708. ASSERT_OK(CompactionFilter::CreateFromString(
  709. config_options_, "id=TtlCompactionFilter; ttl=123", &filter));
  710. ASSERT_NE(filter, nullptr);
  711. ttl = filter->GetOptions<int32_t>("TTL");
  712. ASSERT_NE(ttl, nullptr);
  713. ASSERT_EQ(*ttl, 123);
  714. ASSERT_OK(filter->ValidateOptions(DBOptions(), ColumnFamilyOptions()));
  715. delete filter;
  716. filter = nullptr;
  717. ASSERT_OK(CompactionFilter::CreateFromString(
  718. config_options_,
  719. "id=TtlCompactionFilter; ttl=456; user_filter=DummyFilter;", &filter));
  720. ASSERT_NE(filter, nullptr);
  721. auto inner = filter->CheckedCast<DummyFilter>();
  722. ASSERT_NE(inner, nullptr);
  723. ASSERT_OK(filter->ValidateOptions(DBOptions(), ColumnFamilyOptions()));
  724. std::string mismatch;
  725. std::string opts_str = filter->ToString(config_options_);
  726. const CompactionFilter* copy = nullptr;
  727. ASSERT_OK(
  728. CompactionFilter::CreateFromString(config_options_, opts_str, &copy));
  729. ASSERT_TRUE(filter->AreEquivalent(config_options_, copy, &mismatch));
  730. delete filter;
  731. delete copy;
  732. }
  733. TEST_F(TtlOptionsTest, LoadTtlCompactionFilterFactory) {
  734. std::shared_ptr<CompactionFilterFactory> cff;
  735. ASSERT_OK(CompactionFilterFactory::CreateFromString(
  736. config_options_, TtlCompactionFilterFactory::kClassName(), &cff));
  737. ASSERT_NE(cff.get(), nullptr);
  738. ASSERT_STREQ(cff->Name(), TtlCompactionFilterFactory::kClassName());
  739. auto ttl = cff->GetOptions<int32_t>("TTL");
  740. ASSERT_NE(ttl, nullptr);
  741. ASSERT_EQ(*ttl, 0);
  742. ASSERT_OK(cff->ValidateOptions(DBOptions(), ColumnFamilyOptions()));
  743. ASSERT_OK(CompactionFilterFactory::CreateFromString(
  744. config_options_, "id=TtlCompactionFilterFactory; ttl=123", &cff));
  745. ASSERT_NE(cff.get(), nullptr);
  746. ASSERT_STREQ(cff->Name(), TtlCompactionFilterFactory::kClassName());
  747. ttl = cff->GetOptions<int32_t>("TTL");
  748. ASSERT_NE(ttl, nullptr);
  749. ASSERT_EQ(*ttl, 123);
  750. ASSERT_OK(cff->ValidateOptions(DBOptions(), ColumnFamilyOptions()));
  751. ASSERT_OK(CompactionFilterFactory::CreateFromString(
  752. config_options_,
  753. "id=TtlCompactionFilterFactory; ttl=456; "
  754. "user_filter_factory=DummyFilterFactory;",
  755. &cff));
  756. ASSERT_NE(cff.get(), nullptr);
  757. auto filter = cff->CreateCompactionFilter(CompactionFilter::Context());
  758. ASSERT_NE(filter.get(), nullptr);
  759. auto ttlf = filter->CheckedCast<TtlCompactionFilter>();
  760. ASSERT_EQ(filter.get(), ttlf);
  761. auto user = filter->CheckedCast<DummyFilter>();
  762. ASSERT_NE(user, nullptr);
  763. ASSERT_OK(cff->ValidateOptions(DBOptions(), ColumnFamilyOptions()));
  764. std::string opts_str = cff->ToString(config_options_);
  765. std::string mismatch;
  766. std::shared_ptr<CompactionFilterFactory> copy;
  767. ASSERT_OK(CompactionFilterFactory::CreateFromString(config_options_, opts_str,
  768. &copy));
  769. ASSERT_TRUE(cff->AreEquivalent(config_options_, copy.get(), &mismatch));
  770. }
  771. TEST_F(TtlOptionsTest, LoadTtlMergeOperator) {
  772. std::shared_ptr<MergeOperator> mo;
  773. config_options_.invoke_prepare_options = false;
  774. ASSERT_OK(MergeOperator::CreateFromString(
  775. config_options_, TtlMergeOperator::kClassName(), &mo));
  776. ASSERT_NE(mo.get(), nullptr);
  777. ASSERT_STREQ(mo->Name(), TtlMergeOperator::kClassName());
  778. ASSERT_NOK(mo->ValidateOptions(DBOptions(), ColumnFamilyOptions()));
  779. config_options_.invoke_prepare_options = true;
  780. ASSERT_OK(MergeOperator::CreateFromString(
  781. config_options_, "id=TtlMergeOperator; user_operator=bytesxor", &mo));
  782. ASSERT_NE(mo.get(), nullptr);
  783. ASSERT_STREQ(mo->Name(), TtlMergeOperator::kClassName());
  784. ASSERT_OK(mo->ValidateOptions(DBOptions(), ColumnFamilyOptions()));
  785. auto ttl_mo = mo->CheckedCast<TtlMergeOperator>();
  786. ASSERT_EQ(mo.get(), ttl_mo);
  787. auto user = ttl_mo->CheckedCast<BytesXOROperator>();
  788. ASSERT_NE(user, nullptr);
  789. std::string mismatch;
  790. std::string opts_str = mo->ToString(config_options_);
  791. std::shared_ptr<MergeOperator> copy;
  792. ASSERT_OK(MergeOperator::CreateFromString(config_options_, opts_str, &copy));
  793. ASSERT_TRUE(mo->AreEquivalent(config_options_, copy.get(), &mismatch));
  794. // An unregistered user_operator will be null, which is not supported by the
  795. // `TtlMergeOperator` implementation.
  796. ASSERT_OK(MergeOperator::CreateFromString(
  797. config_options_, "id=TtlMergeOperator; user_operator=unknown", &mo));
  798. ASSERT_NE(mo.get(), nullptr);
  799. ASSERT_STREQ(mo->Name(), TtlMergeOperator::kClassName());
  800. ASSERT_NOK(mo->ValidateOptions(DBOptions(), ColumnFamilyOptions()));
  801. }
  802. } // namespace ROCKSDB_NAMESPACE
  803. // A black-box test for the ttl wrapper around rocksdb
  804. int main(int argc, char** argv) {
  805. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  806. ::testing::InitGoogleTest(&argc, argv);
  807. return RUN_ALL_TESTS();
  808. }