libfoedus-core
FOEDUS Core Library
masstree_storage_verify.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/engine.hpp"
29 #include "foedus/thread/thread.hpp"
30 
31 namespace foedus {
32 namespace storage {
33 namespace masstree {
34 
35 #define CHECK_AND_ASSERT(x) do { ASSERT_ND(x); if (!(x)) \
36  return ERROR_STACK(kErrorCodeStrMasstreeFailedVerification); } while (0)
37 
38 
40  MasstreeIntermediatePage* layer_root;
41  WRAP_ERROR_CODE(get_first_root(context, false, &layer_root));
42  CHECK_AND_ASSERT(!layer_root->is_border()); // root of first layer is always intermediate page
43  CHECK_ERROR(verify_single_thread_layer(context, 0, layer_root));
44  return kRetOk;
45 }
46 
48  thread::Thread* context,
49  uint8_t layer,
50  MasstreePage* layer_root) {
51  CHECK_AND_ASSERT(layer_root->get_layer() == layer);
52  HighFence high_fence(kSupremumSlice, true);
53  if (layer_root->is_border()) {
55  context,
57  high_fence,
58  reinterpret_cast<MasstreeBorderPage*>(layer_root)));
59  } else {
61  context,
63  high_fence,
64  reinterpret_cast<MasstreeIntermediatePage*>(layer_root)));
65  }
66  return kRetOk;
67 }
68 
70  thread::Thread* context,
71  MasstreePage* page,
72  PageType page_type,
73  KeySlice low_fence,
74  HighFence high_fence) {
75  CHECK_AND_ASSERT(!page->is_locked());
76  CHECK_AND_ASSERT(page->header().get_page_type() == page_type);
77  CHECK_AND_ASSERT(page->is_border() || page->get_btree_level() > 0);
78  CHECK_AND_ASSERT(!page->is_border() || page->get_btree_level() == 0);
79  CHECK_AND_ASSERT(page->get_low_fence() == low_fence);
80  CHECK_AND_ASSERT(page->get_high_fence() == high_fence.slice_);
81  CHECK_AND_ASSERT(page->is_high_fence_supremum() == high_fence.supremum_);
83  (page->is_moved() && page->has_foster_child()
84  && !page->is_foster_major_null() && !page->is_foster_minor_null()) ||
85  (!page->is_moved() && !page->has_foster_child()
86  && page->is_foster_major_null() && page->is_foster_minor_null()));
87 
88  if (!page->is_foster_major_null()) {
90  CHECK_AND_ASSERT(!context->resolve(page->get_foster_major())->get_header().snapshot_);
91  CHECK_AND_ASSERT(context->resolve(page->get_foster_major())->get_header().get_page_type()
92  == page_type);
93  }
94  if (!page->is_foster_minor_null()) {
96  CHECK_AND_ASSERT(!context->resolve(page->get_foster_minor())->get_header().snapshot_);
97  CHECK_AND_ASSERT(context->resolve(page->get_foster_minor())->get_header().get_page_type()
98  == page_type);
99  }
100  return kRetOk;
101 }
102 
104  thread::Thread* context,
105  KeySlice low_fence,
106  HighFence high_fence,
107  MasstreeIntermediatePage* page) {
108  CHECK_ERROR(
109  verify_page_basic(context, page, kMasstreeIntermediatePageType, low_fence, high_fence));
110 
111  if (page->is_empty_range()) {
112  CHECK_AND_ASSERT(page->get_key_count() == 0);
113  CHECK_AND_ASSERT(!page->is_moved());
114  return kRetOk;
115  }
116 
117  if (page->is_moved()) {
118  auto* minor = context->resolve_cast<MasstreeIntermediatePage>(page->get_foster_minor());
119  auto* major = context->resolve_cast<MasstreeIntermediatePage>(page->get_foster_major());
120  // If major is empty-range, the minor might be supremum
121  bool is_minor_supremum = high_fence.supremum_ && page->get_foster_fence() == kSupremumSlice;
123  context,
124  low_fence,
125  HighFence(page->get_foster_fence(), is_minor_supremum),
126  minor));
128  context,
129  page->get_foster_fence(),
130  high_fence,
131  major));
132  return kRetOk;
133  }
134 
135  uint8_t key_count = page->get_key_count();
137  KeySlice previous_low = low_fence;
138  for (uint8_t i = 0; i <= key_count; ++i) {
139  HighFence mini_high(0, false);
140  if (i < key_count) {
141  mini_high.slice_ = page->get_separator(i);
142  mini_high.supremum_ = false;
143  CHECK_AND_ASSERT(high_fence.supremum_ || mini_high.slice_ < high_fence.slice_);
144  if (i == 0) {
145  CHECK_AND_ASSERT(mini_high.slice_ > low_fence);
146  } else {
147  CHECK_AND_ASSERT(mini_high.slice_ > page->get_separator(i - 1));
148  }
149  } else {
150  mini_high = high_fence;
151  }
152 
154  uint8_t mini_count = minipage.key_count_;
156  KeySlice page_low = previous_low;
157  for (uint8_t j = 0; j <= mini_count; ++j) {
158  HighFence page_high(0, false);
159  if (j < mini_count) {
160  page_high.slice_ = minipage.separators_[j];
161  page_high.supremum_ = false;
162  CHECK_AND_ASSERT(page_high.slice_ < mini_high.slice_ || mini_high.supremum_);
163  if (j == 0) {
164  CHECK_AND_ASSERT(page_high.slice_ > previous_low);
165  } else {
166  CHECK_AND_ASSERT(page_high.slice_ > minipage.separators_[j - 1]);
167  }
168  } else {
169  page_high = mini_high;
170  }
171  CHECK_AND_ASSERT(!minipage.pointers_[j].is_both_null());
172  MasstreePage* next;
173  // TASK(Hideaki) probably two versions: always follow volatile vs snapshot
174  // so far check volatile only
175  WRAP_ERROR_CODE(follow_page(context, true, &minipage.pointers_[j], &next));
176  CHECK_AND_ASSERT(next->get_layer() == page->get_layer());
177  CHECK_AND_ASSERT(next->get_btree_level() + 1U == page->get_btree_level());
178  if (next->is_border()) {
180  context,
181  page_low,
182  page_high,
183  reinterpret_cast<MasstreeBorderPage*>(next)));
184  } else {
186  context,
187  page_low,
188  page_high,
189  reinterpret_cast<MasstreeIntermediatePage*>(next)));
190  }
191 
192  page_low = page_high.slice_;
193  }
194 
195  previous_low = mini_high.slice_;
196  }
197 
198  return kRetOk;
199 }
200 
202  thread::Thread* context,
203  KeySlice low_fence,
204  HighFence high_fence,
205  MasstreeBorderPage* page) {
206  CHECK_ERROR(verify_page_basic(context, page, kMasstreeBorderPageType, low_fence, high_fence));
207  // check consecutive_inserts_. this should be consistent whether it's moved or not.
208  bool sorted = true;
209  for (SlotIndex i = 1; i < page->get_key_count(); ++i) {
210  KeySlice prev = page->get_slice(i - 1);
211  KeySlice slice = page->get_slice(i);
212  KeyLength prev_len = page->get_remainder_length(i - 1);
213  KeyLength len = page->get_remainder_length(i);
214  if (prev > slice || (prev == slice && prev_len > len)) {
215  sorted = false;
216  break;
217  }
218  }
219  CHECK_AND_ASSERT(page->is_consecutive_inserts() == sorted);
220 
221  if (page->is_moved()) {
222  auto* minor = context->resolve_cast<MasstreeBorderPage>(page->get_foster_minor());
223  auto* major = context->resolve_cast<MasstreeBorderPage>(page->get_foster_major());
224  // If major is empty-range, the minor might be supremum
225  bool is_minor_supremum = high_fence.supremum_ && page->get_foster_fence() == kSupremumSlice;
227  context,
228  low_fence,
229  HighFence(page->get_foster_fence(), is_minor_supremum),
230  minor));
232  context,
233  page->get_foster_fence(),
234  high_fence,
235  major));
236  return kRetOk;
237  }
238 
239  CHECK_AND_ASSERT(!page->is_moved());
241  for (SlotIndex i = 0; i < page->get_key_count(); ++i) {
246  KeySlice slice = page->get_slice(i);
247  CHECK_AND_ASSERT(slice >= low_fence);
248  CHECK_AND_ASSERT(slice < high_fence.slice_ || page->is_high_fence_supremum());
249  if (page->does_point_to_layer(i)) {
252  MasstreePage* next;
253  // TASK(Hideaki) probably two versions: always follow volatile vs snapshot
254  // so far check volatile only
255  WRAP_ERROR_CODE(follow_page(context, true, page->get_next_layer(i), &next));
256  CHECK_ERROR(verify_single_thread_layer(context, page->get_layer() + 1, next));
257  } else {
259  }
260  }
261 
262  return kRetOk;
263 }
264 
265 
266 } // namespace masstree
267 } // namespace storage
268 } // namespace foedus
KeyLength get_remainder_length(SlotIndex index) const __attribute__((always_inline))
const KeySlice kInfimumSlice
SlotIndex get_key_count() const __attribute__((always_inline))
physical key count (those keys might be deleted) in this page.
#define CHECK_AND_ASSERT(x)
KeySlice get_slice(SlotIndex index) const __attribute__((always_inline))
bool is_empty_range() const __attribute__((always_inline))
An empty-range page, either intermediate or border, never has any entries.
uint16_t SlotIndex
Index of a record in a (border) page.
bool is_locked() const __attribute__((always_inline))
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.
ErrorStack verify_single_thread_border(thread::Thread *context, KeySlice low_fence, HighFence high_fence, MasstreeBorderPage *page)
bool is_foster_minor_null() const __attribute__((always_inline))
PageType
The following 1-byte value is stored in the common page header.
Definition: page.hpp:46
bool is_foster_major_null() const __attribute__((always_inline))
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
bool is_consecutive_inserts() const
Whether this page is receiving only sequential inserts.
DualPagePointer * get_next_layer(SlotIndex index) __attribute__((always_inline))
XctId xct_id_
the second 64bit: Persistent status part of TID.
Definition: xct_id.hpp:1137
uint64_t KeySlice
Each key slice is an 8-byte integer.
Definitions of IDs in this package and a few related constant values.
const uint16_t kMaxIntermediateMiniSeparators
Max number of separators stored in the second level of intermediate pages.
uint16_t KeyLength
Represents a byte-length of a key in this package.
Definition: masstree_id.hpp:69
Common base of MasstreeIntermediatePage and MasstreeBorderPage.
VolatilePagePointer get_foster_minor() const __attribute__((always_inline))
VolatilePagePointer get_foster_major() const __attribute__((always_inline))
McsRwLock lock_
the first 64bit: Locking part of TID
Definition: xct_id.hpp:1134
KeySlice get_separator(uint8_t index) const __attribute__((always_inline))
storage::Page * resolve(storage::VolatilePagePointer ptr) const
Shorthand for get_global_volatile_page_resolver.resolve_offset()
Definition: thread.cpp:129
DualPagePointer pointers_[kMaxIntermediateMiniSeparators+1]
const uint16_t kMaxIntermediateSeparators
Max number of separators stored in the first level of intermediate pages.
PageType get_page_type() const
Definition: page.hpp:338
PageType get_page_type() const
Definition: page.hpp:280
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.
uint8_t get_btree_level() const __attribute__((always_inline))
used only in masstree.
Epoch get_epoch() const __attribute__((always_inline))
Definition: xct_id.hpp:964
const SlotIndex kBorderPageMaxSlots
Maximum number of slots in one MasstreeBorderPage.
bool is_next_layer() const __attribute__((always_inline))
Definition: xct_id.hpp:1042
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))
P * resolve_cast(storage::VolatilePagePointer ptr) const
resolve() plus reinterpret_cast
Definition: thread.hpp:110
KeySlice separators_[kMaxIntermediateMiniSeparators]
Same semantics as separators_ in enclosing class.
Used only for debugging as this is not space efficient.
bool is_locked() const
Definition: xct_id.hpp:857
bool has_foster_child() const __attribute__((always_inline))
KeySlice get_low_fence() const __attribute__((always_inline))
KeySlice get_foster_fence() const __attribute__((always_inline))
ErrorStack verify_page_basic(thread::Thread *context, MasstreePage *page, PageType page_type, KeySlice low_fence, HighFence high_fence)
bool is_valid() const
Definition: epoch.hpp:96
bool is_high_fence_supremum() const __attribute__((always_inline))
#define CHECK_ERROR(x)
This macro calls x and checks its returned value.
const ErrorStack kRetOk
Normal return value for no-error case.
ErrorStack verify_single_thread(thread::Thread *context)
These are defined in masstree_storage_verify.cpp.
ErrorStack verify_single_thread_intermediate(thread::Thread *context, KeySlice low_fence, HighFence high_fence, MasstreeIntermediatePage *page)
bool is_being_written() const __attribute__((always_inline))
Definition: xct_id.hpp:1038
const KeySlice kSupremumSlice
xct::RwLockableXctId * get_owner_id(SlotIndex index) __attribute__((always_inline))
Represents one intermediate page in Masstree Storage.
bool snapshot_
Whether this page image is of a snapshot page.
Definition: page.hpp:211
#define WRAP_ERROR_CODE(x)
Same as CHECK_ERROR(x) except it receives only an error code, thus more efficient.
bool does_point_to_layer(SlotIndex index) const __attribute__((always_inline))
ErrorStack verify_single_thread_layer(thread::Thread *context, uint8_t layer, MasstreePage *layer_root)