libfoedus-core
FOEDUS Core Library
foedus::storage::masstree::Adopt Struct Referencefinal

A system transaction to adopt foster twins into an intermediate page. More...

Detailed Description

A system transaction to adopt foster twins into an intermediate page.

See also
Physical-only, short-living system transactions.

This system transaction follows page-splits, which leave foster twins. The sysxct replaces an existing separator and pointer to the old-page with two new separators and pointers to the grandchildren. The parent-page thus need a room for one more separator/pointer, which might require page split.

There are following cases:

  • Case-A – Easies/fastest case. One of the grandchildren is an empty-range page. We simply discard such a page, thus we just replace the old pointer in parent page with a pointer to the non-empty grandchild.
  • Case-B – Normal cases. The grandchildren are not empty. When the pointer to old-page is the last entry of a non-full minipage in parent page, we can append at last of minipage. The same applies to the case where the pointer is the last entry of a full minipage that is the last minipage in a non-full parent page. Otherwise, we have to split the parent page (see below).

The caller of this sysxct reads and follows pointers without page-latch or fences because reads/traversals must be lock-free and as efficient as possible. This sysxct must make sure we are replacing the right pointer with adopted pointer(s).

This does nothing and returns kErrorCodeOk in the following cases:

  • The parent page turns out to be moved.
  • The old page turns out to be retired (already adopted).

Locks taken in this sysxct:

  • Page-lock of the parent page and old page (will be retied).

So far this sysxct adopts only one child at a time. TASK(Hideaki): Probably it helps by delaying and batching several adoptions.

See also
SplitIntermediate

Definition at line 67 of file masstree_adopt_impl.hpp.

#include <masstree_adopt_impl.hpp>

Inheritance diagram for foedus::storage::masstree::Adopt:
Collaboration diagram for foedus::storage::masstree::Adopt:

Public Member Functions

 Adopt (thread::Thread *context, MasstreeIntermediatePage *parent, MasstreePage *old)
 
virtual ErrorCode run (xct::SysxctWorkspace *sysxct_workspace) override
 Execute the system transaction. More...
 
ErrorCode adopt_case_a (uint16_t minipage_index, uint16_t pointer_index)
 
ErrorCode adopt_case_b (uint16_t minipage_index, uint16_t pointer_index)
 

Public Attributes

thread::Thread *const context_
 Thread context. More...
 
MasstreeIntermediatePage *const parent_
 The parent page that currently points to the old page. More...
 
MasstreePage *const old_
 The old page that was split, whose foster-twins are being adopted. More...
 

Constructor & Destructor Documentation

foedus::storage::masstree::Adopt::Adopt ( thread::Thread context,
MasstreeIntermediatePage parent,
MasstreePage old 
)
inline

Definition at line 78 of file masstree_adopt_impl.hpp.

82  : xct::SysxctFunctor(),
83  context_(context),
84  parent_(parent),
85  old_(old) {
86  }
MasstreePage *const old_
The old page that was split, whose foster-twins are being adopted.
MasstreeIntermediatePage *const parent_
The parent page that currently points to the old page.
thread::Thread *const context_
Thread context.

Member Function Documentation

ErrorCode foedus::storage::masstree::Adopt::adopt_case_a ( uint16_t  minipage_index,
uint16_t  pointer_index 
)

Definition at line 73 of file masstree_adopt_impl.cpp.

References ASSERT_ND, foedus::thread::Thread::collect_retired_volatile_page(), context_, foedus::storage::masstree::MasstreePage::get_foster_fence(), foedus::storage::masstree::MasstreePage::get_foster_major(), foedus::storage::masstree::MasstreePage::get_foster_minor(), foedus::storage::masstree::MasstreePage::get_high_fence(), foedus::storage::masstree::MasstreePage::get_low_fence(), foedus::storage::masstree::MasstreeIntermediatePage::get_minipage(), foedus::storage::masstree::MasstreePage::get_version_address(), foedus::storage::masstree::MasstreePage::get_volatile_page_id(), foedus::storage::masstree::MasstreePage::header(), foedus::storage::masstree::MasstreePage::is_empty_range(), foedus::storage::masstree::MasstreePage::is_locked(), foedus::kErrorCodeOk, foedus::storage::PageVersionStatus::kRetiredBit, old_, parent_, foedus::thread::Thread::resolve_cast(), foedus::storage::masstree::MasstreePage::set_retired(), foedus::storage::PageHeader::snapshot_, foedus::storage::PageVersionStatus::status_, and foedus::storage::PageVersion::status_.

