Add test for iteration+mutation of WBWI
Summary: We should support use-cases that mutate WBWI while they're iterating it. This diff adds a unit test to check this behavior. Test Plan: this is a test Reviewers: sdong Reviewed By: sdong Subscribers: dhruba, leveldb Differential Revision: https://reviews.facebook.net/D39501
This commit is contained in:
parent
d9b3338ebe
commit
62c3a95796
@ -1182,6 +1182,188 @@ TEST_F(WriteBatchWithIndexTest, TestGetFromBatchAndDBMerge) {
|
||||
DestroyDB(dbname, options);
|
||||
}
|
||||
|
||||
void AssertKey(std::string key, WBWIIterator* iter) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(key, iter->Entry().key.ToString());
|
||||
}
|
||||
|
||||
void AssertValue(std::string value, WBWIIterator* iter) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(value, iter->Entry().value.ToString());
|
||||
}
|
||||
|
||||
// Tests that we can write to the WBWI while we iterate (from a single thread).
|
||||
// iteration should see the newest writes
|
||||
TEST_F(WriteBatchWithIndexTest, MutateWhileIteratingCorrectnessTest) {
|
||||
WriteBatchWithIndex batch(BytewiseComparator(), 0, true);
|
||||
for (char c = 'a'; c <= 'z'; ++c) {
|
||||
batch.Put(std::string(1, c), std::string(1, c));
|
||||
}
|
||||
|
||||
std::unique_ptr<WBWIIterator> iter(batch.NewIterator());
|
||||
iter->Seek("k");
|
||||
AssertKey("k", iter.get());
|
||||
iter->Next();
|
||||
AssertKey("l", iter.get());
|
||||
batch.Put("ab", "cc");
|
||||
iter->Next();
|
||||
AssertKey("m", iter.get());
|
||||
batch.Put("mm", "kk");
|
||||
iter->Next();
|
||||
AssertKey("mm", iter.get());
|
||||
AssertValue("kk", iter.get());
|
||||
batch.Delete("mm");
|
||||
|
||||
iter->Next();
|
||||
AssertKey("n", iter.get());
|
||||
iter->Prev();
|
||||
AssertKey("mm", iter.get());
|
||||
ASSERT_EQ(kDeleteRecord, iter->Entry().type);
|
||||
|
||||
iter->Seek("ab");
|
||||
AssertKey("ab", iter.get());
|
||||
batch.Delete("x");
|
||||
iter->Seek("x");
|
||||
AssertKey("x", iter.get());
|
||||
ASSERT_EQ(kDeleteRecord, iter->Entry().type);
|
||||
iter->Prev();
|
||||
AssertKey("w", iter.get());
|
||||
}
|
||||
|
||||
void AssertIterKey(std::string key, Iterator* iter) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(key, iter->key().ToString());
|
||||
}
|
||||
|
||||
void AssertIterValue(std::string value, Iterator* iter) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(value, iter->value().ToString());
|
||||
}
|
||||
|
||||
// same thing as above, but testing IteratorWithBase
|
||||
TEST_F(WriteBatchWithIndexTest, MutateWhileIteratingBaseCorrectnessTest) {
|
||||
WriteBatchWithIndex batch(BytewiseComparator(), 0, true);
|
||||
for (char c = 'a'; c <= 'z'; ++c) {
|
||||
batch.Put(std::string(1, c), std::string(1, c));
|
||||
}
|
||||
|
||||
KVMap map;
|
||||
map["aa"] = "aa";
|
||||
map["cc"] = "cc";
|
||||
map["ee"] = "ee";
|
||||
map["em"] = "me";
|
||||
|
||||
std::unique_ptr<Iterator> iter(
|
||||
batch.NewIteratorWithBase(new KVIter(&map)));
|
||||
iter->Seek("k");
|
||||
AssertIterKey("k", iter.get());
|
||||
iter->Next();
|
||||
AssertIterKey("l", iter.get());
|
||||
batch.Put("ab", "cc");
|
||||
iter->Next();
|
||||
AssertIterKey("m", iter.get());
|
||||
batch.Put("mm", "kk");
|
||||
iter->Next();
|
||||
AssertIterKey("mm", iter.get());
|
||||
AssertIterValue("kk", iter.get());
|
||||
batch.Delete("mm");
|
||||
// still mm even though it's deleted
|
||||
AssertIterKey("mm", iter.get());
|
||||
AssertIterValue("kk", iter.get());
|
||||
iter->Next();
|
||||
AssertIterKey("n", iter.get());
|
||||
iter->Prev();
|
||||
// "mm" is deleted, so we're back at "m"
|
||||
AssertIterKey("m", iter.get());
|
||||
|
||||
iter->Seek("ab");
|
||||
AssertIterKey("ab", iter.get());
|
||||
iter->Prev();
|
||||
AssertIterKey("aa", iter.get());
|
||||
iter->Prev();
|
||||
AssertIterKey("a", iter.get());
|
||||
batch.Delete("aa");
|
||||
iter->Next();
|
||||
AssertIterKey("ab", iter.get());
|
||||
iter->Prev();
|
||||
AssertIterKey("a", iter.get());
|
||||
|
||||
batch.Delete("x");
|
||||
iter->Seek("x");
|
||||
AssertIterKey("y", iter.get());
|
||||
iter->Next();
|
||||
AssertIterKey("z", iter.get());
|
||||
iter->Prev();
|
||||
iter->Prev();
|
||||
AssertIterKey("w", iter.get());
|
||||
|
||||
batch.Delete("e");
|
||||
iter->Seek("e");
|
||||
AssertIterKey("ee", iter.get());
|
||||
AssertIterValue("ee", iter.get());
|
||||
batch.Put("ee", "xx");
|
||||
// still the same value
|
||||
AssertIterValue("ee", iter.get());
|
||||
iter->Next();
|
||||
AssertIterKey("em", iter.get());
|
||||
iter->Prev();
|
||||
// new value
|
||||
AssertIterValue("xx", iter.get());
|
||||
}
|
||||
|
||||
// stress testing mutations with IteratorWithBase
|
||||
TEST_F(WriteBatchWithIndexTest, MutateWhileIteratingBaseStressTest) {
|
||||
WriteBatchWithIndex batch(BytewiseComparator(), 0, true);
|
||||
for (char c = 'a'; c <= 'z'; ++c) {
|
||||
batch.Put(std::string(1, c), std::string(1, c));
|
||||
}
|
||||
|
||||
KVMap map;
|
||||
for (char c = 'a'; c <= 'z'; ++c) {
|
||||
map[std::string(2, c)] = std::string(2, c);
|
||||
}
|
||||
|
||||
std::unique_ptr<Iterator> iter(
|
||||
batch.NewIteratorWithBase(new KVIter(&map)));
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 1000000; ++i) {
|
||||
int random = rnd.Uniform(8);
|
||||
char c = static_cast<char>(rnd.Uniform(26) + 'a');
|
||||
switch (random) {
|
||||
case 0:
|
||||
batch.Put(std::string(1, c), "xxx");
|
||||
break;
|
||||
case 1:
|
||||
batch.Put(std::string(2, c), "xxx");
|
||||
break;
|
||||
case 2:
|
||||
batch.Delete(std::string(1, c));
|
||||
break;
|
||||
case 3:
|
||||
batch.Delete(std::string(2, c));
|
||||
break;
|
||||
case 4:
|
||||
iter->Seek(std::string(1, c));
|
||||
break;
|
||||
case 5:
|
||||
iter->Seek(std::string(2, c));
|
||||
break;
|
||||
case 6:
|
||||
if (iter->Valid()) {
|
||||
iter->Next();
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
if (iter->Valid()) {
|
||||
iter->Prev();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
Loading…
Reference in New Issue
Block a user