]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
11fdf7f2 TL |
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). | |
7c673cae FG |
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 | #ifndef ROCKSDB_LITE | |
11 | ||
12 | #include <algorithm> | |
13 | #include <set> | |
14 | ||
15 | #include "db/db_impl.h" | |
16 | #include "db/version_set.h" | |
17 | #include "db/write_batch_internal.h" | |
18 | #include "rocksdb/cache.h" | |
19 | #include "rocksdb/compaction_filter.h" | |
20 | #include "rocksdb/db.h" | |
21 | #include "rocksdb/env.h" | |
22 | #include "rocksdb/filter_policy.h" | |
23 | #include "rocksdb/slice_transform.h" | |
24 | #include "rocksdb/table.h" | |
25 | #include "table/bloom_block.h" | |
26 | #include "table/meta_blocks.h" | |
27 | #include "table/plain_table_factory.h" | |
28 | #include "table/plain_table_key_coding.h" | |
29 | #include "table/plain_table_reader.h" | |
30 | #include "table/table_builder.h" | |
31 | #include "util/filename.h" | |
32 | #include "util/hash.h" | |
33 | #include "util/logging.h" | |
34 | #include "util/mutexlock.h" | |
35 | #include "util/string_util.h" | |
36 | #include "util/testharness.h" | |
37 | #include "util/testutil.h" | |
38 | #include "utilities/merge_operators.h" | |
39 | ||
40 | using std::unique_ptr; | |
41 | ||
42 | namespace rocksdb { | |
43 | class PlainTableKeyDecoderTest : public testing::Test {}; | |
44 | ||
45 | TEST_F(PlainTableKeyDecoderTest, ReadNonMmap) { | |
46 | std::string tmp; | |
47 | Random rnd(301); | |
48 | const uint32_t kLength = 2222; | |
49 | Slice contents = test::RandomString(&rnd, kLength, &tmp); | |
50 | test::StringSource* string_source = | |
51 | new test::StringSource(contents, 0, false); | |
52 | ||
494da23a | 53 | std::unique_ptr<RandomAccessFileReader> file_reader( |
7c673cae | 54 | test::GetRandomAccessFileReader(string_source)); |
494da23a TL |
55 | std::unique_ptr<PlainTableReaderFileInfo> file_info( |
56 | new PlainTableReaderFileInfo(std::move(file_reader), EnvOptions(), | |
57 | kLength)); | |
7c673cae FG |
58 | |
59 | { | |
60 | PlainTableFileReader reader(file_info.get()); | |
61 | ||
62 | const uint32_t kReadSize = 77; | |
63 | for (uint32_t pos = 0; pos < kLength; pos += kReadSize) { | |
64 | uint32_t read_size = std::min(kLength - pos, kReadSize); | |
65 | Slice out; | |
66 | ASSERT_TRUE(reader.Read(pos, read_size, &out)); | |
67 | ASSERT_EQ(0, out.compare(tmp.substr(pos, read_size))); | |
68 | } | |
69 | ||
70 | ASSERT_LT(uint32_t(string_source->total_reads()), kLength / kReadSize / 2); | |
71 | } | |
72 | ||
73 | std::vector<std::vector<std::pair<uint32_t, uint32_t>>> reads = { | |
74 | {{600, 30}, {590, 30}, {600, 20}, {600, 40}}, | |
75 | {{800, 20}, {100, 20}, {500, 20}, {1500, 20}, {100, 20}, {80, 20}}, | |
76 | {{1000, 20}, {500, 20}, {1000, 50}}, | |
77 | {{1000, 20}, {500, 20}, {500, 20}}, | |
78 | {{1000, 20}, {500, 20}, {200, 20}, {500, 20}}, | |
79 | {{1000, 20}, {500, 20}, {200, 20}, {1000, 50}}, | |
80 | {{600, 500}, {610, 20}, {100, 20}}, | |
81 | {{500, 100}, {490, 100}, {550, 50}}, | |
82 | }; | |
83 | ||
84 | std::vector<int> num_file_reads = {2, 6, 2, 2, 4, 3, 2, 2}; | |
85 | ||
86 | for (size_t i = 0; i < reads.size(); i++) { | |
87 | string_source->set_total_reads(0); | |
88 | PlainTableFileReader reader(file_info.get()); | |
89 | for (auto p : reads[i]) { | |
90 | Slice out; | |
91 | ASSERT_TRUE(reader.Read(p.first, p.second, &out)); | |
92 | ASSERT_EQ(0, out.compare(tmp.substr(p.first, p.second))); | |
93 | } | |
94 | ASSERT_EQ(num_file_reads[i], string_source->total_reads()); | |
95 | } | |
96 | } | |
97 | ||
98 | class PlainTableDBTest : public testing::Test, | |
99 | public testing::WithParamInterface<bool> { | |
100 | protected: | |
101 | private: | |
102 | std::string dbname_; | |
103 | Env* env_; | |
104 | DB* db_; | |
105 | ||
106 | bool mmap_mode_; | |
107 | Options last_options_; | |
108 | ||
109 | public: | |
110 | PlainTableDBTest() : env_(Env::Default()) {} | |
111 | ||
494da23a | 112 | ~PlainTableDBTest() override { |
7c673cae FG |
113 | delete db_; |
114 | EXPECT_OK(DestroyDB(dbname_, Options())); | |
115 | } | |
116 | ||
117 | void SetUp() override { | |
118 | mmap_mode_ = GetParam(); | |
11fdf7f2 | 119 | dbname_ = test::PerThreadDBPath("plain_table_db_test"); |
7c673cae FG |
120 | EXPECT_OK(DestroyDB(dbname_, Options())); |
121 | db_ = nullptr; | |
122 | Reopen(); | |
123 | } | |
124 | ||
125 | // Return the current option configuration. | |
126 | Options CurrentOptions() { | |
127 | Options options; | |
128 | ||
129 | PlainTableOptions plain_table_options; | |
130 | plain_table_options.user_key_len = 0; | |
131 | plain_table_options.bloom_bits_per_key = 2; | |
132 | plain_table_options.hash_table_ratio = 0.8; | |
133 | plain_table_options.index_sparseness = 3; | |
134 | plain_table_options.huge_page_tlb_size = 0; | |
135 | plain_table_options.encoding_type = kPrefix; | |
136 | plain_table_options.full_scan_mode = false; | |
137 | plain_table_options.store_index_in_file = false; | |
138 | ||
139 | options.table_factory.reset(NewPlainTableFactory(plain_table_options)); | |
140 | options.memtable_factory.reset(NewHashLinkListRepFactory(4, 0, 3, true)); | |
141 | ||
142 | options.prefix_extractor.reset(NewFixedPrefixTransform(8)); | |
143 | options.allow_mmap_reads = mmap_mode_; | |
144 | options.allow_concurrent_memtable_write = false; | |
145 | return options; | |
146 | } | |
147 | ||
148 | DBImpl* dbfull() { | |
149 | return reinterpret_cast<DBImpl*>(db_); | |
150 | } | |
151 | ||
152 | void Reopen(Options* options = nullptr) { | |
153 | ASSERT_OK(TryReopen(options)); | |
154 | } | |
155 | ||
156 | void Close() { | |
157 | delete db_; | |
158 | db_ = nullptr; | |
159 | } | |
160 | ||
494da23a TL |
161 | bool mmap_mode() const { return mmap_mode_; } |
162 | ||
7c673cae FG |
163 | void DestroyAndReopen(Options* options = nullptr) { |
164 | //Destroy using last options | |
165 | Destroy(&last_options_); | |
166 | ASSERT_OK(TryReopen(options)); | |
167 | } | |
168 | ||
169 | void Destroy(Options* options) { | |
170 | delete db_; | |
171 | db_ = nullptr; | |
172 | ASSERT_OK(DestroyDB(dbname_, *options)); | |
173 | } | |
174 | ||
175 | Status PureReopen(Options* options, DB** db) { | |
176 | return DB::Open(*options, dbname_, db); | |
177 | } | |
178 | ||
494da23a TL |
179 | Status ReopenForReadOnly(Options* options) { |
180 | delete db_; | |
181 | db_ = nullptr; | |
182 | return DB::OpenForReadOnly(*options, dbname_, &db_); | |
183 | } | |
184 | ||
7c673cae FG |
185 | Status TryReopen(Options* options = nullptr) { |
186 | delete db_; | |
187 | db_ = nullptr; | |
188 | Options opts; | |
189 | if (options != nullptr) { | |
190 | opts = *options; | |
191 | } else { | |
192 | opts = CurrentOptions(); | |
193 | opts.create_if_missing = true; | |
194 | } | |
195 | last_options_ = opts; | |
196 | ||
197 | return DB::Open(opts, dbname_, &db_); | |
198 | } | |
199 | ||
200 | Status Put(const Slice& k, const Slice& v) { | |
201 | return db_->Put(WriteOptions(), k, v); | |
202 | } | |
203 | ||
204 | Status Delete(const std::string& k) { | |
205 | return db_->Delete(WriteOptions(), k); | |
206 | } | |
207 | ||
208 | std::string Get(const std::string& k, const Snapshot* snapshot = nullptr) { | |
209 | ReadOptions options; | |
210 | options.snapshot = snapshot; | |
211 | std::string result; | |
212 | Status s = db_->Get(options, k, &result); | |
213 | if (s.IsNotFound()) { | |
214 | result = "NOT_FOUND"; | |
215 | } else if (!s.ok()) { | |
216 | result = s.ToString(); | |
217 | } | |
218 | return result; | |
219 | } | |
220 | ||
221 | ||
222 | int NumTableFilesAtLevel(int level) { | |
223 | std::string property; | |
224 | EXPECT_TRUE(db_->GetProperty( | |
225 | "rocksdb.num-files-at-level" + NumberToString(level), &property)); | |
226 | return atoi(property.c_str()); | |
227 | } | |
228 | ||
229 | // Return spread of files per level | |
230 | std::string FilesPerLevel() { | |
231 | std::string result; | |
232 | size_t last_non_zero_offset = 0; | |
233 | for (int level = 0; level < db_->NumberLevels(); level++) { | |
234 | int f = NumTableFilesAtLevel(level); | |
235 | char buf[100]; | |
236 | snprintf(buf, sizeof(buf), "%s%d", (level ? "," : ""), f); | |
237 | result += buf; | |
238 | if (f > 0) { | |
239 | last_non_zero_offset = result.size(); | |
240 | } | |
241 | } | |
242 | result.resize(last_non_zero_offset); | |
243 | return result; | |
244 | } | |
245 | ||
246 | std::string IterStatus(Iterator* iter) { | |
247 | std::string result; | |
248 | if (iter->Valid()) { | |
249 | result = iter->key().ToString() + "->" + iter->value().ToString(); | |
250 | } else { | |
251 | result = "(invalid)"; | |
252 | } | |
253 | return result; | |
254 | } | |
255 | }; | |
256 | ||
257 | TEST_P(PlainTableDBTest, Empty) { | |
258 | ASSERT_TRUE(dbfull() != nullptr); | |
259 | ASSERT_EQ("NOT_FOUND", Get("0000000000000foo")); | |
260 | } | |
261 | ||
262 | extern const uint64_t kPlainTableMagicNumber; | |
263 | ||
264 | class TestPlainTableReader : public PlainTableReader { | |
265 | public: | |
266 | TestPlainTableReader(const EnvOptions& env_options, | |
267 | const InternalKeyComparator& icomparator, | |
268 | EncodingType encoding_type, uint64_t file_size, | |
269 | int bloom_bits_per_key, double hash_table_ratio, | |
270 | size_t index_sparseness, | |
271 | const TableProperties* table_properties, | |
494da23a | 272 | std::unique_ptr<RandomAccessFileReader>&& file, |
7c673cae | 273 | const ImmutableCFOptions& ioptions, |
11fdf7f2 | 274 | const SliceTransform* prefix_extractor, |
7c673cae FG |
275 | bool* expect_bloom_not_match, bool store_index_in_file, |
276 | uint32_t column_family_id, | |
277 | const std::string& column_family_name) | |
278 | : PlainTableReader(ioptions, std::move(file), env_options, icomparator, | |
11fdf7f2 TL |
279 | encoding_type, file_size, table_properties, |
280 | prefix_extractor), | |
7c673cae FG |
281 | expect_bloom_not_match_(expect_bloom_not_match) { |
282 | Status s = MmapDataIfNeeded(); | |
283 | EXPECT_TRUE(s.ok()); | |
284 | ||
285 | s = PopulateIndex(const_cast<TableProperties*>(table_properties), | |
286 | bloom_bits_per_key, hash_table_ratio, index_sparseness, | |
287 | 2 * 1024 * 1024); | |
288 | EXPECT_TRUE(s.ok()); | |
289 | ||
290 | TableProperties* props = const_cast<TableProperties*>(table_properties); | |
291 | EXPECT_EQ(column_family_id, static_cast<uint32_t>(props->column_family_id)); | |
292 | EXPECT_EQ(column_family_name, props->column_family_name); | |
293 | if (store_index_in_file) { | |
294 | auto bloom_version_ptr = props->user_collected_properties.find( | |
295 | PlainTablePropertyNames::kBloomVersion); | |
296 | EXPECT_TRUE(bloom_version_ptr != props->user_collected_properties.end()); | |
297 | EXPECT_EQ(bloom_version_ptr->second, std::string("1")); | |
298 | if (ioptions.bloom_locality > 0) { | |
299 | auto num_blocks_ptr = props->user_collected_properties.find( | |
300 | PlainTablePropertyNames::kNumBloomBlocks); | |
301 | EXPECT_TRUE(num_blocks_ptr != props->user_collected_properties.end()); | |
302 | } | |
303 | } | |
304 | } | |
305 | ||
494da23a | 306 | ~TestPlainTableReader() override {} |
7c673cae FG |
307 | |
308 | private: | |
494da23a | 309 | bool MatchBloom(uint32_t hash) const override { |
7c673cae FG |
310 | bool ret = PlainTableReader::MatchBloom(hash); |
311 | if (*expect_bloom_not_match_) { | |
312 | EXPECT_TRUE(!ret); | |
313 | } else { | |
314 | EXPECT_TRUE(ret); | |
315 | } | |
316 | return ret; | |
317 | } | |
318 | bool* expect_bloom_not_match_; | |
319 | }; | |
320 | ||
321 | extern const uint64_t kPlainTableMagicNumber; | |
322 | class TestPlainTableFactory : public PlainTableFactory { | |
323 | public: | |
324 | explicit TestPlainTableFactory(bool* expect_bloom_not_match, | |
325 | const PlainTableOptions& options, | |
326 | uint32_t column_family_id, | |
327 | std::string column_family_name) | |
328 | : PlainTableFactory(options), | |
329 | bloom_bits_per_key_(options.bloom_bits_per_key), | |
330 | hash_table_ratio_(options.hash_table_ratio), | |
331 | index_sparseness_(options.index_sparseness), | |
332 | store_index_in_file_(options.store_index_in_file), | |
333 | expect_bloom_not_match_(expect_bloom_not_match), | |
334 | column_family_id_(column_family_id), | |
335 | column_family_name_(std::move(column_family_name)) {} | |
336 | ||
337 | Status NewTableReader( | |
338 | const TableReaderOptions& table_reader_options, | |
494da23a TL |
339 | std::unique_ptr<RandomAccessFileReader>&& file, uint64_t file_size, |
340 | std::unique_ptr<TableReader>* table, | |
11fdf7f2 | 341 | bool /*prefetch_index_and_filter_in_cache*/) const override { |
7c673cae FG |
342 | TableProperties* props = nullptr; |
343 | auto s = | |
344 | ReadTableProperties(file.get(), file_size, kPlainTableMagicNumber, | |
11fdf7f2 TL |
345 | table_reader_options.ioptions, &props, |
346 | true /* compression_type_missing */); | |
7c673cae FG |
347 | EXPECT_TRUE(s.ok()); |
348 | ||
349 | if (store_index_in_file_) { | |
350 | BlockHandle bloom_block_handle; | |
351 | s = FindMetaBlock(file.get(), file_size, kPlainTableMagicNumber, | |
352 | table_reader_options.ioptions, | |
11fdf7f2 TL |
353 | BloomBlockBuilder::kBloomBlock, &bloom_block_handle, |
354 | /* compression_type_missing */ true); | |
7c673cae FG |
355 | EXPECT_TRUE(s.ok()); |
356 | ||
357 | BlockHandle index_block_handle; | |
358 | s = FindMetaBlock(file.get(), file_size, kPlainTableMagicNumber, | |
359 | table_reader_options.ioptions, | |
360 | PlainTableIndexBuilder::kPlainTableIndexBlock, | |
11fdf7f2 | 361 | &index_block_handle, /* compression_type_missing */ true); |
7c673cae FG |
362 | EXPECT_TRUE(s.ok()); |
363 | } | |
364 | ||
365 | auto& user_props = props->user_collected_properties; | |
366 | auto encoding_type_prop = | |
367 | user_props.find(PlainTablePropertyNames::kEncodingType); | |
368 | assert(encoding_type_prop != user_props.end()); | |
369 | EncodingType encoding_type = static_cast<EncodingType>( | |
370 | DecodeFixed32(encoding_type_prop->second.c_str())); | |
371 | ||
372 | std::unique_ptr<PlainTableReader> new_reader(new TestPlainTableReader( | |
373 | table_reader_options.env_options, | |
374 | table_reader_options.internal_comparator, encoding_type, file_size, | |
375 | bloom_bits_per_key_, hash_table_ratio_, index_sparseness_, props, | |
11fdf7f2 TL |
376 | std::move(file), table_reader_options.ioptions, |
377 | table_reader_options.prefix_extractor, expect_bloom_not_match_, | |
7c673cae FG |
378 | store_index_in_file_, column_family_id_, column_family_name_)); |
379 | ||
380 | *table = std::move(new_reader); | |
381 | return s; | |
382 | } | |
383 | ||
384 | private: | |
385 | int bloom_bits_per_key_; | |
386 | double hash_table_ratio_; | |
387 | size_t index_sparseness_; | |
388 | bool store_index_in_file_; | |
389 | bool* expect_bloom_not_match_; | |
390 | const uint32_t column_family_id_; | |
391 | const std::string column_family_name_; | |
392 | }; | |
393 | ||
394 | TEST_P(PlainTableDBTest, Flush) { | |
395 | for (size_t huge_page_tlb_size = 0; huge_page_tlb_size <= 2 * 1024 * 1024; | |
396 | huge_page_tlb_size += 2 * 1024 * 1024) { | |
397 | for (EncodingType encoding_type : {kPlain, kPrefix}) { | |
398 | for (int bloom_bits = 0; bloom_bits <= 117; bloom_bits += 117) { | |
399 | for (int total_order = 0; total_order <= 1; total_order++) { | |
400 | for (int store_index_in_file = 0; store_index_in_file <= 1; | |
401 | ++store_index_in_file) { | |
402 | Options options = CurrentOptions(); | |
403 | options.create_if_missing = true; | |
404 | // Set only one bucket to force bucket conflict. | |
405 | // Test index interval for the same prefix to be 1, 2 and 4 | |
406 | if (total_order) { | |
407 | options.prefix_extractor.reset(); | |
408 | ||
409 | PlainTableOptions plain_table_options; | |
410 | plain_table_options.user_key_len = 0; | |
411 | plain_table_options.bloom_bits_per_key = bloom_bits; | |
412 | plain_table_options.hash_table_ratio = 0; | |
413 | plain_table_options.index_sparseness = 2; | |
414 | plain_table_options.huge_page_tlb_size = huge_page_tlb_size; | |
415 | plain_table_options.encoding_type = encoding_type; | |
416 | plain_table_options.full_scan_mode = false; | |
417 | plain_table_options.store_index_in_file = store_index_in_file; | |
418 | ||
419 | options.table_factory.reset( | |
420 | NewPlainTableFactory(plain_table_options)); | |
421 | } else { | |
422 | PlainTableOptions plain_table_options; | |
423 | plain_table_options.user_key_len = 0; | |
424 | plain_table_options.bloom_bits_per_key = bloom_bits; | |
425 | plain_table_options.hash_table_ratio = 0.75; | |
426 | plain_table_options.index_sparseness = 16; | |
427 | plain_table_options.huge_page_tlb_size = huge_page_tlb_size; | |
428 | plain_table_options.encoding_type = encoding_type; | |
429 | plain_table_options.full_scan_mode = false; | |
430 | plain_table_options.store_index_in_file = store_index_in_file; | |
431 | ||
432 | options.table_factory.reset( | |
433 | NewPlainTableFactory(plain_table_options)); | |
434 | } | |
435 | DestroyAndReopen(&options); | |
436 | uint64_t int_num; | |
437 | ASSERT_TRUE(dbfull()->GetIntProperty( | |
438 | "rocksdb.estimate-table-readers-mem", &int_num)); | |
439 | ASSERT_EQ(int_num, 0U); | |
440 | ||
441 | ASSERT_OK(Put("1000000000000foo", "v1")); | |
442 | ASSERT_OK(Put("0000000000000bar", "v2")); | |
443 | ASSERT_OK(Put("1000000000000foo", "v3")); | |
444 | dbfull()->TEST_FlushMemTable(); | |
445 | ||
446 | ASSERT_TRUE(dbfull()->GetIntProperty( | |
447 | "rocksdb.estimate-table-readers-mem", &int_num)); | |
448 | ASSERT_GT(int_num, 0U); | |
449 | ||
450 | TablePropertiesCollection ptc; | |
451 | reinterpret_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc); | |
452 | ASSERT_EQ(1U, ptc.size()); | |
453 | auto row = ptc.begin(); | |
454 | auto tp = row->second; | |
455 | ||
456 | if (!store_index_in_file) { | |
457 | ASSERT_EQ(total_order ? "4" : "12", | |
458 | (tp->user_collected_properties) | |
459 | .at("plain_table_hash_table_size")); | |
460 | ASSERT_EQ("0", (tp->user_collected_properties) | |
461 | .at("plain_table_sub_index_size")); | |
462 | } else { | |
463 | ASSERT_EQ("0", (tp->user_collected_properties) | |
464 | .at("plain_table_hash_table_size")); | |
465 | ASSERT_EQ("0", (tp->user_collected_properties) | |
466 | .at("plain_table_sub_index_size")); | |
467 | } | |
468 | ASSERT_EQ("v3", Get("1000000000000foo")); | |
469 | ASSERT_EQ("v2", Get("0000000000000bar")); | |
470 | } | |
471 | } | |
472 | } | |
473 | } | |
474 | } | |
475 | } | |
476 | ||
477 | TEST_P(PlainTableDBTest, Flush2) { | |
478 | for (size_t huge_page_tlb_size = 0; huge_page_tlb_size <= 2 * 1024 * 1024; | |
479 | huge_page_tlb_size += 2 * 1024 * 1024) { | |
480 | for (EncodingType encoding_type : {kPlain, kPrefix}) { | |
481 | for (int bloom_bits = 0; bloom_bits <= 117; bloom_bits += 117) { | |
482 | for (int total_order = 0; total_order <= 1; total_order++) { | |
483 | for (int store_index_in_file = 0; store_index_in_file <= 1; | |
484 | ++store_index_in_file) { | |
485 | if (encoding_type == kPrefix && total_order) { | |
486 | continue; | |
487 | } | |
488 | if (!bloom_bits && store_index_in_file) { | |
489 | continue; | |
490 | } | |
491 | if (total_order && store_index_in_file) { | |
492 | continue; | |
493 | } | |
494 | bool expect_bloom_not_match = false; | |
495 | Options options = CurrentOptions(); | |
496 | options.create_if_missing = true; | |
497 | // Set only one bucket to force bucket conflict. | |
498 | // Test index interval for the same prefix to be 1, 2 and 4 | |
499 | PlainTableOptions plain_table_options; | |
500 | if (total_order) { | |
501 | options.prefix_extractor = nullptr; | |
502 | plain_table_options.hash_table_ratio = 0; | |
503 | plain_table_options.index_sparseness = 2; | |
504 | } else { | |
505 | plain_table_options.hash_table_ratio = 0.75; | |
506 | plain_table_options.index_sparseness = 16; | |
507 | } | |
508 | plain_table_options.user_key_len = kPlainTableVariableLength; | |
509 | plain_table_options.bloom_bits_per_key = bloom_bits; | |
510 | plain_table_options.huge_page_tlb_size = huge_page_tlb_size; | |
511 | plain_table_options.encoding_type = encoding_type; | |
512 | plain_table_options.store_index_in_file = store_index_in_file; | |
513 | options.table_factory.reset(new TestPlainTableFactory( | |
514 | &expect_bloom_not_match, plain_table_options, | |
515 | 0 /* column_family_id */, kDefaultColumnFamilyName)); | |
516 | ||
517 | DestroyAndReopen(&options); | |
518 | ASSERT_OK(Put("0000000000000bar", "b")); | |
519 | ASSERT_OK(Put("1000000000000foo", "v1")); | |
520 | dbfull()->TEST_FlushMemTable(); | |
521 | ||
522 | ASSERT_OK(Put("1000000000000foo", "v2")); | |
523 | dbfull()->TEST_FlushMemTable(); | |
524 | ASSERT_EQ("v2", Get("1000000000000foo")); | |
525 | ||
526 | ASSERT_OK(Put("0000000000000eee", "v3")); | |
527 | dbfull()->TEST_FlushMemTable(); | |
528 | ASSERT_EQ("v3", Get("0000000000000eee")); | |
529 | ||
530 | ASSERT_OK(Delete("0000000000000bar")); | |
531 | dbfull()->TEST_FlushMemTable(); | |
532 | ASSERT_EQ("NOT_FOUND", Get("0000000000000bar")); | |
533 | ||
534 | ASSERT_OK(Put("0000000000000eee", "v5")); | |
535 | ASSERT_OK(Put("9000000000000eee", "v5")); | |
536 | dbfull()->TEST_FlushMemTable(); | |
537 | ASSERT_EQ("v5", Get("0000000000000eee")); | |
538 | ||
539 | // Test Bloom Filter | |
540 | if (bloom_bits > 0) { | |
541 | // Neither key nor value should exist. | |
542 | expect_bloom_not_match = true; | |
543 | ASSERT_EQ("NOT_FOUND", Get("5_not00000000bar")); | |
544 | // Key doesn't exist any more but prefix exists. | |
545 | if (total_order) { | |
546 | ASSERT_EQ("NOT_FOUND", Get("1000000000000not")); | |
547 | ASSERT_EQ("NOT_FOUND", Get("0000000000000not")); | |
548 | } | |
549 | expect_bloom_not_match = false; | |
550 | } | |
551 | } | |
552 | } | |
553 | } | |
554 | } | |
555 | } | |
556 | } | |
557 | ||
494da23a TL |
558 | TEST_P(PlainTableDBTest, Immortal) { |
559 | for (EncodingType encoding_type : {kPlain, kPrefix}) { | |
560 | Options options = CurrentOptions(); | |
561 | options.create_if_missing = true; | |
562 | options.max_open_files = -1; | |
563 | // Set only one bucket to force bucket conflict. | |
564 | // Test index interval for the same prefix to be 1, 2 and 4 | |
565 | PlainTableOptions plain_table_options; | |
566 | plain_table_options.hash_table_ratio = 0.75; | |
567 | plain_table_options.index_sparseness = 16; | |
568 | plain_table_options.user_key_len = kPlainTableVariableLength; | |
569 | plain_table_options.bloom_bits_per_key = 10; | |
570 | plain_table_options.encoding_type = encoding_type; | |
571 | options.table_factory.reset(NewPlainTableFactory(plain_table_options)); | |
572 | ||
573 | DestroyAndReopen(&options); | |
574 | ASSERT_OK(Put("0000000000000bar", "b")); | |
575 | ASSERT_OK(Put("1000000000000foo", "v1")); | |
576 | dbfull()->TEST_FlushMemTable(); | |
577 | ||
578 | int copied = 0; | |
579 | rocksdb::SyncPoint::GetInstance()->SetCallBack( | |
580 | "GetContext::SaveValue::PinSelf", [&](void* /*arg*/) { copied++; }); | |
581 | rocksdb::SyncPoint::GetInstance()->EnableProcessing(); | |
582 | ASSERT_EQ("b", Get("0000000000000bar")); | |
583 | ASSERT_EQ("v1", Get("1000000000000foo")); | |
584 | ASSERT_EQ(2, copied); | |
585 | copied = 0; | |
586 | ||
587 | Close(); | |
588 | ASSERT_OK(ReopenForReadOnly(&options)); | |
589 | ||
590 | ASSERT_EQ("b", Get("0000000000000bar")); | |
591 | ASSERT_EQ("v1", Get("1000000000000foo")); | |
592 | ASSERT_EQ("NOT_FOUND", Get("1000000000000bar")); | |
593 | if (mmap_mode()) { | |
594 | ASSERT_EQ(0, copied); | |
595 | } else { | |
596 | ASSERT_EQ(2, copied); | |
597 | } | |
598 | rocksdb::SyncPoint::GetInstance()->DisableProcessing(); | |
599 | } | |
600 | } | |
601 | ||
7c673cae FG |
602 | TEST_P(PlainTableDBTest, Iterator) { |
603 | for (size_t huge_page_tlb_size = 0; huge_page_tlb_size <= 2 * 1024 * 1024; | |
604 | huge_page_tlb_size += 2 * 1024 * 1024) { | |
605 | for (EncodingType encoding_type : {kPlain, kPrefix}) { | |
606 | for (int bloom_bits = 0; bloom_bits <= 117; bloom_bits += 117) { | |
607 | for (int total_order = 0; total_order <= 1; total_order++) { | |
608 | if (encoding_type == kPrefix && total_order == 1) { | |
609 | continue; | |
610 | } | |
611 | bool expect_bloom_not_match = false; | |
612 | Options options = CurrentOptions(); | |
613 | options.create_if_missing = true; | |
614 | // Set only one bucket to force bucket conflict. | |
615 | // Test index interval for the same prefix to be 1, 2 and 4 | |
616 | if (total_order) { | |
617 | options.prefix_extractor = nullptr; | |
618 | ||
619 | PlainTableOptions plain_table_options; | |
620 | plain_table_options.user_key_len = 16; | |
621 | plain_table_options.bloom_bits_per_key = bloom_bits; | |
622 | plain_table_options.hash_table_ratio = 0; | |
623 | plain_table_options.index_sparseness = 2; | |
624 | plain_table_options.huge_page_tlb_size = huge_page_tlb_size; | |
625 | plain_table_options.encoding_type = encoding_type; | |
626 | ||
627 | options.table_factory.reset(new TestPlainTableFactory( | |
628 | &expect_bloom_not_match, plain_table_options, | |
629 | 0 /* column_family_id */, kDefaultColumnFamilyName)); | |
630 | } else { | |
631 | PlainTableOptions plain_table_options; | |
632 | plain_table_options.user_key_len = 16; | |
633 | plain_table_options.bloom_bits_per_key = bloom_bits; | |
634 | plain_table_options.hash_table_ratio = 0.75; | |
635 | plain_table_options.index_sparseness = 16; | |
636 | plain_table_options.huge_page_tlb_size = huge_page_tlb_size; | |
637 | plain_table_options.encoding_type = encoding_type; | |
638 | ||
639 | options.table_factory.reset(new TestPlainTableFactory( | |
640 | &expect_bloom_not_match, plain_table_options, | |
641 | 0 /* column_family_id */, kDefaultColumnFamilyName)); | |
642 | } | |
643 | DestroyAndReopen(&options); | |
644 | ||
645 | ASSERT_OK(Put("1000000000foo002", "v_2")); | |
646 | ASSERT_OK(Put("0000000000000bar", "random")); | |
647 | ASSERT_OK(Put("1000000000foo001", "v1")); | |
648 | ASSERT_OK(Put("3000000000000bar", "bar_v")); | |
649 | ASSERT_OK(Put("1000000000foo003", "v__3")); | |
650 | ASSERT_OK(Put("1000000000foo004", "v__4")); | |
651 | ASSERT_OK(Put("1000000000foo005", "v__5")); | |
652 | ASSERT_OK(Put("1000000000foo007", "v__7")); | |
653 | ASSERT_OK(Put("1000000000foo008", "v__8")); | |
654 | dbfull()->TEST_FlushMemTable(); | |
655 | ASSERT_EQ("v1", Get("1000000000foo001")); | |
656 | ASSERT_EQ("v__3", Get("1000000000foo003")); | |
657 | Iterator* iter = dbfull()->NewIterator(ReadOptions()); | |
658 | iter->Seek("1000000000foo000"); | |
659 | ASSERT_TRUE(iter->Valid()); | |
660 | ASSERT_EQ("1000000000foo001", iter->key().ToString()); | |
661 | ASSERT_EQ("v1", iter->value().ToString()); | |
662 | ||
663 | iter->Next(); | |
664 | ASSERT_TRUE(iter->Valid()); | |
665 | ASSERT_EQ("1000000000foo002", iter->key().ToString()); | |
666 | ASSERT_EQ("v_2", iter->value().ToString()); | |
667 | ||
668 | iter->Next(); | |
669 | ASSERT_TRUE(iter->Valid()); | |
670 | ASSERT_EQ("1000000000foo003", iter->key().ToString()); | |
671 | ASSERT_EQ("v__3", iter->value().ToString()); | |
672 | ||
673 | iter->Next(); | |
674 | ASSERT_TRUE(iter->Valid()); | |
675 | ASSERT_EQ("1000000000foo004", iter->key().ToString()); | |
676 | ASSERT_EQ("v__4", iter->value().ToString()); | |
677 | ||
678 | iter->Seek("3000000000000bar"); | |
679 | ASSERT_TRUE(iter->Valid()); | |
680 | ASSERT_EQ("3000000000000bar", iter->key().ToString()); | |
681 | ASSERT_EQ("bar_v", iter->value().ToString()); | |
682 | ||
683 | iter->Seek("1000000000foo000"); | |
684 | ASSERT_TRUE(iter->Valid()); | |
685 | ASSERT_EQ("1000000000foo001", iter->key().ToString()); | |
686 | ASSERT_EQ("v1", iter->value().ToString()); | |
687 | ||
688 | iter->Seek("1000000000foo005"); | |
689 | ASSERT_TRUE(iter->Valid()); | |
690 | ASSERT_EQ("1000000000foo005", iter->key().ToString()); | |
691 | ASSERT_EQ("v__5", iter->value().ToString()); | |
692 | ||
693 | iter->Seek("1000000000foo006"); | |
694 | ASSERT_TRUE(iter->Valid()); | |
695 | ASSERT_EQ("1000000000foo007", iter->key().ToString()); | |
696 | ASSERT_EQ("v__7", iter->value().ToString()); | |
697 | ||
698 | iter->Seek("1000000000foo008"); | |
699 | ASSERT_TRUE(iter->Valid()); | |
700 | ASSERT_EQ("1000000000foo008", iter->key().ToString()); | |
701 | ASSERT_EQ("v__8", iter->value().ToString()); | |
702 | ||
703 | if (total_order == 0) { | |
704 | iter->Seek("1000000000foo009"); | |
705 | ASSERT_TRUE(iter->Valid()); | |
706 | ASSERT_EQ("3000000000000bar", iter->key().ToString()); | |
707 | } | |
708 | ||
709 | // Test Bloom Filter | |
710 | if (bloom_bits > 0) { | |
711 | if (!total_order) { | |
712 | // Neither key nor value should exist. | |
713 | expect_bloom_not_match = true; | |
714 | iter->Seek("2not000000000bar"); | |
715 | ASSERT_TRUE(!iter->Valid()); | |
716 | ASSERT_EQ("NOT_FOUND", Get("2not000000000bar")); | |
717 | expect_bloom_not_match = false; | |
718 | } else { | |
719 | expect_bloom_not_match = true; | |
720 | ASSERT_EQ("NOT_FOUND", Get("2not000000000bar")); | |
721 | expect_bloom_not_match = false; | |
722 | } | |
723 | } | |
724 | ||
725 | delete iter; | |
726 | } | |
727 | } | |
728 | } | |
729 | } | |
730 | } | |
731 | ||
732 | namespace { | |
733 | std::string MakeLongKey(size_t length, char c) { | |
734 | return std::string(length, c); | |
735 | } | |
736 | } // namespace | |
737 | ||
738 | TEST_P(PlainTableDBTest, IteratorLargeKeys) { | |
739 | Options options = CurrentOptions(); | |
740 | ||
741 | PlainTableOptions plain_table_options; | |
742 | plain_table_options.user_key_len = 0; | |
743 | plain_table_options.bloom_bits_per_key = 0; | |
744 | plain_table_options.hash_table_ratio = 0; | |
745 | ||
746 | options.table_factory.reset(NewPlainTableFactory(plain_table_options)); | |
747 | options.create_if_missing = true; | |
748 | options.prefix_extractor.reset(); | |
749 | DestroyAndReopen(&options); | |
750 | ||
751 | std::string key_list[] = { | |
752 | MakeLongKey(30, '0'), | |
753 | MakeLongKey(16, '1'), | |
754 | MakeLongKey(32, '2'), | |
755 | MakeLongKey(60, '3'), | |
756 | MakeLongKey(90, '4'), | |
757 | MakeLongKey(50, '5'), | |
758 | MakeLongKey(26, '6') | |
759 | }; | |
760 | ||
761 | for (size_t i = 0; i < 7; i++) { | |
762 | ASSERT_OK(Put(key_list[i], ToString(i))); | |
763 | } | |
764 | ||
765 | dbfull()->TEST_FlushMemTable(); | |
766 | ||
767 | Iterator* iter = dbfull()->NewIterator(ReadOptions()); | |
768 | iter->Seek(key_list[0]); | |
769 | ||
770 | for (size_t i = 0; i < 7; i++) { | |
771 | ASSERT_TRUE(iter->Valid()); | |
772 | ASSERT_EQ(key_list[i], iter->key().ToString()); | |
773 | ASSERT_EQ(ToString(i), iter->value().ToString()); | |
774 | iter->Next(); | |
775 | } | |
776 | ||
777 | ASSERT_TRUE(!iter->Valid()); | |
778 | ||
779 | delete iter; | |
780 | } | |
781 | ||
782 | namespace { | |
783 | std::string MakeLongKeyWithPrefix(size_t length, char c) { | |
784 | return "00000000" + std::string(length - 8, c); | |
785 | } | |
786 | } // namespace | |
787 | ||
788 | TEST_P(PlainTableDBTest, IteratorLargeKeysWithPrefix) { | |
789 | Options options = CurrentOptions(); | |
790 | ||
791 | PlainTableOptions plain_table_options; | |
792 | plain_table_options.user_key_len = 16; | |
793 | plain_table_options.bloom_bits_per_key = 0; | |
794 | plain_table_options.hash_table_ratio = 0.8; | |
795 | plain_table_options.index_sparseness = 3; | |
796 | plain_table_options.huge_page_tlb_size = 0; | |
797 | plain_table_options.encoding_type = kPrefix; | |
798 | ||
799 | options.table_factory.reset(NewPlainTableFactory(plain_table_options)); | |
800 | options.create_if_missing = true; | |
801 | DestroyAndReopen(&options); | |
802 | ||
803 | std::string key_list[] = { | |
804 | MakeLongKeyWithPrefix(30, '0'), MakeLongKeyWithPrefix(16, '1'), | |
805 | MakeLongKeyWithPrefix(32, '2'), MakeLongKeyWithPrefix(60, '3'), | |
806 | MakeLongKeyWithPrefix(90, '4'), MakeLongKeyWithPrefix(50, '5'), | |
807 | MakeLongKeyWithPrefix(26, '6')}; | |
808 | ||
809 | for (size_t i = 0; i < 7; i++) { | |
810 | ASSERT_OK(Put(key_list[i], ToString(i))); | |
811 | } | |
812 | ||
813 | dbfull()->TEST_FlushMemTable(); | |
814 | ||
815 | Iterator* iter = dbfull()->NewIterator(ReadOptions()); | |
816 | iter->Seek(key_list[0]); | |
817 | ||
818 | for (size_t i = 0; i < 7; i++) { | |
819 | ASSERT_TRUE(iter->Valid()); | |
820 | ASSERT_EQ(key_list[i], iter->key().ToString()); | |
821 | ASSERT_EQ(ToString(i), iter->value().ToString()); | |
822 | iter->Next(); | |
823 | } | |
824 | ||
825 | ASSERT_TRUE(!iter->Valid()); | |
826 | ||
827 | delete iter; | |
828 | } | |
829 | ||
830 | TEST_P(PlainTableDBTest, IteratorReverseSuffixComparator) { | |
831 | Options options = CurrentOptions(); | |
832 | options.create_if_missing = true; | |
833 | // Set only one bucket to force bucket conflict. | |
834 | // Test index interval for the same prefix to be 1, 2 and 4 | |
835 | test::SimpleSuffixReverseComparator comp; | |
836 | options.comparator = ∁ | |
837 | DestroyAndReopen(&options); | |
838 | ||
839 | ASSERT_OK(Put("1000000000foo002", "v_2")); | |
840 | ASSERT_OK(Put("0000000000000bar", "random")); | |
841 | ASSERT_OK(Put("1000000000foo001", "v1")); | |
842 | ASSERT_OK(Put("3000000000000bar", "bar_v")); | |
843 | ASSERT_OK(Put("1000000000foo003", "v__3")); | |
844 | ASSERT_OK(Put("1000000000foo004", "v__4")); | |
845 | ASSERT_OK(Put("1000000000foo005", "v__5")); | |
846 | ASSERT_OK(Put("1000000000foo007", "v__7")); | |
847 | ASSERT_OK(Put("1000000000foo008", "v__8")); | |
848 | dbfull()->TEST_FlushMemTable(); | |
849 | ASSERT_EQ("v1", Get("1000000000foo001")); | |
850 | ASSERT_EQ("v__3", Get("1000000000foo003")); | |
851 | Iterator* iter = dbfull()->NewIterator(ReadOptions()); | |
852 | iter->Seek("1000000000foo009"); | |
853 | ASSERT_TRUE(iter->Valid()); | |
854 | ASSERT_EQ("1000000000foo008", iter->key().ToString()); | |
855 | ASSERT_EQ("v__8", iter->value().ToString()); | |
856 | ||
857 | iter->Next(); | |
858 | ASSERT_TRUE(iter->Valid()); | |
859 | ASSERT_EQ("1000000000foo007", iter->key().ToString()); | |
860 | ASSERT_EQ("v__7", iter->value().ToString()); | |
861 | ||
862 | iter->Next(); | |
863 | ASSERT_TRUE(iter->Valid()); | |
864 | ASSERT_EQ("1000000000foo005", iter->key().ToString()); | |
865 | ASSERT_EQ("v__5", iter->value().ToString()); | |
866 | ||
867 | iter->Next(); | |
868 | ASSERT_TRUE(iter->Valid()); | |
869 | ASSERT_EQ("1000000000foo004", iter->key().ToString()); | |
870 | ASSERT_EQ("v__4", iter->value().ToString()); | |
871 | ||
872 | iter->Seek("3000000000000bar"); | |
873 | ASSERT_TRUE(iter->Valid()); | |
874 | ASSERT_EQ("3000000000000bar", iter->key().ToString()); | |
875 | ASSERT_EQ("bar_v", iter->value().ToString()); | |
876 | ||
877 | iter->Seek("1000000000foo005"); | |
878 | ASSERT_TRUE(iter->Valid()); | |
879 | ASSERT_EQ("1000000000foo005", iter->key().ToString()); | |
880 | ASSERT_EQ("v__5", iter->value().ToString()); | |
881 | ||
882 | iter->Seek("1000000000foo006"); | |
883 | ASSERT_TRUE(iter->Valid()); | |
884 | ASSERT_EQ("1000000000foo005", iter->key().ToString()); | |
885 | ASSERT_EQ("v__5", iter->value().ToString()); | |
886 | ||
887 | iter->Seek("1000000000foo008"); | |
888 | ASSERT_TRUE(iter->Valid()); | |
889 | ASSERT_EQ("1000000000foo008", iter->key().ToString()); | |
890 | ASSERT_EQ("v__8", iter->value().ToString()); | |
891 | ||
892 | iter->Seek("1000000000foo000"); | |
893 | ASSERT_TRUE(iter->Valid()); | |
894 | ASSERT_EQ("3000000000000bar", iter->key().ToString()); | |
895 | ||
896 | delete iter; | |
897 | } | |
898 | ||
899 | TEST_P(PlainTableDBTest, HashBucketConflict) { | |
900 | for (size_t huge_page_tlb_size = 0; huge_page_tlb_size <= 2 * 1024 * 1024; | |
901 | huge_page_tlb_size += 2 * 1024 * 1024) { | |
902 | for (unsigned char i = 1; i <= 3; i++) { | |
903 | Options options = CurrentOptions(); | |
904 | options.create_if_missing = true; | |
905 | // Set only one bucket to force bucket conflict. | |
906 | // Test index interval for the same prefix to be 1, 2 and 4 | |
907 | ||
908 | PlainTableOptions plain_table_options; | |
909 | plain_table_options.user_key_len = 16; | |
910 | plain_table_options.bloom_bits_per_key = 0; | |
911 | plain_table_options.hash_table_ratio = 0; | |
912 | plain_table_options.index_sparseness = 2 ^ i; | |
913 | plain_table_options.huge_page_tlb_size = huge_page_tlb_size; | |
914 | ||
915 | options.table_factory.reset(NewPlainTableFactory(plain_table_options)); | |
916 | ||
917 | DestroyAndReopen(&options); | |
918 | ASSERT_OK(Put("5000000000000fo0", "v1")); | |
919 | ASSERT_OK(Put("5000000000000fo1", "v2")); | |
920 | ASSERT_OK(Put("5000000000000fo2", "v")); | |
921 | ASSERT_OK(Put("2000000000000fo0", "v3")); | |
922 | ASSERT_OK(Put("2000000000000fo1", "v4")); | |
923 | ASSERT_OK(Put("2000000000000fo2", "v")); | |
924 | ASSERT_OK(Put("2000000000000fo3", "v")); | |
925 | ||
926 | dbfull()->TEST_FlushMemTable(); | |
927 | ||
928 | ASSERT_EQ("v1", Get("5000000000000fo0")); | |
929 | ASSERT_EQ("v2", Get("5000000000000fo1")); | |
930 | ASSERT_EQ("v3", Get("2000000000000fo0")); | |
931 | ASSERT_EQ("v4", Get("2000000000000fo1")); | |
932 | ||
933 | ASSERT_EQ("NOT_FOUND", Get("5000000000000bar")); | |
934 | ASSERT_EQ("NOT_FOUND", Get("2000000000000bar")); | |
935 | ASSERT_EQ("NOT_FOUND", Get("5000000000000fo8")); | |
936 | ASSERT_EQ("NOT_FOUND", Get("2000000000000fo8")); | |
937 | ||
938 | ReadOptions ro; | |
939 | Iterator* iter = dbfull()->NewIterator(ro); | |
940 | ||
941 | iter->Seek("5000000000000fo0"); | |
942 | ASSERT_TRUE(iter->Valid()); | |
943 | ASSERT_EQ("5000000000000fo0", iter->key().ToString()); | |
944 | iter->Next(); | |
945 | ASSERT_TRUE(iter->Valid()); | |
946 | ASSERT_EQ("5000000000000fo1", iter->key().ToString()); | |
947 | ||
948 | iter->Seek("5000000000000fo1"); | |
949 | ASSERT_TRUE(iter->Valid()); | |
950 | ASSERT_EQ("5000000000000fo1", iter->key().ToString()); | |
951 | ||
952 | iter->Seek("2000000000000fo0"); | |
953 | ASSERT_TRUE(iter->Valid()); | |
954 | ASSERT_EQ("2000000000000fo0", iter->key().ToString()); | |
955 | iter->Next(); | |
956 | ASSERT_TRUE(iter->Valid()); | |
957 | ASSERT_EQ("2000000000000fo1", iter->key().ToString()); | |
958 | ||
959 | iter->Seek("2000000000000fo1"); | |
960 | ASSERT_TRUE(iter->Valid()); | |
961 | ASSERT_EQ("2000000000000fo1", iter->key().ToString()); | |
962 | ||
963 | iter->Seek("2000000000000bar"); | |
964 | ASSERT_TRUE(iter->Valid()); | |
965 | ASSERT_EQ("2000000000000fo0", iter->key().ToString()); | |
966 | ||
967 | iter->Seek("5000000000000bar"); | |
968 | ASSERT_TRUE(iter->Valid()); | |
969 | ASSERT_EQ("5000000000000fo0", iter->key().ToString()); | |
970 | ||
971 | iter->Seek("2000000000000fo8"); | |
972 | ASSERT_TRUE(!iter->Valid() || | |
973 | options.comparator->Compare(iter->key(), "20000001") > 0); | |
974 | ||
975 | iter->Seek("5000000000000fo8"); | |
976 | ASSERT_TRUE(!iter->Valid()); | |
977 | ||
978 | iter->Seek("1000000000000fo2"); | |
979 | ASSERT_TRUE(!iter->Valid()); | |
980 | ||
981 | iter->Seek("3000000000000fo2"); | |
982 | ASSERT_TRUE(!iter->Valid()); | |
983 | ||
984 | iter->Seek("8000000000000fo2"); | |
985 | ASSERT_TRUE(!iter->Valid()); | |
986 | ||
987 | delete iter; | |
988 | } | |
989 | } | |
990 | } | |
991 | ||
992 | TEST_P(PlainTableDBTest, HashBucketConflictReverseSuffixComparator) { | |
993 | for (size_t huge_page_tlb_size = 0; huge_page_tlb_size <= 2 * 1024 * 1024; | |
994 | huge_page_tlb_size += 2 * 1024 * 1024) { | |
995 | for (unsigned char i = 1; i <= 3; i++) { | |
996 | Options options = CurrentOptions(); | |
997 | options.create_if_missing = true; | |
998 | test::SimpleSuffixReverseComparator comp; | |
999 | options.comparator = ∁ | |
1000 | // Set only one bucket to force bucket conflict. | |
1001 | // Test index interval for the same prefix to be 1, 2 and 4 | |
1002 | ||
1003 | PlainTableOptions plain_table_options; | |
1004 | plain_table_options.user_key_len = 16; | |
1005 | plain_table_options.bloom_bits_per_key = 0; | |
1006 | plain_table_options.hash_table_ratio = 0; | |
1007 | plain_table_options.index_sparseness = 2 ^ i; | |
1008 | plain_table_options.huge_page_tlb_size = huge_page_tlb_size; | |
1009 | ||
1010 | options.table_factory.reset(NewPlainTableFactory(plain_table_options)); | |
1011 | DestroyAndReopen(&options); | |
1012 | ASSERT_OK(Put("5000000000000fo0", "v1")); | |
1013 | ASSERT_OK(Put("5000000000000fo1", "v2")); | |
1014 | ASSERT_OK(Put("5000000000000fo2", "v")); | |
1015 | ASSERT_OK(Put("2000000000000fo0", "v3")); | |
1016 | ASSERT_OK(Put("2000000000000fo1", "v4")); | |
1017 | ASSERT_OK(Put("2000000000000fo2", "v")); | |
1018 | ASSERT_OK(Put("2000000000000fo3", "v")); | |
1019 | ||
1020 | dbfull()->TEST_FlushMemTable(); | |
1021 | ||
1022 | ASSERT_EQ("v1", Get("5000000000000fo0")); | |
1023 | ASSERT_EQ("v2", Get("5000000000000fo1")); | |
1024 | ASSERT_EQ("v3", Get("2000000000000fo0")); | |
1025 | ASSERT_EQ("v4", Get("2000000000000fo1")); | |
1026 | ||
1027 | ASSERT_EQ("NOT_FOUND", Get("5000000000000bar")); | |
1028 | ASSERT_EQ("NOT_FOUND", Get("2000000000000bar")); | |
1029 | ASSERT_EQ("NOT_FOUND", Get("5000000000000fo8")); | |
1030 | ASSERT_EQ("NOT_FOUND", Get("2000000000000fo8")); | |
1031 | ||
1032 | ReadOptions ro; | |
1033 | Iterator* iter = dbfull()->NewIterator(ro); | |
1034 | ||
1035 | iter->Seek("5000000000000fo1"); | |
1036 | ASSERT_TRUE(iter->Valid()); | |
1037 | ASSERT_EQ("5000000000000fo1", iter->key().ToString()); | |
1038 | iter->Next(); | |
1039 | ASSERT_TRUE(iter->Valid()); | |
1040 | ASSERT_EQ("5000000000000fo0", iter->key().ToString()); | |
1041 | ||
1042 | iter->Seek("5000000000000fo1"); | |
1043 | ASSERT_TRUE(iter->Valid()); | |
1044 | ASSERT_EQ("5000000000000fo1", iter->key().ToString()); | |
1045 | ||
1046 | iter->Seek("2000000000000fo1"); | |
1047 | ASSERT_TRUE(iter->Valid()); | |
1048 | ASSERT_EQ("2000000000000fo1", iter->key().ToString()); | |
1049 | iter->Next(); | |
1050 | ASSERT_TRUE(iter->Valid()); | |
1051 | ASSERT_EQ("2000000000000fo0", iter->key().ToString()); | |
1052 | ||
1053 | iter->Seek("2000000000000fo1"); | |
1054 | ASSERT_TRUE(iter->Valid()); | |
1055 | ASSERT_EQ("2000000000000fo1", iter->key().ToString()); | |
1056 | ||
1057 | iter->Seek("2000000000000var"); | |
1058 | ASSERT_TRUE(iter->Valid()); | |
1059 | ASSERT_EQ("2000000000000fo3", iter->key().ToString()); | |
1060 | ||
1061 | iter->Seek("5000000000000var"); | |
1062 | ASSERT_TRUE(iter->Valid()); | |
1063 | ASSERT_EQ("5000000000000fo2", iter->key().ToString()); | |
1064 | ||
1065 | std::string seek_key = "2000000000000bar"; | |
1066 | iter->Seek(seek_key); | |
1067 | ASSERT_TRUE(!iter->Valid() || | |
1068 | options.prefix_extractor->Transform(iter->key()) != | |
1069 | options.prefix_extractor->Transform(seek_key)); | |
1070 | ||
1071 | iter->Seek("1000000000000fo2"); | |
1072 | ASSERT_TRUE(!iter->Valid()); | |
1073 | ||
1074 | iter->Seek("3000000000000fo2"); | |
1075 | ASSERT_TRUE(!iter->Valid()); | |
1076 | ||
1077 | iter->Seek("8000000000000fo2"); | |
1078 | ASSERT_TRUE(!iter->Valid()); | |
1079 | ||
1080 | delete iter; | |
1081 | } | |
1082 | } | |
1083 | } | |
1084 | ||
1085 | TEST_P(PlainTableDBTest, NonExistingKeyToNonEmptyBucket) { | |
1086 | Options options = CurrentOptions(); | |
1087 | options.create_if_missing = true; | |
1088 | // Set only one bucket to force bucket conflict. | |
1089 | // Test index interval for the same prefix to be 1, 2 and 4 | |
1090 | PlainTableOptions plain_table_options; | |
1091 | plain_table_options.user_key_len = 16; | |
1092 | plain_table_options.bloom_bits_per_key = 0; | |
1093 | plain_table_options.hash_table_ratio = 0; | |
1094 | plain_table_options.index_sparseness = 5; | |
1095 | ||
1096 | options.table_factory.reset(NewPlainTableFactory(plain_table_options)); | |
1097 | DestroyAndReopen(&options); | |
1098 | ASSERT_OK(Put("5000000000000fo0", "v1")); | |
1099 | ASSERT_OK(Put("5000000000000fo1", "v2")); | |
1100 | ASSERT_OK(Put("5000000000000fo2", "v3")); | |
1101 | ||
1102 | dbfull()->TEST_FlushMemTable(); | |
1103 | ||
1104 | ASSERT_EQ("v1", Get("5000000000000fo0")); | |
1105 | ASSERT_EQ("v2", Get("5000000000000fo1")); | |
1106 | ASSERT_EQ("v3", Get("5000000000000fo2")); | |
1107 | ||
1108 | ASSERT_EQ("NOT_FOUND", Get("8000000000000bar")); | |
1109 | ASSERT_EQ("NOT_FOUND", Get("1000000000000bar")); | |
1110 | ||
1111 | Iterator* iter = dbfull()->NewIterator(ReadOptions()); | |
1112 | ||
1113 | iter->Seek("5000000000000bar"); | |
1114 | ASSERT_TRUE(iter->Valid()); | |
1115 | ASSERT_EQ("5000000000000fo0", iter->key().ToString()); | |
1116 | ||
1117 | iter->Seek("5000000000000fo8"); | |
1118 | ASSERT_TRUE(!iter->Valid()); | |
1119 | ||
1120 | iter->Seek("1000000000000fo2"); | |
1121 | ASSERT_TRUE(!iter->Valid()); | |
1122 | ||
1123 | iter->Seek("8000000000000fo2"); | |
1124 | ASSERT_TRUE(!iter->Valid()); | |
1125 | ||
1126 | delete iter; | |
1127 | } | |
1128 | ||
1129 | static std::string Key(int i) { | |
1130 | char buf[100]; | |
1131 | snprintf(buf, sizeof(buf), "key_______%06d", i); | |
1132 | return std::string(buf); | |
1133 | } | |
1134 | ||
1135 | static std::string RandomString(Random* rnd, int len) { | |
1136 | std::string r; | |
1137 | test::RandomString(rnd, len, &r); | |
1138 | return r; | |
1139 | } | |
1140 | ||
1141 | TEST_P(PlainTableDBTest, CompactionTrigger) { | |
1142 | Options options = CurrentOptions(); | |
1143 | options.write_buffer_size = 120 << 10; // 100KB | |
1144 | options.num_levels = 3; | |
1145 | options.level0_file_num_compaction_trigger = 3; | |
1146 | Reopen(&options); | |
1147 | ||
1148 | Random rnd(301); | |
1149 | ||
1150 | for (int num = 0; num < options.level0_file_num_compaction_trigger - 1; | |
1151 | num++) { | |
1152 | std::vector<std::string> values; | |
1153 | // Write 120KB (10 values, each 12K) | |
1154 | for (int i = 0; i < 10; i++) { | |
1155 | values.push_back(RandomString(&rnd, 12000)); | |
1156 | ASSERT_OK(Put(Key(i), values[i])); | |
1157 | } | |
1158 | ASSERT_OK(Put(Key(999), "")); | |
1159 | dbfull()->TEST_WaitForFlushMemTable(); | |
1160 | ASSERT_EQ(NumTableFilesAtLevel(0), num + 1); | |
1161 | } | |
1162 | ||
1163 | //generate one more file in level-0, and should trigger level-0 compaction | |
1164 | std::vector<std::string> values; | |
1165 | for (int i = 0; i < 12; i++) { | |
1166 | values.push_back(RandomString(&rnd, 10000)); | |
1167 | ASSERT_OK(Put(Key(i), values[i])); | |
1168 | } | |
1169 | ASSERT_OK(Put(Key(999), "")); | |
1170 | dbfull()->TEST_WaitForCompact(); | |
1171 | ||
1172 | ASSERT_EQ(NumTableFilesAtLevel(0), 0); | |
1173 | ASSERT_EQ(NumTableFilesAtLevel(1), 1); | |
1174 | } | |
1175 | ||
1176 | TEST_P(PlainTableDBTest, AdaptiveTable) { | |
1177 | Options options = CurrentOptions(); | |
1178 | options.create_if_missing = true; | |
1179 | ||
1180 | options.table_factory.reset(NewPlainTableFactory()); | |
1181 | DestroyAndReopen(&options); | |
1182 | ||
1183 | ASSERT_OK(Put("1000000000000foo", "v1")); | |
1184 | ASSERT_OK(Put("0000000000000bar", "v2")); | |
1185 | ASSERT_OK(Put("1000000000000foo", "v3")); | |
1186 | dbfull()->TEST_FlushMemTable(); | |
1187 | ||
1188 | options.create_if_missing = false; | |
1189 | std::shared_ptr<TableFactory> dummy_factory; | |
1190 | std::shared_ptr<TableFactory> block_based_factory( | |
1191 | NewBlockBasedTableFactory()); | |
1192 | options.table_factory.reset(NewAdaptiveTableFactory( | |
1193 | block_based_factory, dummy_factory, dummy_factory)); | |
1194 | Reopen(&options); | |
1195 | ASSERT_EQ("v3", Get("1000000000000foo")); | |
1196 | ASSERT_EQ("v2", Get("0000000000000bar")); | |
1197 | ||
1198 | ASSERT_OK(Put("2000000000000foo", "v4")); | |
1199 | ASSERT_OK(Put("3000000000000bar", "v5")); | |
1200 | dbfull()->TEST_FlushMemTable(); | |
1201 | ASSERT_EQ("v4", Get("2000000000000foo")); | |
1202 | ASSERT_EQ("v5", Get("3000000000000bar")); | |
1203 | ||
1204 | Reopen(&options); | |
1205 | ASSERT_EQ("v3", Get("1000000000000foo")); | |
1206 | ASSERT_EQ("v2", Get("0000000000000bar")); | |
1207 | ASSERT_EQ("v4", Get("2000000000000foo")); | |
1208 | ASSERT_EQ("v5", Get("3000000000000bar")); | |
1209 | ||
1210 | options.table_factory.reset(NewBlockBasedTableFactory()); | |
1211 | Reopen(&options); | |
1212 | ASSERT_NE("v3", Get("1000000000000foo")); | |
1213 | ||
1214 | options.table_factory.reset(NewPlainTableFactory()); | |
1215 | Reopen(&options); | |
1216 | ASSERT_NE("v5", Get("3000000000000bar")); | |
1217 | } | |
1218 | ||
1219 | INSTANTIATE_TEST_CASE_P(PlainTableDBTest, PlainTableDBTest, ::testing::Bool()); | |
1220 | ||
1221 | } // namespace rocksdb | |
1222 | ||
1223 | int main(int argc, char** argv) { | |
1224 | ::testing::InitGoogleTest(&argc, argv); | |
1225 | return RUN_ALL_TESTS(); | |
1226 | } | |
1227 | ||
1228 | #else | |
1229 | #include <stdio.h> | |
1230 | ||
11fdf7f2 | 1231 | int main(int /*argc*/, char** /*argv*/) { |
7c673cae FG |
1232 | fprintf(stderr, "SKIPPED as plain table is not supported in ROCKSDB_LITE\n"); |
1233 | return 0; | |
1234 | } | |
1235 | ||
1236 | #endif // !ROCKSDB_LITE |