20 #include <glog/logging.h>
142 consecutive_inserts_ =
true;
160 consecutive_inserts_ =
true;
191 std::vector<std::thread> threads;
193 for (
int i = 0; i < 2; ++i) {
202 for (uint8_t i = 0; i < key_count + 1; ++i) {
206 for (uint8_t j = 0; j < mini_count + 1; ++j) {
215 for (
auto& t : threads) {
231 for (
int i = 0; i < 2; ++i) {
242 for (uint8_t i = 0; i < key_count + 1; ++i) {
246 for (uint8_t j = 0; j < mini_count + 1; ++j) {
270 for (
int i = 0; i < 2; ++i) {
280 for (
SlotIndex i = 0; i < key_count; ++i) {
308 const Slot* parent_slot = copy_from->
get_slot(copy_index);
334 next_offset_ += record_size;
335 consecutive_inserts_ =
true;
342 if (suffix_length_aligned > 0) {
347 suffix_length_aligned);
349 if (payload_length > 0) {
368 <<
", which must become " << payload_count;
381 if (
get_next_offset() == lengthes.offset_ + lengthes.physical_record_length_) {
382 const DataOffset diff = record_length - lengthes.physical_record_length_;
383 DVLOG(1) <<
"Lucky, expanding a record at last record region. diff=" << diff;
384 if (available >= diff) {
385 DVLOG(2) <<
"woo. yes, we can just increase the length";
395 if (available >= record_length) {
396 DVLOG(2) <<
"Okay, in-page record expansion.";
405 if (lengthes.physical_record_length_ > 0) {
406 std::memcpy(new_record, old_record, lengthes.physical_record_length_);
424 DVLOG(1) <<
"Umm, we need to split this page for record expansion. available="
425 << available <<
", record_length=" << record_length
426 <<
", record_index=" << record_index
440 ASSERT_ND(parent_slot->tid_.is_keylocked());
441 ASSERT_ND(!parent_slot->tid_.is_moved());
442 ASSERT_ND(!parent_slot->does_point_to_layer());
460 SlotLengthPart new_lengthes = parent_slot->read_lengthes_oneshot();
465 std::memcpy(parent_payload, &pointer,
sizeof(pointer));
467 parent_slot->write_lengthes_oneshot(new_lengthes);
469 parent_slot->tid_.xct_id_.set_next_layer();
486 low = separators_[i - 1];
490 high = separators_[i];
497 low = separators_[i - 1];
502 for (uint8_t j = 0; j <= minipage.
key_count_; ++j) {
525 ASSERT_ND(cur_page->within_foster_major(slice));
543 Slot* slot =
reinterpret_cast<Slot*
>(owner_address);
563 const char* suffix =
get_record(original_index);
574 cur_page = cur_page->track_foster_child(slice, resolver);
584 VLOG(0) <<
"Very interesting. moved record not found due to concurrent updates";
590 char* new_record_address = cur_page->
get_record(index);
593 if (remainder <=
sizeof(
KeySlice)) {
595 VLOG(0) <<
"Wtf 2. moved record in next layer not found. Probably due to concurrent thread";
599 VLOG(0) <<
"Interesting. moved record are now in another layer. further track.";
616 Slot* slot =
reinterpret_cast<Slot*
>(owner_address);
627 const KeyLength remainder = slot->remainder_length_;
630 LOG(ERROR) <<
"WTF. The original record was too short to get moved to next-layer, but it did?";
634 const char* original_suffix =
get_record(original_index);
636 const uint8_t next_layer = cur_layer + 1U;
639 const char* next_suffix = original_suffix +
sizeof(
KeySlice);
642 if (root_pointer.is_null()) {
643 VLOG(0) <<
"Wtf. moved record in next layer not found. Probably due to concurrent thread";
665 VLOG(0) <<
"Wtf 1. moved record in next layer not found. Probably due to concurrent thread";
680 casted = casted->track_foster_child(next_slice, resolver);
684 VLOG(0) <<
"Very interesting. moved record not found due to concurrent updates";
689 ASSERT_ND(new_owner_address != owner_address);
690 char* new_record_address = casted->
get_record(index);
693 if (next_remainder <=
sizeof(
KeySlice)) {
695 VLOG(0) <<
"Wtf 2. moved record in next layer not found. Probably due to concurrent thread";
699 VLOG(0) <<
"Interesting. moved record are now in another layer. further track.";
711 if (lengthes.
offset_ % 8 != 0) {
void set_slice(SlotIndex index, KeySlice slice) __attribute__((always_inline))
DataOffset physical_record_length_
Byte count this record occupies.
const KeySlice kInfimumSlice
void write_lengthes_oneshot(SlotLengthPart new_value) __attribute__((always_inline))
Writes lengthes_ in one-shot.
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 verify_slot_lengthes(SlotIndex index) const
uint16_t unused_
unused so far
PayloadLength get_max_payload_peek() const __attribute__((always_inline))
This might be affected by concurrent threads.
KeySlice get_slice(SlotIndex index) const __attribute__((always_inline))
T align8(T value)
8-alignment.
void release_pages_recursive_common(const memory::GlobalVolatilePageResolver &page_resolver, memory::PageReleaseBatch *batch)
void release_pages_recursive(const memory::GlobalVolatilePageResolver &page_resolver, memory::PageReleaseBatch *batch)
KeyLength calculate_suffix_length_aligned(KeyLength remainder_length) __attribute__((always_inline))
SlotLengthUnion lengthes_
Stores mutable length information of the record.
Represents a pointer to another page (usually a child page).
bool is_empty_range() const __attribute__((always_inline))
An empty-range page, either intermediate or border, never has any entries.
uint16_t PayloadLength
Represents a byte-length of a payload in this package.
uint16_t SlotIndex
Index of a record in a (border) page.
bool is_both_null() const
const char * get_record_payload_from_offsets(DataOffset record_offset, KeyLength remainder_length) const __attribute__((always_inline))
void initialize_snapshot_page(StorageId storage_id, SnapshotPagePointer page_id, uint8_t layer, KeySlice low_fence, KeySlice high_fence)
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))
Page pool for volatile read/write store (VolatilePage) and the read-only bufferpool (SnapshotPage)...
bool is_locked() const __attribute__((always_inline))
PayloadLength payload_length_
Byte length of the payload.
uint32_t StorageId
Unique ID for storage.
Root package of FOEDUS (Fast Optimistic Engine for Data Unification Services).
Represents a record of write-access during a transaction.
bool is_moved() const __attribute__((always_inline))
bool is_border() const __attribute__((always_inline))
KeySlice low_fence_
Inclusive low fence of this page.
DataOffset get_next_offset() const
Declares all log types used in this storage type.
const GlobalVolatilePageResolver & get_global_volatile_page_resolver() const
Returns the page resolver to convert volatile page ID to page pointer.
Slot * get_new_slot(SlotIndex index) __attribute__((always_inline))
uint16_t DataOffset
Byte-offset in a page.
bool is_foster_minor_null() const __attribute__((always_inline))
PageType
The following 1-byte value is stored in the common page header.
bool is_foster_major_null() const __attribute__((always_inline))
Result of track_moved_record().
void release_one(PagePoolOffset offset)
Returns only one page.
KeySlice foster_fence_
Inclusive low_fence of foster child.
Represents a pointer to a volatile page with modification count for preventing ABA.
MiniPage & get_minipage(uint8_t index) __attribute__((always_inline))
SlotLengthPart read_lengthes_oneshot() const __attribute__((always_inline))
Reads lengthes_ of this slot in one-shot.
Represents one border page in Masstree Storage.
DualPagePointer * get_next_layer(SlotIndex index) __attribute__((always_inline))
void increase_next_offset(DataOffset length)
XctId xct_id_
the second 64bit: Persistent status part of TID.
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 foster_twin_[2]
Points to foster children, or tentative child pages.
uint16_t KeyLength
Represents a byte-length of a key in this package.
Common base of MasstreeIntermediatePage and MasstreeBorderPage.
char * get_record(SlotIndex index) __attribute__((always_inline))
bool is_retired() const __attribute__((always_inline))
SlotLengthPart components
McsRwLock lock_
the first 64bit: Locking part of TID
bool does_point_to_layer() const __attribute__((always_inline))
void initialize_volatile_common(StorageId storage_id, VolatilePagePointer page_id, PageType page_type, uint8_t layer, uint8_t level, KeySlice low_fence, KeySlice high_fence)
The MCS reader-writer lock variant of LockableXctId.
KeySlice slice_key(const void *be_bytes, KeyLength slice_length)
Extract a part of a big-endian byte array of given length as KeySlice.
DualPagePointer pointers_[kMaxIntermediateMiniSeparators+1]
DataOffset available_space() const
Returns usable data space in bytes.
VolatilePagePointer volatile_pointer_
bool is_moved() const __attribute__((always_inline))
void release_pages_recursive_parallel(Engine *engine)
This method is used when we release a large number of volatile pages, most likely when we drop a stor...
bool is_moved() const __attribute__((always_inline))
DataOffset original_offset_
The value of offset_ as of the creation of this record.
memory::PagePoolOffset get_offset() const
const uint16_t kMaxIntermediateSeparators
Max number of separators stored in the first level of intermediate pages.
uint64_t SnapshotPagePointer
Page ID of a snapshot page.
DataOffset original_physical_record_length_
The value of physical_record_length_ as of the creation of this record.
const KeyLength kInitiallyNextLayer
A special key length value that denotes the record in a border page was initially a next-layer pointe...
Database engine object that holds all resources and provides APIs.
NumaNodeMemoryRef * get_node_memory(foedus::thread::ThreadGroupId group) const
Fix-sized slot for each record, which is placed at the end of data region.
void initialize_snapshot_common(StorageId storage_id, SnapshotPagePointer page_id, PageType page_type, uint8_t layer, uint8_t level, KeySlice low_fence, KeySlice high_fence)
const Slot * get_slot(SlotIndex index) const __attribute__((always_inline))
SnapshotPagePointer snapshot_pointer_
const SlotIndex kBorderPageMaxSlots
Maximum number of slots in one MasstreeBorderPage.
bool is_next_layer() const __attribute__((always_inline))
A piece of Slot object that must be read/written in one-shot, meaning no one reads half-written value...
uint8_t get_layer() const __attribute__((always_inline))
Layer-0 stores the first 8 byte slice, Layer-1 next 8 byte...
bool within_foster_minor(KeySlice slice) const __attribute__((always_inline))
KeySlice separators_[kMaxIntermediateMiniSeparators]
Same semantics as separators_ in enclosing class.
void initialize_layer_root(const MasstreeBorderPage *copy_from, SlotIndex copy_index)
Copy the initial record that will be the only record for a new root page.
SlotIndex to_slot_index(const Slot *slot) const __attribute__((always_inline))
void release_parallel(Engine *engine, VolatilePagePointer pointer)
bool has_foster_child() const __attribute__((always_inline))
KeySlice get_low_fence() const __attribute__((always_inline))
void initialize_volatile_page(StorageId storage_id, VolatilePagePointer page_id, uint8_t layer, KeySlice low_fence, KeySlice high_fence)
SlotIndex find_key(KeySlice slice, const void *suffix, KeyLength remainder) const __attribute__((always_inline))
Navigates a searching key-slice to one of the record in this page.
MasstreeBorderPage()=delete
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.
bool is_high_fence_supremum() const __attribute__((always_inline))
xct::TrackMovedRecordResult track_moved_record(Engine *engine, xct::RwLockableXctId *old_address, xct::WriteXctAccess *write_set)
xct::TrackMovedRecordResult track_moved_record_next_layer(Engine *engine, xct::RwLockableXctId *old_address)
This one further tracks it to next layer.
KeyLength remainder_length_
Followings are immutable.
void initialize_volatile_page(StorageId storage_id, VolatilePagePointer page_id, uint8_t layer, uint8_t level, KeySlice low_fence, KeySlice high_fence)
char * get_record_payload(SlotIndex index) __attribute__((always_inline))
void release_pages_recursive(const memory::GlobalVolatilePageResolver &page_resolver, memory::PageReleaseBatch *batch)
Atomic fence methods and load/store with fences that work for both C++11/non-C++11 code...
static DataOffset to_record_length(KeyLength remainder_length, PayloadLength payload_length)
returns minimal physical_record_length_ for the given remainder/payload length.
const KeySlice kSupremumSlice
xct::RwLockableXctId * get_owner_id(SlotIndex index) __attribute__((always_inline))
Represents one intermediate page in Masstree Storage.
void verify_separators() const
#define UNLIKELY(x)
Hints that x is highly likely false.
#define ASSERT_ND(x)
A warning-free wrapper macro of assert() that has no performance effect in release mode even when 'x'...
bool within_foster_major(KeySlice slice) const __attribute__((always_inline))
uint8_t get_numa_node() const
xct::RwLockableXctId tid_
TID of the record.
void memory_fence_release()
Equivalent to std::atomic_thread_fence(std::memory_order_release).
bool is_next_layer() const __attribute__((always_inline))
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.
uint8_t find_minipage(KeySlice slice) const __attribute__((always_inline))
Navigates a searching key-slice to one of the mini pages in this page.
DataOffset offset_
Byte offset in data_ where this record starts.
uint8_t find_pointer(KeySlice slice) const __attribute__((always_inline))
Navigates a searching key-slice to one of pointers in this mini-page.
bool is_keylocked() const __attribute__((always_inline))
void initialize_snapshot_page(StorageId storage_id, SnapshotPagePointer page_id, uint8_t layer, uint8_t level, KeySlice low_fence, KeySlice high_fence)
char * get_record_from_offset(DataOffset record_offset) __attribute__((always_inline))
Offset versions.