Referenced by run().

75  {
76  VLOG(0) << "Adopting from a child page that contains an empty-range page. This happens when"
77  << " record compaction/expansion created a page without a record.";
78 
79  MasstreePage* grandchild_minor = context_->resolve_cast<MasstreePage>(old_->get_foster_minor());
80  ASSERT_ND(grandchild_minor->get_low_fence() == old_->get_low_fence());
81  ASSERT_ND(grandchild_minor->get_high_fence() == old_->get_foster_fence());
82  MasstreePage* grandchild_major = context_->resolve_cast<MasstreePage>(old_->get_foster_major());
83  ASSERT_ND(grandchild_major->get_low_fence() == old_->get_foster_fence());
84  ASSERT_ND(grandchild_major->get_high_fence() == old_->get_high_fence());
85  ASSERT_ND(!grandchild_minor->header().snapshot_);
86  ASSERT_ND(!grandchild_major->header().snapshot_);
87 
88  ASSERT_ND(grandchild_minor->is_empty_range() != grandchild_major->is_empty_range());
89 
90  VolatilePagePointer nonempty_grandchild_pointer;
91  MasstreePage* empty_grandchild = nullptr;
92  MasstreePage* nonempty_grandchild = nullptr;
93  if (grandchild_minor->is_empty_range()) {
94  empty_grandchild = grandchild_minor;
95  nonempty_grandchild = grandchild_major;
96  } else {
97  empty_grandchild = grandchild_major;
98  nonempty_grandchild = grandchild_minor;
99  }
100 
101  auto& minipage = parent_->get_minipage(minipage_index);
102  ASSERT_ND(old_->get_volatile_page_id() == minipage.pointers_[pointer_index].volatile_pointer_);
103  minipage.pointers_[pointer_index].snapshot_pointer_ = 0;
104  minipage.pointers_[pointer_index].volatile_pointer_ = nonempty_grandchild->get_volatile_page_id();
105 
106  // The only thread that might be retiring this empty page must be in this function,
107  // holding a page-lock in scope_child. Thus we don't need a lock in empty_grandchild.
108  ASSERT_ND(!empty_grandchild->is_locked()); // none else holding lock on it
109  // and we can safely retire the page. We do not use set_retired because is_moved() is false
110  // It's a special retirement path.
111  empty_grandchild->get_version_address()->status_.status_ |= PageVersionStatus::kRetiredBit;
112  context_->collect_retired_volatile_page(empty_grandchild->get_volatile_page_id());
113  old_->set_retired();
115  return kErrorCodeOk;
116 }
void collect_retired_volatile_page(storage::VolatilePagePointer ptr)
Keeps the specified volatile page as retired as of the current epoch.
Definition: thread.cpp:113
MasstreePage *const old_
The old page that was split, whose foster-twins are being adopted.
MiniPage & get_minipage(uint8_t index) __attribute__((always_inline))
VolatilePagePointer get_foster_minor() const __attribute__((always_inline))
MasstreeIntermediatePage *const parent_
The parent page that currently points to the old page.
VolatilePagePointer get_foster_major() const __attribute__((always_inline))
0 means no-error.
Definition: error_code.hpp:87
thread::Thread *const context_
Thread context.
void set_retired() __attribute__((always_inline))
KeySlice get_high_fence() const __attribute__((always_inline))
P * resolve_cast(storage::VolatilePagePointer ptr) const
resolve() plus reinterpret_cast
Definition: thread.hpp:110
KeySlice get_low_fence() const __attribute__((always_inline))
KeySlice get_foster_fence() const __attribute__((always_inline))
VolatilePagePointer get_volatile_page_id() const
#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

Here is the call graph for this function:

Here is the caller graph for this function:

ErrorCode foedus::storage::masstree::Adopt::adopt_case_b ( uint16_t  minipage_index,
uint16_t  pointer_index 
)

Definition at line 118 of file masstree_adopt_impl.cpp.

