// Copyright (c) 2011-present, 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.
+// 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).
#include <memory>
#include <string>
class MockMemTableRep : public MemTableRep {
public:
- explicit MockMemTableRep(MemTableAllocator* allocator, MemTableRep* rep)
+ explicit MockMemTableRep(Allocator* allocator, MemTableRep* rep)
: MemTableRep(allocator), rep_(rep), num_insert_with_hint_(0) {}
virtual KeyHandle Allocate(const size_t len, char** buf) override {
return rep_->Allocate(len, buf);
}
- virtual void Insert(KeyHandle handle) override {
- return rep_->Insert(handle);
- }
+ virtual void Insert(KeyHandle handle) override { rep_->Insert(handle); }
virtual void InsertWithHint(KeyHandle handle, void** hint) override {
num_insert_with_hint_++;
- ASSERT_NE(nullptr, hint);
+ EXPECT_NE(nullptr, hint);
last_hint_in_ = *hint;
rep_->InsertWithHint(handle, hint);
last_hint_out_ = *hint;
class MockMemTableRepFactory : public MemTableRepFactory {
public:
virtual MemTableRep* CreateMemTableRep(const MemTableRep::KeyComparator& cmp,
- MemTableAllocator* allocator,
+ Allocator* allocator,
const SliceTransform* transform,
Logger* logger) override {
SkipListFactory factory;
return mock_rep_;
}
+ virtual MemTableRep* CreateMemTableRep(const MemTableRep::KeyComparator& cmp,
+ Allocator* allocator,
+ const SliceTransform* transform,
+ Logger* logger,
+ uint32_t column_family_id) override {
+ last_column_family_id_ = column_family_id;
+ return CreateMemTableRep(cmp, allocator, transform, logger);
+ }
+
virtual const char* Name() const override { return "MockMemTableRepFactory"; }
MockMemTableRep* rep() { return mock_rep_; }
bool IsInsertConcurrentlySupported() const override { return false; }
+ uint32_t GetLastColumnFamilyId() { return last_column_family_id_; }
+
private:
MockMemTableRep* mock_rep_;
+ // workaround since there's no port::kMaxUint32 yet.
+ uint32_t last_column_family_id_ = static_cast<uint32_t>(-1);
};
class TestPrefixExtractor : public SliceTransform {
return separator(key) != nullptr;
}
- virtual bool InRange(const Slice& key) const override { return false; }
+ virtual bool InRange(const Slice& /*key*/) const override { return false; }
private:
const char* separator(const Slice& key) const {
}
};
+// Test that ::Add properly returns false when inserting duplicate keys
+TEST_F(DBMemTableTest, DuplicateSeq) {
+ SequenceNumber seq = 123;
+ std::string value;
+ Status s;
+ MergeContext merge_context;
+ Options options;
+ InternalKeyComparator ikey_cmp(options.comparator);
+ RangeDelAggregator range_del_agg(ikey_cmp, {} /* snapshots */);
+
+ // Create a MemTable
+ InternalKeyComparator cmp(BytewiseComparator());
+ auto factory = std::make_shared<SkipListFactory>();
+ options.memtable_factory = factory;
+ ImmutableCFOptions ioptions(options);
+ WriteBufferManager wb(options.db_write_buffer_size);
+ MemTable* mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
+ kMaxSequenceNumber, 0 /* column_family_id */);
+
+ // Write some keys and make sure it returns false on duplicates
+ bool res;
+ res = mem->Add(seq, kTypeValue, "key", "value2");
+ ASSERT_TRUE(res);
+ res = mem->Add(seq, kTypeValue, "key", "value2");
+ ASSERT_FALSE(res);
+ // Changing the type should still cause the duplicatae key
+ res = mem->Add(seq, kTypeMerge, "key", "value2");
+ ASSERT_FALSE(res);
+ // Changing the seq number will make the key fresh
+ res = mem->Add(seq + 1, kTypeMerge, "key", "value2");
+ ASSERT_TRUE(res);
+ // Test with different types for duplicate keys
+ res = mem->Add(seq, kTypeDeletion, "key", "");
+ ASSERT_FALSE(res);
+ res = mem->Add(seq, kTypeSingleDeletion, "key", "");
+ ASSERT_FALSE(res);
+
+ // Test the duplicate keys under stress
+ for (int i = 0; i < 10000; i++) {
+ bool insert_dup = i % 10 == 1;
+ if (!insert_dup) {
+ seq++;
+ }
+ res = mem->Add(seq, kTypeValue, "foo", "value" + ToString(seq));
+ if (insert_dup) {
+ ASSERT_FALSE(res);
+ } else {
+ ASSERT_TRUE(res);
+ }
+ }
+ delete mem;
+
+ // Test with InsertWithHint
+ options.memtable_insert_with_hint_prefix_extractor.reset(
+ new TestPrefixExtractor()); // which uses _ to extract the prefix
+ ioptions = ImmutableCFOptions(options);
+ mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
+ kMaxSequenceNumber, 0 /* column_family_id */);
+ // Insert a duplicate key with _ in it
+ res = mem->Add(seq, kTypeValue, "key_1", "value");
+ ASSERT_TRUE(res);
+ res = mem->Add(seq, kTypeValue, "key_1", "value");
+ ASSERT_FALSE(res);
+ delete mem;
+
+ // Test when InsertConcurrently will be invoked
+ options.allow_concurrent_memtable_write = true;
+ ioptions = ImmutableCFOptions(options);
+ mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
+ kMaxSequenceNumber, 0 /* column_family_id */);
+ MemTablePostProcessInfo post_process_info;
+ res = mem->Add(seq, kTypeValue, "key", "value", true, &post_process_info);
+ ASSERT_TRUE(res);
+ res = mem->Add(seq, kTypeValue, "key", "value", true, &post_process_info);
+ ASSERT_FALSE(res);
+ delete mem;
+}
+
TEST_F(DBMemTableTest, InsertWithHint) {
Options options;
options.allow_concurrent_memtable_write = false;
ASSERT_EQ("vvv", Get("whitelisted"));
}
+TEST_F(DBMemTableTest, ColumnFamilyId) {
+ // Verifies MemTableRepFactory is told the right column family id.
+ Options options;
+ options.allow_concurrent_memtable_write = false;
+ options.create_if_missing = true;
+ options.memtable_factory.reset(new MockMemTableRepFactory());
+ DestroyAndReopen(options);
+ CreateAndReopenWithCF({"pikachu"}, options);
+
+ for (int cf = 0; cf < 2; ++cf) {
+ ASSERT_OK(Put(cf, "key", "val"));
+ ASSERT_OK(Flush(cf));
+ ASSERT_EQ(
+ cf, static_cast<MockMemTableRepFactory*>(options.memtable_factory.get())
+ ->GetLastColumnFamilyId());
+ }
+}
+
} // namespace rocksdb
int main(int argc, char** argv) {