]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/db/table_properties_collector_test.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / rocksdb / db / table_properties_collector_test.cc
CommitLineData
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 5
20effc67
TL
6#include "db/table_properties_collector.h"
7
7c673cae
FG
8#include <map>
9#include <memory>
10#include <string>
11#include <utility>
12#include <vector>
13
f67539c2 14#include "db/db_impl/db_impl.h"
7c673cae 15#include "db/dbformat.h"
f67539c2
TL
16#include "file/sequence_file_reader.h"
17#include "file/writable_file_writer.h"
7c673cae 18#include "options/cf_options.h"
20effc67 19#include "rocksdb/flush_block_policy.h"
7c673cae 20#include "rocksdb/table.h"
f67539c2 21#include "table/block_based/block_based_table_factory.h"
7c673cae 22#include "table/meta_blocks.h"
f67539c2 23#include "table/plain/plain_table_factory.h"
7c673cae 24#include "table/table_builder.h"
f67539c2
TL
25#include "test_util/testharness.h"
26#include "test_util/testutil.h"
7c673cae 27#include "util/coding.h"
7c673cae 28
f67539c2 29namespace ROCKSDB_NAMESPACE {
7c673cae
FG
30
31class TablePropertiesTest : public testing::Test,
32 public testing::WithParamInterface<bool> {
33 public:
494da23a 34 void SetUp() override { backward_mode_ = GetParam(); }
7c673cae
FG
35
36 bool backward_mode_;
37};
38
39// Utilities test functions
40namespace {
41static const uint32_t kTestColumnFamilyId = 66;
42static const std::string kTestColumnFamilyName = "test_column_fam";
1e59de90
TL
43static const int kTestLevel = 1;
44
45void MakeBuilder(
46 const Options& options, const ImmutableOptions& ioptions,
47 const MutableCFOptions& moptions,
48 const InternalKeyComparator& internal_comparator,
49 const IntTblPropCollectorFactories* int_tbl_prop_collector_factories,
50 std::unique_ptr<WritableFileWriter>* writable,
51 std::unique_ptr<TableBuilder>* builder) {
52 std::unique_ptr<FSWritableFile> wf(new test::StringSink);
11fdf7f2 53 writable->reset(
1e59de90
TL
54 new WritableFileWriter(std::move(wf), "" /* don't care */, EnvOptions()));
55 TableBuilderOptions tboptions(
11fdf7f2 56 ioptions, moptions, internal_comparator, int_tbl_prop_collector_factories,
1e59de90
TL
57 options.compression, options.compression_opts, kTestColumnFamilyId,
58 kTestColumnFamilyName, kTestLevel);
59 builder->reset(NewTableBuilder(tboptions, writable->get()));
7c673cae
FG
60}
61} // namespace
62
63// Collects keys that starts with "A" in a table.
1e59de90 64class RegularKeysStartWithA : public TablePropertiesCollector {
7c673cae
FG
65 public:
66 const char* Name() const override { return "RegularKeysStartWithA"; }
67
68 Status Finish(UserCollectedProperties* properties) override {
1e59de90
TL
69 std::string encoded;
70 std::string encoded_num_puts;
71 std::string encoded_num_deletes;
72 std::string encoded_num_single_deletes;
73 std::string encoded_num_size_changes;
74 PutVarint32(&encoded, count_);
75 PutVarint32(&encoded_num_puts, num_puts_);
76 PutVarint32(&encoded_num_deletes, num_deletes_);
77 PutVarint32(&encoded_num_single_deletes, num_single_deletes_);
78 PutVarint32(&encoded_num_size_changes, num_size_changes_);
79 *properties = UserCollectedProperties{
80 {"TablePropertiesTest", message_},
81 {"Count", encoded},
82 {"NumPuts", encoded_num_puts},
83 {"NumDeletes", encoded_num_deletes},
84 {"NumSingleDeletes", encoded_num_single_deletes},
85 {"NumSizeChanges", encoded_num_size_changes},
86 };
87 return Status::OK();
7c673cae
FG
88 }
89
11fdf7f2
TL
90 Status AddUserKey(const Slice& user_key, const Slice& /*value*/,
91 EntryType type, SequenceNumber /*seq*/,
92 uint64_t file_size) override {
7c673cae
FG
93 // simply asssume all user keys are not empty.
94 if (user_key.data()[0] == 'A') {
95 ++count_;
96 }
97 if (type == kEntryPut) {
98 num_puts_++;
99 } else if (type == kEntryDelete) {
100 num_deletes_++;
101 } else if (type == kEntrySingleDelete) {
102 num_single_deletes_++;
103 }
104 if (file_size < file_size_) {
105 message_ = "File size should not decrease.";
106 } else if (file_size != file_size_) {
107 num_size_changes_++;
108 }
109
110 return Status::OK();
111 }
112
494da23a 113 UserCollectedProperties GetReadableProperties() const override {
7c673cae
FG
114 return UserCollectedProperties{};
115 }
116
117 private:
118 std::string message_ = "Rocksdb";
119 uint32_t count_ = 0;
120 uint32_t num_puts_ = 0;
121 uint32_t num_deletes_ = 0;
122 uint32_t num_single_deletes_ = 0;
123 uint32_t num_size_changes_ = 0;
124 uint64_t file_size_ = 0;
125};
126
127// Collects keys that starts with "A" in a table. Backward compatible mode
128// It is also used to test internal key table property collector
129class RegularKeysStartWithABackwardCompatible
130 : public TablePropertiesCollector {
131 public:
132 const char* Name() const override { return "RegularKeysStartWithA"; }
133
134 Status Finish(UserCollectedProperties* properties) override {
135 std::string encoded;
136 PutVarint32(&encoded, count_);
137 *properties = UserCollectedProperties{{"TablePropertiesTest", "Rocksdb"},
138 {"Count", encoded}};
139 return Status::OK();
140 }
141
11fdf7f2 142 Status Add(const Slice& user_key, const Slice& /*value*/) override {
7c673cae
FG
143 // simply asssume all user keys are not empty.
144 if (user_key.data()[0] == 'A') {
145 ++count_;
146 }
147 return Status::OK();
148 }
149
494da23a 150 UserCollectedProperties GetReadableProperties() const override {
7c673cae
FG
151 return UserCollectedProperties{};
152 }
153
154 private:
155 uint32_t count_ = 0;
156};
157
158class RegularKeysStartWithAInternal : public IntTblPropCollector {
159 public:
160 const char* Name() const override { return "RegularKeysStartWithA"; }
161
162 Status Finish(UserCollectedProperties* properties) override {
163 std::string encoded;
164 PutVarint32(&encoded, count_);
165 *properties = UserCollectedProperties{{"TablePropertiesTest", "Rocksdb"},
166 {"Count", encoded}};
167 return Status::OK();
168 }
169
11fdf7f2
TL
170 Status InternalAdd(const Slice& user_key, const Slice& /*value*/,
171 uint64_t /*file_size*/) override {
7c673cae
FG
172 // simply asssume all user keys are not empty.
173 if (user_key.data()[0] == 'A') {
174 ++count_;
175 }
176 return Status::OK();
177 }
178
1e59de90
TL
179 void BlockAdd(uint64_t /* block_uncomp_bytes */,
180 uint64_t /* block_compressed_bytes_fast */,
181 uint64_t /* block_compressed_bytes_slow */) override {
494da23a
TL
182 // Nothing to do.
183 return;
184 }
185
186 UserCollectedProperties GetReadableProperties() const override {
7c673cae
FG
187 return UserCollectedProperties{};
188 }
189
190 private:
191 uint32_t count_ = 0;
192};
193
194class RegularKeysStartWithAFactory : public IntTblPropCollectorFactory,
195 public TablePropertiesCollectorFactory {
196 public:
197 explicit RegularKeysStartWithAFactory(bool backward_mode)
198 : backward_mode_(backward_mode) {}
494da23a 199 TablePropertiesCollector* CreateTablePropertiesCollector(
7c673cae
FG
200 TablePropertiesCollectorFactory::Context context) override {
201 EXPECT_EQ(kTestColumnFamilyId, context.column_family_id);
1e59de90 202 EXPECT_EQ(kTestLevel, context.level_at_creation);
7c673cae
FG
203 if (!backward_mode_) {
204 return new RegularKeysStartWithA();
205 } else {
206 return new RegularKeysStartWithABackwardCompatible();
207 }
208 }
494da23a 209 IntTblPropCollector* CreateIntTblPropCollector(
1e59de90 210 uint32_t /*column_family_id*/, int /* level_at_creation */) override {
7c673cae
FG
211 return new RegularKeysStartWithAInternal();
212 }
213 const char* Name() const override { return "RegularKeysStartWithA"; }
214
215 bool backward_mode_;
216};
217
218class FlushBlockEveryThreePolicy : public FlushBlockPolicy {
219 public:
494da23a 220 bool Update(const Slice& /*key*/, const Slice& /*value*/) override {
7c673cae
FG
221 return (++count_ % 3U == 0);
222 }
223
224 private:
225 uint64_t count_ = 0;
226};
227
228class FlushBlockEveryThreePolicyFactory : public FlushBlockPolicyFactory {
229 public:
230 explicit FlushBlockEveryThreePolicyFactory() {}
231
232 const char* Name() const override {
233 return "FlushBlockEveryThreePolicyFactory";
234 }
235
236 FlushBlockPolicy* NewFlushBlockPolicy(
11fdf7f2
TL
237 const BlockBasedTableOptions& /*table_options*/,
238 const BlockBuilder& /*data_block_builder*/) const override {
7c673cae
FG
239 return new FlushBlockEveryThreePolicy;
240 }
241};
242
243extern const uint64_t kBlockBasedTableMagicNumber;
244extern const uint64_t kPlainTableMagicNumber;
245namespace {
246void TestCustomizedTablePropertiesCollector(
247 bool backward_mode, uint64_t magic_number, bool test_int_tbl_prop_collector,
248 const Options& options, const InternalKeyComparator& internal_comparator) {
249 // make sure the entries will be inserted with order.
250 std::map<std::pair<std::string, ValueType>, std::string> kvs = {
251 {{"About ", kTypeValue}, "val5"}, // starts with 'A'
252 {{"Abstract", kTypeValue}, "val2"}, // starts with 'A'
253 {{"Around ", kTypeValue}, "val7"}, // starts with 'A'
254 {{"Beyond ", kTypeValue}, "val3"},
255 {{"Builder ", kTypeValue}, "val1"},
256 {{"Love ", kTypeDeletion}, ""},
257 {{"Cancel ", kTypeValue}, "val4"},
258 {{"Find ", kTypeValue}, "val6"},
259 {{"Rocks ", kTypeDeletion}, ""},
260 {{"Foo ", kTypeSingleDeletion}, ""},
261 };
262
263 // -- Step 1: build table
264 std::unique_ptr<TableBuilder> builder;
265 std::unique_ptr<WritableFileWriter> writer;
1e59de90 266 const ImmutableOptions ioptions(options);
11fdf7f2 267 const MutableCFOptions moptions(options);
1e59de90 268 IntTblPropCollectorFactories int_tbl_prop_collector_factories;
7c673cae
FG
269 if (test_int_tbl_prop_collector) {
270 int_tbl_prop_collector_factories.emplace_back(
271 new RegularKeysStartWithAFactory(backward_mode));
272 } else {
273 GetIntTblPropCollectorFactory(ioptions, &int_tbl_prop_collector_factories);
274 }
11fdf7f2 275 MakeBuilder(options, ioptions, moptions, internal_comparator,
7c673cae
FG
276 &int_tbl_prop_collector_factories, &writer, &builder);
277
278 SequenceNumber seqNum = 0U;
279 for (const auto& kv : kvs) {
280 InternalKey ikey(kv.first.first, seqNum++, kv.first.second);
281 builder->Add(ikey.Encode(), kv.second);
282 }
283 ASSERT_OK(builder->Finish());
1e59de90 284 ASSERT_OK(writer->Flush());
7c673cae
FG
285
286 // -- Step 2: Read properties
1e59de90
TL
287 test::StringSink* fwf =
288 static_cast<test::StringSink*>(writer->writable_file());
289 std::unique_ptr<FSRandomAccessFile> source(
290 new test::StringSource(fwf->contents()));
7c673cae 291 std::unique_ptr<RandomAccessFileReader> fake_file_reader(
1e59de90
TL
292 new RandomAccessFileReader(std::move(source), "test"));
293
294 std::unique_ptr<TableProperties> props;
7c673cae 295 Status s = ReadTableProperties(fake_file_reader.get(), fwf->contents().size(),
1e59de90 296 magic_number, ioptions, &props);
7c673cae
FG
297 ASSERT_OK(s);
298
299 auto user_collected = props->user_collected_properties;
300
301 ASSERT_NE(user_collected.find("TablePropertiesTest"), user_collected.end());
302 ASSERT_EQ("Rocksdb", user_collected.at("TablePropertiesTest"));
303
304 uint32_t starts_with_A = 0;
305 ASSERT_NE(user_collected.find("Count"), user_collected.end());
306 Slice key(user_collected.at("Count"));
307 ASSERT_TRUE(GetVarint32(&key, &starts_with_A));
308 ASSERT_EQ(3u, starts_with_A);
309
310 if (!backward_mode && !test_int_tbl_prop_collector) {
311 uint32_t num_puts;
312 ASSERT_NE(user_collected.find("NumPuts"), user_collected.end());
313 Slice key_puts(user_collected.at("NumPuts"));
314 ASSERT_TRUE(GetVarint32(&key_puts, &num_puts));
315 ASSERT_EQ(7u, num_puts);
316
317 uint32_t num_deletes;
318 ASSERT_NE(user_collected.find("NumDeletes"), user_collected.end());
319 Slice key_deletes(user_collected.at("NumDeletes"));
320 ASSERT_TRUE(GetVarint32(&key_deletes, &num_deletes));
321 ASSERT_EQ(2u, num_deletes);
322
323 uint32_t num_single_deletes;
324 ASSERT_NE(user_collected.find("NumSingleDeletes"), user_collected.end());
325 Slice key_single_deletes(user_collected.at("NumSingleDeletes"));
326 ASSERT_TRUE(GetVarint32(&key_single_deletes, &num_single_deletes));
327 ASSERT_EQ(1u, num_single_deletes);
328
329 uint32_t num_size_changes;
330 ASSERT_NE(user_collected.find("NumSizeChanges"), user_collected.end());
331 Slice key_size_changes(user_collected.at("NumSizeChanges"));
332 ASSERT_TRUE(GetVarint32(&key_size_changes, &num_size_changes));
333 ASSERT_GE(num_size_changes, 2u);
334 }
335}
336} // namespace
337
338TEST_P(TablePropertiesTest, CustomizedTablePropertiesCollector) {
339 // Test properties collectors with internal keys or regular keys
340 // for block based table
1e59de90 341 for (bool encode_as_internal : {true, false}) {
7c673cae
FG
342 Options options;
343 BlockBasedTableOptions table_options;
344 table_options.flush_block_policy_factory =
345 std::make_shared<FlushBlockEveryThreePolicyFactory>();
346 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
347
348 test::PlainInternalKeyComparator ikc(options.comparator);
349 std::shared_ptr<TablePropertiesCollectorFactory> collector_factory(
350 new RegularKeysStartWithAFactory(backward_mode_));
351 options.table_properties_collector_factories.resize(1);
352 options.table_properties_collector_factories[0] = collector_factory;
353
354 TestCustomizedTablePropertiesCollector(backward_mode_,
355 kBlockBasedTableMagicNumber,
356 encode_as_internal, options, ikc);
357
358#ifndef ROCKSDB_LITE // PlainTable is not supported in Lite
359 // test plain table
360 PlainTableOptions plain_table_options;
361 plain_table_options.user_key_len = 8;
362 plain_table_options.bloom_bits_per_key = 8;
363 plain_table_options.hash_table_ratio = 0;
364
365 options.table_factory =
366 std::make_shared<PlainTableFactory>(plain_table_options);
367 TestCustomizedTablePropertiesCollector(backward_mode_,
368 kPlainTableMagicNumber,
369 encode_as_internal, options, ikc);
370#endif // !ROCKSDB_LITE
371 }
372}
373
374namespace {
375void TestInternalKeyPropertiesCollector(
376 bool backward_mode, uint64_t magic_number, bool sanitized,
377 std::shared_ptr<TableFactory> table_factory) {
378 InternalKey keys[] = {
379 InternalKey("A ", 0, ValueType::kTypeValue),
380 InternalKey("B ", 1, ValueType::kTypeValue),
381 InternalKey("C ", 2, ValueType::kTypeValue),
382 InternalKey("W ", 3, ValueType::kTypeDeletion),
383 InternalKey("X ", 4, ValueType::kTypeDeletion),
384 InternalKey("Y ", 5, ValueType::kTypeDeletion),
385 InternalKey("Z ", 6, ValueType::kTypeDeletion),
386 InternalKey("a ", 7, ValueType::kTypeSingleDeletion),
387 InternalKey("b ", 8, ValueType::kTypeMerge),
388 InternalKey("c ", 9, ValueType::kTypeMerge),
389 };
390
391 std::unique_ptr<TableBuilder> builder;
392 std::unique_ptr<WritableFileWriter> writable;
393 Options options;
394 test::PlainInternalKeyComparator pikc(options.comparator);
395
1e59de90 396 IntTblPropCollectorFactories int_tbl_prop_collector_factories;
7c673cae
FG
397 options.table_factory = table_factory;
398 if (sanitized) {
399 options.table_properties_collector_factories.emplace_back(
400 new RegularKeysStartWithAFactory(backward_mode));
401 // with sanitization, even regular properties collector will be able to
402 // handle internal keys.
403 auto comparator = options.comparator;
404 // HACK: Set options.info_log to avoid writing log in
405 // SanitizeOptions().
406 options.info_log = std::make_shared<test::NullLogger>();
1e59de90 407 options = SanitizeOptions("db", // just a place holder
7c673cae 408 options);
1e59de90 409 ImmutableOptions ioptions(options);
7c673cae
FG
410 GetIntTblPropCollectorFactory(ioptions, &int_tbl_prop_collector_factories);
411 options.comparator = comparator;
7c673cae 412 }
1e59de90 413 const ImmutableOptions ioptions(options);
11fdf7f2 414 MutableCFOptions moptions(options);
7c673cae
FG
415
416 for (int iter = 0; iter < 2; ++iter) {
11fdf7f2
TL
417 MakeBuilder(options, ioptions, moptions, pikc,
418 &int_tbl_prop_collector_factories, &writable, &builder);
7c673cae
FG
419 for (const auto& k : keys) {
420 builder->Add(k.Encode(), "val");
421 }
422
423 ASSERT_OK(builder->Finish());
1e59de90 424 ASSERT_OK(writable->Flush());
7c673cae 425
1e59de90
TL
426 test::StringSink* fwf =
427 static_cast<test::StringSink*>(writable->writable_file());
428 std::unique_ptr<FSRandomAccessFile> source(
429 new test::StringSource(fwf->contents()));
494da23a 430 std::unique_ptr<RandomAccessFileReader> reader(
1e59de90
TL
431 new RandomAccessFileReader(std::move(source), "test"));
432
433 std::unique_ptr<TableProperties> props;
434 Status s = ReadTableProperties(reader.get(), fwf->contents().size(),
435 magic_number, ioptions, &props);
7c673cae
FG
436 ASSERT_OK(s);
437
7c673cae
FG
438 auto user_collected = props->user_collected_properties;
439 uint64_t deleted = GetDeletedKeys(user_collected);
440 ASSERT_EQ(5u, deleted); // deletes + single-deletes
441
442 bool property_present;
443 uint64_t merges = GetMergeOperands(user_collected, &property_present);
444 ASSERT_TRUE(property_present);
445 ASSERT_EQ(2u, merges);
446
447 if (sanitized) {
448 uint32_t starts_with_A = 0;
449 ASSERT_NE(user_collected.find("Count"), user_collected.end());
450 Slice key(user_collected.at("Count"));
451 ASSERT_TRUE(GetVarint32(&key, &starts_with_A));
452 ASSERT_EQ(1u, starts_with_A);
453
454 if (!backward_mode) {
455 uint32_t num_puts;
456 ASSERT_NE(user_collected.find("NumPuts"), user_collected.end());
457 Slice key_puts(user_collected.at("NumPuts"));
458 ASSERT_TRUE(GetVarint32(&key_puts, &num_puts));
459 ASSERT_EQ(3u, num_puts);
460
461 uint32_t num_deletes;
462 ASSERT_NE(user_collected.find("NumDeletes"), user_collected.end());
463 Slice key_deletes(user_collected.at("NumDeletes"));
464 ASSERT_TRUE(GetVarint32(&key_deletes, &num_deletes));
465 ASSERT_EQ(4u, num_deletes);
466
467 uint32_t num_single_deletes;
468 ASSERT_NE(user_collected.find("NumSingleDeletes"),
469 user_collected.end());
470 Slice key_single_deletes(user_collected.at("NumSingleDeletes"));
471 ASSERT_TRUE(GetVarint32(&key_single_deletes, &num_single_deletes));
472 ASSERT_EQ(1u, num_single_deletes);
473 }
474 }
475 }
476}
477} // namespace
478
479TEST_P(TablePropertiesTest, InternalKeyPropertiesCollector) {
480 TestInternalKeyPropertiesCollector(
481 backward_mode_, kBlockBasedTableMagicNumber, true /* sanitize */,
482 std::make_shared<BlockBasedTableFactory>());
483 if (backward_mode_) {
484 TestInternalKeyPropertiesCollector(
485 backward_mode_, kBlockBasedTableMagicNumber, false /* not sanitize */,
486 std::make_shared<BlockBasedTableFactory>());
487 }
488
489#ifndef ROCKSDB_LITE // PlainTable is not supported in Lite
490 PlainTableOptions plain_table_options;
491 plain_table_options.user_key_len = 8;
492 plain_table_options.bloom_bits_per_key = 8;
493 plain_table_options.hash_table_ratio = 0;
494
495 TestInternalKeyPropertiesCollector(
496 backward_mode_, kPlainTableMagicNumber, false /* not sanitize */,
497 std::make_shared<PlainTableFactory>(plain_table_options));
498#endif // !ROCKSDB_LITE
499}
500
501INSTANTIATE_TEST_CASE_P(InternalKeyPropertiesCollector, TablePropertiesTest,
502 ::testing::Bool());
503
504INSTANTIATE_TEST_CASE_P(CustomizedTablePropertiesCollector, TablePropertiesTest,
505 ::testing::Bool());
506
f67539c2 507} // namespace ROCKSDB_NAMESPACE
7c673cae
FG
508
509int main(int argc, char** argv) {
1e59de90 510 ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
7c673cae
FG
511 ::testing::InitGoogleTest(&argc, argv);
512 return RUN_ALL_TESTS();
513}