References ASSERT_ND, CHECK_ERROR_CODE, foedus::thread::Thread::collect_retired_volatile_page(), context_, foedus::storage::masstree::MasstreePage::get_foster_fence(), foedus::storage::masstree::MasstreePage::get_foster_major(), foedus::storage::masstree::MasstreePage::get_foster_minor(), foedus::storage::masstree::MasstreePage::get_key_count(), foedus::storage::masstree::MasstreeIntermediatePage::get_minipage(), foedus::storage::masstree::MasstreePage::get_volatile_page_id(), foedus::thread::GrabFreeVolatilePagesScope::grab(), foedus::storage::masstree::MasstreePage::increment_key_count(), foedus::storage::masstree::MasstreePage::is_retired(), foedus::kErrorCodeOk, foedus::storage::masstree::MasstreeIntermediatePage::MiniPage::key_count_, foedus::storage::masstree::kMaxIntermediateMiniSeparators, foedus::storage::masstree::kMaxIntermediateSeparators, foedus::assorted::memory_fence_release(), old_, parent_, foedus::storage::masstree::MasstreePage::set_retired(), foedus::storage::masstree::MasstreeIntermediatePage::set_separator(), foedus::storage::DualPagePointer::snapshot_pointer_, and foedus::storage::DualPagePointer::volatile_pointer_.

Referenced by run().

