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

A system transaction to split an intermediate page in Master-Tree. More...

Detailed Description

A system transaction to split an intermediate page in Master-Tree.

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

Same as border page split.

This does nothing and returns kErrorCodeOk in the following cases:

  • The page turns out to be already split.
  • piggyback_adopt_child_ turns out to be already retired (meaning already adopted)

Locks taken in this sysxct (in order of taking):

  • Page-lock of the target page, and piggyback_adopt_child_ if piggyback_adopt_child_ is non-null. We lock target/piggyback_adopt_child_ in canonical order.

Obviously no chance of deadlocks or even any conditional locks after releasing enclosing user transaction's locks. max_retries=2 should be enough in run_nested_sysxct().

Definition at line 158 of file masstree_split_impl.hpp.

#include <masstree_split_impl.hpp>

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

Classes

struct  SplitStrategy
 Constructed by hierarchically reading all separators and pointers in old page. More...
 

Public Member Functions

 SplitIntermediate (thread::Thread *context, MasstreeIntermediatePage *target, MasstreePage *piggyback_adopt_child=nullptr)
 
virtual ErrorCode run (xct::SysxctWorkspace *sysxct_workspace) override
 Interior node's Split. More...
 
void split_impl_no_error (thread::GrabFreeVolatilePagesScope *free_pages)
 The core implementation after locking relevant pages and acquiring free page resource. More...
 
void decide_strategy (SplitStrategy *out) const
 Subroutine to decide how we will split this page. More...
 
void migrate_pointers (const SplitStrategy &strategy, uint16_t from, uint16_t to, KeySlice expected_last_separator, MasstreeIntermediatePage *dest) const
 Subroutine to construct a new page. More...
 

Public Attributes

thread::Thread *const context_
 Thread context. More...
 
MasstreeIntermediatePage *const target_
 The page to split. More...
 
MasstreePage *const piggyback_adopt_child_
 An optimization for the common case: splitting the parent page to adopt foster twins of a child page. More...
 

Constructor & Destructor Documentation

foedus::storage::masstree::SplitIntermediate::SplitIntermediate ( thread::Thread context,
MasstreeIntermediatePage target,
MasstreePage piggyback_adopt_child = nullptr 
)
inline

Definition at line 179 of file masstree_split_impl.hpp.

183  : xct::SysxctFunctor(),
184  context_(context),
185  target_(target),
186  piggyback_adopt_child_(piggyback_adopt_child) {
187  }
MasstreeIntermediatePage *const target_
The page to split.
MasstreePage *const piggyback_adopt_child_
An optimization for the common case: splitting the parent page to adopt foster twins of a child page...
thread::Thread *const context_
Thread context.

Member Function Documentation

void foedus::storage::masstree::SplitIntermediate::decide_strategy ( SplitIntermediate::SplitStrategy out) const

Subroutine to decide how we will split this page.

Definition at line 552 of file masstree_split_impl.cpp.

References ASSERT_ND, foedus::storage::masstree::SplitIntermediate::SplitStrategy::compact_adopt_, 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::MasstreePage::header(), foedus::storage::VolatilePagePointer::is_equivalent(), foedus::storage::masstree::MasstreePage::is_locked(), foedus::storage::masstree::MasstreePage::is_moved(), foedus::storage::masstree::MasstreeIntermediatePointerIterator::is_valid(), foedus::storage::masstree::kMaxIntermediatePointers, foedus::storage::masstree::SplitIntermediate::SplitStrategy::mid_index_, foedus::storage::masstree::SplitIntermediate::SplitStrategy::mid_separator_, foedus::storage::PageHeader::page_id_, piggyback_adopt_child_, foedus::storage::masstree::SplitIntermediate::SplitStrategy::pointers_, foedus::storage::masstree::SplitIntermediate::SplitStrategy::separators_, foedus::storage::DualPagePointer::snapshot_pointer_, target_, foedus::storage::masstree::SplitIntermediate::SplitStrategy::total_separator_count_, and foedus::storage::DualPagePointer::volatile_pointer_.

