libfoedus-core
FOEDUS Core Library
masstree_storage_fatify.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014-2015, Hewlett-Packard Development Company, LP.
3  * This program is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU General Public License as published by the Free
5  * Software Foundation; either version 2 of the License, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11  * more details. You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the Free Software
13  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14  *
15  * HP designates this particular file as subject to the "Classpath" exception
16  * as provided by HP in the LICENSE.txt file that accompanied this code.
17  */
19 
20 #include <glog/logging.h>
21 
22 #include "foedus/assert_nd.hpp"
27 
28 namespace foedus {
29 namespace storage {
30 namespace masstree {
31 
33  // this method doesn't need page-lock, but instead it might be inaccurate
34  const uint16_t key_count = page->get_key_count();
35  uint32_t current_count = 0;
36  for (uint16_t minipage_index = 0; minipage_index <= key_count; ++minipage_index) {
37  const auto& minipage = page->get_minipage(minipage_index);
38  current_count += minipage.key_count_ + 1;
39  }
40  return current_count;
41 }
43  thread::Thread* context,
44  uint32_t* out) {
45  *out = 0;
47  CHECK_ERROR_CODE(get_first_root(context, true, &root));
48  *out = count_children_approximate(root);
49  return kErrorCodeOk;
50 }
51 
52 constexpr uint32_t kIntermediateAlmostFull = kMaxIntermediatePointers * 9U / 10U;
53 
55  thread::Thread* context,
56  uint32_t desired_count,
57  bool disable_no_record_split) {
58  LOG(INFO) << "Masstree-" << get_name() << " being fatified for " << desired_count
59  << ", disable_no_record_split=" << disable_no_record_split;
60 
61  if (desired_count > kIntermediateAlmostFull) {
62  LOG(INFO) << "desired_count too large. adjusted to the max";
63  desired_count = kIntermediateAlmostFull;
64  }
65  uint32_t initial_children;
66  WRAP_ERROR_CODE(approximate_count_root_children(context, &initial_children));
67  LOG(INFO) << "initial_children=" << initial_children;
68 
69  // We keep doubling the direct root-children
71  watch.start();
72  uint16_t iterations = 0;
73  for (uint32_t count = initial_children; count < desired_count && iterations < 10U; ++iterations) {
74  CHECK_ERROR(fatify_first_root_double(context, disable_no_record_split));
75  uint32_t new_count;
77  if (count == new_count) {
78  LOG(WARNING) << "Not enough descendants for further fatification. Stopped here";
79  break;
80  }
81  count = new_count;
82  }
83 
84  uint32_t after_children;
85  WRAP_ERROR_CODE(approximate_count_root_children(context, &after_children));
86  watch.stop();
87  LOG(INFO) << "fatify done: Iterations=" << iterations
88  << ", took " << watch.elapsed_us() << "us in total."
89  << " child count: " << initial_children << "->" << after_children;
90 
91  return kRetOk;
92 }
93 
95  thread::Thread* context,
96  bool disable_no_record_split) {
97  // We invoke split sysxct and adopt sysxct many times.
99  watch.start();
100  uint16_t root_retries = 0;
101  KeySlice cur_slice = kInfimumSlice;
102  uint16_t skipped_children = 0;
103  uint16_t adopted_children = 0;
104  uint32_t initial_children;
105  WRAP_ERROR_CODE(approximate_count_root_children(context, &initial_children));
106  while (cur_slice != kSupremumSlice) {
107  if (initial_children + adopted_children >= kIntermediateAlmostFull) {
108  LOG(INFO) << "Root page nearing full. Stopped fatification";
109  break;
110  }
111 
112  // Get a non-moved root. This might trigger grow-root.
113  // grow-root is a non-mandatory operation, so we might keep seeing a moved root.
115  WRAP_ERROR_CODE(get_first_root(context, true, &root));
118  if (root->is_moved()) {
119  ++root_retries;
120  if (root_retries > 50U) {
121  LOG(WARNING) << "Hm? there might be some contention to prevent grow-root. Gave up";
122  break;
123  }
124  continue;
125  }
126 
127  root_retries = 0;
128  const auto minipage_index = root->find_minipage(cur_slice);
129  auto& minipage = root->get_minipage(minipage_index);
130  auto pointer_index = minipage.find_pointer(cur_slice);
131 
132  MasstreePage* child;
134  context,
135  true,
136  minipage.pointers_ + pointer_index,
137  &child));
138  ASSERT_ND(!child->header().snapshot_);
139  cur_slice = child->get_high_fence(); // go on to next
140 
141  if (!child->is_moved()) {
142  // Split the child so that we can adopt it to the root
143  if (child->is_border()) {
144  if (child->get_key_count() >= 2U) {
145  MasstreeBorderPage* casted = reinterpret_cast<MasstreeBorderPage*>(child);
146  auto low_slice = child->get_low_fence();
147  auto high_slice = child->get_high_fence();
148  auto mid_slice = low_slice + (high_slice - low_slice) / 2U;
149  SplitBorder split(context, casted, mid_slice, disable_no_record_split);
150  WRAP_ERROR_CODE(context->run_nested_sysxct(&split, 2U));
151  } else {
152  ++skipped_children;
153  continue; // not worth splitting
154  }
155  } else {
156  MasstreeIntermediatePage* casted = reinterpret_cast<MasstreeIntermediatePage*>(child);
157  uint32_t grandchild_count = count_children_approximate(casted);
158  if (grandchild_count >= 2U) {
159  SplitIntermediate split(context, casted);
160  WRAP_ERROR_CODE(context->run_nested_sysxct(&split, 2U));
161  } else {
162  ++skipped_children;
163  continue; // not worth splitting
164  }
165  }
166  }
167 
168  Adopt adopt(context, root, child);
169  WRAP_ERROR_CODE(context->run_nested_sysxct(&adopt, 2U));
170  ++adopted_children;
171  }
172 
173  uint32_t after_children;
174  WRAP_ERROR_CODE(approximate_count_root_children(context, &after_children));
175  watch.stop();
176  LOG(INFO) << "fatify_double: adopted " << adopted_children << " root-children and skipped "
177  << skipped_children << " that are already too sparse in " << watch.elapsed_us() << "us."
178  << " child count: " << initial_children << "->" << after_children;
179 
180  return kRetOk;
181 }
182 } // namespace masstree
183 } // namespace storage
184 } // namespace foedus
const KeySlice kInfimumSlice
void start()
Take current time tick.
Definition: stop_watch.cpp:30
SlotIndex get_key_count() const __attribute__((always_inline))
physical key count (those keys might be deleted) in this page.
ErrorStack fatify_first_root(thread::Thread *context, uint32_t desired_count, bool disable_no_record_split)
Defined in masstree_storage_fatify.cpp.
Root package of FOEDUS (Fast Optimistic Engine for Data Unification Services).
Definition: assert_nd.hpp:44
bool is_moved() const __attribute__((always_inline))
bool is_border() const __attribute__((always_inline))
Represents one thread running on one NUMA core.
Definition: thread.hpp:48
ErrorCode follow_page(thread::Thread *context, bool for_writes, storage::DualPagePointer *pointer, MasstreePage **page)
Thread::follow_page_pointer() for masstree.
MiniPage & get_minipage(uint8_t index) __attribute__((always_inline))
Represents one border page in Masstree Storage.
Brings error stacktrace information as return value of functions.
Definition: error_stack.hpp:81
uint64_t KeySlice
Each key slice is an 8-byte integer.
constexpr uint32_t kIntermediateAlmostFull
Common base of MasstreeIntermediatePage and MasstreeBorderPage.
uint32_t count_children_approximate(const MasstreeIntermediatePage *page)
ErrorStack fatify_first_root_double(thread::Thread *context, bool disable_no_record_split)
0 means no-error.
Definition: error_code.hpp:87
ErrorCode get_first_root(thread::Thread *context, bool for_write, MasstreeIntermediatePage **root)
Root-node related, such as a method to retrieve 1st-root, to grow, etc.
A system transaction to split a border page in Master-Tree.
uint64_t stop()
Take another current time tick.
Definition: stop_watch.cpp:35
A system transaction to split an intermediate page in Master-Tree.
KeySlice get_high_fence() const __attribute__((always_inline))
KeySlice get_low_fence() const __attribute__((always_inline))
double elapsed_us() const
Definition: stop_watch.hpp:45
#define CHECK_ERROR_CODE(x)
This macro calls x and checks its returned error code.
Definition: error_code.hpp:155
#define CHECK_ERROR(x)
This macro calls x and checks its returned value.
const ErrorStack kRetOk
Normal return value for no-error case.
const uint32_t kMaxIntermediatePointers
Max number of pointers (if completely filled) stored in an intermediate pages.
const KeySlice kSupremumSlice
Represents one intermediate page in Masstree Storage.
bool snapshot_
Whether this page image is of a snapshot page.
Definition: page.hpp:211
#define ASSERT_ND(x)
A warning-free wrapper macro of assert() that has no performance effect in release mode even when 'x'...
Definition: assert_nd.hpp:72
A high-resolution stop watch.
Definition: stop_watch.hpp:30
ErrorCode run_nested_sysxct(xct::SysxctFunctor *functor, uint32_t max_retries=0)
Methods related to System transactions (sysxct) nested under this thread.
#define WRAP_ERROR_CODE(x)
Same as CHECK_ERROR(x) except it receives only an error code, thus more efficient.
A system transaction to adopt foster twins into an intermediate page.
ErrorCode
Enum of error codes defined in error_code.xmacro.
Definition: error_code.hpp:85
uint8_t find_minipage(KeySlice slice) const __attribute__((always_inline))
Navigates a searching key-slice to one of the mini pages in this page.
uint8_t find_pointer(KeySlice slice) const __attribute__((always_inline))
Navigates a searching key-slice to one of pointers in this mini-page.
ErrorCode approximate_count_root_children(thread::Thread *context, uint32_t *out)
Returns the count of direct children in the first-root node.