120  {
121  const auto key_count = parent_->get_key_count();
122  const KeySlice new_separator = old_->get_foster_fence();
123  const VolatilePagePointer minor_pointer = old_->get_foster_minor();
124  const VolatilePagePointer major_pointer = old_->get_foster_major();
125  auto& minipage = parent_->get_minipage(minipage_index);
126  ASSERT_ND(old_->get_volatile_page_id() == minipage.pointers_[pointer_index].volatile_pointer_);
127 
128  // Can we simply append the new pointer either as a new minipage at the end of this page
129  // or as a new separator at the end of a minipage? Then we don't need major change.
130  // We simply append without any change, which is easy to make right.
131  if (minipage.key_count_ == pointer_index) {
132  if (minipage.key_count_ < kMaxIntermediateMiniSeparators) {
133  // We can append a new separator at the end of the minipage.
134  ASSERT_ND(!minipage.pointers_[pointer_index].is_both_null());
135  ASSERT_ND(pointer_index == 0 || minipage.separators_[pointer_index - 1] < new_separator);
136  minipage.separators_[pointer_index] = new_separator;
137  minipage.pointers_[pointer_index + 1].snapshot_pointer_ = 0;
138  minipage.pointers_[pointer_index + 1].volatile_pointer_ = major_pointer;
139 
140  // we don't have to adopt the foster-minor because that's the child page itself,
141  // but we have to switch the pointer
142  minipage.pointers_[pointer_index].snapshot_pointer_ = 0;
143  minipage.pointers_[pointer_index].volatile_pointer_ = minor_pointer;
144 
145  // we increase key count after above, with fence, so that concurrent transactions
146  // never see an empty slot.
148  ++minipage.key_count_;
149  ASSERT_ND(minipage.key_count_ <= kMaxIntermediateMiniSeparators);
150 
151  old_->set_retired();
153  return kErrorCodeOk;
154  } else if (key_count == minipage_index && key_count < kMaxIntermediateSeparators) {
155  // The minipage is full.. and the minipage is the last one!
156  // We can add it as a new minipage
157  auto& new_minipage = parent_->get_minipage(minipage_index + 1);
158  new_minipage.key_count_ = 0;
159 
160 #ifndef NDEBUG
161  // for ease of debugging zero-out the page first (only data part). only for debug build.
162  for (uint8_t j = 0; j <= kMaxIntermediateMiniSeparators; ++j) {
164  new_minipage.separators_[j] = 0;
165  }
166  new_minipage.pointers_[j].snapshot_pointer_ = 0;
167  new_minipage.pointers_[j].volatile_pointer_.word = 0;
168  }
169 #endif // NDEBUG
170 
171  ASSERT_ND(new_minipage.key_count_ == 0);
172  new_minipage.pointers_[0].snapshot_pointer_ = 0;
173  new_minipage.pointers_[0].volatile_pointer_ = major_pointer;
174 
175  // also handle foster-twin if it's border page
176  DualPagePointer& old_pointer = minipage.pointers_[minipage.key_count_];
177  old_pointer.snapshot_pointer_ = 0;
178  old_pointer.volatile_pointer_ = minor_pointer;
179 
180  parent_->set_separator(minipage_index, new_separator);
181 
182  // increment key count after all with fence so that concurrent transactions never see
183  // a minipage that is not ready for read
186  ASSERT_ND(parent_->get_key_count() == minipage_index + 1);
187 
188  old_->set_retired();
190  return kErrorCodeOk;
191  }
192  }
193 
194  // In all other cases, we split this page.
195  // We initially had more complex code to do in-page rebalance and
196  // in-minipage "shifting", but got some bugs. Pulled out too many hairs.
197  // Let's keep it simple. If we really observe bottleneck here, we can reconsider.
198  // Splitting is way more robust because it changes nothing in this existing page.
199  // It just places new foster-twin pointers.
200 
201  // Reuse SplitIntermediate. We are directly invoking the sysxct's internal logic
202  // rather than nesting sysxct.
203  memory::PagePoolOffset offsets[2];
204  thread::GrabFreeVolatilePagesScope free_pages_scope(context_, offsets);
205  CHECK_ERROR_CODE(free_pages_scope.grab(2));
206  SplitIntermediate split(context_, parent_, old_);
207  split.split_impl_no_error(&free_pages_scope);
208  ASSERT_ND(old_->is_retired()); // the above internally retires old page
209  return kErrorCodeOk;
210 }
SlotIndex get_key_count() const __attribute__((always_inline))
physical key count (those keys might be deleted) in this page.
uint32_t PagePoolOffset
Offset in PagePool that compactly represents the page address (unlike 8 bytes pointer).
Definition: memory_id.hpp:44
void collect_retired_volatile_page(storage::VolatilePagePointer ptr)
Keeps the specified volatile page as retired as of the current epoch.
Definition: thread.cpp:113
MasstreePage *const old_
The old page that was split, whose foster-twins are being adopted.
MiniPage & get_minipage(uint8_t index) __attribute__((always_inline))
uint64_t KeySlice
Each key slice is an 8-byte integer.
const uint16_t kMaxIntermediateMiniSeparators
Max number of separators stored in the second level of intermediate pages.
VolatilePagePointer get_foster_minor() const __attribute__((always_inline))
bool is_retired() const __attribute__((always_inline))
MasstreeIntermediatePage *const parent_
The parent page that currently points to the old page.
VolatilePagePointer get_foster_major() const __attribute__((always_inline))
0 means no-error.
Definition: error_code.hpp:87
const uint16_t kMaxIntermediateSeparators
Max number of separators stored in the first level of intermediate pages.
thread::Thread *const context_
Thread context.
void increment_key_count() __attribute__((always_inline))
void set_retired() __attribute__((always_inline))
void set_separator(uint8_t minipage_index, KeySlice new_separator)
Place a new separator for a new minipage.
KeySlice get_foster_fence() const __attribute__((always_inline))
#define CHECK_ERROR_CODE(x)
This macro calls x and checks its returned error code.
Definition: error_code.hpp:155
VolatilePagePointer get_volatile_page_id() const
#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
void memory_fence_release()
Equivalent to std::atomic_thread_fence(std::memory_order_release).

Here is the call graph for this function:

Here is the caller graph for this function:

ErrorCode foedus::storage::masstree::Adopt::run ( xct::SysxctWorkspace sysxct_workspace)
overridevirtual

Execute the system transaction.

You should override this method.

Implements foedus::xct::SysxctFunctor.

Definition at line 32 of file masstree_adopt_impl.cpp.

References adopt_case_a(), adopt_case_b(), ASSERT_ND, CHECK_ERROR_CODE, context_, foedus::storage::masstree::MasstreeIntermediatePage::find_minipage(), foedus::storage::masstree::MasstreeIntermediatePage::MiniPage::find_pointer(), foedus::storage::masstree::MasstreePage::get_foster_fence(), foedus::storage::masstree::MasstreePage::get_high_fence(), foedus::storage::masstree::MasstreePage::get_low_fence(), foedus::storage::masstree::MasstreeIntermediatePage::get_minipage(), foedus::storage::masstree::MasstreePage::get_volatile_page_id(), foedus::storage::masstree::MasstreePage::header(), foedus::storage::masstree::MasstreePage::is_moved(), foedus::storage::masstree::MasstreePage::is_retired(), foedus::kErrorCodeOk, foedus::storage::masstree::kMaxIntermediateMiniSeparators, old_, parent_, foedus::storage::PageHeader::snapshot_, foedus::thread::Thread::sysxct_batch_page_locks(), and foedus::storage::masstree::MasstreeIntermediatePage::verify_separators().