Referenced by split_impl_no_error().

552  {
554  out->compact_adopt_ = false;
555  out->total_separator_count_ = 0;
556 
557  // While collecting pointers from the old page, we look for the piggyback_adopt_child_
558  // and replace it with new pointers (so, this increases the total # of separators).
559  KeySlice old_separator = 0;
560  KeySlice new_separator = 0;
561  VolatilePagePointer old_pointer;
564  old_separator = piggyback_adopt_child_->get_low_fence();
565  new_separator = piggyback_adopt_child_->get_foster_fence();
566  old_pointer = VolatilePagePointer(piggyback_adopt_child_->header().page_id_);
567  }
568 
569  bool found_old = false;
570  for (MasstreeIntermediatePointerIterator iter(target_); iter.is_valid(); iter.next()) {
571  const KeySlice low = iter.get_low_key();
572  const KeySlice high = iter.get_high_key();
573  const DualPagePointer& pointer = iter.get_pointer();
574  if (piggyback_adopt_child_ && low == old_separator) {
575  // Found the existing pointer to replace with foster-minor
576  ASSERT_ND(pointer.volatile_pointer_.is_equivalent(old_pointer));
578  out->separators_[out->total_separator_count_] = new_separator;
579  out->pointers_[out->total_separator_count_].volatile_pointer_
581  out->pointers_[out->total_separator_count_].snapshot_pointer_ = 0;
582  ++(out->total_separator_count_);
583 
584  // Also add foster-major as a new entry
585  out->separators_[out->total_separator_count_] = high;
586  out->pointers_[out->total_separator_count_].volatile_pointer_
588  out->pointers_[out->total_separator_count_].snapshot_pointer_ = 0;
589  ++(out->total_separator_count_);
590  found_old = true;
591  } else {
592  out->separators_[out->total_separator_count_] = high;
593  out->pointers_[out->total_separator_count_] = pointer;
594  ++(out->total_separator_count_);
595  }
596  ASSERT_ND(piggyback_adopt_child_ == nullptr || low < old_separator || found_old);
597  }
598 
599  ASSERT_ND(piggyback_adopt_child_ == nullptr || found_old);
600  ASSERT_ND(out->total_separator_count_ >= 2U);
601  ASSERT_ND(out->total_separator_count_ <= kMaxIntermediatePointers + 1U);
602 
603  if (out->total_separator_count_ <= kMaxIntermediatePointers) {
604  // Then, compact_adopt:
605  // we create a dummy foster with empty
606  // range on right side, and just re-structures target page onto left foster twin.
607  // In other words, this is compaction/restructure that is done in another page in RCU fashion.
608  out->compact_adopt_ = true;
609  out->mid_index_ = out->total_separator_count_ - 1U;
610  out->mid_separator_ = target_->get_high_fence();
611  } else if (piggyback_adopt_child_
613  == out->pointers_[out->total_separator_count_ - 1].volatile_pointer_) {
614  DVLOG(0) << "Seems like a sequential insert. let's do no-record split";
615  out->mid_index_ = out->total_separator_count_ - 2U;
616  out->mid_separator_ = out->separators_[out->mid_index_];
617  } else {
618  // Usual: otherwise, we split them into 50-50 distribution.
619  out->mid_index_ = (out->total_separator_count_ - 1U) / 2;
620  out->mid_separator_ = out->separators_[out->mid_index_];
621  }
622 }
MasstreeIntermediatePage *const target_
The page to split.
bool is_locked() const __attribute__((always_inline))
bool is_moved() const __attribute__((always_inline))
uint64_t KeySlice
Each key slice is an 8-byte integer.
VolatilePagePointer get_foster_minor() const __attribute__((always_inline))
VolatilePagePointer get_foster_major() const __attribute__((always_inline))
MasstreePage *const piggyback_adopt_child_
An optimization for the common case: splitting the parent page to adopt foster twins of a child page...
KeySlice get_high_fence() const __attribute__((always_inline))
KeySlice get_low_fence() const __attribute__((always_inline))
KeySlice get_foster_fence() const __attribute__((always_inline))
const uint32_t kMaxIntermediatePointers
Max number of pointers (if completely filled) stored in an intermediate pages.
#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
uint64_t page_id_
Page ID of this page.
Definition: page.hpp:191

