libfoedus-core
FOEDUS Core Library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
masstree_log_types.hpp
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  */
18 #ifndef FOEDUS_STORAGE_MASSTREE_MASSTREE_LOG_TYPES_HPP_
19 #define FOEDUS_STORAGE_MASSTREE_MASSTREE_LOG_TYPES_HPP_
20 #include <stdint.h>
21 
22 #include <algorithm>
23 #include <cstring>
24 #include <iosfwd>
25 
26 #include "foedus/compiler.hpp"
29 #include "foedus/log/log_type.hpp"
30 #include "foedus/storage/page.hpp"
37 
43 namespace foedus {
44 namespace storage {
45 namespace masstree {
60 
61  void apply_storage(Engine* engine, StorageId storage_id);
62  void assert_valid();
63  friend std::ostream& operator<<(std::ostream& o, const MasstreeCreateLogType& v);
64 };
65 
72 inline uint8_t extract_page_layer(const void* in_page_pointer) {
73  const Page* page = to_page(in_page_pointer);
74  return page->get_header().masstree_layer_;
75 }
76 
88  KeyLength key_length_; // +2 => 18
89  PayloadLength payload_offset_; // +2 => 20
90  PayloadLength payload_count_; // +2 => 22
91  uint16_t reserved_; // +2 => 24
96  char aligned_data_[8]; // ~ (+align8(key_length_)+align8(payload_count_))
97 
98  static uint16_t calculate_log_length(
99  KeyLength key_length,
100  PayloadLength payload_count) ALWAYS_INLINE {
101  return 24U + assorted::align8(key_length) + assorted::align8(payload_count);
102  }
103 
104  char* get_key() { return aligned_data_; }
105  const char* get_key() const { return aligned_data_; }
106  KeySlice get_first_slice() const { return normalize_be_bytes_full_aligned(aligned_data_); }
107  KeyLength get_key_length_aligned() const { return assorted::align8(key_length_); }
108  char* get_payload() {
109  return reinterpret_cast<char*>(ASSUME_ALIGNED(aligned_data_ + get_key_length_aligned(), 8U));
110  }
111  const char* get_payload() const {
112  return reinterpret_cast<const char*>(
113  ASSUME_ALIGNED(aligned_data_ + get_key_length_aligned(), 8U));
114  }
115 
117  log::LogCode type,
118  StorageId storage_id,
119  const void* key,
120  KeyLength key_length,
121  const void* payload = CXX11_NULLPTR,
122  PayloadLength payload_offset = 0,
123  PayloadLength payload_count = 0) ALWAYS_INLINE {
124  header_.log_type_code_ = type;
125  header_.log_length_ = calculate_log_length(key_length, payload_count);
126  header_.storage_id_ = storage_id;
127  key_length_ = key_length;
128  payload_offset_ = payload_offset;
129  payload_count_ = payload_count;
130  reserved_ = 0;
131 
132  std::memcpy(aligned_data_, key, key_length);
133  KeyLength aligned_key_length = assorted::align8(key_length);
134  if (aligned_key_length != key_length) {
135  std::memset(aligned_data_ + key_length, 0, aligned_key_length - key_length);
136  }
137  if (payload_count > 0) {
138  PayloadLength aligned_payload_count = assorted::align8(payload_count);
139  char* payload_base = aligned_data_ + aligned_key_length;
140  std::memcpy(payload_base, payload, payload_count);
141  if (aligned_payload_count != payload_count) {
142  std::memset(payload_base + payload_count, 0, aligned_payload_count - payload_count);
143  }
144  }
145  }
146 
148  bool equal_record_and_log_suffixes(const char* data) const {
149  uint8_t layer = extract_page_layer(data);
150  // Keys shorter than 8h+8 bytes are stored at layer <= h
151  ASSERT_ND(layer <= key_length_ / sizeof(KeySlice));
152  // both record and log keep 8-byte padded keys. so we can simply compare keys
153  KeyLength skipped = (layer + 1U) * sizeof(KeySlice);
154  if (key_length_ > skipped) {
155  const char* key = get_key();
156  KeyLength suffix_length = key_length_ - skipped;
157  KeyLength suffix_length_aligned = assorted::align8(suffix_length);
158  // both record and log's suffix keys are 8-byte aligned with zero-padding, so we can
159  // compare multiply of 8 bytes
160  return std::memcmp(data, key + skipped, suffix_length_aligned) == 0;
161  }
162  return true;
163  }
164 
170  inline static int compare_logs(
171  const MasstreeCommonLogType* left,
172  const MasstreeCommonLogType* right) ALWAYS_INLINE {
173  ASSERT_ND(left->header_.storage_id_ == right->header_.storage_id_);
174  if (left == right) {
175  return 0;
176  }
177  if (LIKELY(left->key_length_ > 0 && right->key_length_ > 0)) {
178  // Compare the first slice. This should be enough to differentiate most logs
179  const char* left_key = left->get_key();
180  const char* right_key = right->get_key();
181  ASSERT_ND(is_key_aligned_and_zero_padded(left_key, left->key_length_));
182  ASSERT_ND(is_key_aligned_and_zero_padded(right_key, right->key_length_));
183  KeySlice left_slice = normalize_be_bytes_full_aligned(left_key);
184  KeySlice right_slice = normalize_be_bytes_full_aligned(right_key);
185  if (left_slice < right_slice) {
186  return -1;
187  } else if (left_slice > right_slice) {
188  return 1;
189  }
190 
191  // compare the rest with memcmp.
192  KeyLength min_length = std::min(left->key_length_, right->key_length_);
193  if (min_length > kSliceLen) {
194  KeyLength remainder = min_length - kSliceLen;
195  int key_result = std::memcmp(left_key + kSliceLen, right_key + kSliceLen, remainder);
196  if (key_result != 0) {
197  return key_result;
198  }
199  }
200  }
201  if (left->key_length_ < right->key_length_) {
202  return -1;
203  } else if (left->key_length_ < right->key_length_) {
204  return 1;
205  }
206 
207  // same key, now compare xct_id
208  return left->header_.xct_id_.compare_epoch_and_orginal(right->header_.xct_id_);
209  }
210 
214  };
215 
248  inline RecordAddresses apply_record_prepare(xct::RwLockableXctId* owner_id, char* record) const {
249  ASSERT_ND(owner_id->is_keylocked());
250  ASSERT_ND(!owner_id->xct_id_.is_next_layer());
251  ASSERT_ND(!owner_id->xct_id_.is_moved());
252  const uint8_t layer = extract_page_layer(owner_id);
253  const KeyLength skipped = (layer + 1U) * sizeof(KeySlice);
254  const KeyLength key_length_aligned = this->get_key_length_aligned();
255  ASSERT_ND(key_length_aligned >= skipped);
256  const KeyLength suffix_length_aligned = key_length_aligned - skipped;
257  // no need to set key in apply(). it's already set when the record is physically inserted
258  // (or in other places if this is recovery).
259  ASSERT_ND(equal_record_and_log_suffixes(record));
260 
261  // In the MasstreeBorderPage Slot, lengthes come right after TID.
262  // [0]: offset, [1]: physical_record_length_, [3]: payload_length_, [4]: remainder_length_
263  // [5]: original_physical_record_length_, [6]: original_offset_
264  uint16_t* lengthes = reinterpret_cast<uint16_t*>(owner_id + 1);
265  const DataOffset offset = lengthes[0];
266  ASSERT_ND(reinterpret_cast<uint64_t>(reinterpret_cast<uintptr_t>(record)) % kPageSize
267  == static_cast<uint64_t>(offset + kBorderPageDataPartOffset));
268  ASSERT_ND(lengthes[1] >= suffix_length_aligned + assorted::align8(this->payload_count_));
269  ASSERT_ND(lengthes[1] + offset + kBorderPageDataPartOffset <= kPageSize);
270  ASSERT_ND(lengthes[1] >= suffix_length_aligned + assorted::align8(lengthes[3]));
271  ASSERT_ND(lengthes[4] == this->key_length_ - (layer * sizeof(KeySlice)));
272 
273  Page* page = to_page(record);
274  char* page_char = reinterpret_cast<char*>(page);
275  ASSERT_ND(record == page_char + offset + kBorderPageDataPartOffset);
276  const DataOffset old_offset = (record - page_char) - kBorderPageDataPartOffset;
277  if (old_offset != offset) {
278  // This happens only when we expanded the record
279  ASSERT_ND(lengthes[5] > lengthes[1]);
280  // Data-region grows forward, so the new offset must be larger
281  ASSERT_ND(lengthes[6] < offset);
282  ASSERT_ND(old_offset < offset);
283 
284  record = page_char + kBorderPageDataPartOffset + offset;
285  ASSERT_ND(equal_record_and_log_suffixes(record));
286  }
287 
288  PayloadLength* record_payload_count = lengthes + 3;
289  // record's payload is also 8-byte aligned, so copy multiply of 8 bytes.
290  // if the compiler is smart enough, it will do some optimization here.
291  char* record_payload = reinterpret_cast<char*>(
292  ASSUME_ALIGNED(record + suffix_length_aligned, 8U));
293  RecordAddresses ret = { record_payload_count, record_payload };
294  return ret;
295  }
296 };
297 
298 
310 
311  void populate(
312  StorageId storage_id,
313  const void* key,
314  KeyLength key_length,
315  const void* payload,
316  PayloadLength payload_count) ALWAYS_INLINE {
318  populate_base(type, storage_id, key, key_length, payload, 0, payload_count);
319  }
320 
322  thread::Thread* /*context*/,
323  StorageId /*storage_id*/,
324  xct::RwLockableXctId* owner_id,
325  char* data) const ALWAYS_INLINE {
326  RecordAddresses addresses = apply_record_prepare(owner_id, data);
327  ASSERT_ND(owner_id->xct_id_.is_deleted());
328  *addresses.record_payload_count_ = payload_count_;
329  if (payload_count_ > 0U) {
330  const char* log_payload = get_payload();
331  std::memcpy(addresses.record_payload_, log_payload, assorted::align8(payload_count_));
332  }
333  owner_id->xct_id_.set_notdeleted();
334  }
335 
338  ASSERT_ND(header_.log_length_ == calculate_log_length(key_length_, payload_count_));
340  }
341 
342  friend std::ostream& operator<<(std::ostream& o, const MasstreeInsertLogType& v);
343 };
344 
353 
354  static uint16_t calculate_log_length(KeyLength key_length) ALWAYS_INLINE {
355  return MasstreeCommonLogType::calculate_log_length(key_length, 0);
356  }
357 
358  void populate(
359  StorageId storage_id,
360  const void* key,
361  KeyLength key_length) ALWAYS_INLINE {
363  populate_base(type, storage_id, key, key_length);
364  }
365 
367  thread::Thread* /*context*/,
368  StorageId /*storage_id*/,
369  xct::RwLockableXctId* owner_id,
370  char* data) const ALWAYS_INLINE {
371  apply_record_prepare(owner_id, data); // In this log type, just for sanity checks
372  ASSERT_ND(!owner_id->xct_id_.is_deleted());
373  owner_id->xct_id_.set_deleted();
374  }
375 
378  ASSERT_ND(header_.log_length_ == calculate_log_length(key_length_));
380  }
381 
382  friend std::ostream& operator<<(std::ostream& o, const MasstreeDeleteLogType& v);
383 };
384 
393 
394  void populate(
395  StorageId storage_id,
396  const void* key,
397  KeyLength key_length,
398  const void* payload,
399  PayloadLength payload_count) ALWAYS_INLINE {
401  populate_base(type, storage_id, key, key_length, payload, 0, payload_count);
402  }
403 
405  thread::Thread* /*context*/,
406  StorageId /*storage_id*/,
407  xct::RwLockableXctId* owner_id,
408  char* data) const ALWAYS_INLINE {
409  RecordAddresses addresses = apply_record_prepare(owner_id, data);
410  ASSERT_ND(!owner_id->xct_id_.is_deleted());
411  *addresses.record_payload_count_ = payload_count_;
412  if (payload_count_ > 0U) {
413  const char* log_payload = get_payload();
414  std::memcpy(addresses.record_payload_, log_payload, assorted::align8(payload_count_));
415  }
416  }
417 
420  ASSERT_ND(header_.log_length_ == calculate_log_length(key_length_, payload_count_));
422  }
423 
424  friend std::ostream& operator<<(std::ostream& o, const MasstreeUpdateLogType& v);
425 };
426 
435 
436  void populate(
437  StorageId storage_id,
438  const void* key,
439  KeyLength key_length,
440  const void* payload,
441  PayloadLength payload_offset,
442  PayloadLength payload_count) ALWAYS_INLINE {
444  ASSERT_ND(payload_count > 0U);
445  ASSERT_ND(key_length > 0U);
446  populate_base(type, storage_id, key, key_length, payload, payload_offset, payload_count);
447  }
448 
450  thread::Thread* /*context*/,
451  StorageId /*storage_id*/,
452  xct::RwLockableXctId* owner_id,
453  char* data) const ALWAYS_INLINE {
454  RecordAddresses addresses = apply_record_prepare(owner_id, data);
455  ASSERT_ND(!owner_id->xct_id_.is_deleted());
456  ASSERT_ND(*addresses.record_payload_count_ >= payload_count_ + payload_offset_);
457  if (payload_count_ > 0U) {
458  const char* log_payload = get_payload();
459  std::memcpy(
460  addresses.record_payload_ + payload_offset_,
461  log_payload,
462  payload_count_);
463  }
464  }
465 
468  ASSERT_ND(header_.log_length_ == calculate_log_length(key_length_, payload_count_));
470  }
471 
472  friend std::ostream& operator<<(std::ostream& o, const MasstreeOverwriteLogType& v);
473 };
474 
475 
476 } // namespace masstree
477 } // namespace storage
478 } // namespace foedus
479 #endif // FOEDUS_STORAGE_MASSTREE_MASSTREE_LOG_TYPES_HPP_
LogCode
A unique identifier of all log types.
Definition: log_type.hpp:87
Base class for log type of storage-wide operation.
0x0033 : foedus::storage::masstree::MasstreeInsertLogType .
Definition: log_type.hpp:127
void apply_record(thread::Thread *, StorageId, xct::RwLockableXctId *owner_id, char *data) const __attribute__((always_inline))
T align8(T value)
8-alignment.
const uint64_t kSliceLen
Shorthand for sizeof(KeySlice).
uint16_t PayloadLength
Represents a byte-length of a payload in this package.
Definition: masstree_id.hpp:92
Definitions of IDs in this package and a few related constant values.
#define CXX11_NULLPTR
Used in public headers in place of "nullptr" of C++11.
Definition: cxx11.hpp:132
Page * to_page(const void *address)
super-dirty way to obtain Page the address belongs to.
Definition: page.hpp:395
Log type of CREATE MASSTREE STORAGE operation.
uint32_t StorageId
Unique ID for storage.
Definition: storage_id.hpp:55
Root package of FOEDUS (Fast Optimistic Engine for Data Unification Services).
Definition: assert_nd.hpp:44
void apply_record(thread::Thread *, StorageId, xct::RwLockableXctId *owner_id, char *data) const __attribute__((always_inline))
Represents one thread running on one NUMA core.
Definition: thread.hpp:48
uint8_t masstree_layer_
used only in masstree.
Definition: page.hpp:226
uint16_t DataOffset
Byte-offset in a page.
STL namespace.
const DataOffset kBorderPageDataPartOffset
Offset of data_ member in MasstreeBorderPage.
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.
void assert_valid() const __attribute__((always_inline))
Declares common log types used in all packages.
A base class for MasstreeInsertLogType/MasstreeDeleteLogType/MasstreeOverwriteLogType.
Definitions of IDs in this package and a few related constant values.
uint16_t KeyLength
Represents a byte-length of a key in this package.
Definition: masstree_id.hpp:69
#define ASSUME_ALIGNED(x, y)
Wraps GCC's __builtin_assume_aligned.
Definition: compiler.hpp:111
Log type of masstree-storage's insert operation.
#define LIKELY(x)
Hints that x is highly likely true.
Definition: compiler.hpp:103
void assert_valid() const __attribute__((always_inline))
The MCS reader-writer lock variant of LockableXctId.
Definition: xct_id.hpp:1132
0x0032 : foedus::storage::masstree::MasstreeOverwriteLogType .
Definition: log_type.hpp:126
RecordAddresses apply_record_prepare(xct::RwLockableXctId *owner_id, char *record) const
Common parts of apply_record() in these log types that extract relevant addresses from the record hea...
void populate_base(log::LogCode type, StorageId storage_id, const void *key, KeyLength key_length, const void *payload=nullptr, PayloadLength payload_offset=0, PayloadLength payload_count=0) __attribute__((always_inline))
Log type of masstree-storage's delete operation.
void assert_valid() const __attribute__((always_inline))
static int compare_logs(const MasstreeCommonLogType *left, const MasstreeCommonLogType *right) __attribute__((always_inline))
Returns -1, 0, 1 when left is less than, same, larger than right in terms of key and xct_id...
Log type of masstree-storage's overwrite operation.
uint8_t extract_page_layer(const void *in_page_pointer)
Retrieve masstree layer information from the header of the page that contains the pointer...
Database engine object that holds all resources and provides APIs.
Definition: engine.hpp:109
void apply_storage(Engine *engine, StorageId storage_id)
void assert_valid() const __attribute__((always_inline))
friend std::ostream & operator<<(std::ostream &o, const MasstreeCreateLogType &v)
bool is_next_layer() const __attribute__((always_inline))
Definition: xct_id.hpp:1042
Just a marker to denote that the memory region represents a data page.
Definition: page.hpp:334
uint16_t log_type_code_
Actually of LogCode defined in the X-Macro, but we want to make sure the type size is 2 bytes...
void apply_record(thread::Thread *, StorageId, xct::RwLockableXctId *owner_id, char *data) const __attribute__((always_inline))
bool equal_record_and_log_suffixes(const char *data) const
used only for sanity check.
uint16_t log_length_
Byte size of this log entry including this header itself and everything.
Log type of masstree-storage's update operation.
static uint16_t calculate_log_length(KeyLength key_length, PayloadLength payload_count) __attribute__((always_inline))
0x0034 : foedus::storage::masstree::MasstreeDeleteLogType .
Definition: log_type.hpp:128
void apply_record(thread::Thread *, StorageId, xct::RwLockableXctId *owner_id, char *data) const __attribute__((always_inline))
0x0035 : foedus::storage::masstree::MasstreeUpdateLogType .
Definition: log_type.hpp:129
bool is_key_aligned_and_zero_padded(const char *key, KeyLength key_length)
Returns if the given key is 8-bytes aligned and also zero-padded to 8-bytes for easier slicing (which...
#define LOG_TYPE_NO_CONSTRUCT(clazz)
Macro to delete all constructors/destructors to prevent misuse for log type classes.
storage::StorageId storage_id_
The storage this loggable operation mainly affects.
LogCode get_type() const __attribute__((always_inline))
Convenience method to cast into LogCode.
void populate(StorageId storage_id, const void *key, KeyLength key_length) __attribute__((always_inline))
PageHeader & get_header()
At least the basic header exists in all pages.
Definition: page.hpp:336
Base class for log type of record-wise operation.
Forward declarations of classes in masstree storage package.
bool is_moved() const __attribute__((always_inline))
Definition: xct_id.hpp:1041
#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
#define ALWAYS_INLINE
A function suffix to hint that the function should always be inlined.
Definition: compiler.hpp:106
void assert_valid_generic() __attribute__((always_inline))
Verifies the log contains essential fields set.
const uint16_t kPageSize
A constant defining the page size (in bytes) of both snapshot pages and volatile pages.
Definition: storage_id.hpp:45
KeySlice normalize_be_bytes_full_aligned(const void *be_bytes)
Convert an aligned big-endian byte array of at least 8-bytes-length to KeySlice.
bool is_keylocked() const __attribute__((always_inline))
Definition: xct_id.hpp:1140