commit
551e9428ce
@ -0,0 +1,14 @@ |
||||
// Copyright (c) 2013, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#include "db/db_statistics.h" |
||||
|
||||
namespace rocksdb { |
||||
|
||||
std::shared_ptr<Statistics> CreateDBStatistics() { |
||||
return std::make_shared<DBStatistics>(); |
||||
} |
||||
|
||||
} // namespace rocksdb
|
@ -0,0 +1,45 @@ |
||||
// Copyright (c) 2013, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once |
||||
#include "rocksdb/slice_transform.h" |
||||
#include "rocksdb/memtablerep.h" |
||||
|
||||
namespace rocksdb { |
||||
|
||||
class HashSkipListRepFactory : public MemTableRepFactory { |
||||
public: |
||||
explicit HashSkipListRepFactory( |
||||
const SliceTransform* transform, |
||||
size_t bucket_count, |
||||
int32_t skiplist_height, |
||||
int32_t skiplist_branching_factor) |
||||
: transform_(transform), |
||||
bucket_count_(bucket_count), |
||||
skiplist_height_(skiplist_height), |
||||
skiplist_branching_factor_(skiplist_branching_factor) { } |
||||
|
||||
virtual ~HashSkipListRepFactory() { delete transform_; } |
||||
|
||||
virtual std::shared_ptr<MemTableRep> CreateMemTableRep( |
||||
MemTableRep::KeyComparator& compare, Arena* arena) override; |
||||
|
||||
virtual const char* Name() const override { |
||||
return "HashSkipListRepFactory"; |
||||
} |
||||
|
||||
const SliceTransform* GetTransform() { return transform_; } |
||||
|
||||
private: |
||||
const SliceTransform* transform_; |
||||
const size_t bucket_count_; |
||||
const int32_t skiplist_height_; |
||||
const int32_t skiplist_branching_factor_; |
||||
}; |
||||
|
||||
} |
@ -1,426 +0,0 @@ |
||||
// Copyright (c) 2013, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
#include <unordered_map> |
||||
#include <set> |
||||
#include <vector> |
||||
#include <algorithm> |
||||
#include <iostream> |
||||
|
||||
#include "rocksdb/memtablerep.h" |
||||
#include "rocksdb/arena.h" |
||||
#include "rocksdb/slice.h" |
||||
#include "rocksdb/slice_transform.h" |
||||
#include "db/memtable.h" |
||||
#include "port/port.h" |
||||
#include "util/mutexlock.h" |
||||
#include "util/murmurhash.h" |
||||
#include "util/stl_wrappers.h" |
||||
|
||||
namespace std { |
||||
template <> |
||||
struct hash<rocksdb::Slice> { |
||||
size_t operator()(const rocksdb::Slice& slice) const { |
||||
return MurmurHash(slice.data(), slice.size(), 0); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
namespace rocksdb { |
||||
namespace { |
||||
|
||||
using namespace stl_wrappers; |
||||
|
||||
class TransformRep : public MemTableRep { |
||||
public: |
||||
TransformRep(const KeyComparator& compare, Arena* arena, |
||||
const SliceTransform* transform, size_t bucket_size, |
||||
size_t num_locks); |
||||
|
||||
virtual void Insert(const char* key) override; |
||||
|
||||
virtual bool Contains(const char* key) const override; |
||||
|
||||
virtual size_t ApproximateMemoryUsage() override; |
||||
|
||||
virtual ~TransformRep() { } |
||||
|
||||
virtual std::shared_ptr<MemTableRep::Iterator> GetIterator() override; |
||||
|
||||
virtual std::shared_ptr<MemTableRep::Iterator> GetIterator( |
||||
const Slice& slice) override; |
||||
|
||||
virtual std::shared_ptr<MemTableRep::Iterator> GetDynamicPrefixIterator() |
||||
override { |
||||
return std::make_shared<DynamicPrefixIterator>(*this); |
||||
} |
||||
|
||||
std::shared_ptr<MemTableRep::Iterator> GetTransformIterator( |
||||
const Slice& transformed); |
||||
|
||||
private: |
||||
friend class DynamicPrefixIterator; |
||||
typedef std::set<const char*, Compare> Bucket; |
||||
typedef std::unordered_map<Slice, std::shared_ptr<Bucket>> BucketMap; |
||||
|
||||
// Maps slices (which are transformed user keys) to buckets of keys sharing
|
||||
// the same transform.
|
||||
BucketMap buckets_; |
||||
|
||||
// rwlock_ protects access to the buckets_ data structure itself. Each bucket
|
||||
// has its own read-write lock as well.
|
||||
mutable port::RWMutex rwlock_; |
||||
|
||||
// Keep track of approximately how much memory is being used.
|
||||
size_t memory_usage_ = 0; |
||||
|
||||
// The user-supplied transform whose domain is the user keys.
|
||||
const SliceTransform* transform_; |
||||
|
||||
// Get a bucket from buckets_. If the bucket hasn't been initialized yet,
|
||||
// initialize it before returning. Must be externally synchronized.
|
||||
std::shared_ptr<Bucket>& GetBucket(const Slice& transformed); |
||||
|
||||
port::RWMutex* GetLock(const Slice& transformed) const; |
||||
|
||||
mutable std::vector<port::RWMutex> locks_; |
||||
|
||||
const KeyComparator& compare_; |
||||
|
||||
class Iterator : public MemTableRep::Iterator { |
||||
public: |
||||
explicit Iterator(std::shared_ptr<Bucket> items); |
||||
|
||||
virtual ~Iterator() { }; |
||||
|
||||
// Returns true iff the iterator is positioned at a valid node.
|
||||
virtual bool Valid() const; |
||||
|
||||
// Returns the key at the current position.
|
||||
// REQUIRES: Valid()
|
||||
virtual const char* key() const; |
||||
|
||||
// Advances to the next position.
|
||||
// REQUIRES: Valid()
|
||||
virtual void Next(); |
||||
|
||||
// Advances to the previous position.
|
||||
// REQUIRES: Valid()
|
||||
virtual void Prev(); |
||||
|
||||
// Advance to the first entry with a key >= target
|
||||
virtual void Seek(const Slice& user_key, const char* memtable_key); |
||||
|
||||
// Position at the first entry in collection.
|
||||
// Final state of iterator is Valid() iff collection is not empty.
|
||||
virtual void SeekToFirst(); |
||||
|
||||
// Position at the last entry in collection.
|
||||
// Final state of iterator is Valid() iff collection is not empty.
|
||||
virtual void SeekToLast(); |
||||
private: |
||||
std::shared_ptr<Bucket> items_; |
||||
Bucket::const_iterator cit_; |
||||
std::string tmp_; // For passing to EncodeKey
|
||||
}; |
||||
|
||||
class EmptyIterator : public MemTableRep::Iterator { |
||||
// This is used when there wasn't a bucket. It is cheaper than
|
||||
// instantiating an empty bucket over which to iterate.
|
||||
public: |
||||
virtual bool Valid() const { |
||||
return false; |
||||
} |
||||
virtual const char* key() const { |
||||
assert(false); |
||||
return nullptr; |
||||
} |
||||
virtual void Next() { } |
||||
virtual void Prev() { } |
||||
virtual void Seek(const Slice& user_key, const char* memtable_key) { } |
||||
virtual void SeekToFirst() { } |
||||
virtual void SeekToLast() { } |
||||
static std::shared_ptr<EmptyIterator> GetInstance(); |
||||
private: |
||||
static std::shared_ptr<EmptyIterator> instance; |
||||
EmptyIterator() { } |
||||
}; |
||||
|
||||
class TransformIterator : public Iterator { |
||||
public: |
||||
explicit TransformIterator(std::shared_ptr<Bucket> items, |
||||
port::RWMutex* rwlock); |
||||
virtual ~TransformIterator() { } |
||||
private: |
||||
const ReadLock l_; |
||||
}; |
||||
|
||||
|
||||
class DynamicPrefixIterator : public MemTableRep::Iterator { |
||||
private: |
||||
// the underlying memtable rep
|
||||
const TransformRep& memtable_rep_; |
||||
// the result of a prefix seek
|
||||
std::unique_ptr<MemTableRep::Iterator> bucket_iterator_; |
||||
|
||||
public: |
||||
explicit DynamicPrefixIterator(const TransformRep& memtable_rep) |
||||
: memtable_rep_(memtable_rep) {} |
||||
|
||||
virtual ~DynamicPrefixIterator() { }; |
||||
|
||||
// Returns true iff the iterator is positioned at a valid node.
|
||||
virtual bool Valid() const { |
||||
return bucket_iterator_ && bucket_iterator_->Valid(); |
||||
} |
||||
|
||||
// Returns the key at the current position.
|
||||
// REQUIRES: Valid()
|
||||
virtual const char* key() const { |
||||
assert(Valid()); |
||||
return bucket_iterator_->key(); |
||||
} |
||||
|
||||
// Advances to the next position.
|
||||
// REQUIRES: Valid()
|
||||
virtual void Next() { |
||||
assert(Valid()); |
||||
bucket_iterator_->Next(); |
||||
} |
||||
|
||||
// Advances to the previous position.
|
||||
// REQUIRES: Valid()
|
||||
virtual void Prev() { |
||||
assert(Valid()); |
||||
bucket_iterator_->Prev(); |
||||
} |
||||
|
||||
// Advance to the first entry with a key >= target within the
|
||||
// same bucket as target
|
||||
virtual void Seek(const Slice& user_key, const char* memtable_key) { |
||||
Slice prefix = memtable_rep_.transform_->Transform(user_key); |
||||
|
||||
ReadLock l(&memtable_rep_.rwlock_); |
||||
auto bucket = memtable_rep_.buckets_.find(prefix); |
||||
if (bucket == memtable_rep_.buckets_.end()) { |
||||
bucket_iterator_.reset(nullptr); |
||||
} else { |
||||
bucket_iterator_.reset( |
||||
new TransformIterator(bucket->second, memtable_rep_.GetLock(prefix))); |
||||
bucket_iterator_->Seek(user_key, memtable_key); |
||||
} |
||||
} |
||||
|
||||
// Position at the first entry in collection.
|
||||
// Final state of iterator is Valid() iff collection is not empty.
|
||||
virtual void SeekToFirst() { |
||||
// Prefix iterator does not support total order.
|
||||
// We simply set the iterator to invalid state
|
||||
bucket_iterator_.reset(nullptr); |
||||
} |
||||
|
||||
// Position at the last entry in collection.
|
||||
// Final state of iterator is Valid() iff collection is not empty.
|
||||
virtual void SeekToLast() { |
||||
// Prefix iterator does not support total order.
|
||||
// We simply set the iterator to invalid state
|
||||
bucket_iterator_.reset(nullptr); |
||||
} |
||||
}; |
||||
}; |
||||
|
||||
class PrefixHashRep : public TransformRep { |
||||
public: |
||||
PrefixHashRep(const KeyComparator& compare, Arena* arena, |
||||
const SliceTransform* transform, size_t bucket_size, |
||||
size_t num_locks) |
||||
: TransformRep(compare, arena, transform, |
||||
bucket_size, num_locks) { } |
||||
|
||||
virtual std::shared_ptr<MemTableRep::Iterator> GetPrefixIterator( |
||||
const Slice& prefix) override; |
||||
}; |
||||
|
||||
std::shared_ptr<TransformRep::Bucket>& TransformRep::GetBucket( |
||||
const Slice& transformed) { |
||||
WriteLock l(&rwlock_); |
||||
auto& bucket = buckets_[transformed]; |
||||
if (!bucket) { |
||||
bucket.reset( |
||||
new decltype(buckets_)::mapped_type::element_type(Compare(compare_))); |
||||
// To memory_usage_ we add the size of the std::set and the size of the
|
||||
// std::pair (decltype(buckets_)::value_type) which includes the
|
||||
// Slice and the std::shared_ptr
|
||||
memory_usage_ += sizeof(*bucket) + |
||||
sizeof(decltype(buckets_)::value_type); |
||||
} |
||||
return bucket; |
||||
} |
||||
|
||||
port::RWMutex* TransformRep::GetLock(const Slice& transformed) const { |
||||
return &locks_[std::hash<Slice>()(transformed) % locks_.size()]; |
||||
} |
||||
|
||||
TransformRep::TransformRep(const KeyComparator& compare, Arena* arena, |
||||
const SliceTransform* transform, size_t bucket_size, |
||||
size_t num_locks) |
||||
: buckets_(bucket_size), |
||||
transform_(transform), |
||||
locks_(num_locks), |
||||
compare_(compare) { } |
||||
|
||||
void TransformRep::Insert(const char* key) { |
||||
assert(!Contains(key)); |
||||
auto transformed = transform_->Transform(UserKey(key)); |
||||
auto& bucket = GetBucket(transformed); |
||||
WriteLock bl(GetLock(transformed)); |
||||
bucket->insert(key); |
||||
memory_usage_ += sizeof(key); |
||||
} |
||||
|
||||
bool TransformRep::Contains(const char* key) const { |
||||
ReadLock l(&rwlock_); |
||||
auto transformed = transform_->Transform(UserKey(key)); |
||||
auto bucket = buckets_.find(transformed); |
||||
if (bucket == buckets_.end()) { |
||||
return false; |
||||
} |
||||
ReadLock bl(GetLock(transformed)); |
||||
return bucket->second->count(key) != 0; |
||||
} |
||||
|
||||
size_t TransformRep::ApproximateMemoryUsage() { |
||||
return memory_usage_; |
||||
} |
||||
|
||||
std::shared_ptr<TransformRep::EmptyIterator> |
||||
TransformRep::EmptyIterator::GetInstance() { |
||||
if (!instance) { |
||||
instance.reset(new TransformRep::EmptyIterator); |
||||
} |
||||
return instance; |
||||
} |
||||
|
||||
TransformRep::Iterator::Iterator(std::shared_ptr<Bucket> items) |
||||
: items_(items), |
||||
cit_(items_->begin()) { } |
||||
|
||||
// Returns true iff the iterator is positioned at a valid node.
|
||||
bool TransformRep::Iterator::Valid() const { |
||||
return cit_ != items_->end(); |
||||
} |
||||
|
||||
// Returns the key at the current position.
|
||||
// REQUIRES: Valid()
|
||||
const char* TransformRep::Iterator::key() const { |
||||
assert(Valid()); |
||||
return *cit_; |
||||
} |
||||
|
||||
// Advances to the next position.
|
||||
// REQUIRES: Valid()
|
||||
void TransformRep::Iterator::Next() { |
||||
assert(Valid()); |
||||
if (cit_ == items_->end()) { |
||||
return; |
||||
} |
||||
++cit_; |
||||
} |
||||
|
||||
// Advances to the previous position.
|
||||
// REQUIRES: Valid()
|
||||
void TransformRep::Iterator::Prev() { |
||||
assert(Valid()); |
||||
if (cit_ == items_->begin()) { |
||||
// If you try to go back from the first element, the iterator should be
|
||||
// invalidated. So we set it to past-the-end. This means that you can
|
||||
// treat the container circularly.
|
||||
cit_ = items_->end(); |
||||
} else { |
||||
--cit_; |
||||
} |
||||
} |
||||
|
||||
// Advance to the first entry with a key >= target
|
||||
void TransformRep::Iterator::Seek(const Slice& user_key, |
||||
const char* memtable_key) { |
||||
const char* encoded_key = |
||||
(memtable_key != nullptr) ? memtable_key : EncodeKey(&tmp_, user_key); |
||||
cit_ = items_->lower_bound(encoded_key); |
||||
} |
||||
|
||||
// Position at the first entry in collection.
|
||||
// Final state of iterator is Valid() iff collection is not empty.
|
||||
void TransformRep::Iterator::SeekToFirst() { |
||||
cit_ = items_->begin(); |
||||
} |
||||
|
||||
void TransformRep::Iterator::SeekToLast() { |
||||
cit_ = items_->end(); |
||||
if (items_->size() != 0) { |
||||
--cit_; |
||||
} |
||||
} |
||||
|
||||
TransformRep::TransformIterator::TransformIterator( |
||||
std::shared_ptr<Bucket> items, port::RWMutex* rwlock) |
||||
: Iterator(items), l_(rwlock) { } |
||||
|
||||
std::shared_ptr<MemTableRep::Iterator> TransformRep::GetIterator() { |
||||
auto items = std::make_shared<Bucket>(Compare(compare_)); |
||||
// Hold read locks on all locks
|
||||
ReadLock l(&rwlock_); |
||||
std::for_each(locks_.begin(), locks_.end(), [] (port::RWMutex& lock) { |
||||
lock.ReadLock(); |
||||
}); |
||||
for (auto& bucket : buckets_) { |
||||
items->insert(bucket.second->begin(), bucket.second->end()); |
||||
} |
||||
std::for_each(locks_.begin(), locks_.end(), [] (port::RWMutex& lock) { |
||||
lock.Unlock(); |
||||
}); |
||||
return std::make_shared<Iterator>(std::move(items)); |
||||
} |
||||
|
||||
std::shared_ptr<MemTableRep::Iterator> TransformRep::GetTransformIterator( |
||||
const Slice& transformed) { |
||||
ReadLock l(&rwlock_); |
||||
auto bucket = buckets_.find(transformed); |
||||
if (bucket == buckets_.end()) { |
||||
return EmptyIterator::GetInstance(); |
||||
} |
||||
return std::make_shared<TransformIterator>(bucket->second, |
||||
GetLock(transformed)); |
||||
} |
||||
|
||||
std::shared_ptr<MemTableRep::Iterator> TransformRep::GetIterator( |
||||
const Slice& slice) { |
||||
auto transformed = transform_->Transform(slice); |
||||
return GetTransformIterator(transformed); |
||||
} |
||||
|
||||
std::shared_ptr<TransformRep::EmptyIterator> |
||||
TransformRep::EmptyIterator::instance; |
||||
|
||||
} // anon namespace
|
||||
|
||||
std::shared_ptr<MemTableRep> TransformRepFactory::CreateMemTableRep( |
||||
MemTableRep::KeyComparator& compare, Arena* arena) { |
||||
return std::make_shared<TransformRep>(compare, arena, transform_, |
||||
bucket_count_, num_locks_); |
||||
} |
||||
|
||||
std::shared_ptr<MemTableRep> PrefixHashRepFactory::CreateMemTableRep( |
||||
MemTableRep::KeyComparator& compare, Arena* arena) { |
||||
return std::make_shared<PrefixHashRep>(compare, arena, transform_, |
||||
bucket_count_, num_locks_); |
||||
} |
||||
|
||||
std::shared_ptr<MemTableRep::Iterator> PrefixHashRep::GetPrefixIterator( |
||||
const Slice& prefix) { |
||||
return TransformRep::GetTransformIterator(prefix); |
||||
} |
||||
|
||||
} // namespace rocksdb
|
Loading…
Reference in new issue