Here is the call graph for this function:

Here is the caller graph for this function:

void foedus::storage::masstree::SplitIntermediate::migrate_pointers ( const SplitStrategy strategy,
uint16_t  from,
uint16_t  to,
KeySlice  expected_last_separator,
MasstreeIntermediatePage dest 
) const

Subroutine to construct a new page.

Definition at line 624 of file masstree_split_impl.cpp.

References ASSERT_ND, foedus::storage::masstree::MasstreePage::get_key_count(), foedus::storage::masstree::MasstreeIntermediatePage::get_minipage(), foedus::storage::masstree::MasstreePage::header_, foedus::storage::DualPagePointer::is_both_null(), foedus::storage::masstree::kMaxIntermediateMiniSeparators, foedus::storage::masstree::kMaxIntermediateSeparators, foedus::assorted::memory_fence_release(), foedus::storage::masstree::SplitIntermediate::SplitStrategy::pointers_, foedus::storage::masstree::MasstreeIntermediatePage::MiniPage::pointers_, foedus::storage::masstree::SplitIntermediate::SplitStrategy::separators_, foedus::storage::PageHeader::set_key_count(), and foedus::storage::masstree::MasstreeIntermediatePage::verify_separators().

Referenced by split_impl_no_error().

629  {
630  // construct dest page. copy the separators and pointers.
631  // we distribute them as much as possible in first level. if mini pages have little
632  // entries to start with, following adoption would be only local.
633  float entries_per_mini = static_cast<float>(to - from) / (kMaxIntermediateSeparators + 1);
634  ASSERT_ND(to > from);
635  const uint16_t move_count = to - from;
636 
637  // it looks a bit complicated because each separator is "one-off" due to first-level separator.
638  // so we buffer one separator.
639  float next_mini_threshold = entries_per_mini;
640  uint8_t cur_mini = 0;
641  uint8_t cur_mini_separators = 0;
642  auto* cur_mini_page = &dest->get_minipage(0);
643  cur_mini_page->pointers_[0] = strategy.pointers_[from];
644  ASSERT_ND(!strategy.pointers_[from].is_both_null());
645  KeySlice next_separator = strategy.separators_[from];
646 
647  for (uint16_t i = 1; i < move_count; ++i) {
648  uint16_t original_index = i + from;
649  ASSERT_ND(!strategy.pointers_[original_index].is_both_null());
650  if (i >= next_mini_threshold && cur_mini < kMaxIntermediateSeparators) {
651  // switch to next mini page. so, the separator goes to the first level
652  assorted::memory_fence_release(); // set key count after all
653  cur_mini_page->key_count_ = cur_mini_separators; // close the current
654  ASSERT_ND(cur_mini_page->key_count_ <= kMaxIntermediateMiniSeparators);
655 
656  dest->separators_[cur_mini] = next_separator;
657 
658  next_mini_threshold += entries_per_mini;
659  cur_mini_separators = 0;
660  ++cur_mini;
661  cur_mini_page = &dest->get_minipage(cur_mini);
662  cur_mini_page->pointers_[0] = strategy.pointers_[original_index];
663  } else {
664  // still the same mini page. so, the separator goes to the second level
665  cur_mini_page->separators_[cur_mini_separators] = next_separator;
666  ++cur_mini_separators;
667  ASSERT_ND(cur_mini_separators <= kMaxIntermediateMiniSeparators);
668 
669  cur_mini_page->pointers_[cur_mini_separators] = strategy.pointers_[original_index];
670  }
671  next_separator = strategy.separators_[original_index];
672  }
673  cur_mini_page->key_count_ = cur_mini_separators; // close the last one
674  ASSERT_ND(cur_mini_page->key_count_ <= kMaxIntermediateMiniSeparators);
676  dest->header_.set_key_count(cur_mini); // set key count after all
677  ASSERT_ND(dest->get_key_count() <= kMaxIntermediateSeparators);
678 
679  // the last separator is ignored because it's foster-fence/high-fence.
680  ASSERT_ND(next_separator == expected_last_separator);
681 
682  dest->verify_separators();
683 }
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.
const uint16_t kMaxIntermediateSeparators
Max number of separators stored in the first level of intermediate pages.
#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::SplitIntermediate::run ( xct::SysxctWorkspace sysxct_workspace)
overridevirtual

