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).
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.
15 #include "db/db_impl/db_impl.h"
16 #include "db/version_set.h"
17 #include "db/write_batch_internal.h"
18 #include "file/filename.h"
19 #include "rocksdb/cache.h"
20 #include "rocksdb/compaction_filter.h"
21 #include "rocksdb/db.h"
22 #include "rocksdb/env.h"
23 #include "rocksdb/filter_policy.h"
24 #include "rocksdb/slice_transform.h"
25 #include "rocksdb/table.h"
26 #include "table/meta_blocks.h"
27 #include "table/plain/plain_table_bloom.h"
28 #include "table/plain/plain_table_factory.h"
29 #include "table/plain/plain_table_key_coding.h"
30 #include "table/plain/plain_table_reader.h"
31 #include "table/table_builder.h"
32 #include "test_util/testharness.h"
33 #include "test_util/testutil.h"
34 #include "util/cast_util.h"
35 #include "util/hash.h"
36 #include "util/mutexlock.h"
37 #include "util/random.h"
38 #include "util/string_util.h"
39 #include "utilities/merge_operators.h"
41 namespace ROCKSDB_NAMESPACE
{
42 class PlainTableKeyDecoderTest
: public testing::Test
{};
44 TEST_F(PlainTableKeyDecoderTest
, ReadNonMmap
) {
46 const uint32_t kLength
= 2222;
47 std::string tmp
= rnd
.RandomString(kLength
);
49 test::StringSource
* string_source
=
50 new test::StringSource(contents
, 0, false);
51 std::unique_ptr
<FSRandomAccessFile
> holder(string_source
);
52 std::unique_ptr
<RandomAccessFileReader
> file_reader(
53 new RandomAccessFileReader(std::move(holder
), "test"));
54 std::unique_ptr
<PlainTableReaderFileInfo
> file_info(
55 new PlainTableReaderFileInfo(std::move(file_reader
), EnvOptions(),
59 PlainTableFileReader
reader(file_info
.get());
61 const uint32_t kReadSize
= 77;
62 for (uint32_t pos
= 0; pos
< kLength
; pos
+= kReadSize
) {
63 uint32_t read_size
= std::min(kLength
- pos
, kReadSize
);
65 ASSERT_TRUE(reader
.Read(pos
, read_size
, &out
));
66 ASSERT_EQ(0, out
.compare(tmp
.substr(pos
, read_size
)));
69 ASSERT_LT(uint32_t(string_source
->total_reads()), kLength
/ kReadSize
/ 2);
72 std::vector
<std::vector
<std::pair
<uint32_t, uint32_t>>> reads
= {
73 {{600, 30}, {590, 30}, {600, 20}, {600, 40}},
74 {{800, 20}, {100, 20}, {500, 20}, {1500, 20}, {100, 20}, {80, 20}},
75 {{1000, 20}, {500, 20}, {1000, 50}},
76 {{1000, 20}, {500, 20}, {500, 20}},
77 {{1000, 20}, {500, 20}, {200, 20}, {500, 20}},
78 {{1000, 20}, {500, 20}, {200, 20}, {1000, 50}},
79 {{600, 500}, {610, 20}, {100, 20}},
80 {{500, 100}, {490, 100}, {550, 50}},
83 std::vector
<int> num_file_reads
= {2, 6, 2, 2, 4, 3, 2, 2};
85 for (size_t i
= 0; i
< reads
.size(); i
++) {
86 string_source
->set_total_reads(0);
87 PlainTableFileReader
reader(file_info
.get());
88 for (auto p
: reads
[i
]) {
90 ASSERT_TRUE(reader
.Read(p
.first
, p
.second
, &out
));
91 ASSERT_EQ(0, out
.compare(tmp
.substr(p
.first
, p
.second
)));
93 ASSERT_EQ(num_file_reads
[i
], string_source
->total_reads());
97 class PlainTableDBTest
: public testing::Test
,
98 public testing::WithParamInterface
<bool> {
106 Options last_options_
;
109 PlainTableDBTest() : env_(Env::Default()) {}
111 ~PlainTableDBTest() override
{
113 EXPECT_OK(DestroyDB(dbname_
, Options()));
116 void SetUp() override
{
117 mmap_mode_
= GetParam();
118 dbname_
= test::PerThreadDBPath("plain_table_db_test");
119 EXPECT_OK(DestroyDB(dbname_
, Options()));
124 // Return the current option configuration.
125 Options
CurrentOptions() {
128 PlainTableOptions plain_table_options
;
129 plain_table_options
.user_key_len
= 0;
130 plain_table_options
.bloom_bits_per_key
= 2;
131 plain_table_options
.hash_table_ratio
= 0.8;
132 plain_table_options
.index_sparseness
= 3;
133 plain_table_options
.huge_page_tlb_size
= 0;
134 plain_table_options
.encoding_type
= kPrefix
;
135 plain_table_options
.full_scan_mode
= false;
136 plain_table_options
.store_index_in_file
= false;
138 options
.table_factory
.reset(NewPlainTableFactory(plain_table_options
));
139 options
.memtable_factory
.reset(NewHashLinkListRepFactory(4, 0, 3, true));
141 options
.prefix_extractor
.reset(NewFixedPrefixTransform(8));
142 options
.allow_mmap_reads
= mmap_mode_
;
143 options
.allow_concurrent_memtable_write
= false;
144 options
.unordered_write
= false;
148 DBImpl
* dbfull() { return static_cast_with_check
<DBImpl
>(db_
); }
150 void Reopen(Options
* options
= nullptr) { ASSERT_OK(TryReopen(options
)); }
157 bool mmap_mode() const { return mmap_mode_
; }
159 void DestroyAndReopen(Options
* options
= nullptr) {
160 // Destroy using last options
161 Destroy(&last_options_
);
162 ASSERT_OK(TryReopen(options
));
165 void Destroy(Options
* options
) {
168 ASSERT_OK(DestroyDB(dbname_
, *options
));
171 Status
PureReopen(Options
* options
, DB
** db
) {
172 return DB::Open(*options
, dbname_
, db
);
175 Status
ReopenForReadOnly(Options
* options
) {
178 return DB::OpenForReadOnly(*options
, dbname_
, &db_
);
181 Status
TryReopen(Options
* options
= nullptr) {
185 if (options
!= nullptr) {
188 opts
= CurrentOptions();
189 opts
.create_if_missing
= true;
191 last_options_
= opts
;
193 return DB::Open(opts
, dbname_
, &db_
);
196 Status
Put(const Slice
& k
, const Slice
& v
) {
197 return db_
->Put(WriteOptions(), k
, v
);
200 Status
Delete(const std::string
& k
) { return db_
->Delete(WriteOptions(), k
); }
202 std::string
Get(const std::string
& k
, const Snapshot
* snapshot
= nullptr) {
204 options
.snapshot
= snapshot
;
206 Status s
= db_
->Get(options
, k
, &result
);
207 if (s
.IsNotFound()) {
208 result
= "NOT_FOUND";
209 } else if (!s
.ok()) {
210 result
= s
.ToString();
215 int NumTableFilesAtLevel(int level
) {
216 std::string property
;
217 EXPECT_TRUE(db_
->GetProperty(
218 "rocksdb.num-files-at-level" + std::to_string(level
), &property
));
219 return atoi(property
.c_str());
222 // Return spread of files per level
223 std::string
FilesPerLevel() {
225 size_t last_non_zero_offset
= 0;
226 for (int level
= 0; level
< db_
->NumberLevels(); level
++) {
227 int f
= NumTableFilesAtLevel(level
);
229 snprintf(buf
, sizeof(buf
), "%s%d", (level
? "," : ""), f
);
232 last_non_zero_offset
= result
.size();
235 result
.resize(last_non_zero_offset
);
239 std::string
IterStatus(Iterator
* iter
) {
242 result
= iter
->key().ToString() + "->" + iter
->value().ToString();
244 result
= "(invalid)";
250 TEST_P(PlainTableDBTest
, Empty
) {
251 ASSERT_TRUE(dbfull() != nullptr);
252 ASSERT_EQ("NOT_FOUND", Get("0000000000000foo"));
255 extern const uint64_t kPlainTableMagicNumber
;
257 class TestPlainTableReader
: public PlainTableReader
{
259 TestPlainTableReader(
260 const EnvOptions
& env_options
, const InternalKeyComparator
& icomparator
,
261 EncodingType encoding_type
, uint64_t file_size
, int bloom_bits_per_key
,
262 double hash_table_ratio
, size_t index_sparseness
,
263 std::unique_ptr
<TableProperties
>&& props
,
264 std::unique_ptr
<RandomAccessFileReader
>&& file
,
265 const ImmutableOptions
& ioptions
, const SliceTransform
* prefix_extractor
,
266 bool* expect_bloom_not_match
, bool store_index_in_file
,
267 uint32_t column_family_id
, const std::string
& column_family_name
)
268 : PlainTableReader(ioptions
, std::move(file
), env_options
, icomparator
,
269 encoding_type
, file_size
, props
.get(),
271 expect_bloom_not_match_(expect_bloom_not_match
) {
272 Status s
= MmapDataIfNeeded();
275 s
= PopulateIndex(props
.get(), bloom_bits_per_key
, hash_table_ratio
,
276 index_sparseness
, 2 * 1024 * 1024);
279 EXPECT_EQ(column_family_id
, static_cast<uint32_t>(props
->column_family_id
));
280 EXPECT_EQ(column_family_name
, props
->column_family_name
);
281 if (store_index_in_file
) {
282 auto bloom_version_ptr
= props
->user_collected_properties
.find(
283 PlainTablePropertyNames::kBloomVersion
);
284 EXPECT_TRUE(bloom_version_ptr
!= props
->user_collected_properties
.end());
285 EXPECT_EQ(bloom_version_ptr
->second
, std::string("1"));
286 if (ioptions
.bloom_locality
> 0) {
287 auto num_blocks_ptr
= props
->user_collected_properties
.find(
288 PlainTablePropertyNames::kNumBloomBlocks
);
289 EXPECT_TRUE(num_blocks_ptr
!= props
->user_collected_properties
.end());
292 table_properties_
= std::move(props
);
295 ~TestPlainTableReader() override
{}
298 bool MatchBloom(uint32_t hash
) const override
{
299 bool ret
= PlainTableReader::MatchBloom(hash
);
300 if (*expect_bloom_not_match_
) {
307 bool* expect_bloom_not_match_
;
310 extern const uint64_t kPlainTableMagicNumber
;
311 class TestPlainTableFactory
: public PlainTableFactory
{
313 explicit TestPlainTableFactory(bool* expect_bloom_not_match
,
314 const PlainTableOptions
& options
,
315 uint32_t column_family_id
,
316 std::string column_family_name
)
317 : PlainTableFactory(options
),
318 bloom_bits_per_key_(options
.bloom_bits_per_key
),
319 hash_table_ratio_(options
.hash_table_ratio
),
320 index_sparseness_(options
.index_sparseness
),
321 store_index_in_file_(options
.store_index_in_file
),
322 expect_bloom_not_match_(expect_bloom_not_match
),
323 column_family_id_(column_family_id
),
324 column_family_name_(std::move(column_family_name
)) {}
326 using PlainTableFactory::NewTableReader
;
327 Status
NewTableReader(
328 const ReadOptions
& /*ro*/, const TableReaderOptions
& table_reader_options
,
329 std::unique_ptr
<RandomAccessFileReader
>&& file
, uint64_t file_size
,
330 std::unique_ptr
<TableReader
>* table
,
331 bool /*prefetch_index_and_filter_in_cache*/) const override
{
332 std::unique_ptr
<TableProperties
> props
;
333 auto s
= ReadTableProperties(file
.get(), file_size
, kPlainTableMagicNumber
,
334 table_reader_options
.ioptions
, &props
);
337 if (store_index_in_file_
) {
338 BlockHandle bloom_block_handle
;
339 s
= FindMetaBlockInFile(file
.get(), file_size
, kPlainTableMagicNumber
,
340 table_reader_options
.ioptions
,
341 BloomBlockBuilder::kBloomBlock
,
342 &bloom_block_handle
);
345 BlockHandle index_block_handle
;
346 s
= FindMetaBlockInFile(file
.get(), file_size
, kPlainTableMagicNumber
,
347 table_reader_options
.ioptions
,
348 PlainTableIndexBuilder::kPlainTableIndexBlock
,
349 &index_block_handle
);
353 auto& user_props
= props
->user_collected_properties
;
354 auto encoding_type_prop
=
355 user_props
.find(PlainTablePropertyNames::kEncodingType
);
356 assert(encoding_type_prop
!= user_props
.end());
357 EncodingType encoding_type
= static_cast<EncodingType
>(
358 DecodeFixed32(encoding_type_prop
->second
.c_str()));
360 std::unique_ptr
<PlainTableReader
> new_reader(new TestPlainTableReader(
361 table_reader_options
.env_options
,
362 table_reader_options
.internal_comparator
, encoding_type
, file_size
,
363 bloom_bits_per_key_
, hash_table_ratio_
, index_sparseness_
,
364 std::move(props
), std::move(file
), table_reader_options
.ioptions
,
365 table_reader_options
.prefix_extractor
.get(), expect_bloom_not_match_
,
366 store_index_in_file_
, column_family_id_
, column_family_name_
));
368 *table
= std::move(new_reader
);
373 int bloom_bits_per_key_
;
374 double hash_table_ratio_
;
375 size_t index_sparseness_
;
376 bool store_index_in_file_
;
377 bool* expect_bloom_not_match_
;
378 const uint32_t column_family_id_
;
379 const std::string column_family_name_
;
382 TEST_P(PlainTableDBTest
, BadOptions1
) {
383 // Build with a prefix extractor
384 ASSERT_OK(Put("1000000000000foo", "v1"));
385 ASSERT_OK(dbfull()->TEST_FlushMemTable());
387 // Bad attempt to re-open without a prefix extractor
388 Options options
= CurrentOptions();
389 options
.prefix_extractor
.reset();
391 "Invalid argument: Prefix extractor is missing when opening a PlainTable "
392 "built using a prefix extractor",
393 TryReopen(&options
).ToString());
395 // Bad attempt to re-open with different prefix extractor
396 options
.prefix_extractor
.reset(NewFixedPrefixTransform(6));
398 "Invalid argument: Prefix extractor given doesn't match the one used to "
400 TryReopen(&options
).ToString());
402 // Correct prefix extractor
403 options
.prefix_extractor
.reset(NewFixedPrefixTransform(8));
405 ASSERT_EQ("v1", Get("1000000000000foo"));
408 TEST_P(PlainTableDBTest
, BadOptions2
) {
409 Options options
= CurrentOptions();
410 options
.prefix_extractor
.reset();
411 options
.create_if_missing
= true;
412 DestroyAndReopen(&options
);
413 // Build without a prefix extractor
414 // (apparently works even if hash_table_ratio > 0)
415 ASSERT_OK(Put("1000000000000foo", "v1"));
416 // Build without a prefix extractor, this call will fail and returns the
417 // status for this bad attempt.
418 ASSERT_NOK(dbfull()->TEST_FlushMemTable());
420 // Bad attempt to re-open with hash_table_ratio > 0 and no prefix extractor
421 Status s
= TryReopen(&options
);
423 "Not implemented: PlainTable requires a prefix extractor enable prefix "
427 // OK to open with hash_table_ratio == 0 and no prefix extractor
428 PlainTableOptions plain_table_options
;
429 plain_table_options
.hash_table_ratio
= 0;
430 options
.table_factory
.reset(NewPlainTableFactory(plain_table_options
));
432 ASSERT_EQ("v1", Get("1000000000000foo"));
434 // OK to open newly with a prefix_extractor and hash table; builds index
436 options
= CurrentOptions();
438 ASSERT_EQ("v1", Get("1000000000000foo"));
441 TEST_P(PlainTableDBTest
, Flush
) {
442 for (size_t huge_page_tlb_size
= 0; huge_page_tlb_size
<= 2 * 1024 * 1024;
443 huge_page_tlb_size
+= 2 * 1024 * 1024) {
444 for (EncodingType encoding_type
: {kPlain
, kPrefix
}) {
445 for (int bloom
= -1; bloom
<= 117; bloom
+= 117) {
446 const int bloom_bits
= std::max(bloom
, 0);
447 const bool full_scan_mode
= bloom
< 0;
448 for (int total_order
= 0; total_order
<= 1; total_order
++) {
449 for (int store_index_in_file
= 0; store_index_in_file
<= 1;
450 ++store_index_in_file
) {
451 Options options
= CurrentOptions();
452 options
.create_if_missing
= true;
453 // Set only one bucket to force bucket conflict.
454 // Test index interval for the same prefix to be 1, 2 and 4
456 options
.prefix_extractor
.reset();
458 PlainTableOptions plain_table_options
;
459 plain_table_options
.user_key_len
= 0;
460 plain_table_options
.bloom_bits_per_key
= bloom_bits
;
461 plain_table_options
.hash_table_ratio
= 0;
462 plain_table_options
.index_sparseness
= 2;
463 plain_table_options
.huge_page_tlb_size
= huge_page_tlb_size
;
464 plain_table_options
.encoding_type
= encoding_type
;
465 plain_table_options
.full_scan_mode
= full_scan_mode
;
466 plain_table_options
.store_index_in_file
= store_index_in_file
;
468 options
.table_factory
.reset(
469 NewPlainTableFactory(plain_table_options
));
471 PlainTableOptions plain_table_options
;
472 plain_table_options
.user_key_len
= 0;
473 plain_table_options
.bloom_bits_per_key
= bloom_bits
;
474 plain_table_options
.hash_table_ratio
= 0.75;
475 plain_table_options
.index_sparseness
= 16;
476 plain_table_options
.huge_page_tlb_size
= huge_page_tlb_size
;
477 plain_table_options
.encoding_type
= encoding_type
;
478 plain_table_options
.full_scan_mode
= full_scan_mode
;
479 plain_table_options
.store_index_in_file
= store_index_in_file
;
481 options
.table_factory
.reset(
482 NewPlainTableFactory(plain_table_options
));
484 DestroyAndReopen(&options
);
486 ASSERT_TRUE(dbfull()->GetIntProperty(
487 "rocksdb.estimate-table-readers-mem", &int_num
));
488 ASSERT_EQ(int_num
, 0U);
490 ASSERT_OK(Put("1000000000000foo", "v1"));
491 ASSERT_OK(Put("0000000000000bar", "v2"));
492 ASSERT_OK(Put("1000000000000foo", "v3"));
493 ASSERT_OK(dbfull()->TEST_FlushMemTable());
495 ASSERT_TRUE(dbfull()->GetIntProperty(
496 "rocksdb.estimate-table-readers-mem", &int_num
));
497 ASSERT_GT(int_num
, 0U);
499 TablePropertiesCollection ptc
;
500 ASSERT_OK(reinterpret_cast<DB
*>(dbfull())->GetPropertiesOfAllTables(
502 ASSERT_EQ(1U, ptc
.size());
503 auto row
= ptc
.begin();
504 auto tp
= row
->second
;
506 if (full_scan_mode
) {
507 // Does not support Get/Seek
508 std::unique_ptr
<Iterator
> iter(
509 dbfull()->NewIterator(ReadOptions()));
511 ASSERT_TRUE(iter
->Valid());
512 ASSERT_EQ("0000000000000bar", iter
->key().ToString());
513 ASSERT_EQ("v2", iter
->value().ToString());
515 ASSERT_TRUE(iter
->Valid());
516 ASSERT_EQ("1000000000000foo", iter
->key().ToString());
517 ASSERT_EQ("v3", iter
->value().ToString());
519 ASSERT_TRUE(!iter
->Valid());
520 ASSERT_TRUE(iter
->status().ok());
522 if (!store_index_in_file
) {
523 ASSERT_EQ(total_order
? "4" : "12",
524 (tp
->user_collected_properties
)
525 .at("plain_table_hash_table_size"));
526 ASSERT_EQ("0", (tp
->user_collected_properties
)
527 .at("plain_table_sub_index_size"));
529 ASSERT_EQ("0", (tp
->user_collected_properties
)
530 .at("plain_table_hash_table_size"));
531 ASSERT_EQ("0", (tp
->user_collected_properties
)
532 .at("plain_table_sub_index_size"));
534 ASSERT_EQ("v3", Get("1000000000000foo"));
535 ASSERT_EQ("v2", Get("0000000000000bar"));
544 TEST_P(PlainTableDBTest
, Flush2
) {
545 for (size_t huge_page_tlb_size
= 0; huge_page_tlb_size
<= 2 * 1024 * 1024;
546 huge_page_tlb_size
+= 2 * 1024 * 1024) {
547 for (EncodingType encoding_type
: {kPlain
, kPrefix
}) {
548 for (int bloom_bits
= 0; bloom_bits
<= 117; bloom_bits
+= 117) {
549 for (int total_order
= 0; total_order
<= 1; total_order
++) {
550 for (int store_index_in_file
= 0; store_index_in_file
<= 1;
551 ++store_index_in_file
) {
552 if (encoding_type
== kPrefix
&& total_order
) {
555 if (!bloom_bits
&& store_index_in_file
) {
558 if (total_order
&& store_index_in_file
) {
561 bool expect_bloom_not_match
= false;
562 Options options
= CurrentOptions();
563 options
.create_if_missing
= true;
564 // Set only one bucket to force bucket conflict.
565 // Test index interval for the same prefix to be 1, 2 and 4
566 PlainTableOptions plain_table_options
;
568 options
.prefix_extractor
= nullptr;
569 plain_table_options
.hash_table_ratio
= 0;
570 plain_table_options
.index_sparseness
= 2;
572 plain_table_options
.hash_table_ratio
= 0.75;
573 plain_table_options
.index_sparseness
= 16;
575 plain_table_options
.user_key_len
= kPlainTableVariableLength
;
576 plain_table_options
.bloom_bits_per_key
= bloom_bits
;
577 plain_table_options
.huge_page_tlb_size
= huge_page_tlb_size
;
578 plain_table_options
.encoding_type
= encoding_type
;
579 plain_table_options
.store_index_in_file
= store_index_in_file
;
580 options
.table_factory
.reset(new TestPlainTableFactory(
581 &expect_bloom_not_match
, plain_table_options
,
582 0 /* column_family_id */, kDefaultColumnFamilyName
));
584 DestroyAndReopen(&options
);
585 ASSERT_OK(Put("0000000000000bar", "b"));
586 ASSERT_OK(Put("1000000000000foo", "v1"));
587 ASSERT_OK(dbfull()->TEST_FlushMemTable());
589 ASSERT_OK(Put("1000000000000foo", "v2"));
590 ASSERT_OK(dbfull()->TEST_FlushMemTable());
591 ASSERT_EQ("v2", Get("1000000000000foo"));
593 ASSERT_OK(Put("0000000000000eee", "v3"));
594 ASSERT_OK(dbfull()->TEST_FlushMemTable());
595 ASSERT_EQ("v3", Get("0000000000000eee"));
597 ASSERT_OK(Delete("0000000000000bar"));
598 ASSERT_OK(dbfull()->TEST_FlushMemTable());
599 ASSERT_EQ("NOT_FOUND", Get("0000000000000bar"));
601 ASSERT_OK(Put("0000000000000eee", "v5"));
602 ASSERT_OK(Put("9000000000000eee", "v5"));
603 ASSERT_OK(dbfull()->TEST_FlushMemTable());
604 ASSERT_EQ("v5", Get("0000000000000eee"));
607 if (bloom_bits
> 0) {
608 // Neither key nor value should exist.
609 expect_bloom_not_match
= true;
610 ASSERT_EQ("NOT_FOUND", Get("5_not00000000bar"));
611 // Key doesn't exist any more but prefix exists.
613 ASSERT_EQ("NOT_FOUND", Get("1000000000000not"));
614 ASSERT_EQ("NOT_FOUND", Get("0000000000000not"));
616 expect_bloom_not_match
= false;
625 TEST_P(PlainTableDBTest
, Immortal
) {
626 for (EncodingType encoding_type
: {kPlain
, kPrefix
}) {
627 Options options
= CurrentOptions();
628 options
.create_if_missing
= true;
629 options
.max_open_files
= -1;
630 // Set only one bucket to force bucket conflict.
631 // Test index interval for the same prefix to be 1, 2 and 4
632 PlainTableOptions plain_table_options
;
633 plain_table_options
.hash_table_ratio
= 0.75;
634 plain_table_options
.index_sparseness
= 16;
635 plain_table_options
.user_key_len
= kPlainTableVariableLength
;
636 plain_table_options
.bloom_bits_per_key
= 10;
637 plain_table_options
.encoding_type
= encoding_type
;
638 options
.table_factory
.reset(NewPlainTableFactory(plain_table_options
));
640 DestroyAndReopen(&options
);
641 ASSERT_OK(Put("0000000000000bar", "b"));
642 ASSERT_OK(Put("1000000000000foo", "v1"));
643 ASSERT_OK(dbfull()->TEST_FlushMemTable());
646 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
647 "GetContext::SaveValue::PinSelf", [&](void* /*arg*/) { copied
++; });
648 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
649 ASSERT_EQ("b", Get("0000000000000bar"));
650 ASSERT_EQ("v1", Get("1000000000000foo"));
651 ASSERT_EQ(2, copied
);
655 ASSERT_OK(ReopenForReadOnly(&options
));
657 ASSERT_EQ("b", Get("0000000000000bar"));
658 ASSERT_EQ("v1", Get("1000000000000foo"));
659 ASSERT_EQ("NOT_FOUND", Get("1000000000000bar"));
661 ASSERT_EQ(0, copied
);
663 ASSERT_EQ(2, copied
);
665 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
669 TEST_P(PlainTableDBTest
, Iterator
) {
670 for (size_t huge_page_tlb_size
= 0; huge_page_tlb_size
<= 2 * 1024 * 1024;
671 huge_page_tlb_size
+= 2 * 1024 * 1024) {
672 for (EncodingType encoding_type
: {kPlain
, kPrefix
}) {
673 for (int bloom_bits
= 0; bloom_bits
<= 117; bloom_bits
+= 117) {
674 for (int total_order
= 0; total_order
<= 1; total_order
++) {
675 if (encoding_type
== kPrefix
&& total_order
== 1) {
678 bool expect_bloom_not_match
= false;
679 Options options
= CurrentOptions();
680 options
.create_if_missing
= true;
681 // Set only one bucket to force bucket conflict.
682 // Test index interval for the same prefix to be 1, 2 and 4
684 options
.prefix_extractor
= nullptr;
686 PlainTableOptions plain_table_options
;
687 plain_table_options
.user_key_len
= 16;
688 plain_table_options
.bloom_bits_per_key
= bloom_bits
;
689 plain_table_options
.hash_table_ratio
= 0;
690 plain_table_options
.index_sparseness
= 2;
691 plain_table_options
.huge_page_tlb_size
= huge_page_tlb_size
;
692 plain_table_options
.encoding_type
= encoding_type
;
694 options
.table_factory
.reset(new TestPlainTableFactory(
695 &expect_bloom_not_match
, plain_table_options
,
696 0 /* column_family_id */, kDefaultColumnFamilyName
));
698 PlainTableOptions plain_table_options
;
699 plain_table_options
.user_key_len
= 16;
700 plain_table_options
.bloom_bits_per_key
= bloom_bits
;
701 plain_table_options
.hash_table_ratio
= 0.75;
702 plain_table_options
.index_sparseness
= 16;
703 plain_table_options
.huge_page_tlb_size
= huge_page_tlb_size
;
704 plain_table_options
.encoding_type
= encoding_type
;
706 options
.table_factory
.reset(new TestPlainTableFactory(
707 &expect_bloom_not_match
, plain_table_options
,
708 0 /* column_family_id */, kDefaultColumnFamilyName
));
710 DestroyAndReopen(&options
);
712 ASSERT_OK(Put("1000000000foo002", "v_2"));
713 ASSERT_OK(Put("0000000000000bar", "random"));
714 ASSERT_OK(Put("1000000000foo001", "v1"));
715 ASSERT_OK(Put("3000000000000bar", "bar_v"));
716 ASSERT_OK(Put("1000000000foo003", "v__3"));
717 ASSERT_OK(Put("1000000000foo004", "v__4"));
718 ASSERT_OK(Put("1000000000foo005", "v__5"));
719 ASSERT_OK(Put("1000000000foo007", "v__7"));
720 ASSERT_OK(Put("1000000000foo008", "v__8"));
721 ASSERT_OK(dbfull()->TEST_FlushMemTable());
722 ASSERT_EQ("v1", Get("1000000000foo001"));
723 ASSERT_EQ("v__3", Get("1000000000foo003"));
724 Iterator
* iter
= dbfull()->NewIterator(ReadOptions());
725 iter
->Seek("1000000000foo000");
726 ASSERT_TRUE(iter
->Valid());
727 ASSERT_EQ("1000000000foo001", iter
->key().ToString());
728 ASSERT_EQ("v1", iter
->value().ToString());
731 ASSERT_TRUE(iter
->Valid());
732 ASSERT_EQ("1000000000foo002", iter
->key().ToString());
733 ASSERT_EQ("v_2", iter
->value().ToString());
736 ASSERT_TRUE(iter
->Valid());
737 ASSERT_EQ("1000000000foo003", iter
->key().ToString());
738 ASSERT_EQ("v__3", iter
->value().ToString());
741 ASSERT_TRUE(iter
->Valid());
742 ASSERT_EQ("1000000000foo004", iter
->key().ToString());
743 ASSERT_EQ("v__4", iter
->value().ToString());
745 iter
->Seek("3000000000000bar");
746 ASSERT_TRUE(iter
->Valid());
747 ASSERT_EQ("3000000000000bar", iter
->key().ToString());
748 ASSERT_EQ("bar_v", iter
->value().ToString());
750 iter
->Seek("1000000000foo000");
751 ASSERT_TRUE(iter
->Valid());
752 ASSERT_EQ("1000000000foo001", iter
->key().ToString());
753 ASSERT_EQ("v1", iter
->value().ToString());
755 iter
->Seek("1000000000foo005");
756 ASSERT_TRUE(iter
->Valid());
757 ASSERT_EQ("1000000000foo005", iter
->key().ToString());
758 ASSERT_EQ("v__5", iter
->value().ToString());
760 iter
->Seek("1000000000foo006");
761 ASSERT_TRUE(iter
->Valid());
762 ASSERT_EQ("1000000000foo007", iter
->key().ToString());
763 ASSERT_EQ("v__7", iter
->value().ToString());
765 iter
->Seek("1000000000foo008");
766 ASSERT_TRUE(iter
->Valid());
767 ASSERT_EQ("1000000000foo008", iter
->key().ToString());
768 ASSERT_EQ("v__8", iter
->value().ToString());
770 if (total_order
== 0) {
771 iter
->Seek("1000000000foo009");
772 ASSERT_TRUE(iter
->Valid());
773 ASSERT_EQ("3000000000000bar", iter
->key().ToString());
777 if (bloom_bits
> 0) {
779 // Neither key nor value should exist.
780 expect_bloom_not_match
= true;
781 iter
->Seek("2not000000000bar");
782 ASSERT_TRUE(!iter
->Valid());
783 ASSERT_EQ("NOT_FOUND", Get("2not000000000bar"));
784 expect_bloom_not_match
= false;
786 expect_bloom_not_match
= true;
787 ASSERT_EQ("NOT_FOUND", Get("2not000000000bar"));
788 expect_bloom_not_match
= false;
791 ASSERT_OK(iter
->status());
800 std::string
NthKey(size_t n
, char filler
) {
801 std::string
rv(16, filler
);
803 rv
[1] = (n
/ 10) % 10;
804 rv
[2] = (n
/ 100) % 10;
805 rv
[3] = (n
/ 1000) % 10;
808 } // anonymous namespace
810 TEST_P(PlainTableDBTest
, BloomSchema
) {
811 Options options
= CurrentOptions();
812 options
.create_if_missing
= true;
813 for (int bloom_locality
= 0; bloom_locality
<= 1; bloom_locality
++) {
814 options
.bloom_locality
= bloom_locality
;
815 PlainTableOptions plain_table_options
;
816 plain_table_options
.user_key_len
= 16;
817 plain_table_options
.bloom_bits_per_key
= 3; // high FP rate for test
818 plain_table_options
.hash_table_ratio
= 0.75;
819 plain_table_options
.index_sparseness
= 16;
820 plain_table_options
.huge_page_tlb_size
= 0;
821 plain_table_options
.encoding_type
= kPlain
;
823 bool expect_bloom_not_match
= false;
824 options
.table_factory
.reset(new TestPlainTableFactory(
825 &expect_bloom_not_match
, plain_table_options
, 0 /* column_family_id */,
826 kDefaultColumnFamilyName
));
827 DestroyAndReopen(&options
);
829 for (unsigned i
= 0; i
< 2345; ++i
) {
830 ASSERT_OK(Put(NthKey(i
, 'y'), "added"));
832 ASSERT_OK(dbfull()->TEST_FlushMemTable());
833 ASSERT_EQ("added", Get(NthKey(42, 'y')));
835 for (unsigned i
= 0; i
< 32; ++i
) {
836 // Known pattern of Bloom filter false positives can detect schema change
837 // with high probability. Known FPs stuffed into bits:
839 if (!bloom_locality
) {
840 pattern
= 1785868347UL;
841 } else if (CACHE_LINE_SIZE
== 64U) {
842 pattern
= 2421694657UL;
843 } else if (CACHE_LINE_SIZE
== 128U) {
844 pattern
= 788710956UL;
846 ASSERT_EQ(CACHE_LINE_SIZE
, 256U);
849 bool expect_fp
= pattern
& (1UL << i
);
850 // fprintf(stderr, "expect_fp@%u: %d\n", i, (int)expect_fp);
851 expect_bloom_not_match
= !expect_fp
;
852 ASSERT_EQ("NOT_FOUND", Get(NthKey(i
, 'n')));
858 std::string
MakeLongKey(size_t length
, char c
) {
859 return std::string(length
, c
);
861 } // anonymous namespace
863 TEST_P(PlainTableDBTest
, IteratorLargeKeys
) {
864 Options options
= CurrentOptions();
866 PlainTableOptions plain_table_options
;
867 plain_table_options
.user_key_len
= 0;
868 plain_table_options
.bloom_bits_per_key
= 0;
869 plain_table_options
.hash_table_ratio
= 0;
871 options
.table_factory
.reset(NewPlainTableFactory(plain_table_options
));
872 options
.create_if_missing
= true;
873 options
.prefix_extractor
.reset();
874 DestroyAndReopen(&options
);
876 std::string key_list
[] = {MakeLongKey(30, '0'), MakeLongKey(16, '1'),
877 MakeLongKey(32, '2'), MakeLongKey(60, '3'),
878 MakeLongKey(90, '4'), MakeLongKey(50, '5'),
879 MakeLongKey(26, '6')};
881 for (size_t i
= 0; i
< 7; i
++) {
882 ASSERT_OK(Put(key_list
[i
], std::to_string(i
)));
885 ASSERT_OK(dbfull()->TEST_FlushMemTable());
887 Iterator
* iter
= dbfull()->NewIterator(ReadOptions());
888 iter
->Seek(key_list
[0]);
890 for (size_t i
= 0; i
< 7; i
++) {
891 ASSERT_TRUE(iter
->Valid());
892 ASSERT_EQ(key_list
[i
], iter
->key().ToString());
893 ASSERT_EQ(std::to_string(i
), iter
->value().ToString());
897 ASSERT_TRUE(!iter
->Valid());
903 std::string
MakeLongKeyWithPrefix(size_t length
, char c
) {
904 return "00000000" + std::string(length
- 8, c
);
906 } // anonymous namespace
908 TEST_P(PlainTableDBTest
, IteratorLargeKeysWithPrefix
) {
909 Options options
= CurrentOptions();
911 PlainTableOptions plain_table_options
;
912 plain_table_options
.user_key_len
= 16;
913 plain_table_options
.bloom_bits_per_key
= 0;
914 plain_table_options
.hash_table_ratio
= 0.8;
915 plain_table_options
.index_sparseness
= 3;
916 plain_table_options
.huge_page_tlb_size
= 0;
917 plain_table_options
.encoding_type
= kPrefix
;
919 options
.table_factory
.reset(NewPlainTableFactory(plain_table_options
));
920 options
.create_if_missing
= true;
921 DestroyAndReopen(&options
);
923 std::string key_list
[] = {
924 MakeLongKeyWithPrefix(30, '0'), MakeLongKeyWithPrefix(16, '1'),
925 MakeLongKeyWithPrefix(32, '2'), MakeLongKeyWithPrefix(60, '3'),
926 MakeLongKeyWithPrefix(90, '4'), MakeLongKeyWithPrefix(50, '5'),
927 MakeLongKeyWithPrefix(26, '6')};
929 for (size_t i
= 0; i
< 7; i
++) {
930 ASSERT_OK(Put(key_list
[i
], std::to_string(i
)));
933 ASSERT_OK(dbfull()->TEST_FlushMemTable());
935 Iterator
* iter
= dbfull()->NewIterator(ReadOptions());
936 iter
->Seek(key_list
[0]);
938 for (size_t i
= 0; i
< 7; i
++) {
939 ASSERT_TRUE(iter
->Valid());
940 ASSERT_EQ(key_list
[i
], iter
->key().ToString());
941 ASSERT_EQ(std::to_string(i
), iter
->value().ToString());
945 ASSERT_TRUE(!iter
->Valid());
950 TEST_P(PlainTableDBTest
, IteratorReverseSuffixComparator
) {
951 Options options
= CurrentOptions();
952 options
.create_if_missing
= true;
953 // Set only one bucket to force bucket conflict.
954 // Test index interval for the same prefix to be 1, 2 and 4
955 test::SimpleSuffixReverseComparator comp
;
956 options
.comparator
= &comp
;
957 DestroyAndReopen(&options
);
959 ASSERT_OK(Put("1000000000foo002", "v_2"));
960 ASSERT_OK(Put("0000000000000bar", "random"));
961 ASSERT_OK(Put("1000000000foo001", "v1"));
962 ASSERT_OK(Put("3000000000000bar", "bar_v"));
963 ASSERT_OK(Put("1000000000foo003", "v__3"));
964 ASSERT_OK(Put("1000000000foo004", "v__4"));
965 ASSERT_OK(Put("1000000000foo005", "v__5"));
966 ASSERT_OK(Put("1000000000foo007", "v__7"));
967 ASSERT_OK(Put("1000000000foo008", "v__8"));
968 ASSERT_OK(dbfull()->TEST_FlushMemTable());
969 ASSERT_EQ("v1", Get("1000000000foo001"));
970 ASSERT_EQ("v__3", Get("1000000000foo003"));
971 Iterator
* iter
= dbfull()->NewIterator(ReadOptions());
972 iter
->Seek("1000000000foo009");
973 ASSERT_TRUE(iter
->Valid());
974 ASSERT_EQ("1000000000foo008", iter
->key().ToString());
975 ASSERT_EQ("v__8", iter
->value().ToString());
978 ASSERT_TRUE(iter
->Valid());
979 ASSERT_EQ("1000000000foo007", iter
->key().ToString());
980 ASSERT_EQ("v__7", iter
->value().ToString());
983 ASSERT_TRUE(iter
->Valid());
984 ASSERT_EQ("1000000000foo005", iter
->key().ToString());
985 ASSERT_EQ("v__5", iter
->value().ToString());
988 ASSERT_TRUE(iter
->Valid());
989 ASSERT_EQ("1000000000foo004", iter
->key().ToString());
990 ASSERT_EQ("v__4", iter
->value().ToString());
992 iter
->Seek("3000000000000bar");
993 ASSERT_TRUE(iter
->Valid());
994 ASSERT_EQ("3000000000000bar", iter
->key().ToString());
995 ASSERT_EQ("bar_v", iter
->value().ToString());
997 iter
->Seek("1000000000foo005");
998 ASSERT_TRUE(iter
->Valid());
999 ASSERT_EQ("1000000000foo005", iter
->key().ToString());
1000 ASSERT_EQ("v__5", iter
->value().ToString());
1002 iter
->Seek("1000000000foo006");
1003 ASSERT_TRUE(iter
->Valid());
1004 ASSERT_EQ("1000000000foo005", iter
->key().ToString());
1005 ASSERT_EQ("v__5", iter
->value().ToString());
1007 iter
->Seek("1000000000foo008");
1008 ASSERT_TRUE(iter
->Valid());
1009 ASSERT_EQ("1000000000foo008", iter
->key().ToString());
1010 ASSERT_EQ("v__8", iter
->value().ToString());
1012 iter
->Seek("1000000000foo000");
1013 ASSERT_TRUE(iter
->Valid());
1014 ASSERT_EQ("3000000000000bar", iter
->key().ToString());
1019 TEST_P(PlainTableDBTest
, HashBucketConflict
) {
1020 for (size_t huge_page_tlb_size
= 0; huge_page_tlb_size
<= 2 * 1024 * 1024;
1021 huge_page_tlb_size
+= 2 * 1024 * 1024) {
1022 for (unsigned char i
= 1; i
<= 3; i
++) {
1023 Options options
= CurrentOptions();
1024 options
.create_if_missing
= true;
1025 // Set only one bucket to force bucket conflict.
1026 // Test index interval for the same prefix to be 1, 2 and 4
1028 PlainTableOptions plain_table_options
;
1029 plain_table_options
.user_key_len
= 16;
1030 plain_table_options
.bloom_bits_per_key
= 0;
1031 plain_table_options
.hash_table_ratio
= 0;
1032 plain_table_options
.index_sparseness
= 2 ^ i
;
1033 plain_table_options
.huge_page_tlb_size
= huge_page_tlb_size
;
1035 options
.table_factory
.reset(NewPlainTableFactory(plain_table_options
));
1037 DestroyAndReopen(&options
);
1038 ASSERT_OK(Put("5000000000000fo0", "v1"));
1039 ASSERT_OK(Put("5000000000000fo1", "v2"));
1040 ASSERT_OK(Put("5000000000000fo2", "v"));
1041 ASSERT_OK(Put("2000000000000fo0", "v3"));
1042 ASSERT_OK(Put("2000000000000fo1", "v4"));
1043 ASSERT_OK(Put("2000000000000fo2", "v"));
1044 ASSERT_OK(Put("2000000000000fo3", "v"));
1046 ASSERT_OK(dbfull()->TEST_FlushMemTable());
1048 ASSERT_EQ("v1", Get("5000000000000fo0"));
1049 ASSERT_EQ("v2", Get("5000000000000fo1"));
1050 ASSERT_EQ("v3", Get("2000000000000fo0"));
1051 ASSERT_EQ("v4", Get("2000000000000fo1"));
1053 ASSERT_EQ("NOT_FOUND", Get("5000000000000bar"));
1054 ASSERT_EQ("NOT_FOUND", Get("2000000000000bar"));
1055 ASSERT_EQ("NOT_FOUND", Get("5000000000000fo8"));
1056 ASSERT_EQ("NOT_FOUND", Get("2000000000000fo8"));
1059 Iterator
* iter
= dbfull()->NewIterator(ro
);
1061 iter
->Seek("5000000000000fo0");
1062 ASSERT_TRUE(iter
->Valid());
1063 ASSERT_EQ("5000000000000fo0", iter
->key().ToString());
1065 ASSERT_TRUE(iter
->Valid());
1066 ASSERT_EQ("5000000000000fo1", iter
->key().ToString());
1068 iter
->Seek("5000000000000fo1");
1069 ASSERT_TRUE(iter
->Valid());
1070 ASSERT_EQ("5000000000000fo1", iter
->key().ToString());
1072 iter
->Seek("2000000000000fo0");
1073 ASSERT_TRUE(iter
->Valid());
1074 ASSERT_EQ("2000000000000fo0", iter
->key().ToString());
1076 ASSERT_TRUE(iter
->Valid());
1077 ASSERT_EQ("2000000000000fo1", iter
->key().ToString());
1079 iter
->Seek("2000000000000fo1");
1080 ASSERT_TRUE(iter
->Valid());
1081 ASSERT_EQ("2000000000000fo1", iter
->key().ToString());
1083 iter
->Seek("2000000000000bar");
1084 ASSERT_TRUE(iter
->Valid());
1085 ASSERT_EQ("2000000000000fo0", iter
->key().ToString());
1087 iter
->Seek("5000000000000bar");
1088 ASSERT_TRUE(iter
->Valid());
1089 ASSERT_EQ("5000000000000fo0", iter
->key().ToString());
1091 iter
->Seek("2000000000000fo8");
1092 ASSERT_TRUE(!iter
->Valid() ||
1093 options
.comparator
->Compare(iter
->key(), "20000001") > 0);
1095 iter
->Seek("5000000000000fo8");
1096 ASSERT_TRUE(!iter
->Valid());
1098 iter
->Seek("1000000000000fo2");
1099 ASSERT_TRUE(!iter
->Valid());
1101 iter
->Seek("3000000000000fo2");
1102 ASSERT_TRUE(!iter
->Valid());
1104 iter
->Seek("8000000000000fo2");
1105 ASSERT_TRUE(!iter
->Valid());
1107 ASSERT_OK(iter
->status());
1113 TEST_P(PlainTableDBTest
, HashBucketConflictReverseSuffixComparator
) {
1114 for (size_t huge_page_tlb_size
= 0; huge_page_tlb_size
<= 2 * 1024 * 1024;
1115 huge_page_tlb_size
+= 2 * 1024 * 1024) {
1116 for (unsigned char i
= 1; i
<= 3; i
++) {
1117 Options options
= CurrentOptions();
1118 options
.create_if_missing
= true;
1119 test::SimpleSuffixReverseComparator comp
;
1120 options
.comparator
= &comp
;
1121 // Set only one bucket to force bucket conflict.
1122 // Test index interval for the same prefix to be 1, 2 and 4
1124 PlainTableOptions plain_table_options
;
1125 plain_table_options
.user_key_len
= 16;
1126 plain_table_options
.bloom_bits_per_key
= 0;
1127 plain_table_options
.hash_table_ratio
= 0;
1128 plain_table_options
.index_sparseness
= 2 ^ i
;
1129 plain_table_options
.huge_page_tlb_size
= huge_page_tlb_size
;
1131 options
.table_factory
.reset(NewPlainTableFactory(plain_table_options
));
1132 DestroyAndReopen(&options
);
1133 ASSERT_OK(Put("5000000000000fo0", "v1"));
1134 ASSERT_OK(Put("5000000000000fo1", "v2"));
1135 ASSERT_OK(Put("5000000000000fo2", "v"));
1136 ASSERT_OK(Put("2000000000000fo0", "v3"));
1137 ASSERT_OK(Put("2000000000000fo1", "v4"));
1138 ASSERT_OK(Put("2000000000000fo2", "v"));
1139 ASSERT_OK(Put("2000000000000fo3", "v"));
1141 ASSERT_OK(dbfull()->TEST_FlushMemTable());
1143 ASSERT_EQ("v1", Get("5000000000000fo0"));
1144 ASSERT_EQ("v2", Get("5000000000000fo1"));
1145 ASSERT_EQ("v3", Get("2000000000000fo0"));
1146 ASSERT_EQ("v4", Get("2000000000000fo1"));
1148 ASSERT_EQ("NOT_FOUND", Get("5000000000000bar"));
1149 ASSERT_EQ("NOT_FOUND", Get("2000000000000bar"));
1150 ASSERT_EQ("NOT_FOUND", Get("5000000000000fo8"));
1151 ASSERT_EQ("NOT_FOUND", Get("2000000000000fo8"));
1154 Iterator
* iter
= dbfull()->NewIterator(ro
);
1156 iter
->Seek("5000000000000fo1");
1157 ASSERT_TRUE(iter
->Valid());
1158 ASSERT_EQ("5000000000000fo1", iter
->key().ToString());
1160 ASSERT_TRUE(iter
->Valid());
1161 ASSERT_EQ("5000000000000fo0", iter
->key().ToString());
1163 iter
->Seek("5000000000000fo1");
1164 ASSERT_TRUE(iter
->Valid());
1165 ASSERT_EQ("5000000000000fo1", iter
->key().ToString());
1167 iter
->Seek("2000000000000fo1");
1168 ASSERT_TRUE(iter
->Valid());
1169 ASSERT_EQ("2000000000000fo1", iter
->key().ToString());
1171 ASSERT_TRUE(iter
->Valid());
1172 ASSERT_EQ("2000000000000fo0", iter
->key().ToString());
1174 iter
->Seek("2000000000000fo1");
1175 ASSERT_TRUE(iter
->Valid());
1176 ASSERT_EQ("2000000000000fo1", iter
->key().ToString());
1178 iter
->Seek("2000000000000var");
1179 ASSERT_TRUE(iter
->Valid());
1180 ASSERT_EQ("2000000000000fo3", iter
->key().ToString());
1182 iter
->Seek("5000000000000var");
1183 ASSERT_TRUE(iter
->Valid());
1184 ASSERT_EQ("5000000000000fo2", iter
->key().ToString());
1186 std::string seek_key
= "2000000000000bar";
1187 iter
->Seek(seek_key
);
1188 ASSERT_TRUE(!iter
->Valid() ||
1189 options
.prefix_extractor
->Transform(iter
->key()) !=
1190 options
.prefix_extractor
->Transform(seek_key
));
1192 iter
->Seek("1000000000000fo2");
1193 ASSERT_TRUE(!iter
->Valid());
1195 iter
->Seek("3000000000000fo2");
1196 ASSERT_TRUE(!iter
->Valid());
1198 iter
->Seek("8000000000000fo2");
1199 ASSERT_TRUE(!iter
->Valid());
1201 ASSERT_OK(iter
->status());
1207 TEST_P(PlainTableDBTest
, NonExistingKeyToNonEmptyBucket
) {
1208 Options options
= CurrentOptions();
1209 options
.create_if_missing
= true;
1210 // Set only one bucket to force bucket conflict.
1211 // Test index interval for the same prefix to be 1, 2 and 4
1212 PlainTableOptions plain_table_options
;
1213 plain_table_options
.user_key_len
= 16;
1214 plain_table_options
.bloom_bits_per_key
= 0;
1215 plain_table_options
.hash_table_ratio
= 0;
1216 plain_table_options
.index_sparseness
= 5;
1218 options
.table_factory
.reset(NewPlainTableFactory(plain_table_options
));
1219 DestroyAndReopen(&options
);
1220 ASSERT_OK(Put("5000000000000fo0", "v1"));
1221 ASSERT_OK(Put("5000000000000fo1", "v2"));
1222 ASSERT_OK(Put("5000000000000fo2", "v3"));
1224 ASSERT_OK(dbfull()->TEST_FlushMemTable());
1226 ASSERT_EQ("v1", Get("5000000000000fo0"));
1227 ASSERT_EQ("v2", Get("5000000000000fo1"));
1228 ASSERT_EQ("v3", Get("5000000000000fo2"));
1230 ASSERT_EQ("NOT_FOUND", Get("8000000000000bar"));
1231 ASSERT_EQ("NOT_FOUND", Get("1000000000000bar"));
1233 Iterator
* iter
= dbfull()->NewIterator(ReadOptions());
1235 iter
->Seek("5000000000000bar");
1236 ASSERT_TRUE(iter
->Valid());
1237 ASSERT_EQ("5000000000000fo0", iter
->key().ToString());
1239 iter
->Seek("5000000000000fo8");
1240 ASSERT_TRUE(!iter
->Valid());
1242 iter
->Seek("1000000000000fo2");
1243 ASSERT_TRUE(!iter
->Valid());
1245 iter
->Seek("8000000000000fo2");
1246 ASSERT_TRUE(!iter
->Valid());
1248 ASSERT_OK(iter
->status());
1252 static std::string
Key(int i
) {
1254 snprintf(buf
, sizeof(buf
), "key_______%06d", i
);
1255 return std::string(buf
);
1258 TEST_P(PlainTableDBTest
, CompactionTrigger
) {
1259 Options options
= CurrentOptions();
1260 options
.write_buffer_size
= 120 << 10; // 120KB
1261 options
.num_levels
= 3;
1262 options
.level0_file_num_compaction_trigger
= 3;
1267 for (int num
= 0; num
< options
.level0_file_num_compaction_trigger
- 1;
1269 std::vector
<std::string
> values
;
1270 // Write 120KB (10 values, each 12K)
1271 for (int i
= 0; i
< 10; i
++) {
1272 values
.push_back(rnd
.RandomString(12 << 10));
1273 ASSERT_OK(Put(Key(i
), values
[i
]));
1275 ASSERT_OK(Put(Key(999), ""));
1276 ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
1277 ASSERT_EQ(NumTableFilesAtLevel(0), num
+ 1);
1280 // generate one more file in level-0, and should trigger level-0 compaction
1281 std::vector
<std::string
> values
;
1282 for (int i
= 0; i
< 12; i
++) {
1283 values
.push_back(rnd
.RandomString(10000));
1284 ASSERT_OK(Put(Key(i
), values
[i
]));
1286 ASSERT_OK(Put(Key(999), ""));
1287 ASSERT_OK(dbfull()->TEST_WaitForCompact());
1289 ASSERT_EQ(NumTableFilesAtLevel(0), 0);
1290 ASSERT_EQ(NumTableFilesAtLevel(1), 1);
1293 TEST_P(PlainTableDBTest
, AdaptiveTable
) {
1294 Options options
= CurrentOptions();
1295 options
.create_if_missing
= true;
1297 options
.table_factory
.reset(NewPlainTableFactory());
1298 DestroyAndReopen(&options
);
1300 ASSERT_OK(Put("1000000000000foo", "v1"));
1301 ASSERT_OK(Put("0000000000000bar", "v2"));
1302 ASSERT_OK(Put("1000000000000foo", "v3"));
1303 ASSERT_OK(dbfull()->TEST_FlushMemTable());
1305 options
.create_if_missing
= false;
1306 std::shared_ptr
<TableFactory
> block_based_factory(
1307 NewBlockBasedTableFactory());
1308 std::shared_ptr
<TableFactory
> plain_table_factory(NewPlainTableFactory());
1309 std::shared_ptr
<TableFactory
> dummy_factory
;
1310 options
.table_factory
.reset(NewAdaptiveTableFactory(
1311 block_based_factory
, block_based_factory
, plain_table_factory
));
1313 ASSERT_EQ("v3", Get("1000000000000foo"));
1314 ASSERT_EQ("v2", Get("0000000000000bar"));
1316 ASSERT_OK(Put("2000000000000foo", "v4"));
1317 ASSERT_OK(Put("3000000000000bar", "v5"));
1318 ASSERT_OK(dbfull()->TEST_FlushMemTable());
1319 ASSERT_EQ("v4", Get("2000000000000foo"));
1320 ASSERT_EQ("v5", Get("3000000000000bar"));
1323 ASSERT_EQ("v3", Get("1000000000000foo"));
1324 ASSERT_EQ("v2", Get("0000000000000bar"));
1325 ASSERT_EQ("v4", Get("2000000000000foo"));
1326 ASSERT_EQ("v5", Get("3000000000000bar"));
1328 options
.paranoid_checks
= false;
1329 options
.table_factory
.reset(NewBlockBasedTableFactory());
1331 ASSERT_NE("v3", Get("1000000000000foo"));
1333 options
.paranoid_checks
= false;
1334 options
.table_factory
.reset(NewPlainTableFactory());
1336 ASSERT_NE("v5", Get("3000000000000bar"));
1339 INSTANTIATE_TEST_CASE_P(PlainTableDBTest
, PlainTableDBTest
, ::testing::Bool());
1341 } // namespace ROCKSDB_NAMESPACE
1343 int main(int argc
, char** argv
) {
1344 ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
1345 ::testing::InitGoogleTest(&argc
, argv
);
1346 return RUN_ALL_TESTS();
1352 int main(int /*argc*/, char** /*argv*/) {
1353 fprintf(stderr
, "SKIPPED as plain table is not supported in ROCKSDB_LITE\n");
1357 #endif // !ROCKSDB_LITE