db_blob_index_test.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. // Copyright (c) 2011-present, 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 <functional>
  10. #include <string>
  11. #include <utility>
  12. #include <vector>
  13. #include "db/arena_wrapped_db_iter.h"
  14. #include "db/blob/blob_index.h"
  15. #include "db/column_family.h"
  16. #include "db/db_iter.h"
  17. #include "db/db_test_util.h"
  18. #include "db/dbformat.h"
  19. #include "db/write_batch_internal.h"
  20. #include "port/port.h"
  21. #include "port/stack_trace.h"
  22. #include "util/string_util.h"
  23. #include "utilities/merge_operators.h"
  24. namespace ROCKSDB_NAMESPACE {
  25. // kTypeBlobIndex is a value type used by BlobDB only. The base rocksdb
  26. // should accept the value type on write, and report not supported value
  27. // for reads, unless caller request for it explicitly. The base rocksdb
  28. // doesn't understand format of actual blob index (the value).
  29. class DBBlobIndexTest : public DBTestBase {
  30. public:
  31. enum Tier {
  32. kMemtable = 0,
  33. kImmutableMemtables = 1,
  34. kL0SstFile = 2,
  35. kLnSstFile = 3,
  36. };
  37. const std::vector<Tier> kAllTiers = {Tier::kMemtable,
  38. Tier::kImmutableMemtables,
  39. Tier::kL0SstFile, Tier::kLnSstFile};
  40. DBBlobIndexTest() : DBTestBase("db_blob_index_test", /*env_do_fsync=*/true) {}
  41. ColumnFamilyHandle* cfh() { return dbfull()->DefaultColumnFamily(); }
  42. ColumnFamilyHandleImpl* cfh_impl() {
  43. return static_cast_with_check<ColumnFamilyHandleImpl>(cfh());
  44. }
  45. ColumnFamilyData* cfd() { return cfh_impl()->cfd(); }
  46. Status PutBlobIndex(WriteBatch* batch, const Slice& key,
  47. const Slice& blob_index) {
  48. return WriteBatchInternal::PutBlobIndex(batch, cfd()->GetID(), key,
  49. blob_index);
  50. }
  51. Status Write(WriteBatch* batch) {
  52. return dbfull()->Write(WriteOptions(), batch);
  53. }
  54. std::string GetImpl(const Slice& key, bool* is_blob_index = nullptr,
  55. const Snapshot* snapshot = nullptr) {
  56. ReadOptions read_options;
  57. read_options.snapshot = snapshot;
  58. PinnableSlice value;
  59. DBImpl::GetImplOptions get_impl_options;
  60. get_impl_options.column_family = cfh();
  61. get_impl_options.value = &value;
  62. get_impl_options.is_blob_index = is_blob_index;
  63. auto s = dbfull()->GetImpl(read_options, key, get_impl_options);
  64. if (s.IsNotFound()) {
  65. return "NOT_FOUND";
  66. }
  67. if (s.IsCorruption()) {
  68. return "CORRUPTION";
  69. }
  70. if (s.IsNotSupported()) {
  71. return "NOT_SUPPORTED";
  72. }
  73. if (!s.ok()) {
  74. return s.ToString();
  75. }
  76. return value.ToString();
  77. }
  78. std::string GetBlobIndex(const Slice& key,
  79. const Snapshot* snapshot = nullptr) {
  80. bool is_blob_index = false;
  81. std::string value = GetImpl(key, &is_blob_index, snapshot);
  82. if (!is_blob_index) {
  83. return "NOT_BLOB";
  84. }
  85. return value;
  86. }
  87. ArenaWrappedDBIter* GetBlobIterator() {
  88. DBImpl* db_impl = dbfull();
  89. return db_impl->NewIteratorImpl(
  90. ReadOptions(), cfh_impl(), cfd()->GetReferencedSuperVersion(db_impl),
  91. db_impl->GetLatestSequenceNumber(), nullptr /*read_callback*/,
  92. true /*expose_blob_index*/);
  93. }
  94. Options GetTestOptions() {
  95. Options options;
  96. options.env = CurrentOptions().env;
  97. options.create_if_missing = true;
  98. options.num_levels = 2;
  99. options.disable_auto_compactions = true;
  100. // Disable auto flushes.
  101. options.max_write_buffer_number = 10;
  102. options.min_write_buffer_number_to_merge = 10;
  103. options.merge_operator = MergeOperators::CreateStringAppendOperator();
  104. return options;
  105. }
  106. void MoveDataTo(Tier tier) {
  107. switch (tier) {
  108. case Tier::kMemtable:
  109. break;
  110. case Tier::kImmutableMemtables:
  111. ASSERT_OK(dbfull()->TEST_SwitchMemtable());
  112. break;
  113. case Tier::kL0SstFile:
  114. ASSERT_OK(Flush());
  115. break;
  116. case Tier::kLnSstFile:
  117. ASSERT_OK(Flush());
  118. ASSERT_OK(Put("a", "dummy"));
  119. ASSERT_OK(Put("z", "dummy"));
  120. ASSERT_OK(Flush());
  121. ASSERT_OK(
  122. dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
  123. ASSERT_EQ("0,1", FilesPerLevel());
  124. break;
  125. }
  126. }
  127. };
  128. // Note: the following test case pertains to the StackableDB-based BlobDB
  129. // implementation. We should be able to write kTypeBlobIndex to memtables and
  130. // SST files.
  131. TEST_F(DBBlobIndexTest, Write) {
  132. for (auto tier : kAllTiers) {
  133. DestroyAndReopen(GetTestOptions());
  134. std::vector<std::pair<std::string, std::string>> key_values;
  135. constexpr size_t num_key_values = 5;
  136. key_values.reserve(num_key_values);
  137. for (size_t i = 1; i <= num_key_values; ++i) {
  138. std::string key = "key" + std::to_string(i);
  139. std::string blob_index;
  140. BlobIndex::EncodeInlinedTTL(&blob_index, /* expiration */ 9876543210,
  141. "blob" + std::to_string(i));
  142. key_values.emplace_back(std::move(key), std::move(blob_index));
  143. }
  144. for (const auto& key_value : key_values) {
  145. WriteBatch batch;
  146. ASSERT_OK(PutBlobIndex(&batch, key_value.first, key_value.second));
  147. ASSERT_OK(Write(&batch));
  148. }
  149. MoveDataTo(tier);
  150. for (const auto& key_value : key_values) {
  151. ASSERT_EQ(GetBlobIndex(key_value.first), key_value.second);
  152. }
  153. }
  154. }
  155. // Note: the following test case pertains to the StackableDB-based BlobDB
  156. // implementation. Get should be able to return blob index if is_blob_index is
  157. // provided, otherwise it should return Status::NotSupported (when reading from
  158. // memtable) or Status::Corruption (when reading from SST). Reading from SST
  159. // returns Corruption because we can't differentiate between the application
  160. // accidentally opening the base DB of a stacked BlobDB and actual corruption
  161. // when using the integrated BlobDB.
  162. TEST_F(DBBlobIndexTest, Get) {
  163. std::string blob_index;
  164. BlobIndex::EncodeInlinedTTL(&blob_index, /* expiration */ 9876543210, "blob");
  165. for (auto tier : kAllTiers) {
  166. DestroyAndReopen(GetTestOptions());
  167. WriteBatch batch;
  168. ASSERT_OK(batch.Put("key", "value"));
  169. ASSERT_OK(PutBlobIndex(&batch, "blob_key", blob_index));
  170. ASSERT_OK(Write(&batch));
  171. MoveDataTo(tier);
  172. // Verify normal value
  173. bool is_blob_index = false;
  174. PinnableSlice value;
  175. ASSERT_EQ("value", Get("key"));
  176. ASSERT_EQ("value", GetImpl("key"));
  177. ASSERT_EQ("value", GetImpl("key", &is_blob_index));
  178. ASSERT_FALSE(is_blob_index);
  179. // Verify blob index
  180. if (tier <= kImmutableMemtables) {
  181. ASSERT_TRUE(Get("blob_key", &value).IsNotSupported());
  182. ASSERT_EQ("NOT_SUPPORTED", GetImpl("blob_key"));
  183. } else {
  184. ASSERT_TRUE(Get("blob_key", &value).IsCorruption());
  185. ASSERT_EQ("CORRUPTION", GetImpl("blob_key"));
  186. }
  187. ASSERT_EQ(blob_index, GetImpl("blob_key", &is_blob_index));
  188. ASSERT_TRUE(is_blob_index);
  189. }
  190. }
  191. // Note: the following test case pertains to the StackableDB-based BlobDB
  192. // implementation. Get should NOT return Status::NotSupported/Status::Corruption
  193. // if blob index is updated with a normal value. See the test case above for
  194. // more details.
  195. TEST_F(DBBlobIndexTest, Updated) {
  196. std::string blob_index;
  197. BlobIndex::EncodeInlinedTTL(&blob_index, /* expiration */ 9876543210, "blob");
  198. for (auto tier : kAllTiers) {
  199. DestroyAndReopen(GetTestOptions());
  200. WriteBatch batch;
  201. for (int i = 0; i < 10; i++) {
  202. ASSERT_OK(PutBlobIndex(&batch, "key" + std::to_string(i), blob_index));
  203. }
  204. ASSERT_OK(Write(&batch));
  205. // Avoid blob values from being purged.
  206. const Snapshot* snapshot = dbfull()->GetSnapshot();
  207. ASSERT_OK(Put("key1", "new_value"));
  208. ASSERT_OK(Merge("key2", "a"));
  209. ASSERT_OK(Merge("key2", "b"));
  210. ASSERT_OK(Merge("key2", "c"));
  211. ASSERT_OK(Delete("key3"));
  212. ASSERT_OK(SingleDelete("key4"));
  213. ASSERT_OK(Delete("key5"));
  214. ASSERT_OK(Merge("key5", "a"));
  215. ASSERT_OK(Merge("key5", "b"));
  216. ASSERT_OK(Merge("key5", "c"));
  217. ASSERT_OK(dbfull()->DeleteRange(WriteOptions(), cfh(), "key6", "key9"));
  218. MoveDataTo(tier);
  219. for (int i = 0; i < 10; i++) {
  220. ASSERT_EQ(blob_index, GetBlobIndex("key" + std::to_string(i), snapshot));
  221. }
  222. ASSERT_EQ("new_value", Get("key1"));
  223. if (tier <= kImmutableMemtables) {
  224. ASSERT_EQ("NOT_SUPPORTED", GetImpl("key2"));
  225. } else {
  226. ASSERT_EQ("CORRUPTION", GetImpl("key2"));
  227. }
  228. ASSERT_EQ("NOT_FOUND", Get("key3"));
  229. ASSERT_EQ("NOT_FOUND", Get("key4"));
  230. ASSERT_EQ("a,b,c", GetImpl("key5"));
  231. for (int i = 6; i < 9; i++) {
  232. ASSERT_EQ("NOT_FOUND", Get("key" + std::to_string(i)));
  233. }
  234. ASSERT_EQ(blob_index, GetBlobIndex("key9"));
  235. dbfull()->ReleaseSnapshot(snapshot);
  236. }
  237. }
  238. // Note: the following test case pertains to the StackableDB-based BlobDB
  239. // implementation. When a blob iterator is used, it should set the
  240. // expose_blob_index flag for the underlying DBIter, and retrieve/return the
  241. // corresponding blob value. If a regular DBIter is created (i.e.
  242. // expose_blob_index is not set), it should return Status::Corruption.
  243. TEST_F(DBBlobIndexTest, Iterate) {
  244. const std::vector<std::vector<ValueType>> data = {
  245. /*00*/ {kTypeValue},
  246. /*01*/ {kTypeBlobIndex},
  247. /*02*/ {kTypeValue},
  248. /*03*/ {kTypeBlobIndex, kTypeValue},
  249. /*04*/ {kTypeValue},
  250. /*05*/ {kTypeValue, kTypeBlobIndex},
  251. /*06*/ {kTypeValue},
  252. /*07*/ {kTypeDeletion, kTypeBlobIndex},
  253. /*08*/ {kTypeValue},
  254. /*09*/ {kTypeSingleDeletion, kTypeBlobIndex},
  255. /*10*/ {kTypeValue},
  256. /*11*/ {kTypeMerge, kTypeMerge, kTypeMerge, kTypeBlobIndex},
  257. /*12*/ {kTypeValue},
  258. /*13*/
  259. {kTypeMerge, kTypeMerge, kTypeMerge, kTypeDeletion, kTypeBlobIndex},
  260. /*14*/ {kTypeValue},
  261. /*15*/ {kTypeBlobIndex},
  262. /*16*/ {kTypeValue},
  263. };
  264. auto get_key = [](int index) {
  265. char buf[20];
  266. snprintf(buf, sizeof(buf), "%02d", index);
  267. return "key" + std::string(buf);
  268. };
  269. auto get_value = [&](int index, int version) {
  270. return get_key(index) + "_value" + std::to_string(version);
  271. };
  272. auto check_iterator = [&](Iterator* iterator, Status::Code expected_status,
  273. const Slice& expected_value) {
  274. ASSERT_EQ(expected_status, iterator->status().code());
  275. if (expected_status == Status::kOk) {
  276. ASSERT_TRUE(iterator->Valid());
  277. ASSERT_EQ(expected_value, iterator->value());
  278. } else {
  279. ASSERT_FALSE(iterator->Valid());
  280. }
  281. };
  282. auto create_normal_iterator = [&]() -> Iterator* {
  283. return dbfull()->NewIterator(ReadOptions());
  284. };
  285. auto create_blob_iterator = [&]() -> Iterator* { return GetBlobIterator(); };
  286. auto check_is_blob = [&](bool is_blob) {
  287. return [is_blob](Iterator* iterator) {
  288. ASSERT_EQ(is_blob, static_cast<ArenaWrappedDBIter*>(iterator)->IsBlob());
  289. };
  290. };
  291. auto verify = [&](int index, Status::Code expected_status,
  292. const Slice& forward_value, const Slice& backward_value,
  293. std::function<Iterator*()> create_iterator,
  294. std::function<void(Iterator*)> extra_check = nullptr) {
  295. // Seek
  296. auto* iterator = create_iterator();
  297. ASSERT_OK(iterator->status());
  298. ASSERT_OK(iterator->Refresh());
  299. iterator->Seek(get_key(index));
  300. check_iterator(iterator, expected_status, forward_value);
  301. if (extra_check) {
  302. extra_check(iterator);
  303. }
  304. delete iterator;
  305. // Next
  306. iterator = create_iterator();
  307. ASSERT_OK(iterator->Refresh());
  308. iterator->Seek(get_key(index - 1));
  309. ASSERT_TRUE(iterator->Valid());
  310. ASSERT_OK(iterator->status());
  311. iterator->Next();
  312. check_iterator(iterator, expected_status, forward_value);
  313. if (extra_check) {
  314. extra_check(iterator);
  315. }
  316. delete iterator;
  317. // SeekForPrev
  318. iterator = create_iterator();
  319. ASSERT_OK(iterator->status());
  320. ASSERT_OK(iterator->Refresh());
  321. iterator->SeekForPrev(get_key(index));
  322. check_iterator(iterator, expected_status, backward_value);
  323. if (extra_check) {
  324. extra_check(iterator);
  325. }
  326. delete iterator;
  327. // Prev
  328. iterator = create_iterator();
  329. iterator->Seek(get_key(index + 1));
  330. ASSERT_TRUE(iterator->Valid());
  331. ASSERT_OK(iterator->status());
  332. iterator->Prev();
  333. check_iterator(iterator, expected_status, backward_value);
  334. if (extra_check) {
  335. extra_check(iterator);
  336. }
  337. delete iterator;
  338. };
  339. for (auto tier : {Tier::kMemtable} /*kAllTiers*/) {
  340. // Avoid values from being purged.
  341. std::vector<const Snapshot*> snapshots;
  342. DestroyAndReopen(GetTestOptions());
  343. // fill data
  344. for (int i = 0; i < static_cast<int>(data.size()); i++) {
  345. for (int j = static_cast<int>(data[i].size()) - 1; j >= 0; j--) {
  346. std::string key = get_key(i);
  347. std::string value = get_value(i, j);
  348. WriteBatch batch;
  349. switch (data[i][j]) {
  350. case kTypeValue:
  351. ASSERT_OK(Put(key, value));
  352. break;
  353. case kTypeDeletion:
  354. ASSERT_OK(Delete(key));
  355. break;
  356. case kTypeSingleDeletion:
  357. ASSERT_OK(SingleDelete(key));
  358. break;
  359. case kTypeMerge:
  360. ASSERT_OK(Merge(key, value));
  361. break;
  362. case kTypeBlobIndex:
  363. ASSERT_OK(PutBlobIndex(&batch, key, value));
  364. ASSERT_OK(Write(&batch));
  365. break;
  366. default:
  367. FAIL();
  368. };
  369. }
  370. snapshots.push_back(dbfull()->GetSnapshot());
  371. }
  372. ASSERT_OK(
  373. dbfull()->DeleteRange(WriteOptions(), cfh(), get_key(15), get_key(16)));
  374. snapshots.push_back(dbfull()->GetSnapshot());
  375. MoveDataTo(tier);
  376. // Normal iterator
  377. verify(1, Status::kCorruption, "", "", create_normal_iterator);
  378. verify(3, Status::kCorruption, "", "", create_normal_iterator);
  379. verify(5, Status::kOk, get_value(5, 0), get_value(5, 0),
  380. create_normal_iterator);
  381. verify(7, Status::kOk, get_value(8, 0), get_value(6, 0),
  382. create_normal_iterator);
  383. verify(9, Status::kOk, get_value(10, 0), get_value(8, 0),
  384. create_normal_iterator);
  385. verify(11, Status::kCorruption, "", "", create_normal_iterator);
  386. verify(13, Status::kOk,
  387. get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
  388. get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
  389. create_normal_iterator);
  390. verify(15, Status::kOk, get_value(16, 0), get_value(14, 0),
  391. create_normal_iterator);
  392. // Iterator with blob support
  393. verify(1, Status::kOk, get_value(1, 0), get_value(1, 0),
  394. create_blob_iterator, check_is_blob(true));
  395. verify(3, Status::kOk, get_value(3, 0), get_value(3, 0),
  396. create_blob_iterator, check_is_blob(true));
  397. verify(5, Status::kOk, get_value(5, 0), get_value(5, 0),
  398. create_blob_iterator, check_is_blob(false));
  399. verify(7, Status::kOk, get_value(8, 0), get_value(6, 0),
  400. create_blob_iterator, check_is_blob(false));
  401. verify(9, Status::kOk, get_value(10, 0), get_value(8, 0),
  402. create_blob_iterator, check_is_blob(false));
  403. if (tier <= kImmutableMemtables) {
  404. verify(11, Status::kNotSupported, "", "", create_blob_iterator);
  405. } else {
  406. verify(11, Status::kCorruption, "", "", create_blob_iterator);
  407. }
  408. verify(13, Status::kOk,
  409. get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
  410. get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
  411. create_blob_iterator, check_is_blob(false));
  412. verify(15, Status::kOk, get_value(16, 0), get_value(14, 0),
  413. create_blob_iterator, check_is_blob(false));
  414. // Iterator with blob support and using seek.
  415. ASSERT_OK(dbfull()->SetOptions(
  416. cfh(), {{"max_sequential_skip_in_iterations", "0"}}));
  417. verify(1, Status::kOk, get_value(1, 0), get_value(1, 0),
  418. create_blob_iterator, check_is_blob(true));
  419. verify(3, Status::kOk, get_value(3, 0), get_value(3, 0),
  420. create_blob_iterator, check_is_blob(true));
  421. verify(5, Status::kOk, get_value(5, 0), get_value(5, 0),
  422. create_blob_iterator, check_is_blob(false));
  423. verify(7, Status::kOk, get_value(8, 0), get_value(6, 0),
  424. create_blob_iterator, check_is_blob(false));
  425. verify(9, Status::kOk, get_value(10, 0), get_value(8, 0),
  426. create_blob_iterator, check_is_blob(false));
  427. if (tier <= kImmutableMemtables) {
  428. verify(11, Status::kNotSupported, "", "", create_blob_iterator);
  429. } else {
  430. verify(11, Status::kCorruption, "", "", create_blob_iterator);
  431. }
  432. verify(13, Status::kOk,
  433. get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
  434. get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
  435. create_blob_iterator, check_is_blob(false));
  436. verify(15, Status::kOk, get_value(16, 0), get_value(14, 0),
  437. create_blob_iterator, check_is_blob(false));
  438. for (auto* snapshot : snapshots) {
  439. dbfull()->ReleaseSnapshot(snapshot);
  440. }
  441. }
  442. }
  443. TEST_F(DBBlobIndexTest, IntegratedBlobIterate) {
  444. const std::vector<std::vector<std::string>> data = {
  445. /*00*/ {"Put"},
  446. /*01*/ {"Put", "Merge", "Merge", "Merge"},
  447. /*02*/ {"Put"}};
  448. auto get_key = [](size_t index) { return ("key" + std::to_string(index)); };
  449. auto get_value = [&](size_t index, size_t version) {
  450. return get_key(index) + "_value" + std::to_string(version);
  451. };
  452. auto check_iterator = [&](Iterator* iterator, Status expected_status,
  453. const Slice& expected_value) {
  454. ASSERT_EQ(expected_status, iterator->status());
  455. if (expected_status.ok()) {
  456. ASSERT_TRUE(iterator->Valid());
  457. ASSERT_EQ(expected_value, iterator->value());
  458. } else {
  459. ASSERT_FALSE(iterator->Valid());
  460. }
  461. };
  462. auto verify = [&](size_t index, Status expected_status,
  463. const Slice& expected_value) {
  464. // Seek
  465. {
  466. Iterator* iterator = db_->NewIterator(ReadOptions());
  467. std::unique_ptr<Iterator> iterator_guard(iterator);
  468. ASSERT_OK(iterator->status());
  469. ASSERT_OK(iterator->Refresh());
  470. iterator->Seek(get_key(index));
  471. check_iterator(iterator, expected_status, expected_value);
  472. }
  473. // Next
  474. {
  475. Iterator* iterator = db_->NewIterator(ReadOptions());
  476. std::unique_ptr<Iterator> iterator_guard(iterator);
  477. ASSERT_OK(iterator->Refresh());
  478. iterator->Seek(get_key(index - 1));
  479. ASSERT_TRUE(iterator->Valid());
  480. ASSERT_OK(iterator->status());
  481. iterator->Next();
  482. check_iterator(iterator, expected_status, expected_value);
  483. }
  484. // SeekForPrev
  485. {
  486. Iterator* iterator = db_->NewIterator(ReadOptions());
  487. std::unique_ptr<Iterator> iterator_guard(iterator);
  488. ASSERT_OK(iterator->status());
  489. ASSERT_OK(iterator->Refresh());
  490. iterator->SeekForPrev(get_key(index));
  491. check_iterator(iterator, expected_status, expected_value);
  492. }
  493. // Prev
  494. {
  495. Iterator* iterator = db_->NewIterator(ReadOptions());
  496. std::unique_ptr<Iterator> iterator_guard(iterator);
  497. iterator->Seek(get_key(index + 1));
  498. ASSERT_TRUE(iterator->Valid());
  499. ASSERT_OK(iterator->status());
  500. iterator->Prev();
  501. check_iterator(iterator, expected_status, expected_value);
  502. }
  503. };
  504. Options options = GetTestOptions();
  505. options.enable_blob_files = true;
  506. options.min_blob_size = 0;
  507. DestroyAndReopen(options);
  508. // fill data
  509. for (size_t i = 0; i < data.size(); i++) {
  510. for (size_t j = 0; j < data[i].size(); j++) {
  511. std::string key = get_key(i);
  512. std::string value = get_value(i, j);
  513. if (data[i][j] == "Put") {
  514. ASSERT_OK(Put(key, value));
  515. ASSERT_OK(Flush());
  516. } else if (data[i][j] == "Merge") {
  517. ASSERT_OK(Merge(key, value));
  518. ASSERT_OK(Flush());
  519. }
  520. }
  521. }
  522. std::string expected_value = get_value(1, 0) + "," + get_value(1, 1) + "," +
  523. get_value(1, 2) + "," + get_value(1, 3);
  524. Status expected_status;
  525. verify(1, expected_status, expected_value);
  526. // Test DBIter::FindValueForCurrentKeyUsingSeek flow.
  527. ASSERT_OK(dbfull()->SetOptions(cfh(),
  528. {{"max_sequential_skip_in_iterations", "0"}}));
  529. verify(1, expected_status, expected_value);
  530. }
  531. } // namespace ROCKSDB_NAMESPACE
  532. int main(int argc, char** argv) {
  533. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  534. ::testing::InitGoogleTest(&argc, argv);
  535. RegisterCustomObjects(argc, argv);
  536. return RUN_ALL_TESTS();
  537. }