Interior node's Split.

Implements foedus::xct::SysxctFunctor.

Definition at line 443 of file masstree_split_impl.cpp.

References CHECK_ERROR_CODE, context_, foedus::thread::GrabFreeVolatilePagesScope::grab(), foedus::storage::masstree::MasstreePage::has_foster_child(), foedus::storage::masstree::MasstreePage::is_retired(), foedus::kErrorCodeOk, piggyback_adopt_child_, split_impl_no_error(), foedus::thread::Thread::sysxct_batch_page_locks(), foedus::thread::Thread::sysxct_page_lock(), and target_.

443  {
444  DVLOG(1) << "Preparing to split an intermediate page... ";
445 
446  // First, lock the page(s)
447  Page* target_page = reinterpret_cast<Page*>(target_);
449  Page* pages[2];
450  pages[0] = target_page;
451  pages[1] = reinterpret_cast<Page*>(piggyback_adopt_child_);
452  // lock target_ and piggyback_adopt_child_ in batched mode to avoid deadlock.
453  CHECK_ERROR_CODE(context_->sysxct_batch_page_locks(sysxct_workspace, 2, pages));
454  } else {
455  CHECK_ERROR_CODE(context_->sysxct_page_lock(sysxct_workspace, target_page));
456  }
457 
458  // The lock involves atomic operation, so now all we see are finalized.
459  if (target_->has_foster_child()) {
460  DVLOG(0) << "Interesting. the page has been already split";
461  return kErrorCodeOk;
462  }
464  VLOG(0) << "Interesting. this child is now retired, so someone else has already adopted.";
465  return kErrorCodeOk; // fine. the goal is already achieved
466  }
467 
468  // 3 free volatile pages needed.
469  // foster-minor/major (will be placed in successful case), and SplitStrategy (will be discarded)
470  memory::PagePoolOffset offsets[2];
471  thread::GrabFreeVolatilePagesScope free_pages_scope(context_, offsets);
472  CHECK_ERROR_CODE(free_pages_scope.grab(2));
473  split_impl_no_error(&free_pages_scope);
474  return kErrorCodeOk;
475 }
MasstreeIntermediatePage *const target_
The page to split.
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.
uint32_t PagePoolOffset
Offset in PagePool that compactly represents the page address (unlike 8 bytes pointer).
Definition: memory_id.hpp:44
bool is_retired() const __attribute__((always_inline))
0 means no-error.
Definition: error_code.hpp:87
ErrorCode sysxct_page_lock(xct::SysxctWorkspace *sysxct_workspace, storage::Page *page)
Takes a page lock in the same page for a sysxct running under this thread.
MasstreePage *const piggyback_adopt_child_
An optimization for the common case: splitting the parent page to adopt foster twins of a child page...
void split_impl_no_error(thread::GrabFreeVolatilePagesScope *free_pages)
The core implementation after locking relevant pages and acquiring free page resource.
bool has_foster_child() const __attribute__((always_inline))
#define CHECK_ERROR_CODE(x)
This macro calls x and checks its returned error code.
Definition: error_code.hpp:155
thread::Thread *const context_
Thread context.

Here is the call graph for this function:

void foedus::storage::masstree::SplitIntermediate::split_impl_no_error ( thread::GrabFreeVolatilePagesScope free_pages)

The core implementation after locking relevant pages and acquiring free page resource.

This method never fails because all lock/resource are already taken.

