8eb1d445c3
Summary:
The MemTableRep API was broken by this commit: 813719e952
This patch reverts the changes and instead adds InsertKey (and etc.) overloads to extend the MemTableRep API without breaking the existing classes that inherit from it.
Closes https://github.com/facebook/rocksdb/pull/3513
Differential Revision: D7004134
Pulled By: maysamyabandeh
fbshipit-source-id: e568d91fe1e17dd76c0c1f6c7dd51a18633b1c4f
304 lines
8.9 KiB
C++
304 lines
8.9 KiB
C++
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
|
// This source code is licensed under both the GPLv2 (found in the
|
|
// COPYING file in the root directory) and Apache 2.0 License
|
|
// (found in the LICENSE.Apache file in the root directory).
|
|
//
|
|
#ifndef ROCKSDB_LITE
|
|
#include "rocksdb/memtablerep.h"
|
|
|
|
#include <unordered_set>
|
|
#include <set>
|
|
#include <memory>
|
|
#include <algorithm>
|
|
#include <type_traits>
|
|
|
|
#include "util/arena.h"
|
|
#include "db/memtable.h"
|
|
#include "memtable/stl_wrappers.h"
|
|
#include "port/port.h"
|
|
#include "util/mutexlock.h"
|
|
|
|
namespace rocksdb {
|
|
namespace {
|
|
|
|
using namespace stl_wrappers;
|
|
|
|
class VectorRep : public MemTableRep {
|
|
public:
|
|
VectorRep(const KeyComparator& compare, Allocator* allocator, size_t count);
|
|
|
|
// Insert key into the collection. (The caller will pack key and value into a
|
|
// single buffer and pass that in as the parameter to Insert)
|
|
// REQUIRES: nothing that compares equal to key is currently in the
|
|
// collection.
|
|
virtual void Insert(KeyHandle handle) override;
|
|
|
|
// Returns true iff an entry that compares equal to key is in the collection.
|
|
virtual bool Contains(const char* key) const override;
|
|
|
|
virtual void MarkReadOnly() override;
|
|
|
|
virtual size_t ApproximateMemoryUsage() override;
|
|
|
|
virtual void Get(const LookupKey& k, void* callback_args,
|
|
bool (*callback_func)(void* arg,
|
|
const char* entry)) override;
|
|
|
|
virtual ~VectorRep() override { }
|
|
|
|
class Iterator : public MemTableRep::Iterator {
|
|
class VectorRep* vrep_;
|
|
std::shared_ptr<std::vector<const char*>> bucket_;
|
|
std::vector<const char*>::const_iterator mutable cit_;
|
|
const KeyComparator& compare_;
|
|
std::string tmp_; // For passing to EncodeKey
|
|
bool mutable sorted_;
|
|
void DoSort() const;
|
|
public:
|
|
explicit Iterator(class VectorRep* vrep,
|
|
std::shared_ptr<std::vector<const char*>> bucket,
|
|
const KeyComparator& compare);
|
|
|
|
// Initialize an iterator over the specified collection.
|
|
// The returned iterator is not valid.
|
|
// explicit Iterator(const MemTableRep* collection);
|
|
virtual ~Iterator() override { };
|
|
|
|
// Returns true iff the iterator is positioned at a valid node.
|
|
virtual bool Valid() const override;
|
|
|
|
// Returns the key at the current position.
|
|
// REQUIRES: Valid()
|
|
virtual const char* key() const override;
|
|
|
|
// Advances to the next position.
|
|
// REQUIRES: Valid()
|
|
virtual void Next() override;
|
|
|
|
// Advances to the previous position.
|
|
// REQUIRES: Valid()
|
|
virtual void Prev() override;
|
|
|
|
// Advance to the first entry with a key >= target
|
|
virtual void Seek(const Slice& user_key, const char* memtable_key) override;
|
|
|
|
// Advance to the first entry with a key <= target
|
|
virtual void SeekForPrev(const Slice& user_key,
|
|
const char* memtable_key) override;
|
|
|
|
// Position at the first entry in collection.
|
|
// Final state of iterator is Valid() iff collection is not empty.
|
|
virtual void SeekToFirst() override;
|
|
|
|
// Position at the last entry in collection.
|
|
// Final state of iterator is Valid() iff collection is not empty.
|
|
virtual void SeekToLast() override;
|
|
};
|
|
|
|
// Return an iterator over the keys in this representation.
|
|
virtual MemTableRep::Iterator* GetIterator(Arena* arena) override;
|
|
|
|
private:
|
|
friend class Iterator;
|
|
typedef std::vector<const char*> Bucket;
|
|
std::shared_ptr<Bucket> bucket_;
|
|
mutable port::RWMutex rwlock_;
|
|
bool immutable_;
|
|
bool sorted_;
|
|
const KeyComparator& compare_;
|
|
};
|
|
|
|
void VectorRep::Insert(KeyHandle handle) {
|
|
auto* key = static_cast<char*>(handle);
|
|
WriteLock l(&rwlock_);
|
|
assert(!immutable_);
|
|
bucket_->push_back(key);
|
|
}
|
|
|
|
// Returns true iff an entry that compares equal to key is in the collection.
|
|
bool VectorRep::Contains(const char* key) const {
|
|
ReadLock l(&rwlock_);
|
|
return std::find(bucket_->begin(), bucket_->end(), key) != bucket_->end();
|
|
}
|
|
|
|
void VectorRep::MarkReadOnly() {
|
|
WriteLock l(&rwlock_);
|
|
immutable_ = true;
|
|
}
|
|
|
|
size_t VectorRep::ApproximateMemoryUsage() {
|
|
return
|
|
sizeof(bucket_) + sizeof(*bucket_) +
|
|
bucket_->size() *
|
|
sizeof(
|
|
std::remove_reference<decltype(*bucket_)>::type::value_type
|
|
);
|
|
}
|
|
|
|
VectorRep::VectorRep(const KeyComparator& compare, Allocator* allocator,
|
|
size_t count)
|
|
: MemTableRep(allocator),
|
|
bucket_(new Bucket()),
|
|
immutable_(false),
|
|
sorted_(false),
|
|
compare_(compare) {
|
|
bucket_.get()->reserve(count);
|
|
}
|
|
|
|
VectorRep::Iterator::Iterator(class VectorRep* vrep,
|
|
std::shared_ptr<std::vector<const char*>> bucket,
|
|
const KeyComparator& compare)
|
|
: vrep_(vrep),
|
|
bucket_(bucket),
|
|
cit_(bucket_->end()),
|
|
compare_(compare),
|
|
sorted_(false) { }
|
|
|
|
void VectorRep::Iterator::DoSort() const {
|
|
// vrep is non-null means that we are working on an immutable memtable
|
|
if (!sorted_ && vrep_ != nullptr) {
|
|
WriteLock l(&vrep_->rwlock_);
|
|
if (!vrep_->sorted_) {
|
|
std::sort(bucket_->begin(), bucket_->end(), Compare(compare_));
|
|
cit_ = bucket_->begin();
|
|
vrep_->sorted_ = true;
|
|
}
|
|
sorted_ = true;
|
|
}
|
|
if (!sorted_) {
|
|
std::sort(bucket_->begin(), bucket_->end(), Compare(compare_));
|
|
cit_ = bucket_->begin();
|
|
sorted_ = true;
|
|
}
|
|
assert(sorted_);
|
|
assert(vrep_ == nullptr || vrep_->sorted_);
|
|
}
|
|
|
|
// Returns true iff the iterator is positioned at a valid node.
|
|
bool VectorRep::Iterator::Valid() const {
|
|
DoSort();
|
|
return cit_ != bucket_->end();
|
|
}
|
|
|
|
// Returns the key at the current position.
|
|
// REQUIRES: Valid()
|
|
const char* VectorRep::Iterator::key() const {
|
|
assert(sorted_);
|
|
return *cit_;
|
|
}
|
|
|
|
// Advances to the next position.
|
|
// REQUIRES: Valid()
|
|
void VectorRep::Iterator::Next() {
|
|
assert(sorted_);
|
|
if (cit_ == bucket_->end()) {
|
|
return;
|
|
}
|
|
++cit_;
|
|
}
|
|
|
|
// Advances to the previous position.
|
|
// REQUIRES: Valid()
|
|
void VectorRep::Iterator::Prev() {
|
|
assert(sorted_);
|
|
if (cit_ == bucket_->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_ = bucket_->end();
|
|
} else {
|
|
--cit_;
|
|
}
|
|
}
|
|
|
|
// Advance to the first entry with a key >= target
|
|
void VectorRep::Iterator::Seek(const Slice& user_key,
|
|
const char* memtable_key) {
|
|
DoSort();
|
|
// Do binary search to find first value not less than the target
|
|
const char* encoded_key =
|
|
(memtable_key != nullptr) ? memtable_key : EncodeKey(&tmp_, user_key);
|
|
cit_ = std::equal_range(bucket_->begin(),
|
|
bucket_->end(),
|
|
encoded_key,
|
|
[this] (const char* a, const char* b) {
|
|
return compare_(a, b) < 0;
|
|
}).first;
|
|
}
|
|
|
|
// Advance to the first entry with a key <= target
|
|
void VectorRep::Iterator::SeekForPrev(const Slice& user_key,
|
|
const char* memtable_key) {
|
|
assert(false);
|
|
}
|
|
|
|
// Position at the first entry in collection.
|
|
// Final state of iterator is Valid() iff collection is not empty.
|
|
void VectorRep::Iterator::SeekToFirst() {
|
|
DoSort();
|
|
cit_ = bucket_->begin();
|
|
}
|
|
|
|
// Position at the last entry in collection.
|
|
// Final state of iterator is Valid() iff collection is not empty.
|
|
void VectorRep::Iterator::SeekToLast() {
|
|
DoSort();
|
|
cit_ = bucket_->end();
|
|
if (bucket_->size() != 0) {
|
|
--cit_;
|
|
}
|
|
}
|
|
|
|
void VectorRep::Get(const LookupKey& k, void* callback_args,
|
|
bool (*callback_func)(void* arg, const char* entry)) {
|
|
rwlock_.ReadLock();
|
|
VectorRep* vector_rep;
|
|
std::shared_ptr<Bucket> bucket;
|
|
if (immutable_) {
|
|
vector_rep = this;
|
|
} else {
|
|
vector_rep = nullptr;
|
|
bucket.reset(new Bucket(*bucket_)); // make a copy
|
|
}
|
|
VectorRep::Iterator iter(vector_rep, immutable_ ? bucket_ : bucket, compare_);
|
|
rwlock_.ReadUnlock();
|
|
|
|
for (iter.Seek(k.user_key(), k.memtable_key().data());
|
|
iter.Valid() && callback_func(callback_args, iter.key()); iter.Next()) {
|
|
}
|
|
}
|
|
|
|
MemTableRep::Iterator* VectorRep::GetIterator(Arena* arena) {
|
|
char* mem = nullptr;
|
|
if (arena != nullptr) {
|
|
mem = arena->AllocateAligned(sizeof(Iterator));
|
|
}
|
|
ReadLock l(&rwlock_);
|
|
// Do not sort here. The sorting would be done the first time
|
|
// a Seek is performed on the iterator.
|
|
if (immutable_) {
|
|
if (arena == nullptr) {
|
|
return new Iterator(this, bucket_, compare_);
|
|
} else {
|
|
return new (mem) Iterator(this, bucket_, compare_);
|
|
}
|
|
} else {
|
|
std::shared_ptr<Bucket> tmp;
|
|
tmp.reset(new Bucket(*bucket_)); // make a copy
|
|
if (arena == nullptr) {
|
|
return new Iterator(nullptr, tmp, compare_);
|
|
} else {
|
|
return new (mem) Iterator(nullptr, tmp, compare_);
|
|
}
|
|
}
|
|
}
|
|
} // anon namespace
|
|
|
|
MemTableRep* VectorRepFactory::CreateMemTableRep(
|
|
const MemTableRep::KeyComparator& compare, Allocator* allocator,
|
|
const SliceTransform*, Logger* logger) {
|
|
return new VectorRep(compare, allocator, count_);
|
|
}
|
|
} // namespace rocksdb
|
|
#endif // ROCKSDB_LITE
|