Skip to content

Commit

Permalink
Add unit tests for concurrent CF iteration and drop
Browse files Browse the repository at this point in the history
  • Loading branch information
javeme committed Dec 14, 2019
1 parent bd8404f commit c2fa51e
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 0 deletions.
1 change: 1 addition & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
### Bug Fixes
* Fix a bug that can cause unnecessary bg thread to be scheduled(#6104).
* Fix a bug in which a snapshot read could be affected by a DeleteRange after the snapshot (#6062).
* Fix crash caused by concurrent CF iterations and drops(#6147).

## 6.6.0 (11/25/2019)
### Bug Fixes
Expand Down
39 changes: 39 additions & 0 deletions db/column_family_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2365,6 +2365,45 @@ TEST_P(ColumnFamilyTest, ReadDroppedColumnFamily) {
}
}

TEST_P(ColumnFamilyTest, LiveIteratorWithDroppedColumnFamily) {
db_options_.create_missing_column_families = true;
db_options_.max_open_files = 20;
// delete obsolete files always
db_options_.delete_obsolete_files_period_micros = 0;
Open({"default", "one", "two"});
ColumnFamilyOptions options;
options.level0_file_num_compaction_trigger = 100;
options.level0_slowdown_writes_trigger = 200;
options.level0_stop_writes_trigger = 200;
options.write_buffer_size = 100000; // small write buffer size
Reopen({options, options, options});

// 1MB should create ~10 files for each CF
int kKeysNum = 10000;
PutRandomData(1, kKeysNum, 100);

{
std::unique_ptr<Iterator> iterator(
db_->NewIterator(ReadOptions(), handles_[1]));
iterator->SeekToFirst();

DropColumnFamilies({1});

// Make sure iterator created can still be used.
int count = 0;
for (; iterator->Valid(); iterator->Next()) {
ASSERT_OK(iterator->status());
++count;
}
ASSERT_OK(iterator->status());
ASSERT_EQ(count, kKeysNum);
}

Reopen();
Close();
Destroy();
}

TEST_P(ColumnFamilyTest, FlushAndDropRaceCondition) {
db_options_.create_missing_column_families = true;
Open({"default", "one"});
Expand Down
44 changes: 44 additions & 0 deletions db/db_iterator_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,50 @@ TEST_P(DBIteratorTest, IteratorPinsRef) {
} while (ChangeCompactOptions());
}

TEST_P(DBIteratorTest, IteratorDeleteAfterCfDelete) {
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());

Put(1, "foo", "delete-cf-then-delete-iter");
Put(1, "hello", "value2");

ColumnFamilyHandle* cf = handles_[1];
ReadOptions ro;

auto* iter = db_->NewIterator(ro, cf);
iter->SeekToFirst();
ASSERT_EQ(IterStatus(iter), "foo->delete-cf-then-delete-iter");

// delete CF handle
db_->DestroyColumnFamilyHandle(cf);
handles_.erase(std::begin(handles_) + 1);

// delete Iterator after CF handle is deleted
iter->Next();
ASSERT_EQ(IterStatus(iter), "hello->value2");
delete iter;
}

TEST_P(DBIteratorTest, IteratorDeleteAfterCfDrop) {
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());

Put(1, "foo", "drop-cf-then-delete-iter");

ReadOptions ro;
ColumnFamilyHandle* cf = handles_[1];

auto* iter = db_->NewIterator(ro, cf);
iter->SeekToFirst();
ASSERT_EQ(IterStatus(iter), "foo->drop-cf-then-delete-iter");

// drop and delete CF
db_->DropColumnFamily(cf);
db_->DestroyColumnFamilyHandle(cf);
handles_.erase(std::begin(handles_) + 1);

// delete Iterator after CF handle is dropped
delete iter;
}

// SetOptions not defined in ROCKSDB LITE
#ifndef ROCKSDB_LITE
TEST_P(DBIteratorTest, DBIteratorBoundTest) {
Expand Down
51 changes: 51 additions & 0 deletions java/src/test/java/org/rocksdb/RocksIteratorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,55 @@ public void rocksIterator() throws RocksDBException {
}
}
}

@Test
public void rocksIteratorReleaseAfterCfClose() throws RocksDBException {
try (final Options options = new Options()
.setCreateIfMissing(true)
.setCreateMissingColumnFamilies(true);
final RocksDB db = RocksDB.open(options,
this.dbFolder.getRoot().getAbsolutePath())) {
db.put("key".getBytes(), "value".getBytes());

// Test case: release iterator after default CF close
try (final RocksIterator iterator = db.newIterator()) {
// In fact, calling close() on default CF has no effect
db.getDefaultColumnFamily().close();

iterator.seekToFirst();
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo("key".getBytes());
assertThat(iterator.value()).isEqualTo("value".getBytes());
}

// Test case: release iterator after custom CF close
ColumnFamilyDescriptor cfd1 = new ColumnFamilyDescriptor("cf1".getBytes());
ColumnFamilyHandle cfHandle1 = db.createColumnFamily(cfd1);
db.put(cfHandle1, "key1".getBytes(), "value1".getBytes());

try (final RocksIterator iterator = db.newIterator(cfHandle1)) {
cfHandle1.close();

iterator.seekToFirst();
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo("key1".getBytes());
assertThat(iterator.value()).isEqualTo("value1".getBytes());
}

// Test case: release iterator after custom CF drop & close
ColumnFamilyDescriptor cfd2 = new ColumnFamilyDescriptor("cf2".getBytes());
ColumnFamilyHandle cfHandle2 = db.createColumnFamily(cfd2);
db.put(cfHandle2, "key2".getBytes(), "value2".getBytes());

try (final RocksIterator iterator = db.newIterator(cfHandle2)) {
db.dropColumnFamily(cfHandle2);
cfHandle2.close();

iterator.seekToFirst();
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo("key2".getBytes());
assertThat(iterator.value()).isEqualTo("value2".getBytes());
}
}
}
}

0 comments on commit c2fa51e

Please sign in to comment.