Precondition
target_->is_locked()
free_pages->get_count() >= 2
piggyback_adopt_child_ == nullptr || piggyback_adopt_child_->is_locked()
target_->has_foster_child()) : should be checked after locking before calling
piggyback_adopt_child_ == nullptr || !piggyback_adopt_child_->is_retired(): same above

Definition at line 476 of file masstree_split_impl.cpp.

References ASSERT_ND, foedus::thread::Thread::collect_retired_volatile_page(), foedus::storage::masstree::SplitIntermediate::SplitStrategy::compact_adopt_, context_, decide_strategy(), foedus::thread::GrabFreeVolatilePagesScope::dispatch(), foedus::thread::GrabFreeVolatilePagesScope::get(), foedus::storage::masstree::MasstreePage::get_btree_level(), foedus::storage::masstree::MasstreePage::get_key_count(), foedus::storage::masstree::MasstreePage::get_layer(), foedus::thread::Thread::get_local_volatile_page_resolver(), foedus::thread::Thread::get_numa_node(), foedus::storage::masstree::MasstreePage::get_volatile_page_id(), foedus::storage::masstree::MasstreePage::header(), foedus::storage::masstree::MasstreePage::high_fence_, foedus::storage::masstree::MasstreeIntermediatePage::initialize_volatile_page(), foedus::storage::masstree::MasstreePage::install_foster_twin(), foedus::storage::masstree::MasstreePage::is_empty_range(), foedus::storage::masstree::MasstreePage::low_fence_, foedus::assorted::memory_fence_release(), foedus::storage::masstree::SplitIntermediate::SplitStrategy::mid_index_, foedus::storage::masstree::SplitIntermediate::SplitStrategy::mid_separator_, migrate_pointers(), piggyback_adopt_child_, foedus::storage::VolatilePagePointer::set(), foedus::storage::masstree::MasstreePage::set_key_count(), foedus::storage::masstree::MasstreePage::set_moved(), foedus::storage::masstree::MasstreePage::set_retired(), foedus::storage::PageHeader::snapshot_, foedus::storage::PageHeader::storage_id_, target_, foedus::storage::masstree::SplitIntermediate::SplitStrategy::total_separator_count_, and foedus::storage::masstree::MasstreeIntermediatePage::verify_separators().

Referenced by run().

