index_builder.cc 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. //
  6. // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  7. // Use of this source code is governed by a BSD-style license that can be
  8. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  9. #include "table/block_based/index_builder.h"
  10. #include <assert.h>
  11. #include <cinttypes>
  12. #include <list>
  13. #include <string>
  14. #include "rocksdb/comparator.h"
  15. #include "rocksdb/flush_block_policy.h"
  16. #include "table/block_based/partitioned_filter_block.h"
  17. #include "table/format.h"
  18. // Without anonymous namespace here, we fail the warning -Wmissing-prototypes
  19. namespace ROCKSDB_NAMESPACE {
  20. // using namespace rocksdb;
  21. // Create a index builder based on its type.
  22. IndexBuilder* IndexBuilder::CreateIndexBuilder(
  23. BlockBasedTableOptions::IndexType index_type,
  24. const InternalKeyComparator* comparator,
  25. const InternalKeySliceTransform* int_key_slice_transform,
  26. const bool use_value_delta_encoding,
  27. const BlockBasedTableOptions& table_opt) {
  28. IndexBuilder* result = nullptr;
  29. switch (index_type) {
  30. case BlockBasedTableOptions::kBinarySearch: {
  31. result = new ShortenedIndexBuilder(
  32. comparator, table_opt.index_block_restart_interval,
  33. table_opt.format_version, use_value_delta_encoding,
  34. table_opt.index_shortening, /* include_first_key */ false);
  35. } break;
  36. case BlockBasedTableOptions::kHashSearch: {
  37. // Currently kHashSearch is incompatible with index_block_restart_interval
  38. // > 1
  39. assert(table_opt.index_block_restart_interval == 1);
  40. result = new HashIndexBuilder(
  41. comparator, int_key_slice_transform,
  42. table_opt.index_block_restart_interval, table_opt.format_version,
  43. use_value_delta_encoding, table_opt.index_shortening);
  44. } break;
  45. case BlockBasedTableOptions::kTwoLevelIndexSearch: {
  46. result = PartitionedIndexBuilder::CreateIndexBuilder(
  47. comparator, use_value_delta_encoding, table_opt);
  48. } break;
  49. case BlockBasedTableOptions::kBinarySearchWithFirstKey: {
  50. result = new ShortenedIndexBuilder(
  51. comparator, table_opt.index_block_restart_interval,
  52. table_opt.format_version, use_value_delta_encoding,
  53. table_opt.index_shortening, /* include_first_key */ true);
  54. } break;
  55. default: {
  56. assert(!"Do not recognize the index type ");
  57. } break;
  58. }
  59. return result;
  60. }
  61. PartitionedIndexBuilder* PartitionedIndexBuilder::CreateIndexBuilder(
  62. const InternalKeyComparator* comparator,
  63. const bool use_value_delta_encoding,
  64. const BlockBasedTableOptions& table_opt) {
  65. return new PartitionedIndexBuilder(comparator, table_opt,
  66. use_value_delta_encoding);
  67. }
  68. PartitionedIndexBuilder::PartitionedIndexBuilder(
  69. const InternalKeyComparator* comparator,
  70. const BlockBasedTableOptions& table_opt,
  71. const bool use_value_delta_encoding)
  72. : IndexBuilder(comparator),
  73. index_block_builder_(table_opt.index_block_restart_interval,
  74. true /*use_delta_encoding*/,
  75. use_value_delta_encoding),
  76. index_block_builder_without_seq_(table_opt.index_block_restart_interval,
  77. true /*use_delta_encoding*/,
  78. use_value_delta_encoding),
  79. sub_index_builder_(nullptr),
  80. table_opt_(table_opt),
  81. // We start by false. After each partition we revise the value based on
  82. // what the sub_index_builder has decided. If the feature is disabled
  83. // entirely, this will be set to true after switching the first
  84. // sub_index_builder. Otherwise, it could be set to true even one of the
  85. // sub_index_builders could not safely exclude seq from the keys, then it
  86. // wil be enforced on all sub_index_builders on ::Finish.
  87. seperator_is_key_plus_seq_(false),
  88. use_value_delta_encoding_(use_value_delta_encoding) {}
  89. PartitionedIndexBuilder::~PartitionedIndexBuilder() {
  90. delete sub_index_builder_;
  91. }
  92. void PartitionedIndexBuilder::MakeNewSubIndexBuilder() {
  93. assert(sub_index_builder_ == nullptr);
  94. sub_index_builder_ = new ShortenedIndexBuilder(
  95. comparator_, table_opt_.index_block_restart_interval,
  96. table_opt_.format_version, use_value_delta_encoding_,
  97. table_opt_.index_shortening, /* include_first_key */ false);
  98. flush_policy_.reset(FlushBlockBySizePolicyFactory::NewFlushBlockPolicy(
  99. table_opt_.metadata_block_size, table_opt_.block_size_deviation,
  100. // Note: this is sub-optimal since sub_index_builder_ could later reset
  101. // seperator_is_key_plus_seq_ but the probability of that is low.
  102. sub_index_builder_->seperator_is_key_plus_seq_
  103. ? sub_index_builder_->index_block_builder_
  104. : sub_index_builder_->index_block_builder_without_seq_));
  105. partition_cut_requested_ = false;
  106. }
  107. void PartitionedIndexBuilder::RequestPartitionCut() {
  108. partition_cut_requested_ = true;
  109. }
  110. void PartitionedIndexBuilder::AddIndexEntry(
  111. std::string* last_key_in_current_block,
  112. const Slice* first_key_in_next_block, const BlockHandle& block_handle) {
  113. // Note: to avoid two consecuitive flush in the same method call, we do not
  114. // check flush policy when adding the last key
  115. if (UNLIKELY(first_key_in_next_block == nullptr)) { // no more keys
  116. if (sub_index_builder_ == nullptr) {
  117. MakeNewSubIndexBuilder();
  118. }
  119. sub_index_builder_->AddIndexEntry(last_key_in_current_block,
  120. first_key_in_next_block, block_handle);
  121. if (sub_index_builder_->seperator_is_key_plus_seq_) {
  122. // then we need to apply it to all sub-index builders
  123. seperator_is_key_plus_seq_ = true;
  124. }
  125. sub_index_last_key_ = std::string(*last_key_in_current_block);
  126. entries_.push_back(
  127. {sub_index_last_key_,
  128. std::unique_ptr<ShortenedIndexBuilder>(sub_index_builder_)});
  129. sub_index_builder_ = nullptr;
  130. cut_filter_block = true;
  131. } else {
  132. // apply flush policy only to non-empty sub_index_builder_
  133. if (sub_index_builder_ != nullptr) {
  134. std::string handle_encoding;
  135. block_handle.EncodeTo(&handle_encoding);
  136. bool do_flush =
  137. partition_cut_requested_ ||
  138. flush_policy_->Update(*last_key_in_current_block, handle_encoding);
  139. if (do_flush) {
  140. entries_.push_back(
  141. {sub_index_last_key_,
  142. std::unique_ptr<ShortenedIndexBuilder>(sub_index_builder_)});
  143. cut_filter_block = true;
  144. sub_index_builder_ = nullptr;
  145. }
  146. }
  147. if (sub_index_builder_ == nullptr) {
  148. MakeNewSubIndexBuilder();
  149. }
  150. sub_index_builder_->AddIndexEntry(last_key_in_current_block,
  151. first_key_in_next_block, block_handle);
  152. sub_index_last_key_ = std::string(*last_key_in_current_block);
  153. if (sub_index_builder_->seperator_is_key_plus_seq_) {
  154. // then we need to apply it to all sub-index builders
  155. seperator_is_key_plus_seq_ = true;
  156. }
  157. }
  158. }
  159. Status PartitionedIndexBuilder::Finish(
  160. IndexBlocks* index_blocks, const BlockHandle& last_partition_block_handle) {
  161. if (partition_cnt_ == 0) {
  162. partition_cnt_ = entries_.size();
  163. }
  164. // It must be set to null after last key is added
  165. assert(sub_index_builder_ == nullptr);
  166. if (finishing_indexes == true) {
  167. Entry& last_entry = entries_.front();
  168. std::string handle_encoding;
  169. last_partition_block_handle.EncodeTo(&handle_encoding);
  170. std::string handle_delta_encoding;
  171. PutVarsignedint64(
  172. &handle_delta_encoding,
  173. last_partition_block_handle.size() - last_encoded_handle_.size());
  174. last_encoded_handle_ = last_partition_block_handle;
  175. const Slice handle_delta_encoding_slice(handle_delta_encoding);
  176. index_block_builder_.Add(last_entry.key, handle_encoding,
  177. &handle_delta_encoding_slice);
  178. if (!seperator_is_key_plus_seq_) {
  179. index_block_builder_without_seq_.Add(ExtractUserKey(last_entry.key),
  180. handle_encoding,
  181. &handle_delta_encoding_slice);
  182. }
  183. entries_.pop_front();
  184. }
  185. // If there is no sub_index left, then return the 2nd level index.
  186. if (UNLIKELY(entries_.empty())) {
  187. if (seperator_is_key_plus_seq_) {
  188. index_blocks->index_block_contents = index_block_builder_.Finish();
  189. } else {
  190. index_blocks->index_block_contents =
  191. index_block_builder_without_seq_.Finish();
  192. }
  193. top_level_index_size_ = index_blocks->index_block_contents.size();
  194. index_size_ += top_level_index_size_;
  195. return Status::OK();
  196. } else {
  197. // Finish the next partition index in line and Incomplete() to indicate we
  198. // expect more calls to Finish
  199. Entry& entry = entries_.front();
  200. // Apply the policy to all sub-indexes
  201. entry.value->seperator_is_key_plus_seq_ = seperator_is_key_plus_seq_;
  202. auto s = entry.value->Finish(index_blocks);
  203. index_size_ += index_blocks->index_block_contents.size();
  204. finishing_indexes = true;
  205. return s.ok() ? Status::Incomplete() : s;
  206. }
  207. }
  208. size_t PartitionedIndexBuilder::NumPartitions() const { return partition_cnt_; }
  209. } // namespace ROCKSDB_NAMESPACE