32  {
35  ASSERT_ND(old_->is_moved()); // this is guaranteed because these flag are immutable once set.
36 
37  // Lock pages in one shot.
38  Page* pages[2];
39  pages[0] = reinterpret_cast<Page*>(parent_);
40  pages[1] = reinterpret_cast<Page*>(old_);
41  CHECK_ERROR_CODE(context_->sysxct_batch_page_locks(sysxct_workspace, 2, pages));
42 
43  // After the above lock, we check status of the pages
44  if (parent_->is_moved()) {
45  VLOG(0) << "Interesting. concurrent thread has already split this node?";
46  return kErrorCodeOk;
47  } else if (old_->is_retired()) {
48  VLOG(0) << "Interesting. concurrent thread already adopted.";
49  return kErrorCodeOk;
50  }
51 
52  const KeySlice searching_slice = old_->get_low_fence();
53  const auto minipage_index = parent_->find_minipage(searching_slice);
54  auto& minipage = parent_->get_minipage(minipage_index);
55  const auto pointer_index = minipage.find_pointer(searching_slice);
56  ASSERT_ND(minipage.key_count_ <= kMaxIntermediateMiniSeparators);
57  ASSERT_ND(old_->get_volatile_page_id() == minipage.pointers_[pointer_index].volatile_pointer_);
58 
59  // Now, how do we accommodate the new pointer?
62  // Case A. One of the grandchildrens is empty-range
63  CHECK_ERROR_CODE(adopt_case_a(minipage_index, pointer_index));
64  } else {
65  // Case B. More complex, normal case
66  CHECK_ERROR_CODE(adopt_case_b(minipage_index, pointer_index));
67  }
68 
70  return kErrorCodeOk;
71 }
ErrorCode adopt_case_b(uint16_t minipage_index, uint16_t pointer_index)
ErrorCode sysxct_batch_page_locks(xct::SysxctWorkspace *sysxct_workspace, uint32_t lock_count, storage::Page **pages)
Takes a bunch of page locks for a sysxct running under this thread.
bool is_moved() const __attribute__((always_inline))
MasstreePage *const old_
The old page that was split, whose foster-twins are being adopted.
MiniPage & get_minipage(uint8_t index) __attribute__((always_inline))
uint64_t KeySlice
Each key slice is an 8-byte integer.
const uint16_t kMaxIntermediateMiniSeparators
Max number of separators stored in the second level of intermediate pages.
bool is_retired() const __attribute__((always_inline))
MasstreeIntermediatePage *const parent_
The parent page that currently points to the old page.
0 means no-error.
Definition: error_code.hpp:87
ErrorCode adopt_case_a(uint16_t minipage_index, uint16_t pointer_index)
thread::Thread *const context_
Thread context.
KeySlice get_high_fence() const __attribute__((always_inline))
KeySlice get_low_fence() const __attribute__((always_inline))
KeySlice get_foster_fence() const __attribute__((always_inline))
#define CHECK_ERROR_CODE(x)
This macro calls x and checks its returned error code.
Definition: error_code.hpp:155
VolatilePagePointer get_volatile_page_id() const
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
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.

Here is the call graph for this function:

Member Data Documentation

thread::Thread* const foedus::storage::masstree::Adopt::context_

Thread context.

Definition at line 69 of file masstree_adopt_impl.hpp.

Referenced by adopt_case_a(), adopt_case_b(), and run().

MasstreePage* const foedus::storage::masstree::Adopt::old_

The old page that was split, whose foster-twins are being adopted.

Precondition
old_->is_moved()

Definition at line 76 of file masstree_adopt_impl.hpp.

Referenced by adopt_case_a(), adopt_case_b(), and run().

MasstreeIntermediatePage* const foedus::storage::masstree::Adopt::parent_

The parent page that currently points to the old page.

Definition at line 71 of file masstree_adopt_impl.hpp.

Referenced by adopt_case_a(), adopt_case_b(), and run().


The documentation for this struct was generated from the following files: