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