libfoedus-core
FOEDUS Core Library
masstree_reserve_impl.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"
25 #include "foedus/thread/thread.hpp"
26 
27 namespace foedus {
28 namespace storage {
29 namespace masstree {
30 
32  xct::XctId initial_id;
33  initial_id.set(
34  Epoch::kEpochInitialCurrent, // TODO(Hideaki) this should be something else
35  0);
36  return initial_id;
37 }
38 
40  memory::NumaCoreMemory* memory = context->get_thread_memory();
42  if (offset == 0) {
43  return nullptr;
44  }
45 
46  const auto &resolver = context->get_local_volatile_page_resolver();
47  MasstreeBorderPage* new_page
48  = reinterpret_cast<MasstreeBorderPage*>(resolver.resolve_offset_newpage(offset));
49  VolatilePagePointer new_page_pointer;
50  new_page_pointer.set(context->get_numa_node(), offset);
51  new_page->header().page_id_ = new_page_pointer.word;
52  return new_page;
53 }
54 
56  out_split_needed_ = false;
59  CHECK_ERROR_CODE(context_->sysxct_page_lock(sysxct_workspace, reinterpret_cast<Page*>(target_)));
61 
62  // After page-lock, key-count and record-keys (not payloads) are fixed. Check them now!
63  if (target_->is_moved()) {
64  DVLOG(0) << "Interesting. this page has been split";
65  return kErrorCodeOk;
66  }
68  const SlotIndex key_count = target_->get_key_count();
69  const VolatilePagePointer page_id(target_->header().page_id_);
72  key_count,
73  slice_,
74  suffix_,
76 
77  // Let's check whether we need to split this page.
78  // If we can accomodate it without splitting the page, complete it within here.
79  if (match.match_type_ == MasstreeBorderPage::kExactMatchLayerPointer) {
80  // Definitelly done. The caller must folllow the next-layer
81  ASSERT_ND(match.index_ < kBorderPageMaxSlots);
82  return kErrorCodeOk;
83  } else if (match.match_type_ == MasstreeBorderPage::kExactMatchLocalRecord) {
84  // Is it enough spacious?
85  ASSERT_ND(match.index_ < kBorderPageMaxSlots);
86  if (target_->get_max_payload_length(match.index_) >= payload_count_) {
87  return kErrorCodeOk; // Yes! done.
88  }
89 
90  DVLOG(2) << "Need to expand the record.";
91  auto* record = target_->get_owner_id(match.index_);
92  CHECK_ERROR_CODE(context_->sysxct_record_lock(sysxct_workspace, page_id, record));
93  ASSERT_ND(record->is_keylocked());
94 
95  // Now the state of the record is finalized. Let's check it again.
96  ASSERT_ND(!record->is_moved()); // can't be moved as target_ is not moved.
97  if (target_->get_max_payload_length(match.index_) >= payload_count_) {
98  return kErrorCodeOk; // Yes! done.
99  } else if (record->is_next_layer()) {
100  DVLOG(0) << "Interesting. the record now points to next layer";
101  return kErrorCodeOk; // same kExactMatchLayerPointer
102  }
103 
104  bool expanded = target_->try_expand_record_in_page_physical(payload_count_, match.index_);
105  if (expanded) {
107  return kErrorCodeOk;
108  }
109 
110  DVLOG(0) << "Ouch. need to split for allocating a space for record expansion";
111  } else if (match.match_type_ == MasstreeBorderPage::kConflictingLocalRecord) {
112  // We will create a next layer.
113  // In this case, page-lock was actually an overkill because of key-immutability,
114  // However, doing it after page-lock makes the code simpler.
115  // In most cases, the caller finds the conflicting local record before calling this
116  // sysxct. No need to optimize for this rare case.
117  ASSERT_ND(match.index_ < kBorderPageMaxSlots);
118 
119  auto* record = target_->get_owner_id(match.index_);
120  CHECK_ERROR_CODE(context_->sysxct_record_lock(sysxct_workspace, page_id, record));
121  ASSERT_ND(record->is_keylocked());
122 
123  // Now the state of the record is finalized. Let's check it again.
124  ASSERT_ND(!record->is_moved()); // can't be moved as target_ is not moved.
125  if (record->is_next_layer()) {
126  DVLOG(0) << "Interesting. the record now points to next layer";
127  return kErrorCodeOk; // same kExactMatchLayerPointer
128  }
129 
130  // Can we trivially turn this into a next-layer record?
131  if (target_->get_max_payload_length(match.index_) < sizeof(DualPagePointer)) {
132  DVLOG(1) << "We need to expand the record to make it a next-layer."
133  " If this happens too often and is the bottleneck, "
134  " you should have used physical_payload_hint when you initially inserted the record.";
135  bool expanded
137  if (expanded) {
138  ASSERT_ND(target_->get_max_payload_length(match.index_) >= sizeof(DualPagePointer));
139  }
140  }
141 
142  if (target_->get_max_payload_length(match.index_) >= sizeof(DualPagePointer)) {
143  // Turn it into a next-layer
146  if (offset == 0) {
148  }
149  MasstreeBorderPage* new_layer_root = reinterpret_cast<MasstreeBorderPage*>(
151  VolatilePagePointer new_page_id;
152  new_page_id.set(context_->get_numa_node(), offset);
153  new_layer_root->initialize_as_layer_root_physical(new_page_id, target_, match.index_);
154  return kErrorCodeOk;
155  }
156 
157  DVLOG(0) << "Ouch. need to split for allocating a space for next-layer";
158  } else {
159  ASSERT_ND(match.match_type_ == MasstreeBorderPage::kNotFound);
161  target_->can_accomodate(key_count, sizeof(KeySlice), sizeof(DualPagePointer))) {
162  DVLOG(1) << "Aggressively creating a next-layer.";
163 
165  if (root == nullptr) {
167  }
168  DualPagePointer pointer;
169  pointer.snapshot_pointer_ = 0;
170  pointer.volatile_pointer_ = root->get_volatile_page_id();
171 
174  pointer.volatile_pointer_,
175  target_->get_layer() + 1U,
176  kInfimumSlice, // infimum slice
177  kSupremumSlice); // high-fence is supremum
178  ASSERT_ND(!root->is_locked());
179  ASSERT_ND(!root->is_moved());
180  ASSERT_ND(!root->is_retired());
181  ASSERT_ND(root->get_key_count() == 0);
182 
183  xct::XctId initial_id = get_initial_xid();
184  initial_id.set_next_layer();
185  target_->reserve_initially_next_layer(key_count, initial_id, slice_, pointer);
186 
191 
195  return kErrorCodeOk;
196  }
199  xct::XctId initial_id = get_initial_xid();
200  initial_id.set_deleted();
202  key_count,
203  initial_id,
204  slice_,
205  suffix_,
208  // we increment key count AFTER installing the key because otherwise the optimistic read
209  // might see the record but find that the key doesn't match. we need a fence to prevent it.
216  return kErrorCodeOk;
217  }
218 
219  DVLOG(1) << "Ouch. need to split for allocating a space for new record";
220  }
221 
222  // If we are here, we need to split the page for some reason.
223  out_split_needed_ = true;
224  return kErrorCodeOk;
225 }
226 
227 } // namespace masstree
228 } // namespace storage
229 } // namespace foedus
void reserve_record_space(SlotIndex index, xct::XctId initial_owner_id, KeySlice slice, const void *suffix, KeyLength remainder_length, PayloadLength payload_count)
Installs a new physical record that doesn't exist logically (delete bit on).
MasstreeBorderPage *const target_
The page to install a new physical record.
const KeySlice kInfimumSlice
SlotIndex get_key_count() const __attribute__((always_inline))
physical key count (those keys might be deleted) in this page.
MasstreeBorderPage * allocate_new_border_page(thread::Thread *context)
Represents a pointer to another page (usually a child page).
Definition: storage_id.hpp:271
const KeySlice slice_
The slice of the key.
memory::NumaCoreMemory * get_thread_memory() const
Returns the private memory repository of this thread.
Definition: thread.cpp:57
PagePoolOffset grab_free_volatile_page()
Acquires one free volatile page from local page pool.
The first epoch (before wrap-around) that might have transactions is ep-3.
Definition: epoch.hpp:77
uint16_t SlotIndex
Index of a record in a (border) page.
void initialize_as_layer_root_physical(VolatilePagePointer page_id, MasstreeBorderPage *parent, SlotIndex parent_index)
A physical-only method to initialize this page as a volatile page of a layer-root pointed from the gi...
PayloadLength get_max_payload_length(SlotIndex index) const __attribute__((always_inline))
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))
ErrorCode sysxct_record_lock(xct::SysxctWorkspace *sysxct_workspace, storage::VolatilePagePointer page_id, xct::RwLockableXctId *lock)
Takes a lock for a sysxct running under this thread.
Represents one thread running on one NUMA core.
Definition: thread.hpp:48
uint32_t PagePoolOffset
Offset in PagePool that compactly represents the page address (unlike 8 bytes pointer).
Definition: memory_id.hpp:44
StorageId storage_id_
ID of the storage this page belongs to.
Definition: page.hpp:196
Represents a pointer to a volatile page with modification count for preventing ABA.
Definition: storage_id.hpp:194
storage::Page * resolve_offset_newpage(PagePoolOffset offset) const __attribute__((always_inline))
As the name suggests, this version is for new pages, which don't have sanity checks.
Persistent status part of Transaction ID.
Definition: xct_id.hpp:955
Represents one border page in Masstree Storage.
DualPagePointer * get_next_layer(SlotIndex index) __attribute__((always_inline))
const PayloadLength payload_count_
Minimal required length of the payload.
uint64_t KeySlice
Each key slice is an 8-byte integer.
thread::Thread *const context_
Thread context.
bool can_accomodate(SlotIndex new_index, KeyLength remainder_length, PayloadLength payload_count) const __attribute__((always_inline))
bool is_retired() const __attribute__((always_inline))
Repository of memories dynamically acquired within one CPU core (thread).
void set(Epoch::EpochInteger epoch_int, uint32_t ordinal)
Definition: xct_id.hpp:958
FindKeyForReserveResult find_key_for_reserve(SlotIndex from_index, SlotIndex to_index, KeySlice slice, const void *suffix, KeyLength remainder) const __attribute__((always_inline))
This is for the case we are looking for either the matching slot or the slot we will modify...
VolatilePagePointer volatile_pointer_
Definition: storage_id.hpp:308
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.
SnapshotPagePointer snapshot_pointer_
Definition: storage_id.hpp:307
void increment_key_count() __attribute__((always_inline))
virtual ErrorCode run(xct::SysxctWorkspace *sysxct_workspace) override
Execute the system transaction.
const void *const suffix_
Suffix of the key.
const SlotIndex kBorderPageMaxSlots
Maximum number of slots in one MasstreeBorderPage.
void set(uint8_t numa_node, memory::PagePoolOffset offset)
Definition: storage_id.hpp:212
uint8_t get_layer() const __attribute__((always_inline))
Layer-0 stores the first 8 byte slice, Layer-1 next 8 byte...
const bool should_aggresively_create_next_layer_
When we CAN create a next layer for the new record, whether to make it a next-layer from the beginnin...
void assert_entries() __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
0x0301 : "MEMORY : Not enough free volatile pages. Check the config of MemoryOptions" ...
Definition: error_code.hpp:142
void initialize_volatile_page(StorageId storage_id, VolatilePagePointer page_id, uint8_t layer, KeySlice low_fence, KeySlice high_fence)
bool try_expand_record_in_page_physical(PayloadLength payload_count, SlotIndex record_index)
A physical-only method to expand a record within this page without any logical change.
#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
ThreadGroupId get_numa_node() const
Definition: thread.hpp:66
const KeyLength remainder_length_
Length of the remainder.
const KeySlice kSupremumSlice
xct::RwLockableXctId * get_owner_id(SlotIndex index) __attribute__((always_inline))
const SlotIndex hint_check_from_
The in-page location from which this sysxct will look for matching records.
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 reserve_initially_next_layer(SlotIndex index, xct::XctId initial_owner_id, KeySlice slice, const DualPagePointer &pointer)
For creating a record that is initially a next-layer.
void memory_fence_release()
Equivalent to std::atomic_thread_fence(std::memory_order_release).
bool does_point_to_layer(SlotIndex index) const __attribute__((always_inline))
ErrorCode
Enum of error codes defined in error_code.xmacro.
Definition: error_code.hpp:85
Per-thread reused work memory for system transactions.
uint64_t page_id_
Page ID of this page.
Definition: page.hpp:191