mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Update src/rocksdb2 to rocksdb-3.5.1:
Merge commit 'c168d54495d7d7b84639514f6443ad99b89ce996' into develop
This commit is contained in:
@@ -16,23 +16,20 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/meta_blocks.h"
|
||||
#include "table/cuckoo_table_factory.h"
|
||||
#include "table/get_context.h"
|
||||
#include "util/arena.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace rocksdb {
|
||||
namespace {
|
||||
const uint64_t CACHE_LINE_MASK = ~((uint64_t)CACHE_LINE_SIZE - 1);
|
||||
const uint32_t kInvalidIndex = std::numeric_limits<uint32_t>::max();
|
||||
static const uint64_t CACHE_LINE_MASK = ~((uint64_t)CACHE_LINE_SIZE - 1);
|
||||
}
|
||||
|
||||
extern const uint64_t kCuckooTableMagicNumber;
|
||||
|
||||
CuckooTableReader::CuckooTableReader(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const Options& options,
|
||||
std::unique_ptr<RandomAccessFile>&& file,
|
||||
uint64_t file_size,
|
||||
const Comparator* comparator,
|
||||
@@ -40,12 +37,12 @@ CuckooTableReader::CuckooTableReader(
|
||||
: file_(std::move(file)),
|
||||
ucomp_(comparator),
|
||||
get_slice_hash_(get_slice_hash) {
|
||||
if (!ioptions.allow_mmap_reads) {
|
||||
if (!options.allow_mmap_reads) {
|
||||
status_ = Status::InvalidArgument("File is not mmaped");
|
||||
}
|
||||
TableProperties* props = nullptr;
|
||||
status_ = ReadTableProperties(file_.get(), file_size, kCuckooTableMagicNumber,
|
||||
ioptions.env, ioptions.info_log, &props);
|
||||
options.env, options.info_log.get(), &props);
|
||||
if (!status_.ok()) {
|
||||
return;
|
||||
}
|
||||
@@ -53,29 +50,21 @@ CuckooTableReader::CuckooTableReader(
|
||||
auto& user_props = props->user_collected_properties;
|
||||
auto hash_funs = user_props.find(CuckooTablePropertyNames::kNumHashFunc);
|
||||
if (hash_funs == user_props.end()) {
|
||||
status_ = Status::Corruption("Number of hash functions not found");
|
||||
status_ = Status::InvalidArgument("Number of hash functions not found");
|
||||
return;
|
||||
}
|
||||
num_hash_func_ = *reinterpret_cast<const uint32_t*>(hash_funs->second.data());
|
||||
auto unused_key = user_props.find(CuckooTablePropertyNames::kEmptyKey);
|
||||
if (unused_key == user_props.end()) {
|
||||
status_ = Status::Corruption("Empty bucket value not found");
|
||||
status_ = Status::InvalidArgument("Empty bucket value not found");
|
||||
return;
|
||||
}
|
||||
unused_key_ = unused_key->second;
|
||||
|
||||
key_length_ = props->fixed_key_len;
|
||||
auto user_key_len = user_props.find(CuckooTablePropertyNames::kUserKeyLength);
|
||||
if (user_key_len == user_props.end()) {
|
||||
status_ = Status::Corruption("User key length not found");
|
||||
return;
|
||||
}
|
||||
user_key_length_ = *reinterpret_cast<const uint32_t*>(
|
||||
user_key_len->second.data());
|
||||
|
||||
auto value_length = user_props.find(CuckooTablePropertyNames::kValueLength);
|
||||
if (value_length == user_props.end()) {
|
||||
status_ = Status::Corruption("Value length not found");
|
||||
status_ = Status::InvalidArgument("Value length not found");
|
||||
return;
|
||||
}
|
||||
value_length_ = *reinterpret_cast<const uint32_t*>(
|
||||
@@ -85,40 +74,21 @@ CuckooTableReader::CuckooTableReader(
|
||||
auto hash_table_size = user_props.find(
|
||||
CuckooTablePropertyNames::kHashTableSize);
|
||||
if (hash_table_size == user_props.end()) {
|
||||
status_ = Status::Corruption("Hash table size not found");
|
||||
status_ = Status::InvalidArgument("Hash table size not found");
|
||||
return;
|
||||
}
|
||||
table_size_ = *reinterpret_cast<const uint64_t*>(
|
||||
hash_table_size->second.data());
|
||||
|
||||
table_size_minus_one_ = *reinterpret_cast<const uint64_t*>(
|
||||
hash_table_size->second.data()) - 1;
|
||||
auto is_last_level = user_props.find(CuckooTablePropertyNames::kIsLastLevel);
|
||||
if (is_last_level == user_props.end()) {
|
||||
status_ = Status::Corruption("Is last level not found");
|
||||
status_ = Status::InvalidArgument("Is last level not found");
|
||||
return;
|
||||
}
|
||||
is_last_level_ = *reinterpret_cast<const bool*>(is_last_level->second.data());
|
||||
|
||||
auto identity_as_first_hash = user_props.find(
|
||||
CuckooTablePropertyNames::kIdentityAsFirstHash);
|
||||
if (identity_as_first_hash == user_props.end()) {
|
||||
status_ = Status::Corruption("identity as first hash not found");
|
||||
return;
|
||||
}
|
||||
identity_as_first_hash_ = *reinterpret_cast<const bool*>(
|
||||
identity_as_first_hash->second.data());
|
||||
|
||||
auto use_module_hash = user_props.find(
|
||||
CuckooTablePropertyNames::kUseModuleHash);
|
||||
if (use_module_hash == user_props.end()) {
|
||||
status_ = Status::Corruption("hash type is not found");
|
||||
return;
|
||||
}
|
||||
use_module_hash_ = *reinterpret_cast<const bool*>(
|
||||
use_module_hash->second.data());
|
||||
auto cuckoo_block_size = user_props.find(
|
||||
CuckooTablePropertyNames::kCuckooBlockSize);
|
||||
if (cuckoo_block_size == user_props.end()) {
|
||||
status_ = Status::Corruption("Cuckoo block size not found");
|
||||
status_ = Status::InvalidArgument("Cuckoo block size not found");
|
||||
return;
|
||||
}
|
||||
cuckoo_block_size_ = *reinterpret_cast<const uint32_t*>(
|
||||
@@ -127,32 +97,36 @@ CuckooTableReader::CuckooTableReader(
|
||||
status_ = file_->Read(0, file_size, &file_data_, nullptr);
|
||||
}
|
||||
|
||||
Status CuckooTableReader::Get(const ReadOptions& readOptions, const Slice& key,
|
||||
GetContext* get_context) {
|
||||
Status CuckooTableReader::Get(
|
||||
const ReadOptions& readOptions, const Slice& key, void* handle_context,
|
||||
bool (*result_handler)(void* arg, const ParsedInternalKey& k,
|
||||
const Slice& v),
|
||||
void (*mark_key_may_exist_handler)(void* handle_context)) {
|
||||
assert(key.size() == key_length_ + (is_last_level_ ? 8 : 0));
|
||||
Slice user_key = ExtractUserKey(key);
|
||||
for (uint32_t hash_cnt = 0; hash_cnt < num_hash_func_; ++hash_cnt) {
|
||||
uint64_t offset = bucket_length_ * CuckooHash(
|
||||
user_key, hash_cnt, use_module_hash_, table_size_,
|
||||
identity_as_first_hash_, get_slice_hash_);
|
||||
user_key, hash_cnt, table_size_minus_one_, get_slice_hash_);
|
||||
const char* bucket = &file_data_.data()[offset];
|
||||
for (uint32_t block_idx = 0; block_idx < cuckoo_block_size_;
|
||||
++block_idx, bucket += bucket_length_) {
|
||||
++block_idx, bucket += bucket_length_) {
|
||||
if (ucomp_->Compare(Slice(unused_key_.data(), user_key.size()),
|
||||
Slice(bucket, user_key.size())) == 0) {
|
||||
Slice(bucket, user_key.size())) == 0) {
|
||||
return Status::OK();
|
||||
}
|
||||
// Here, we compare only the user key part as we support only one entry
|
||||
// per user key and we don't support sanpshot.
|
||||
if (ucomp_->Compare(user_key, Slice(bucket, user_key.size())) == 0) {
|
||||
Slice value(bucket + key_length_, value_length_);
|
||||
Slice value = Slice(&bucket[key_length_], value_length_);
|
||||
if (is_last_level_) {
|
||||
get_context->SaveValue(value);
|
||||
ParsedInternalKey found_ikey(
|
||||
Slice(bucket, key_length_), 0, kTypeValue);
|
||||
result_handler(handle_context, found_ikey, value);
|
||||
} else {
|
||||
Slice full_key(bucket, key_length_);
|
||||
ParsedInternalKey found_ikey;
|
||||
ParseInternalKey(full_key, &found_ikey);
|
||||
get_context->SaveValue(found_ikey, value);
|
||||
result_handler(handle_context, found_ikey, value);
|
||||
}
|
||||
// We don't support merge operations. So, we return here.
|
||||
return Status::OK();
|
||||
@@ -166,8 +140,7 @@ void CuckooTableReader::Prepare(const Slice& key) {
|
||||
// Prefetch the first Cuckoo Block.
|
||||
Slice user_key = ExtractUserKey(key);
|
||||
uint64_t addr = reinterpret_cast<uint64_t>(file_data_.data()) +
|
||||
bucket_length_ * CuckooHash(user_key, 0, use_module_hash_, table_size_,
|
||||
identity_as_first_hash_, nullptr);
|
||||
bucket_length_ * CuckooHash(user_key, 0, table_size_minus_one_, nullptr);
|
||||
uint64_t end_addr = addr + cuckoo_block_bytes_minus_one_;
|
||||
for (addr &= CACHE_LINE_MASK; addr < end_addr; addr += CACHE_LINE_SIZE) {
|
||||
PREFETCH(reinterpret_cast<const char*>(addr), 0, 3);
|
||||
@@ -187,43 +160,33 @@ class CuckooTableIterator : public Iterator {
|
||||
Slice key() const override;
|
||||
Slice value() const override;
|
||||
Status status() const override { return status_; }
|
||||
void InitIfNeeded();
|
||||
void LoadKeysFromReader();
|
||||
|
||||
private:
|
||||
struct BucketComparator {
|
||||
BucketComparator(const Slice& file_data, const Comparator* ucomp,
|
||||
uint32_t bucket_len, uint32_t user_key_len,
|
||||
const Slice target = Slice())
|
||||
: file_data_(file_data),
|
||||
ucomp_(ucomp),
|
||||
bucket_len_(bucket_len),
|
||||
user_key_len_(user_key_len),
|
||||
target_(target) {}
|
||||
bool operator()(const uint32_t first, const uint32_t second) const {
|
||||
const char* first_bucket =
|
||||
(first == kInvalidIndex) ? target_.data() :
|
||||
&file_data_.data()[first * bucket_len_];
|
||||
const char* second_bucket =
|
||||
(second == kInvalidIndex) ? target_.data() :
|
||||
&file_data_.data()[second * bucket_len_];
|
||||
return ucomp_->Compare(Slice(first_bucket, user_key_len_),
|
||||
Slice(second_bucket, user_key_len_)) < 0;
|
||||
struct CompareKeys {
|
||||
CompareKeys(const Comparator* ucomp, const bool last_level)
|
||||
: ucomp_(ucomp),
|
||||
is_last_level_(last_level) {}
|
||||
bool operator()(const std::pair<Slice, uint32_t>& first,
|
||||
const std::pair<Slice, uint32_t>& second) const {
|
||||
if (is_last_level_) {
|
||||
return ucomp_->Compare(first.first, second.first) < 0;
|
||||
} else {
|
||||
return ucomp_->Compare(ExtractUserKey(first.first),
|
||||
ExtractUserKey(second.first)) < 0;
|
||||
}
|
||||
}
|
||||
private:
|
||||
const Slice file_data_;
|
||||
const Comparator* ucomp_;
|
||||
const uint32_t bucket_len_;
|
||||
const uint32_t user_key_len_;
|
||||
const Slice target_;
|
||||
};
|
||||
|
||||
const BucketComparator bucket_comparator_;
|
||||
private:
|
||||
const Comparator* ucomp_;
|
||||
const bool is_last_level_;
|
||||
};
|
||||
const CompareKeys comparator_;
|
||||
void PrepareKVAtCurrIdx();
|
||||
CuckooTableReader* reader_;
|
||||
bool initialized_;
|
||||
Status status_;
|
||||
// Contains a map of keys to bucket_id sorted in key order.
|
||||
std::vector<uint32_t> sorted_bucket_ids_;
|
||||
std::vector<std::pair<Slice, uint32_t>> key_to_bucket_id_;
|
||||
// We assume that the number of items can be stored in uint32 (4 Billion).
|
||||
uint32_t curr_key_idx_;
|
||||
Slice curr_value_;
|
||||
@@ -234,66 +197,57 @@ class CuckooTableIterator : public Iterator {
|
||||
};
|
||||
|
||||
CuckooTableIterator::CuckooTableIterator(CuckooTableReader* reader)
|
||||
: bucket_comparator_(reader->file_data_, reader->ucomp_,
|
||||
reader->bucket_length_, reader->user_key_length_),
|
||||
: comparator_(reader->ucomp_, reader->is_last_level_),
|
||||
reader_(reader),
|
||||
initialized_(false),
|
||||
curr_key_idx_(kInvalidIndex) {
|
||||
sorted_bucket_ids_.clear();
|
||||
curr_key_idx_(std::numeric_limits<int32_t>::max()) {
|
||||
key_to_bucket_id_.clear();
|
||||
curr_value_.clear();
|
||||
curr_key_.Clear();
|
||||
}
|
||||
|
||||
void CuckooTableIterator::InitIfNeeded() {
|
||||
if (initialized_) {
|
||||
return;
|
||||
}
|
||||
sorted_bucket_ids_.reserve(reader_->GetTableProperties()->num_entries);
|
||||
uint64_t num_buckets = reader_->table_size_ + reader_->cuckoo_block_size_ - 1;
|
||||
assert(num_buckets < kInvalidIndex);
|
||||
const char* bucket = reader_->file_data_.data();
|
||||
for (uint32_t bucket_id = 0; bucket_id < num_buckets; ++bucket_id) {
|
||||
if (Slice(bucket, reader_->key_length_) != Slice(reader_->unused_key_)) {
|
||||
sorted_bucket_ids_.push_back(bucket_id);
|
||||
void CuckooTableIterator::LoadKeysFromReader() {
|
||||
key_to_bucket_id_.reserve(reader_->GetTableProperties()->num_entries);
|
||||
uint64_t num_buckets = reader_->table_size_minus_one_ +
|
||||
reader_->cuckoo_block_size_;
|
||||
for (uint32_t bucket_id = 0; bucket_id < num_buckets; bucket_id++) {
|
||||
Slice read_key;
|
||||
status_ = reader_->file_->Read(bucket_id * reader_->bucket_length_,
|
||||
reader_->key_length_, &read_key, nullptr);
|
||||
if (read_key != Slice(reader_->unused_key_)) {
|
||||
key_to_bucket_id_.push_back(std::make_pair(read_key, bucket_id));
|
||||
}
|
||||
bucket += reader_->bucket_length_;
|
||||
}
|
||||
assert(sorted_bucket_ids_.size() ==
|
||||
assert(key_to_bucket_id_.size() ==
|
||||
reader_->GetTableProperties()->num_entries);
|
||||
std::sort(sorted_bucket_ids_.begin(), sorted_bucket_ids_.end(),
|
||||
bucket_comparator_);
|
||||
curr_key_idx_ = kInvalidIndex;
|
||||
initialized_ = true;
|
||||
std::sort(key_to_bucket_id_.begin(), key_to_bucket_id_.end(), comparator_);
|
||||
curr_key_idx_ = key_to_bucket_id_.size();
|
||||
}
|
||||
|
||||
void CuckooTableIterator::SeekToFirst() {
|
||||
InitIfNeeded();
|
||||
curr_key_idx_ = 0;
|
||||
PrepareKVAtCurrIdx();
|
||||
}
|
||||
|
||||
void CuckooTableIterator::SeekToLast() {
|
||||
InitIfNeeded();
|
||||
curr_key_idx_ = sorted_bucket_ids_.size() - 1;
|
||||
curr_key_idx_ = key_to_bucket_id_.size() - 1;
|
||||
PrepareKVAtCurrIdx();
|
||||
}
|
||||
|
||||
void CuckooTableIterator::Seek(const Slice& target) {
|
||||
InitIfNeeded();
|
||||
const BucketComparator seek_comparator(
|
||||
reader_->file_data_, reader_->ucomp_,
|
||||
reader_->bucket_length_, reader_->user_key_length_,
|
||||
ExtractUserKey(target));
|
||||
auto seek_it = std::lower_bound(sorted_bucket_ids_.begin(),
|
||||
sorted_bucket_ids_.end(),
|
||||
kInvalidIndex,
|
||||
seek_comparator);
|
||||
curr_key_idx_ = std::distance(sorted_bucket_ids_.begin(), seek_it);
|
||||
// We assume that the target is an internal key. If this is last level file,
|
||||
// we need to take only the user key part to seek.
|
||||
Slice target_to_search = reader_->is_last_level_ ?
|
||||
ExtractUserKey(target) : target;
|
||||
auto seek_it = std::lower_bound(key_to_bucket_id_.begin(),
|
||||
key_to_bucket_id_.end(),
|
||||
std::make_pair(target_to_search, 0),
|
||||
comparator_);
|
||||
curr_key_idx_ = std::distance(key_to_bucket_id_.begin(), seek_it);
|
||||
PrepareKVAtCurrIdx();
|
||||
}
|
||||
|
||||
bool CuckooTableIterator::Valid() const {
|
||||
return curr_key_idx_ < sorted_bucket_ids_.size();
|
||||
return curr_key_idx_ < key_to_bucket_id_.size();
|
||||
}
|
||||
|
||||
void CuckooTableIterator::PrepareKVAtCurrIdx() {
|
||||
@@ -302,17 +256,15 @@ void CuckooTableIterator::PrepareKVAtCurrIdx() {
|
||||
curr_key_.Clear();
|
||||
return;
|
||||
}
|
||||
uint32_t id = sorted_bucket_ids_[curr_key_idx_];
|
||||
const char* offset = reader_->file_data_.data() +
|
||||
id * reader_->bucket_length_;
|
||||
uint64_t offset = ((uint64_t) key_to_bucket_id_[curr_key_idx_].second
|
||||
* reader_->bucket_length_) + reader_->key_length_;
|
||||
status_ = reader_->file_->Read(offset, reader_->value_length_,
|
||||
&curr_value_, nullptr);
|
||||
if (reader_->is_last_level_) {
|
||||
// Always return internal key.
|
||||
curr_key_.SetInternalKey(Slice(offset, reader_->user_key_length_),
|
||||
0, kTypeValue);
|
||||
} else {
|
||||
curr_key_.SetKey(Slice(offset, reader_->key_length_));
|
||||
curr_key_.SetInternalKey(
|
||||
key_to_bucket_id_[curr_key_idx_].first, 0, kTypeValue);
|
||||
}
|
||||
curr_value_ = Slice(offset + reader_->key_length_, reader_->value_length_);
|
||||
}
|
||||
|
||||
void CuckooTableIterator::Next() {
|
||||
@@ -327,7 +279,7 @@ void CuckooTableIterator::Next() {
|
||||
|
||||
void CuckooTableIterator::Prev() {
|
||||
if (curr_key_idx_ == 0) {
|
||||
curr_key_idx_ = sorted_bucket_ids_.size();
|
||||
curr_key_idx_ = key_to_bucket_id_.size();
|
||||
}
|
||||
if (!Valid()) {
|
||||
curr_value_.clear();
|
||||
@@ -340,7 +292,11 @@ void CuckooTableIterator::Prev() {
|
||||
|
||||
Slice CuckooTableIterator::key() const {
|
||||
assert(Valid());
|
||||
return curr_key_.GetKey();
|
||||
if (reader_->is_last_level_) {
|
||||
return curr_key_.GetKey();
|
||||
} else {
|
||||
return key_to_bucket_id_[curr_key_idx_].first;
|
||||
}
|
||||
}
|
||||
|
||||
Slice CuckooTableIterator::value() const {
|
||||
@@ -367,6 +323,9 @@ Iterator* CuckooTableReader::NewIterator(
|
||||
auto iter_mem = arena->AllocateAligned(sizeof(CuckooTableIterator));
|
||||
iter = new (iter_mem) CuckooTableIterator(this);
|
||||
}
|
||||
if (iter->status().ok()) {
|
||||
iter->LoadKeysFromReader();
|
||||
}
|
||||
return iter;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user