476  {
477  // similar to border page's split, but simpler in a few places because
478  // 1) intermediate page doesn't have owner_id for each pointer (no lock concerns).
479  // 2) intermediate page is already completely sorted.
480  // thus, this is just a physical operation without any transactional behavior.
481  // even not a system transaction
484 
485  debugging::RdtscWatch watch;
486  DVLOG(1) << "Splitting an intermediate page... ";
487 
488  const uint8_t key_count = target_->get_key_count();
490 
491  // 2 free volatile pages needed.
492  // foster-minor/major (will be placed in successful case)
493  const auto& resolver = context_->get_local_volatile_page_resolver();
494 
495  // SplitStrategy on heap. 4kb is not too large on heap
496  SplitStrategy strategy;
497  decide_strategy(&strategy);
498  const KeySlice new_foster_fence = strategy.mid_separator_;
499 
500  MasstreeIntermediatePage* twin[2];
501  VolatilePagePointer new_pointers[2];
502  for (int i = 0; i < 2; ++i) {
503  twin[i]
504  = reinterpret_cast<MasstreeIntermediatePage*>(
505  resolver.resolve_offset_newpage(free_pages->get(i)));
506  new_pointers[i].set(context_->get_numa_node(), free_pages->get(i));
507 
508  twin[i]->initialize_volatile_page(
510  new_pointers[i],
511  target_->get_layer(),
512  target_->get_btree_level(), // foster child has the same level as foster-parent
513  i == 0 ? target_->low_fence_ : new_foster_fence,
514  i == 0 ? new_foster_fence : target_->high_fence_);
515  }
516 
517  migrate_pointers(strategy, 0, strategy.mid_index_ + 1, new_foster_fence, twin[0]);
518  if (strategy.compact_adopt_) {
519  twin[1]->set_key_count(0); // right is empty-range
520  } else {
522  strategy,
523  strategy.mid_index_ + 1,
524  strategy.total_separator_count_,
526  twin[1]);
527  }
528 
529  // Now we will install the new pages. **From now on no error-return allowed**
531  // We install pointers to the pages AFTER we initialize the pages.
532  target_->install_foster_twin(new_pointers[0], new_pointers[1], new_foster_fence);
533  free_pages->dispatch(0);
534  free_pages->dispatch(1);
536  // invoking set_moved is the point we announce all of these changes. take fence to make it right
537  target_->set_moved();
538 
540  // piggyback_adopt_child_ is adopted and retired.
543  }
544 
545  watch.stop();
546  DVLOG(1) << "Costed " << watch.elapsed() << " cycles to split a node. original node"
547  << " key count: " << static_cast<int>(key_count);
548 
550 }
void migrate_pointers(const SplitStrategy &strategy, uint16_t from, uint16_t to, KeySlice expected_last_separator, MasstreeIntermediatePage *dest) const
Subroutine to construct a new page.
KeySlice high_fence_
Inclusive high fence of this page.
SlotIndex get_key_count() const __attribute__((always_inline))
physical key count (those keys might be deleted) in this page.
bool is_empty_range() const __attribute__((always_inline))
An empty-range page, either intermediate or border, never has any entries.
MasstreeIntermediatePage *const target_
The page to split.
KeySlice low_fence_
Inclusive low fence of this page.
void collect_retired_volatile_page(storage::VolatilePagePointer ptr)
Keeps the specified volatile page as retired as of the current epoch.
Definition: thread.cpp:113
StorageId storage_id_
ID of the storage this page belongs to.
Definition: page.hpp:196
void set_moved() __attribute__((always_inline))
uint64_t KeySlice
Each key slice is an 8-byte integer.
void install_foster_twin(VolatilePagePointer minor, VolatilePagePointer major, KeySlice foster_fence)
uint8_t get_btree_level() const __attribute__((always_inline))
used only in masstree.
MasstreePage *const piggyback_adopt_child_
An optimization for the common case: splitting the parent page to adopt foster twins of a child page...
uint8_t get_layer() const __attribute__((always_inline))
Layer-0 stores the first 8 byte slice, Layer-1 next 8 byte...
void set_retired() __attribute__((always_inline))
const memory::LocalPageResolver & get_local_volatile_page_resolver() const
Returns page resolver to convert only local page ID to page pointer.
Definition: thread.cpp:80
VolatilePagePointer get_volatile_page_id() const
ThreadGroupId get_numa_node() const
Definition: thread.hpp:66
thread::Thread *const context_
Thread context.
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
void decide_strategy(SplitStrategy *out) const
Subroutine to decide how we will split this page.
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:

Member Data Documentation

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

Thread context.

Definition at line 160 of file masstree_split_impl.hpp.

Referenced by run(), and split_impl_no_error().

MasstreePage* const foedus::storage::masstree::SplitIntermediate::piggyback_adopt_child_

An optimization for the common case: splitting the parent page to adopt foster twins of a child page.

The child page whose foster-twins will be adopted in the course of this split. Without this optimization, we need two Sysxct invocation, split and adopt. This also gives a hint whether it might be no-record-split (NRS). null if no such piggy-back adoption (in that case ignores the possibility of NRS).

Precondition
piggyback_adopt_child_ == nullptr || piggyback_adopt_child_->has_foster_child()

Definition at line 177 of file masstree_split_impl.hpp.

Referenced by decide_strategy(), run(), and split_impl_no_error().

MasstreeIntermediatePage* const foedus::storage::masstree::SplitIntermediate::target_

The page to split.

Precondition
!target_->header_.snapshot_ (split happens to only volatile pages)

Definition at line 165 of file masstree_split_impl.hpp.

Referenced by decide_strategy(), run(), and split_impl_no_error().


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