libfoedus-core
FOEDUS Core Library
hash_page_impl.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_HASH_HASH_PAGE_HPP_
19 #define FOEDUS_STORAGE_HASH_HASH_PAGE_HPP_
20 
21 #include <stdint.h>
22 
23 #include <cstring>
24 
25 #include "foedus/assert_nd.hpp"
26 #include "foedus/compiler.hpp"
27 #include "foedus/fwd.hpp"
28 #include "foedus/memory/fwd.hpp"
29 #include "foedus/storage/page.hpp"
35 #include "foedus/xct/xct_id.hpp"
36 
37 namespace foedus {
38 namespace storage {
39 namespace hash {
40 
64 class HashIntermediatePage final {
65  public:
66  HashIntermediatePage() = delete;
67  HashIntermediatePage(const HashIntermediatePage& other) = delete;
68  HashIntermediatePage& operator=(const HashIntermediatePage& other) = delete;
69 
70  PageHeader& header() { return header_; }
71  const PageHeader& header() const { return header_; }
73  ASSERT_ND(!header_.snapshot_);
74  return VolatilePagePointer(header_.page_id_);
75  }
77  ASSERT_ND(header_.snapshot_);
78  return static_cast<SnapshotPagePointer>(header_.page_id_);
79  }
80  DualPagePointer& get_pointer(uint16_t index) { return pointers_[index]; }
81  const DualPagePointer& get_pointer(uint16_t index) const { return pointers_[index]; }
82  DualPagePointer* get_pointer_address(uint16_t index) { return pointers_ + index; }
83  const DualPagePointer* get_pointer_address(uint16_t index) const { return pointers_ + index; }
84 
87  StorageId storage_id,
88  VolatilePagePointer page_id,
89  const HashIntermediatePage* parent,
90  uint8_t level,
91  HashBin start_bin);
92 
94  StorageId storage_id,
95  SnapshotPagePointer page_id,
96  uint8_t level,
97  HashBin start_bin);
98 
101  const memory::GlobalVolatilePageResolver& page_resolver,
102  memory::PageReleaseBatch* batch);
103 
104  const HashBinRange& get_bin_range() const { return bin_range_; }
105  inline uint8_t get_level() const { return header_.get_in_layer_level(); }
106 
107  inline void assert_bin(HashBin bin) const ALWAYS_INLINE {
108  ASSERT_ND(bin_range_.contains(bin));
109  }
110  inline void assert_range() const ALWAYS_INLINE {
111  ASSERT_ND(bin_range_.length() == kHashMaxBins[get_level() + 1U]);
112  }
113 
115  friend std::ostream& operator<<(std::ostream& o, const HashIntermediatePage& v);
116 
117  uint64_t get_dummy_padding_unused() const { return padding_; }
118 
119  private:
121  PageHeader header_; // +40 -> 40
122 
123  uint64_t padding_; // +8 -> 48
124 
131  HashBinRange bin_range_; // +16 -> 64
132 
138 };
139 
155 class HashDataPage final {
156  public:
157  friend struct ReserveRecords;
161  struct Slot {
166 
171  uint16_t offset_; // +2 -> 18
179  uint16_t physical_record_length_; // +2 -> 20
184  uint16_t key_length_; // +2 -> 22
191  uint16_t payload_length_; // +2 -> 24
192 
194  HashValue hash_; // +8 -> 32
195 
196  inline uint16_t get_aligned_key_length() const { return assorted::align8(key_length_); }
197  inline uint16_t get_max_payload() const {
198  return physical_record_length_ - get_aligned_key_length();
199  }
200  };
201 
202  // A page object is never explicitly instantiated. You must reinterpret_cast.
203  HashDataPage() = delete;
204  HashDataPage(const HashDataPage& other) = delete;
205  HashDataPage& operator=(const HashDataPage& other) = delete;
206 
208  StorageId storage_id,
209  VolatilePagePointer page_id,
210  const Page* parent,
211  HashBin bin,
212  uint8_t bin_shifts);
214  StorageId storage_id,
215  SnapshotPagePointer page_id,
216  HashBin bin,
217  uint8_t bin_bits,
218  uint8_t bin_shifts);
220  const memory::GlobalVolatilePageResolver& page_resolver,
221  memory::PageReleaseBatch* batch);
222 
223 
224  // simple accessors
225  PageHeader& header() { return header_; }
226  const PageHeader& header() const { return header_; }
227  bool is_locked() const { return header_.page_version_.is_locked(); }
228 
230  ASSERT_ND(!header_.snapshot_);
231  return VolatilePagePointer(header_.page_id_);
232  }
234  ASSERT_ND(header_.snapshot_);
235  return static_cast<SnapshotPagePointer>(header_.page_id_);
236  }
237 
238  inline const DualPagePointer& next_page() const ALWAYS_INLINE { return next_page_; }
239  inline DualPagePointer& next_page() ALWAYS_INLINE { return next_page_; }
240  inline const DualPagePointer* next_page_address() const ALWAYS_INLINE { return &next_page_; }
241  inline DualPagePointer* next_page_address() ALWAYS_INLINE { return &next_page_; }
242 
243  inline const DataPageBloomFilter& bloom_filter() const ALWAYS_INLINE { return bloom_filter_; }
244  inline DataPageBloomFilter& bloom_filter() ALWAYS_INLINE { return bloom_filter_; }
245 
246  inline uint16_t get_record_count() const ALWAYS_INLINE { return header_.key_count_; }
247 
248  inline const Slot& get_slot(DataPageSlotIndex record) const ALWAYS_INLINE {
249  return reinterpret_cast<const Slot*>(this + 1)[-record - 1];
250  }
252  return reinterpret_cast<Slot*>(this + 1)[-record - 1];
253  }
255  // Only in this method, record is okay to be == key_count
256  return reinterpret_cast<Slot*>(this + 1)[-record - 1];
257  }
259  inline const Slot* get_slot_address(DataPageSlotIndex record) const ALWAYS_INLINE {
260  return reinterpret_cast<const Slot*>(this + 1) - record - 1;
261  }
263  return reinterpret_cast<Slot*>(this + 1) - record - 1;
264  }
265 
266  inline DataPageSlotIndex to_slot_index(const Slot* slot) const ALWAYS_INLINE {
267  ASSERT_ND(this);
268  ASSERT_ND(slot);
269  int64_t index = reinterpret_cast<const Slot*>(this + 1) - slot - 1;
270  ASSERT_ND(index >= 0);
271  ASSERT_ND(index < static_cast<int64_t>(sizeof(data_) / sizeof(Slot)));
272  return static_cast<DataPageSlotIndex>(index);
273  }
274 
275  inline char* record_from_offset(uint16_t offset) { return data_ + offset; }
276  inline const char* record_from_offset(uint16_t offset) const { return data_ + offset; }
277 
278  // methods about available/required spaces
279 
281  inline uint16_t available_space() const {
282  uint16_t consumed = next_offset() + get_record_count() * sizeof(Slot);
283  ASSERT_ND(consumed <= sizeof(data_));
284  if (consumed > sizeof(data_)) { // just to be conservative on release build
285  return 0;
286  }
287  return sizeof(data_) - consumed;
288  }
289 
291  inline uint16_t next_offset() const {
293  if (count == 0) {
294  return 0;
295  }
296  const Slot& last_slot = get_slot(count - 1);
297  ASSERT_ND((last_slot.offset_ + last_slot.physical_record_length_) % 8 == 0);
298  return last_slot.offset_ + last_slot.physical_record_length_;
299  }
300 
302  inline static uint16_t required_space(uint16_t key_length, uint16_t payload_length) {
303  return assorted::align8(key_length) + assorted::align8(payload_length) + sizeof(Slot);
304  }
305 
306 
307  // search, insert, migrate, etc to manipulate records in this page
308 
325  HashValue hash,
326  const BloomFilterFingerprint& fingerprint,
327  const void* key,
328  uint16_t key_length,
329  uint16_t payload_length);
330 
350  xct::XctId xct_id,
351  HashValue hash,
352  const BloomFilterFingerprint& fingerprint,
353  const void* key,
354  uint16_t key_length,
355  const void* payload,
356  uint16_t payload_length) ALWAYS_INLINE;
357 
388  HashValue hash,
389  const BloomFilterFingerprint& fingerprint,
390  const void* key,
391  KeyLength key_length,
392  DataPageSlotIndex record_count,
393  DataPageSlotIndex check_from = 0) const;
394 
396  inline bool compare_slot_key(
397  DataPageSlotIndex index,
398  HashValue hash,
399  const void* key,
400  uint16_t key_length) const {
401  ASSERT_ND(index < get_record_count()); // record count purely increasing
402  const Slot& slot = get_slot(index);
404  // quick check first
405  if (slot.hash_ != hash || slot.key_length_ != key_length) {
406  return false;
407  }
408  const void* data = record_from_offset(slot.offset_);
409  return std::memcmp(data, key, key_length) == 0;
410  }
411 
412  HashBin get_bin() const { return bin_; }
413  inline void assert_bin(HashBin bin) const ALWAYS_INLINE { ASSERT_ND(bin_ == bin); }
414 
421  void protected_set_bin_shifts(uint8_t bin_shifts) { header_.masstree_layer_ = bin_shifts; }
422  inline uint8_t get_bin_shifts() const { return header_.masstree_layer_; }
423 
424  void assert_entries() const ALWAYS_INLINE {
425 #ifndef NDEBUG
427 #endif // NDEBUG
428  }
430  void assert_entries_impl() const;
432  friend std::ostream& operator<<(std::ostream& o, const HashDataPage& v);
433 
434  private:
435  PageHeader header_; // +40 -> 40
436 
440  HashBin bin_; // +8 -> 48
441 
449  DualPagePointer next_page_; // +16 -> 64
450 
456  DataPageBloomFilter bloom_filter_; // +64 -> 128
457 
462  char data_[kHashDataPageDataSize];
463 };
464 
465 
472 
479 
481  xct::XctId xct_id,
482  HashValue hash,
483  const BloomFilterFingerprint& fingerprint,
484  const void* key_arg,
485  uint16_t key_length,
486  const void* payload_arg,
487  uint16_t payload_length) {
488  ASSERT_ND(header_.snapshot_);
489  ASSERT_ND(available_space() >= required_space(key_length, payload_length));
490  ASSERT_ND(reinterpret_cast<uintptr_t>(this) % kPageSize == 0);
491  ASSERT_ND(reinterpret_cast<uintptr_t>(key_arg) % 8 == 0);
492  ASSERT_ND(reinterpret_cast<uintptr_t>(payload_arg) % 8 == 0);
493 
494  const void* key = ASSUME_ALIGNED(key_arg, 8U);
495  const void* payload = ASSUME_ALIGNED(payload_arg, 8U);
497  Slot& slot = get_slot(index);
498  slot.tid_.xct_id_ = xct_id;
499  slot.offset_ = next_offset();
500  slot.hash_ = hash;
501  slot.key_length_ = key_length;
502  uint16_t aligned_key_length = assorted::align8(key_length);
503  slot.physical_record_length_ = aligned_key_length + assorted::align8(payload_length);
504  slot.payload_length_ = payload_length;
505 
506  ASSERT_ND(reinterpret_cast<uintptr_t>(record_from_offset(slot.offset_)) % 8 == 0);
507  char* record = reinterpret_cast<char*>(ASSUME_ALIGNED(record_from_offset(slot.offset_), 8U));
508 
509  ASSERT_ND(key_length > 0);
510  std::memcpy(record, key, key_length);
511  if (key_length != aligned_key_length) {
512  std::memset(record + key_length, 0, aligned_key_length - key_length % 8);
513  }
514 
515  if (payload_length > 0) {
516  char* dest = reinterpret_cast<char*>(ASSUME_ALIGNED(record + aligned_key_length, 8U));
517  uint16_t aligned_payload_length = assorted::align8(payload_length);
518  std::memcpy(dest, payload, payload_length);
519  if (key_length != aligned_payload_length) {
520  std::memset(dest + payload_length, 0, aligned_payload_length - payload_length);
521  }
522  }
523 
524  bloom_filter_.add(fingerprint);
525 
526  header_.increment_key_count();
527 }
528 
529 
530 static_assert(
531  sizeof(HashIntermediatePage) == kPageSize,
532  "sizeof(HashIntermediatePage) is not kPageSize");
533 
534 static_assert(
535  sizeof(HashDataPage) == kPageSize,
536  "sizeof(HashDataPage) is not kPageSize");
537 
538 } // namespace hash
539 } // namespace storage
540 } // namespace foedus
541 #endif // FOEDUS_STORAGE_HASH_HASH_PAGE_HPP_
Slot * get_slot_address(DataPageSlotIndex record) __attribute__((always_inline))
DualPagePointer * next_page_address() __attribute__((always_inline))
T align8(T value)
8-alignment.
Represents a pointer to another page (usually a child page).
Definition: storage_id.hpp:271
const DataPageBloomFilter & bloom_filter() const __attribute__((always_inline))
Definitions of IDs in this package and a few related constant values.
friend std::ostream & operator<<(std::ostream &o, const HashDataPage &v)
defined in hash_page_debug.cpp.
uint16_t next_offset() const
Returns offset of a next record.
uint32_t StorageId
Unique ID for storage.
Definition: storage_id.hpp:55
const HashBinRange & get_bin_range() const
xct::RwLockableXctId tid_
TID of the record.
Root package of FOEDUS (Fast Optimistic Engine for Data Unification Services).
Definition: assert_nd.hpp:44
uint16_t payload_length_
Byte length of the payload.
void increment_key_count() __attribute__((always_inline))
Definition: page.hpp:318
void assert_entries() const __attribute__((always_inline))
uint8_t masstree_layer_
used only in masstree.
Definition: page.hpp:226
void create_record_in_snapshot(xct::XctId xct_id, HashValue hash, const BloomFilterFingerprint &fingerprint, const void *key, uint16_t key_length, const void *payload, uint16_t payload_length) __attribute__((always_inline))
A simplified/efficient version to insert an active record, which must be used only in snapshot pages...
const char * record_from_offset(uint16_t offset) const
Represents a pointer to a volatile page with modification count for preventing ABA.
Definition: storage_id.hpp:194
Forward declarations of classes in root package.
Persistent status part of Transaction ID.
Definition: xct_id.hpp:955
bool compare_slot_key(DataPageSlotIndex index, HashValue hash, const void *key, uint16_t key_length) const
returns whether the slot contains the exact key specified
XctId xct_id_
the second 64bit: Persistent status part of TID.
Definition: xct_id.hpp:1137
SnapshotPagePointer get_snapshot_page_id() const
DataPageSlotIndex reserve_record(HashValue hash, const BloomFilterFingerprint &fingerprint, const void *key, uint16_t key_length, uint16_t payload_length)
A system transaction that creates a logically deleted record in this page for the given key...
void assert_entries_impl() const
defined in hash_page_debug.cpp.
#define ASSUME_ALIGNED(x, y)
Wraps GCC's __builtin_assume_aligned.
Definition: compiler.hpp:111
void assert_bin(HashBin bin) const __attribute__((always_inline))
void initialize_snapshot_page(StorageId storage_id, SnapshotPagePointer page_id, HashBin bin, uint8_t bin_bits, uint8_t bin_shifts)
const Slot & get_slot(DataPageSlotIndex record) const __attribute__((always_inline))
uint16_t key_length_
Byte length of key of the record.
friend std::ostream & operator<<(std::ostream &o, const HashIntermediatePage &v)
defined in hash_page_debug.cpp.
bool contains(HashBin hash) const
Definition: hash_id.hpp:176
const DualPagePointer * next_page_address() const __attribute__((always_inline))
DualPagePointer & next_page() __attribute__((always_inline))
HashValue hash_
Full hash of the key of the record.
The MCS reader-writer lock variant of LockableXctId.
Definition: xct_id.hpp:1132
const Slot * get_slot_address(DataPageSlotIndex record) const __attribute__((always_inline))
same as &get_slot(), but this is more explicit and easier to understand/maintain
Independent utility methods/classes for hashination, or hash functions.
HashValue hashinate(const void *key, uint16_t key_length)
Calculates hash value for general input.
void protected_set_bin_shifts(uint8_t bin_shifts)
this method should be called only in page creation.
uint16_t key_count_
physical key count (those keys might be deleted) in this page.
Definition: page.hpp:219
HashIntermediatePage & operator=(const HashIntermediatePage &other)=delete
Definitions of IDs in this package and a few related constant values.
const DualPagePointer & get_pointer(uint16_t index) const
SnapshotPagePointer get_snapshot_page_id() const
A helper class to return a bunch of pages to individual nodes.
Definition: page_pool.hpp:276
uint64_t SnapshotPagePointer
Page ID of a snapshot page.
Definition: storage_id.hpp:79
Database engine object that holds all resources and provides APIs.
Definition: engine.hpp:109
A system transaction to reserve a physical record(s) in a hash data page.
Just a marker to denote that a memory region represents a data page.
Definition: page.hpp:184
Fix-sized slot for each record, which is placed at the end of data region.
Set of arguments, both inputs and outputs, given to each volatile page initializer.
Definition: page.hpp:365
Just a marker to denote that the memory region represents a data page.
Definition: page.hpp:334
Represents a range of hash bins in a hash storage, such as what an intermediate page is responsible f...
Definition: hash_id.hpp:172
PageVersion page_version_
Used in several storage types as concurrency control mechanism for the page.
Definition: page.hpp:272
uint16_t DataPageSlotIndex
Definition: hash_id.hpp:196
uint16_t physical_record_length_
Byte count this record occupies.
void release_pages_recursive(const memory::GlobalVolatilePageResolver &page_resolver, memory::PageReleaseBatch *batch)
DualPagePointer * get_pointer_address(uint16_t index)
HashDataPage & operator=(const HashDataPage &other)=delete
void initialize_volatile_page(StorageId storage_id, VolatilePagePointer page_id, const HashIntermediatePage *parent, uint8_t level, HashBin start_bin)
Called only when this page is initialized.
void assert_range() const __attribute__((always_inline))
void assert_bin(HashBin bin) const __attribute__((always_inline))
Slot & get_new_slot(DataPageSlotIndex record) __attribute__((always_inline))
Forward declarations of classes in memory package.
Represents an intermediate page in Hashtable Storage.
void initialize_snapshot_page(StorageId storage_id, SnapshotPagePointer page_id, uint8_t level, HashBin start_bin)
static uint16_t required_space(uint16_t key_length, uint16_t payload_length)
returns physical_record_length_ for a new record
const DualPagePointer * get_pointer_address(uint16_t index) const
VolatilePagePointer get_volatile_page_id() const
void hash_intermediate_volatile_page_init(const VolatilePageInitArguments &args)
volatile page initialize callback for HashIntermediatePage.
char * record_from_offset(uint16_t offset)
Slot & get_slot(DataPageSlotIndex record) __attribute__((always_inline))
uint64_t HashBin
Represents a bin of a hash value.
Definition: hash_id.hpp:142
void add(const BloomFilterFingerprint &fingerprint) __attribute__((always_inline))
Adds the fingerprint to this bloom filter.
Represents an individual data page in Hashtable Storage.
uint16_t available_space() const
Returns usable space in bytes.
const DualPagePointer & next_page() const __attribute__((always_inline))
void hash_data_volatile_page_init(const VolatilePageInitArguments &args)
volatile page initialize callback for HashDataPage.
A fingerprint for bloom filter in each HashDataPage.
DataPageSlotIndex to_slot_index(const Slot *slot) const __attribute__((always_inline))
void initialize_volatile_page(StorageId storage_id, VolatilePagePointer page_id, const Page *parent, HashBin bin, uint8_t bin_shifts)
uint16_t KeyLength
Represents a byte-length of a key in this package.
Definition: hash_id.hpp:203
To quickly check whether a HashDataPage might contain a specific hash value, we maintain a non-counti...
Resolves an offset in a volatile page pool to an actual pointer and vice versa.
const uint8_t kHashIntermediatePageFanout
Number of pointers in an intermediate page of hash storage.
Definition: hash_id.hpp:49
const uint64_t kHashMaxBins[]
kHashTotalBins[n] gives the maximum number of hash bins n-level hash can hold.
Definition: hash_id.hpp:74
uint16_t get_record_count() const __attribute__((always_inline))
uint8_t get_in_layer_level() const
Definition: page.hpp:281
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
bool is_locked() const __attribute__((always_inline))
Definition: page.hpp:138
Definitions of IDs in this package and a few related constant values.
#define ALWAYS_INLINE
A function suffix to hint that the function should always be inlined.
Definition: compiler.hpp:106
VolatilePagePointer get_volatile_page_id() const
DualPagePointer & get_pointer(uint16_t index)
const PageHeader & header() const
void release_pages_recursive(const memory::GlobalVolatilePageResolver &page_resolver, memory::PageReleaseBatch *batch)
const uint16_t kHashDataPageDataSize
Body data byte size in data page of hash storage.
Definition: hash_id.hpp:116
const uint16_t kPageSize
A constant defining the page size (in bytes) of both snapshot pages and volatile pages.
Definition: storage_id.hpp:45
DataPageSlotIndex search_key_physical(HashValue hash, const BloomFilterFingerprint &fingerprint, const void *key, KeyLength key_length, DataPageSlotIndex record_count, DataPageSlotIndex check_from=0) const
Search for a physical slot that exactly contains the given key.
uint16_t offset_
Byte offset in data_ where this record starts.
uint64_t HashValue
Represents a full 64-bit hash value calculated from a key.
Definition: hash_id.hpp:129
uint64_t page_id_
Page ID of this page.
Definition: page.hpp:191
DataPageBloomFilter & bloom_filter() __attribute__((always_inline))