table_properties_collector_test.cc 18 KB

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