libfoedus-core
FOEDUS Core Library
masstree_storage_peek.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 
23 #include "foedus/storage/page.hpp"
26 
27 namespace foedus {
28 namespace storage {
29 namespace masstree {
30 
32  Engine* engine, const MasstreeStorage::PeekBoundariesArguments& args) {
33  MasstreeStoragePimpl pimpl(this);
34  return pimpl.peek_volatile_page_boundaries(engine, args);
35 }
36 
38  Engine* engine,
40  *args.found_boundary_count_ = 0;
41 
43  if (root_pointer.is_null()) {
44  VLOG(0) << "This masstree has no volatile pages. Maybe the composer is executed as part of"
45  << " restart?";
46  return kErrorCodeOk;
47  }
48 
51  const MasstreePage* root
52  = reinterpret_cast<const MasstreePage*>(resolver.resolve_offset(root_pointer));
53 
54  if (args.prefix_slice_count_ == 0) {
55  return peek_volatile_page_boundaries_this_layer(root, resolver, args);
56  } else {
57  return peek_volatile_page_boundaries_next_layer(root, resolver, args);
58  }
59 }
60 
62  const MasstreePage* layer_root,
63  const memory::GlobalVolatilePageResolver& resolver,
65  ASSERT_ND(!layer_root->header().snapshot_);
66  uint8_t this_layer = layer_root->get_layer();
67  ASSERT_ND(this_layer < args.prefix_slice_count_);
68  ASSERT_ND(layer_root->get_low_fence() == kInfimumSlice && layer_root->is_high_fence_supremum());
69  KeySlice slice = args.prefix_slices_[layer_root->get_layer()];
70 
71  // look for this slice in this layer. If we can't find it (which is unlikely), no results.
72  // compared to tree-traversal code in the transactional methods, we can give up whenever
73  // something rare happens.
74  const MasstreePage* cur = layer_root;
75  cur->prefetch_general();
76  while (!cur->is_border()) {
77  ASSERT_ND(cur->within_fences(slice));
78  // We do NOT follow foster-twins here.
79  // Foster-twins are sooner or later adopted, and Master-Tree invariant for intermediate page
80  // guarantees that we can just read the old page. no need to follow foster-twins.
81  const MasstreeIntermediatePage* page = reinterpret_cast<const MasstreeIntermediatePage*>(cur);
82  uint8_t minipage_index = page->find_minipage(slice);
83  const MasstreeIntermediatePage::MiniPage& minipage = page->get_minipage(minipage_index);
84 
85  minipage.prefetch();
86  uint8_t pointer_index = minipage.find_pointer(slice);
87  VolatilePagePointer pointer = minipage.pointers_[pointer_index].volatile_pointer_;
88  const MasstreePage* next
89  = reinterpret_cast<const MasstreePage*>(resolver.resolve_offset(pointer));
90  next->prefetch_general();
91  if (LIKELY(next->within_fences(slice))) {
92  cur = next;
93  } else {
94  // even in this case, local retry suffices thanks to foster-twin
95  VLOG(0) << "Interesting. concurrent thread affected the search. local retry";
97  }
98  }
99  const MasstreeBorderPage* border = reinterpret_cast<const MasstreeBorderPage*>(cur);
100 
101  // following code are not transactional at all, but it's fine because these are just hints.
102  // however, make sure we don't hit something like segfault. check nulls etc.
103  ASSERT_ND(border->within_fences(slice));
104  uint8_t key_count = border->get_key_count();
106  border->prefetch_additional_if_needed(key_count);
107  for (SlotIndex i = 0; i < key_count; ++i) {
108  KeySlice cur_slice = border->get_slice(i);
109  if (LIKELY(cur_slice < slice)) {
110  continue;
111  }
112  if (cur_slice > slice) {
113  break;
114  }
115  // one slice might be used for up to 10 keys, length 0 to 8 and pointer to next layer.
116  if (border->does_point_to_layer(i)) {
117  rec = i;
118  break;
119  }
120  }
121 
122  if (UNLIKELY(rec >= key_count || !border->does_point_to_layer(rec))) {
123  LOG(INFO) << "Slice not found during peeking. Gives up finding a page boundary hint";
124  return kErrorCodeOk;
125  }
126 
127  VolatilePagePointer pointer = border->get_next_layer(rec)->volatile_pointer_;
128  if (UNLIKELY(pointer.is_null()
129  || pointer.get_numa_node() >= resolver.numa_node_count_
130  || pointer.get_offset() < resolver.begin_
131  || pointer.get_offset() >= resolver.end_)) {
132  LOG(INFO) << "Encountered invalid page during peeking. Gives up finding a page boundary hint";
133  return kErrorCodeOk;
134  }
135  const MasstreePage* next_root
136  = reinterpret_cast<const MasstreePage*>(resolver.resolve_offset(pointer));
137  if (this_layer + 1U == args.prefix_slice_count_) {
138  return peek_volatile_page_boundaries_this_layer(next_root, resolver, args);
139  } else {
140  return peek_volatile_page_boundaries_next_layer(next_root, resolver, args);
141  }
142 }
143 
145  const MasstreePage* layer_root,
146  const memory::GlobalVolatilePageResolver& resolver,
148  ASSERT_ND(!layer_root->header().snapshot_);
149  ASSERT_ND(layer_root->get_layer() == args.prefix_slice_count_);
150  ASSERT_ND(layer_root->get_low_fence() == kInfimumSlice && layer_root->is_high_fence_supremum());
151  if (layer_root->is_border()) {
152  ASSERT_ND(layer_root->get_layer() > 0); // first layer's root page is always intermediate
153  // the root page of the layer of interest is a border page, hence no boundaries.
154  return kErrorCodeOk;
155  }
156  const MasstreeIntermediatePage* cur
157  = reinterpret_cast<const MasstreeIntermediatePage*>(layer_root);
158  return peek_volatile_page_boundaries_this_layer_recurse(cur, resolver, args);
159 }
160 
162  const MasstreeIntermediatePage* cur,
163  const memory::GlobalVolatilePageResolver& resolver,
165  ASSERT_ND(!cur->header().snapshot_);
166  ASSERT_ND(!cur->is_border());
167  ASSERT_ND(cur->get_layer() == args.prefix_slice_count_);
168  // because of concurrent modifications, this is possible. we just give up finding hints.
169  if (UNLIKELY(cur->get_low_fence() >= args.to_ || cur->get_high_fence() <= args.from_)) {
170  return kErrorCodeOk;
171  }
172 
173  uint8_t begin_index = 0;
174  uint8_t begin_mini_index = 0;
175  if (cur->within_fences(args.from_)) {
176  begin_index = cur->find_minipage(args.from_);
177  const MasstreeIntermediatePage::MiniPage& minipage = cur->get_minipage(begin_index);
178  minipage.prefetch();
179  begin_mini_index = minipage.find_pointer(args.from_);
180  }
181 
182  // if children of this page are intermediate pages (this level>=2), we further recurse.
183  // if children of this page are border pages, just append the page boundaries stored in this page.
184  const bool needs_recurse = (cur->get_btree_level() >= 2U);
185  // we don't care wherther the child has foster twins. it's rare, and most keys are already
186  // pushed up to this page. again, this method is opportunistic.
187  // this guarantees that the cost of peeking is cheap. we just read intermediate pages.
188  // again, this code is not protected from concurrent transactions at all.
189  // we just make sure it doesn't hit segfault etc.
191  it.index_ = begin_index;
192  it.index_mini_ = begin_mini_index;
193  for (; it.is_valid(); it.next()) {
195  KeySlice boundary = it.get_low_key();
196  if (boundary >= args.to_) { // all pages under the pointer are >= boundary.
197  break;
198  }
199  if (boundary > args.from_) {
200  // at least guarantee that the resulting found_boundaries_ is ordered.
201  if ((*args.found_boundary_count_) == 0
202  || args.found_boundaries_[(*args.found_boundary_count_) - 1U] < boundary) {
204  args.found_boundaries_[*args.found_boundary_count_] = boundary;
205  ++(*args.found_boundary_count_);
206  if ((*args.found_boundary_count_) >= args.found_boundary_capacity_) {
207  VLOG(0) << "Found too many boundaries while peeking. stops here.";
208  return kErrorCodeOk;
209  }
210  }
211  }
212  // recurse to find more. note that it might contain pages of interest
213  // even if boundary < args.from_ because it's a *low*-fence.
214  if (needs_recurse && !pointer.is_null()) {
215  if (LIKELY(pointer.get_numa_node() < resolver.numa_node_count_
216  && pointer.get_offset() >= resolver.begin_
217  && pointer.get_offset() < resolver.end_)) {
218  const MasstreeIntermediatePage* next
219  = reinterpret_cast<const MasstreeIntermediatePage*>(resolver.resolve_offset(pointer));
220  if (next->is_border()) {
221  // this is possible because our masstree might be unbalanced.
222  ASSERT_ND(cur->get_layer() == 0); // but it can happen only at first layer's root
224  continue;
225  }
229  if ((*args.found_boundary_count_) >= args.found_boundary_capacity_) {
230  return kErrorCodeOk;
231  }
232  }
233  }
234  }
235 
236  return kErrorCodeOk;
237 }
238 
239 } // namespace masstree
240 } // namespace storage
241 } // namespace foedus
ErrorCode peek_volatile_page_boundaries_this_layer(const MasstreePage *layer_root, const memory::GlobalVolatilePageResolver &resolver, const MasstreeStorage::PeekBoundariesArguments &args)
uint32_t found_boundary_capacity_
[IN] capacity of found_boundaries_.
const KeySlice kInfimumSlice
void prefetch_additional_if_needed(SlotIndex key_count) const __attribute__((always_inline))
storage::Page * resolve_offset(uint8_t numa_node, PagePoolOffset offset) const __attribute__((always_inline))
Resolves offset plus NUMA node ID to storage::Page*.
SlotIndex get_key_count() const __attribute__((always_inline))
physical key count (those keys might be deleted) in this page.
ErrorCode peek_volatile_page_boundaries(Engine *engine, const MasstreeStorage::PeekBoundariesArguments &args)
Defined in masstree_storage_peek.cpp.
const KeySlice * prefix_slices_
[IN] slices of higher layers that lead to the B-trie layer of interest.
KeySlice get_slice(SlotIndex index) const __attribute__((always_inline))
ErrorCode peek_volatile_page_boundaries_this_layer_recurse(const MasstreeIntermediatePage *cur, const memory::GlobalVolatilePageResolver &resolver, const MasstreeStorage::PeekBoundariesArguments &args)
uint16_t SlotIndex
Index of a record in a (border) page.
ErrorCode peek_volatile_page_boundaries_next_layer(const MasstreePage *layer_root, const memory::GlobalVolatilePageResolver &resolver, const MasstreeStorage::PeekBoundariesArguments &args)
Root package of FOEDUS (Fast Optimistic Engine for Data Unification Services).
Definition: assert_nd.hpp:44
KeySlice to_
[IN] lists up page boundaries up to this slice
bool is_border() const __attribute__((always_inline))
void prefetch_general() const __attribute__((always_inline))
prefetch upto keys/separators, whether this page is border or interior.
uint16_t numa_node_count_
number of NUMA nodes in this engine.
const GlobalVolatilePageResolver & get_global_volatile_page_resolver() const
Returns the page resolver to convert volatile page ID to page pointer.
Represents a pointer to a volatile page with modification count for preventing ABA.
Definition: storage_id.hpp:194
MiniPage & get_minipage(uint8_t index) __attribute__((always_inline))
PagePoolOffset begin_
where a valid page entry starts.
Represents one border page in Masstree Storage.
DualPagePointer * get_next_layer(SlotIndex index) __attribute__((always_inline))
uint64_t KeySlice
Each key slice is an 8-byte integer.
PagePoolOffset end_
where a valid page entry ends.
Common base of MasstreeIntermediatePage and MasstreeBorderPage.
#define LIKELY(x)
Hints that x is highly likely true.
Definition: compiler.hpp:103
DualPagePointer pointers_[kMaxIntermediateMiniSeparators+1]
VolatilePagePointer volatile_pointer_
Definition: storage_id.hpp:308
0 means no-error.
Definition: error_code.hpp:87
memory::PagePoolOffset get_offset() const
Definition: storage_id.hpp:202
ErrorCode peek_volatile_page_boundaries(Engine *engine, const PeekBoundariesArguments &args)
Checks the volatile pages of this storage to give hints to decide page boundary keys.
PageType get_page_type() const
Definition: page.hpp:280
Database engine object that holds all resources and provides APIs.
Definition: engine.hpp:109
uint8_t get_btree_level() const __attribute__((always_inline))
used only in masstree.
uint32_t * found_boundary_count_
[OUT] number of found_boundaries_ entries returned
const SlotIndex kBorderPageMaxSlots
Maximum number of slots in one MasstreeBorderPage.
KeySlice * found_boundaries_
[OUT] fence-slices of border pages between from-to
uint8_t get_layer() const __attribute__((always_inline))
Layer-0 stores the first 8 byte slice, Layer-1 next 8 byte...
KeySlice get_high_fence() const __attribute__((always_inline))
KeySlice get_low_fence() const __attribute__((always_inline))
bool is_high_fence_supremum() const __attribute__((always_inline))
#define CHECK_ERROR_CODE(x)
This macro calls x and checks its returned error code.
Definition: error_code.hpp:155
void memory_fence_acquire()
Equivalent to std::atomic_thread_fence(std::memory_order_acquire).
Resolves an offset in a volatile page pool to an actual pointer and vice versa.
Represents one intermediate page in Masstree Storage.
#define UNLIKELY(x)
Hints that x is highly likely false.
Definition: compiler.hpp:104
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
bool within_fences(KeySlice slice) const __attribute__((always_inline))
bool does_point_to_layer(SlotIndex index) const __attribute__((always_inline))
memory::EngineMemory * get_memory_manager() const
See Memory Manager.
Definition: engine.cpp:50
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.
KeySlice from_
[IN] lists up page boundaries from this slice
uint8_t find_pointer(KeySlice slice) const __attribute__((always_inline))
Navigates a searching key-slice to one of pointers in this mini-page.