Make RocksDB instance responsible for closing associated ColumnFamilyHandle instances (#7428)

Summary:
- Takes the burden off developer to close ColumnFamilyHandle instances before closing RocksDB instance
- The change is backward-compatible

----
Previously the pattern for working with Column Families was:

```java
try (final ColumnFamilyOptions cfOpts = new ColumnFamilyOptions().optimizeUniversalStyleCompaction()) {

  // list of column family descriptors, first entry must always be default column family
  final List<ColumnFamilyDescriptor> cfDescriptors = Arrays.asList(
      new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOpts),
      new ColumnFamilyDescriptor("my-first-columnfamily".getBytes(), cfOpts)
  );

  // a list which will hold the handles for the column families once the db is opened
  final List<ColumnFamilyHandle> columnFamilyHandleList =
      new ArrayList<>();

  try (final DBOptions options = new DBOptions()
      .setCreateIfMissing(true)
      .setCreateMissingColumnFamilies(true);
       final RocksDB db = RocksDB.open(options,
           "path/to/do", cfDescriptors,
           columnFamilyHandleList)) {

    try {

      // do something

    } finally {

      // NOTE user must explicitly frees the column family handles before freeing the db
      for (final ColumnFamilyHandle columnFamilyHandle :
          columnFamilyHandleList) {
        columnFamilyHandle.close();
      }
    } // frees the column family options
  }
} // frees the db and the db options
```

With the changes in this PR, the Java user no longer has to worry about manually closing the Column Families, which allows them to write simpler symmetrical create/free oriented code like this:

```java
try (final ColumnFamilyOptions cfOpts = new ColumnFamilyOptions().optimizeUniversalStyleCompaction()) {

  // list of column family descriptors, first entry must always be default column family
  final List<ColumnFamilyDescriptor> cfDescriptors = Arrays.asList(
      new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOpts),
      new ColumnFamilyDescriptor("my-first-columnfamily".getBytes(), cfOpts)
  );

  // a list which will hold the handles for the column families once the db is opened
  final List<ColumnFamilyHandle> columnFamilyHandleList =
      new ArrayList<>();

  try (final DBOptions options = new DBOptions()
      .setCreateIfMissing(true)
      .setCreateMissingColumnFamilies(true);
       final RocksDB db = RocksDB.open(options,
           "path/to/do", cfDescriptors,
           columnFamilyHandleList)) {

        // do something

    } // frees the column family options, then frees the db and the db options
  }
}
```

**NOTE**: The changes in this PR are backwards API compatible, which means existing code using the original approach will also continue to function correctly.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/7428

Reviewed By: cheng-chang

Differential Revision: D24063348

Pulled By: jay-zhuang

fbshipit-source-id: 648d7526669923128c863ead94516bf4d50ac658
This commit is contained in:
Tomasz Posluszny 2020-10-14 14:37:55 -07:00 committed by Facebook GitHub Bot
parent 850cc0dbed
commit 05fba96927
4 changed files with 258 additions and 344 deletions

View File

@ -13,6 +13,12 @@ import java.util.Objects;
* ColumnFamily Pointers. * ColumnFamily Pointers.
*/ */
public class ColumnFamilyHandle extends RocksObject { public class ColumnFamilyHandle extends RocksObject {
/**
* Constructs column family Java object, which operates on underlying native object.
*
* @param rocksDB db instance associated with this column family
* @param nativeHandle native handle to underlying native ColumnFamily object
*/
ColumnFamilyHandle(final RocksDB rocksDB, ColumnFamilyHandle(final RocksDB rocksDB,
final long nativeHandle) { final long nativeHandle) {
super(nativeHandle); super(nativeHandle);

View File

@ -38,6 +38,8 @@ public class RocksDB extends RocksObject {
RocksDB.loadLibrary(); RocksDB.loadLibrary();
} }
private List<ColumnFamilyHandle> ownedColumnFamilyHandles = new ArrayList<>();
/** /**
* Loads the necessary library files. * Loads the necessary library files.
* Calling this method twice will have no effect. * Calling this method twice will have no effect.
@ -307,9 +309,12 @@ public class RocksDB extends RocksObject {
db.storeOptionsInstance(options); db.storeOptionsInstance(options);
for (int i = 1; i < handles.length; i++) { for (int i = 1; i < handles.length; i++) {
columnFamilyHandles.add(new ColumnFamilyHandle(db, handles[i])); final ColumnFamilyHandle columnFamilyHandle = new ColumnFamilyHandle(db, handles[i]);
columnFamilyHandles.add(columnFamilyHandle);
} }
db.ownedColumnFamilyHandles.addAll(columnFamilyHandles);
return db; return db;
} }
@ -484,9 +489,12 @@ public class RocksDB extends RocksObject {
db.storeOptionsInstance(options); db.storeOptionsInstance(options);
for (int i = 1; i < handles.length; i++) { for (int i = 1; i < handles.length; i++) {
columnFamilyHandles.add(new ColumnFamilyHandle(db, handles[i])); final ColumnFamilyHandle columnFamilyHandle = new ColumnFamilyHandle(db, handles[i]);
columnFamilyHandles.add(columnFamilyHandle);
} }
db.ownedColumnFamilyHandles.addAll(columnFamilyHandles);
return db; return db;
} }
@ -577,9 +585,12 @@ public class RocksDB extends RocksObject {
db.storeOptionsInstance(options); db.storeOptionsInstance(options);
for (int i = 1; i < handles.length; i++) { for (int i = 1; i < handles.length; i++) {
columnFamilyHandles.add(new ColumnFamilyHandle(db, handles[i])); final ColumnFamilyHandle columnFamilyHandle = new ColumnFamilyHandle(db, handles[i]);
columnFamilyHandles.add(columnFamilyHandle);
} }
db.ownedColumnFamilyHandles.addAll(columnFamilyHandles);
return db; return db;
} }
@ -597,6 +608,11 @@ public class RocksDB extends RocksObject {
* @throws RocksDBException if an error occurs whilst closing. * @throws RocksDBException if an error occurs whilst closing.
*/ */
public void closeE() throws RocksDBException { public void closeE() throws RocksDBException {
for (final ColumnFamilyHandle columnFamilyHandle : ownedColumnFamilyHandles) {
columnFamilyHandle.close();
}
ownedColumnFamilyHandles.clear();
if (owningHandle_.compareAndSet(true, false)) { if (owningHandle_.compareAndSet(true, false)) {
try { try {
closeDatabase(nativeHandle_); closeDatabase(nativeHandle_);
@ -619,6 +635,11 @@ public class RocksDB extends RocksObject {
*/ */
@Override @Override
public void close() { public void close() {
for (final ColumnFamilyHandle columnFamilyHandle : ownedColumnFamilyHandles) {
columnFamilyHandle.close();
}
ownedColumnFamilyHandles.clear();
if (owningHandle_.compareAndSet(true, false)) { if (owningHandle_.compareAndSet(true, false)) {
try { try {
closeDatabase(nativeHandle_); closeDatabase(nativeHandle_);
@ -661,10 +682,12 @@ public class RocksDB extends RocksObject {
public ColumnFamilyHandle createColumnFamily( public ColumnFamilyHandle createColumnFamily(
final ColumnFamilyDescriptor columnFamilyDescriptor) final ColumnFamilyDescriptor columnFamilyDescriptor)
throws RocksDBException { throws RocksDBException {
return new ColumnFamilyHandle(this, createColumnFamily(nativeHandle_, final ColumnFamilyHandle columnFamilyHandle = new ColumnFamilyHandle(this,
columnFamilyDescriptor.getName(), createColumnFamily(nativeHandle_, columnFamilyDescriptor.getName(),
columnFamilyDescriptor.getName().length, columnFamilyDescriptor.getName().length,
columnFamilyDescriptor.getOptions().nativeHandle_)); columnFamilyDescriptor.getOptions().nativeHandle_));
ownedColumnFamilyHandles.add(columnFamilyHandle);
return columnFamilyHandle;
} }
/** /**
@ -688,8 +711,10 @@ public class RocksDB extends RocksObject {
final List<ColumnFamilyHandle> columnFamilyHandles = final List<ColumnFamilyHandle> columnFamilyHandles =
new ArrayList<>(cfHandles.length); new ArrayList<>(cfHandles.length);
for (int i = 0; i < cfHandles.length; i++) { for (int i = 0; i < cfHandles.length; i++) {
columnFamilyHandles.add(new ColumnFamilyHandle(this, cfHandles[i])); final ColumnFamilyHandle columnFamilyHandle = new ColumnFamilyHandle(this, cfHandles[i]);
columnFamilyHandles.add(columnFamilyHandle);
} }
ownedColumnFamilyHandles.addAll(columnFamilyHandles);
return columnFamilyHandles; return columnFamilyHandles;
} }
@ -719,8 +744,10 @@ public class RocksDB extends RocksObject {
final List<ColumnFamilyHandle> columnFamilyHandles = final List<ColumnFamilyHandle> columnFamilyHandles =
new ArrayList<>(cfHandles.length); new ArrayList<>(cfHandles.length);
for (int i = 0; i < cfHandles.length; i++) { for (int i = 0; i < cfHandles.length; i++) {
columnFamilyHandles.add(new ColumnFamilyHandle(this, cfHandles[i])); final ColumnFamilyHandle columnFamilyHandle = new ColumnFamilyHandle(this, cfHandles[i]);
columnFamilyHandles.add(columnFamilyHandle);
} }
ownedColumnFamilyHandles.addAll(columnFamilyHandles);
return columnFamilyHandles; return columnFamilyHandles;
} }
@ -753,7 +780,22 @@ public class RocksDB extends RocksObject {
dropColumnFamilies(nativeHandle_, cfHandles); dropColumnFamilies(nativeHandle_, cfHandles);
} }
//TODO(AR) what about DestroyColumnFamilyHandle /**
* Deletes native column family handle of given {@link ColumnFamilyHandle} Java object
* and removes reference from {@link RocksDB#ownedColumnFamilyHandles}.
*
* @param columnFamilyHandle column family handle object.
*/
public void destroyColumnFamilyHandle(final ColumnFamilyHandle columnFamilyHandle) {
for (int i = 0; i < ownedColumnFamilyHandles.size(); ++i) {
final ColumnFamilyHandle ownedHandle = ownedColumnFamilyHandles.get(i);
if (ownedHandle.equals(columnFamilyHandle)) {
columnFamilyHandle.close();
ownedColumnFamilyHandles.remove(i);
return;
}
}
}
/** /**
* Set the database entry for "key" to "value". * Set the database entry for "key" to "value".
@ -4479,7 +4521,6 @@ public class RocksDB extends RocksObject {
final long handle, final long cfHandle) throws RocksDBException; final long handle, final long cfHandle) throws RocksDBException;
private native void dropColumnFamilies(final long handle, private native void dropColumnFamilies(final long handle,
final long[] cfHandles) throws RocksDBException; final long[] cfHandles) throws RocksDBException;
//TODO(AR) best way to express DestroyColumnFamilyHandle? ...maybe in ColumnFamilyHandle?
private native void put(final long handle, final byte[] key, private native void put(final long handle, final byte[] key,
final int keyOffset, final int keyLength, final byte[] value, final int keyOffset, final int keyLength, final byte[] value,
final int valueOffset, int valueLength) throws RocksDBException; final int valueOffset, int valueLength) throws RocksDBException;

View File

@ -5,16 +5,17 @@
package org.rocksdb; package org.rocksdb;
import java.util.*; import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.*;
import org.junit.ClassRule; import org.junit.ClassRule;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
public class ColumnFamilyTest { public class ColumnFamilyTest {
@ClassRule @ClassRule
@ -141,33 +142,19 @@ public class ColumnFamilyTest {
final RocksDB db = RocksDB.open(options, final RocksDB db = RocksDB.open(options,
dbFolder.getRoot().getAbsolutePath(), cfNames, dbFolder.getRoot().getAbsolutePath(), cfNames,
columnFamilyHandleList)) { columnFamilyHandleList)) {
assertThat(columnFamilyHandleList.size()).isEqualTo(2);
db.put("dfkey1".getBytes(), "dfvalue".getBytes());
db.put(columnFamilyHandleList.get(0), "dfkey2".getBytes(), "dfvalue".getBytes());
db.put(columnFamilyHandleList.get(1), "newcfkey1".getBytes(), "newcfvalue".getBytes());
try { String retVal = new String(db.get(columnFamilyHandleList.get(1), "newcfkey1".getBytes()));
assertThat(columnFamilyHandleList.size()).isEqualTo(2); assertThat(retVal).isEqualTo("newcfvalue");
db.put("dfkey1".getBytes(), "dfvalue".getBytes()); assertThat((db.get(columnFamilyHandleList.get(1), "dfkey1".getBytes()))).isNull();
db.put(columnFamilyHandleList.get(0), "dfkey2".getBytes(), db.delete(columnFamilyHandleList.get(1), "newcfkey1".getBytes());
"dfvalue".getBytes()); assertThat((db.get(columnFamilyHandleList.get(1), "newcfkey1".getBytes()))).isNull();
db.put(columnFamilyHandleList.get(1), "newcfkey1".getBytes(), db.delete(columnFamilyHandleList.get(0), new WriteOptions(), "dfkey2".getBytes());
"newcfvalue".getBytes()); assertThat(db.get(columnFamilyHandleList.get(0), new ReadOptions(), "dfkey2".getBytes()))
.isNull();
String retVal = new String(db.get(columnFamilyHandleList.get(1),
"newcfkey1".getBytes()));
assertThat(retVal).isEqualTo("newcfvalue");
assertThat((db.get(columnFamilyHandleList.get(1),
"dfkey1".getBytes()))).isNull();
db.delete(columnFamilyHandleList.get(1), "newcfkey1".getBytes());
assertThat((db.get(columnFamilyHandleList.get(1),
"newcfkey1".getBytes()))).isNull();
db.delete(columnFamilyHandleList.get(0), new WriteOptions(),
"dfkey2".getBytes());
assertThat(db.get(columnFamilyHandleList.get(0), new ReadOptions(),
"dfkey2".getBytes())).isNull();
} finally {
for (final ColumnFamilyHandle columnFamilyHandle :
columnFamilyHandleList) {
columnFamilyHandle.close();
}
}
} }
} }
@ -184,30 +171,22 @@ public class ColumnFamilyTest {
final RocksDB db = RocksDB.open(options, final RocksDB db = RocksDB.open(options,
dbFolder.getRoot().getAbsolutePath(), cfDescriptors, dbFolder.getRoot().getAbsolutePath(), cfDescriptors,
columnFamilyHandleList)) { columnFamilyHandleList)) {
try { db.put(
db.put(columnFamilyHandleList.get(0), new WriteOptions(), columnFamilyHandleList.get(0), new WriteOptions(), "key1".getBytes(), "value".getBytes());
"key1".getBytes(), "value".getBytes()); db.put("key2".getBytes(), "12345678".getBytes());
db.put("key2".getBytes(), "12345678".getBytes()); final byte[] outValue = new byte[5];
final byte[] outValue = new byte[5]; // not found value
// not found value int getResult = db.get("keyNotFound".getBytes(), outValue);
int getResult = db.get("keyNotFound".getBytes(), outValue); assertThat(getResult).isEqualTo(RocksDB.NOT_FOUND);
assertThat(getResult).isEqualTo(RocksDB.NOT_FOUND); // found value which fits in outValue
// found value which fits in outValue getResult = db.get(columnFamilyHandleList.get(0), "key1".getBytes(), outValue);
getResult = db.get(columnFamilyHandleList.get(0), "key1".getBytes(), assertThat(getResult).isNotEqualTo(RocksDB.NOT_FOUND);
outValue); assertThat(outValue).isEqualTo("value".getBytes());
assertThat(getResult).isNotEqualTo(RocksDB.NOT_FOUND); // found value which fits partially
assertThat(outValue).isEqualTo("value".getBytes()); getResult =
// found value which fits partially db.get(columnFamilyHandleList.get(0), new ReadOptions(), "key2".getBytes(), outValue);
getResult = db.get(columnFamilyHandleList.get(0), new ReadOptions(), assertThat(getResult).isNotEqualTo(RocksDB.NOT_FOUND);
"key2".getBytes(), outValue); assertThat(outValue).isEqualTo("12345".getBytes());
assertThat(getResult).isNotEqualTo(RocksDB.NOT_FOUND);
assertThat(outValue).isEqualTo("12345".getBytes());
} finally {
for (final ColumnFamilyHandle columnFamilyHandle :
columnFamilyHandleList) {
columnFamilyHandle.close();
}
}
} }
} }
@ -223,22 +202,12 @@ public class ColumnFamilyTest {
final RocksDB db = RocksDB.open(options, final RocksDB db = RocksDB.open(options,
dbFolder.getRoot().getAbsolutePath(), cfDescriptors, dbFolder.getRoot().getAbsolutePath(), cfDescriptors,
columnFamilyHandleList)) { columnFamilyHandleList)) {
ColumnFamilyHandle tmpColumnFamilyHandle = null; ColumnFamilyHandle tmpColumnFamilyHandle;
try { tmpColumnFamilyHandle = db.createColumnFamily(
tmpColumnFamilyHandle = db.createColumnFamily( new ColumnFamilyDescriptor("tmpCF".getBytes(), new ColumnFamilyOptions()));
new ColumnFamilyDescriptor("tmpCF".getBytes(), db.put(tmpColumnFamilyHandle, "key".getBytes(), "value".getBytes());
new ColumnFamilyOptions())); db.dropColumnFamily(tmpColumnFamilyHandle);
db.put(tmpColumnFamilyHandle, "key".getBytes(), "value".getBytes()); assertThat(tmpColumnFamilyHandle.isOwningHandle()).isTrue();
db.dropColumnFamily(tmpColumnFamilyHandle);
assertThat(tmpColumnFamilyHandle.isOwningHandle()).isTrue();
} finally {
if (tmpColumnFamilyHandle != null) {
tmpColumnFamilyHandle.close();
}
for (ColumnFamilyHandle columnFamilyHandle : columnFamilyHandleList) {
columnFamilyHandle.close();
}
}
} }
} }
@ -256,29 +225,15 @@ public class ColumnFamilyTest {
columnFamilyHandleList)) { columnFamilyHandleList)) {
ColumnFamilyHandle tmpColumnFamilyHandle = null; ColumnFamilyHandle tmpColumnFamilyHandle = null;
ColumnFamilyHandle tmpColumnFamilyHandle2 = null; ColumnFamilyHandle tmpColumnFamilyHandle2 = null;
try { tmpColumnFamilyHandle = db.createColumnFamily(
tmpColumnFamilyHandle = db.createColumnFamily( new ColumnFamilyDescriptor("tmpCF".getBytes(), new ColumnFamilyOptions()));
new ColumnFamilyDescriptor("tmpCF".getBytes(), tmpColumnFamilyHandle2 = db.createColumnFamily(
new ColumnFamilyOptions())); new ColumnFamilyDescriptor("tmpCF2".getBytes(), new ColumnFamilyOptions()));
tmpColumnFamilyHandle2 = db.createColumnFamily( db.put(tmpColumnFamilyHandle, "key".getBytes(), "value".getBytes());
new ColumnFamilyDescriptor("tmpCF2".getBytes(), db.put(tmpColumnFamilyHandle2, "key".getBytes(), "value".getBytes());
new ColumnFamilyOptions())); db.dropColumnFamilies(Arrays.asList(tmpColumnFamilyHandle, tmpColumnFamilyHandle2));
db.put(tmpColumnFamilyHandle, "key".getBytes(), "value".getBytes()); assertThat(tmpColumnFamilyHandle.isOwningHandle()).isTrue();
db.put(tmpColumnFamilyHandle2, "key".getBytes(), "value".getBytes()); assertThat(tmpColumnFamilyHandle2.isOwningHandle()).isTrue();
db.dropColumnFamilies(Arrays.asList(tmpColumnFamilyHandle, tmpColumnFamilyHandle2));
assertThat(tmpColumnFamilyHandle.isOwningHandle()).isTrue();
assertThat(tmpColumnFamilyHandle2.isOwningHandle()).isTrue();
} finally {
if (tmpColumnFamilyHandle != null) {
tmpColumnFamilyHandle.close();
}
if (tmpColumnFamilyHandle2 != null) {
tmpColumnFamilyHandle2.close();
}
for (ColumnFamilyHandle columnFamilyHandle : columnFamilyHandleList) {
columnFamilyHandle.close();
}
}
} }
} }
@ -300,36 +255,24 @@ public class ColumnFamilyTest {
cfDescriptors, columnFamilyHandleList); cfDescriptors, columnFamilyHandleList);
final WriteBatch writeBatch = new WriteBatch(); final WriteBatch writeBatch = new WriteBatch();
final WriteOptions writeOpt = new WriteOptions()) { final WriteOptions writeOpt = new WriteOptions()) {
try { writeBatch.put("key".getBytes(), "value".getBytes());
writeBatch.put("key".getBytes(), "value".getBytes()); writeBatch.put(db.getDefaultColumnFamily(), "mergeKey".getBytes(), "merge".getBytes());
writeBatch.put(db.getDefaultColumnFamily(), writeBatch.merge(db.getDefaultColumnFamily(), "mergeKey".getBytes(), "merge".getBytes());
"mergeKey".getBytes(), "merge".getBytes()); writeBatch.put(columnFamilyHandleList.get(1), "newcfkey".getBytes(), "value".getBytes());
writeBatch.merge(db.getDefaultColumnFamily(), "mergeKey".getBytes(), writeBatch.put(columnFamilyHandleList.get(1), "newcfkey2".getBytes(), "value2".getBytes());
"merge".getBytes()); writeBatch.delete("xyz".getBytes());
writeBatch.put(columnFamilyHandleList.get(1), "newcfkey".getBytes(), writeBatch.delete(columnFamilyHandleList.get(1), "xyz".getBytes());
"value".getBytes()); db.write(writeOpt, writeBatch);
writeBatch.put(columnFamilyHandleList.get(1), "newcfkey2".getBytes(),
"value2".getBytes());
writeBatch.delete("xyz".getBytes());
writeBatch.delete(columnFamilyHandleList.get(1), "xyz".getBytes());
db.write(writeOpt, writeBatch);
assertThat(db.get(columnFamilyHandleList.get(1), assertThat(db.get(columnFamilyHandleList.get(1), "xyz".getBytes()) == null);
"xyz".getBytes()) == null); assertThat(new String(db.get(columnFamilyHandleList.get(1), "newcfkey".getBytes())))
assertThat(new String(db.get(columnFamilyHandleList.get(1), .isEqualTo("value");
"newcfkey".getBytes()))).isEqualTo("value"); assertThat(new String(db.get(columnFamilyHandleList.get(1), "newcfkey2".getBytes())))
assertThat(new String(db.get(columnFamilyHandleList.get(1), .isEqualTo("value2");
"newcfkey2".getBytes()))).isEqualTo("value2"); assertThat(new String(db.get("key".getBytes()))).isEqualTo("value");
assertThat(new String(db.get("key".getBytes()))).isEqualTo("value"); // check if key is merged
// check if key is merged assertThat(new String(db.get(db.getDefaultColumnFamily(), "mergeKey".getBytes())))
assertThat(new String(db.get(db.getDefaultColumnFamily(), .isEqualTo("merge,merge");
"mergeKey".getBytes()))).isEqualTo("merge,merge");
} finally {
for (final ColumnFamilyHandle columnFamilyHandle :
columnFamilyHandleList) {
columnFamilyHandle.close();
}
}
} }
} }
} }
@ -346,32 +289,21 @@ public class ColumnFamilyTest {
final RocksDB db = RocksDB.open(options, final RocksDB db = RocksDB.open(options,
dbFolder.getRoot().getAbsolutePath(), dbFolder.getRoot().getAbsolutePath(),
cfDescriptors, columnFamilyHandleList)) { cfDescriptors, columnFamilyHandleList)) {
try { db.put(columnFamilyHandleList.get(1), "newcfkey".getBytes(), "value".getBytes());
db.put(columnFamilyHandleList.get(1), "newcfkey2".getBytes(), "value2".getBytes());
db.put(columnFamilyHandleList.get(1), "newcfkey".getBytes(), try (final RocksIterator rocksIterator = db.newIterator(columnFamilyHandleList.get(1))) {
"value".getBytes()); rocksIterator.seekToFirst();
db.put(columnFamilyHandleList.get(1), "newcfkey2".getBytes(), Map<String, String> refMap = new HashMap<>();
"value2".getBytes()); refMap.put("newcfkey", "value");
try (final RocksIterator rocksIterator = refMap.put("newcfkey2", "value2");
db.newIterator(columnFamilyHandleList.get(1))) { int i = 0;
rocksIterator.seekToFirst(); while (rocksIterator.isValid()) {
Map<String, String> refMap = new HashMap<>(); i++;
refMap.put("newcfkey", "value"); assertThat(refMap.get(new String(rocksIterator.key())))
refMap.put("newcfkey2", "value2"); .isEqualTo(new String(rocksIterator.value()));
int i = 0; rocksIterator.next();
while (rocksIterator.isValid()) {
i++;
assertThat(refMap.get(new String(rocksIterator.key()))).
isEqualTo(new String(rocksIterator.value()));
rocksIterator.next();
}
assertThat(i).isEqualTo(2);
}
} finally {
for (final ColumnFamilyHandle columnFamilyHandle :
columnFamilyHandleList) {
columnFamilyHandle.close();
} }
assertThat(i).isEqualTo(2);
} }
} }
} }
@ -388,35 +320,20 @@ public class ColumnFamilyTest {
final RocksDB db = RocksDB.open(options, final RocksDB db = RocksDB.open(options,
dbFolder.getRoot().getAbsolutePath(), dbFolder.getRoot().getAbsolutePath(),
cfDescriptors, columnFamilyHandleList)) { cfDescriptors, columnFamilyHandleList)) {
try { db.put(columnFamilyHandleList.get(0), "key".getBytes(), "value".getBytes());
db.put(columnFamilyHandleList.get(0), "key".getBytes(), db.put(columnFamilyHandleList.get(1), "newcfkey".getBytes(), "value".getBytes());
"value".getBytes());
db.put(columnFamilyHandleList.get(1), "newcfkey".getBytes(),
"value".getBytes());
final List<byte[]> keys = Arrays.asList(new byte[][]{ final List<byte[]> keys =
"key".getBytes(), "newcfkey".getBytes() Arrays.asList(new byte[][] {"key".getBytes(), "newcfkey".getBytes()});
});
List<byte[]> retValues = db.multiGetAsList(columnFamilyHandleList, keys); List<byte[]> retValues = db.multiGetAsList(columnFamilyHandleList, keys);
assertThat(retValues.size()).isEqualTo(2); assertThat(retValues.size()).isEqualTo(2);
assertThat(new String(retValues.get(0))) assertThat(new String(retValues.get(0))).isEqualTo("value");
.isEqualTo("value"); assertThat(new String(retValues.get(1))).isEqualTo("value");
assertThat(new String(retValues.get(1))) retValues = db.multiGetAsList(new ReadOptions(), columnFamilyHandleList, keys);
.isEqualTo("value"); assertThat(retValues.size()).isEqualTo(2);
retValues = db.multiGetAsList(new ReadOptions(), columnFamilyHandleList, assertThat(new String(retValues.get(0))).isEqualTo("value");
keys); assertThat(new String(retValues.get(1))).isEqualTo("value");
assertThat(retValues.size()).isEqualTo(2);
assertThat(new String(retValues.get(0)))
.isEqualTo("value");
assertThat(new String(retValues.get(1)))
.isEqualTo("value");
} finally {
for (final ColumnFamilyHandle columnFamilyHandle :
columnFamilyHandleList) {
columnFamilyHandle.close();
}
}
} }
} }
@ -432,35 +349,19 @@ public class ColumnFamilyTest {
final RocksDB db = RocksDB.open(options, final RocksDB db = RocksDB.open(options,
dbFolder.getRoot().getAbsolutePath(), dbFolder.getRoot().getAbsolutePath(),
cfDescriptors, columnFamilyHandleList)) { cfDescriptors, columnFamilyHandleList)) {
try { db.put(columnFamilyHandleList.get(0), "key".getBytes(), "value".getBytes());
db.put(columnFamilyHandleList.get(0), "key".getBytes(), db.put(columnFamilyHandleList.get(1), "newcfkey".getBytes(), "value".getBytes());
"value".getBytes());
db.put(columnFamilyHandleList.get(1), "newcfkey".getBytes(),
"value".getBytes());
final List<byte[]> keys = Arrays.asList(new byte[][]{ final List<byte[]> keys =
"key".getBytes(), "newcfkey".getBytes() Arrays.asList(new byte[][] {"key".getBytes(), "newcfkey".getBytes()});
}); List<byte[]> retValues = db.multiGetAsList(columnFamilyHandleList, keys);
List<byte[]> retValues = db.multiGetAsList(columnFamilyHandleList, assertThat(retValues.size()).isEqualTo(2);
keys); assertThat(new String(retValues.get(0))).isEqualTo("value");
assertThat(retValues.size()).isEqualTo(2); assertThat(new String(retValues.get(1))).isEqualTo("value");
assertThat(new String(retValues.get(0))) retValues = db.multiGetAsList(new ReadOptions(), columnFamilyHandleList, keys);
.isEqualTo("value"); assertThat(retValues.size()).isEqualTo(2);
assertThat(new String(retValues.get(1))) assertThat(new String(retValues.get(0))).isEqualTo("value");
.isEqualTo("value"); assertThat(new String(retValues.get(1))).isEqualTo("value");
retValues = db.multiGetAsList(new ReadOptions(), columnFamilyHandleList,
keys);
assertThat(retValues.size()).isEqualTo(2);
assertThat(new String(retValues.get(0)))
.isEqualTo("value");
assertThat(new String(retValues.get(1)))
.isEqualTo("value");
} finally {
for (final ColumnFamilyHandle columnFamilyHandle :
columnFamilyHandleList) {
columnFamilyHandle.close();
}
}
} }
} }
@ -476,30 +377,18 @@ public class ColumnFamilyTest {
final RocksDB db = RocksDB.open(options, final RocksDB db = RocksDB.open(options,
dbFolder.getRoot().getAbsolutePath(), dbFolder.getRoot().getAbsolutePath(),
cfDescriptors, columnFamilyHandleList)) { cfDescriptors, columnFamilyHandleList)) {
try { assertThat(db.getProperty("rocksdb.estimate-num-keys")).isNotNull();
assertThat(db.getProperty("rocksdb.estimate-num-keys")). assertThat(db.getLongProperty(columnFamilyHandleList.get(0), "rocksdb.estimate-num-keys"))
isNotNull(); .isGreaterThanOrEqualTo(0);
assertThat(db.getLongProperty(columnFamilyHandleList.get(0), assertThat(db.getProperty("rocksdb.stats")).isNotNull();
"rocksdb.estimate-num-keys")).isGreaterThanOrEqualTo(0); assertThat(db.getProperty(columnFamilyHandleList.get(0), "rocksdb.sstables")).isNotNull();
assertThat(db.getProperty("rocksdb.stats")).isNotNull(); assertThat(db.getProperty(columnFamilyHandleList.get(1), "rocksdb.estimate-num-keys"))
assertThat(db.getProperty(columnFamilyHandleList.get(0), .isNotNull();
"rocksdb.sstables")).isNotNull(); assertThat(db.getProperty(columnFamilyHandleList.get(1), "rocksdb.stats")).isNotNull();
assertThat(db.getProperty(columnFamilyHandleList.get(1), assertThat(db.getProperty(columnFamilyHandleList.get(1), "rocksdb.sstables")).isNotNull();
"rocksdb.estimate-num-keys")).isNotNull(); assertThat(db.getAggregatedLongProperty("rocksdb.estimate-num-keys")).isNotNull();
assertThat(db.getProperty(columnFamilyHandleList.get(1), assertThat(db.getAggregatedLongProperty("rocksdb.estimate-num-keys"))
"rocksdb.stats")).isNotNull(); .isGreaterThanOrEqualTo(0);
assertThat(db.getProperty(columnFamilyHandleList.get(1),
"rocksdb.sstables")).isNotNull();
assertThat(db.getAggregatedLongProperty("rocksdb.estimate-num-keys")).
isNotNull();
assertThat(db.getAggregatedLongProperty("rocksdb.estimate-num-keys")).
isGreaterThanOrEqualTo(0);
} finally {
for (final ColumnFamilyHandle columnFamilyHandle :
columnFamilyHandleList) {
columnFamilyHandle.close();
}
}
} }
} }
@ -547,10 +436,6 @@ public class ColumnFamilyTest {
rocksIterator.close(); rocksIterator.close();
} }
} }
for (final ColumnFamilyHandle columnFamilyHandle :
columnFamilyHandleList) {
columnFamilyHandle.close();
}
} }
} }
} }
@ -566,15 +451,8 @@ public class ColumnFamilyTest {
final RocksDB db = RocksDB.open(options, final RocksDB db = RocksDB.open(options,
dbFolder.getRoot().getAbsolutePath(), dbFolder.getRoot().getAbsolutePath(),
cfDescriptors, columnFamilyHandleList)) { cfDescriptors, columnFamilyHandleList)) {
try { db.dropColumnFamily(columnFamilyHandleList.get(1));
db.dropColumnFamily(columnFamilyHandleList.get(1)); db.put(columnFamilyHandleList.get(1), "key".getBytes(), "value".getBytes());
db.put(columnFamilyHandleList.get(1), "key".getBytes(),
"value".getBytes());
} finally {
for (ColumnFamilyHandle columnFamilyHandle : columnFamilyHandleList) {
columnFamilyHandle.close();
}
}
} }
} }
@ -589,15 +467,8 @@ public class ColumnFamilyTest {
final RocksDB db = RocksDB.open(options, final RocksDB db = RocksDB.open(options,
dbFolder.getRoot().getAbsolutePath(), dbFolder.getRoot().getAbsolutePath(),
cfDescriptors, columnFamilyHandleList)) { cfDescriptors, columnFamilyHandleList)) {
try { db.dropColumnFamily(columnFamilyHandleList.get(1));
db.dropColumnFamily(columnFamilyHandleList.get(1)); db.delete(columnFamilyHandleList.get(1), "key".getBytes());
db.delete(columnFamilyHandleList.get(1), "key".getBytes());
} finally {
for (final ColumnFamilyHandle columnFamilyHandle :
columnFamilyHandleList) {
columnFamilyHandle.close();
}
}
} }
} }
@ -612,15 +483,8 @@ public class ColumnFamilyTest {
final RocksDB db = RocksDB.open(options, final RocksDB db = RocksDB.open(options,
dbFolder.getRoot().getAbsolutePath(), cfDescriptors, dbFolder.getRoot().getAbsolutePath(), cfDescriptors,
columnFamilyHandleList)) { columnFamilyHandleList)) {
try { db.dropColumnFamily(columnFamilyHandleList.get(1));
db.dropColumnFamily(columnFamilyHandleList.get(1)); db.get(columnFamilyHandleList.get(1), "key".getBytes());
db.get(columnFamilyHandleList.get(1), "key".getBytes());
} finally {
for (final ColumnFamilyHandle columnFamilyHandle :
columnFamilyHandleList) {
columnFamilyHandle.close();
}
}
} }
} }
@ -635,19 +499,11 @@ public class ColumnFamilyTest {
final RocksDB db = RocksDB.open(options, final RocksDB db = RocksDB.open(options,
dbFolder.getRoot().getAbsolutePath(), cfDescriptors, dbFolder.getRoot().getAbsolutePath(), cfDescriptors,
columnFamilyHandleList)) { columnFamilyHandleList)) {
try { final List<byte[]> keys = new ArrayList<>();
final List<byte[]> keys = new ArrayList<>(); keys.add("key".getBytes());
keys.add("key".getBytes()); keys.add("newcfkey".getBytes());
keys.add("newcfkey".getBytes()); final List<ColumnFamilyHandle> cfCustomList = new ArrayList<>();
final List<ColumnFamilyHandle> cfCustomList = new ArrayList<>(); db.multiGetAsList(cfCustomList, keys);
db.multiGetAsList(cfCustomList, keys);
} finally {
for (final ColumnFamilyHandle columnFamilyHandle :
columnFamilyHandleList) {
columnFamilyHandle.close();
}
}
} }
} }
@ -661,25 +517,12 @@ public class ColumnFamilyTest {
final byte[] b0 = new byte[]{(byte) 0x00}; final byte[] b0 = new byte[]{(byte) 0x00};
final byte[] b1 = new byte[]{(byte) 0x01}; final byte[] b1 = new byte[]{(byte) 0x01};
final byte[] b2 = new byte[]{(byte) 0x02}; final byte[] b2 = new byte[]{(byte) 0x02};
ColumnFamilyHandle cf1 = null, cf2 = null, cf3 = null; db.createColumnFamily(new ColumnFamilyDescriptor(b0));
try { db.createColumnFamily(new ColumnFamilyDescriptor(b1));
cf1 = db.createColumnFamily(new ColumnFamilyDescriptor(b0)); final List<byte[]> families =
cf2 = db.createColumnFamily(new ColumnFamilyDescriptor(b1)); RocksDB.listColumnFamilies(options, dbFolder.getRoot().getAbsolutePath());
final List<byte[]> families = RocksDB.listColumnFamilies(options, assertThat(families).contains("default".getBytes(), b0, b1);
dbFolder.getRoot().getAbsolutePath()); db.createColumnFamily(new ColumnFamilyDescriptor(b2));
assertThat(families).contains("default".getBytes(), b0, b1);
cf3 = db.createColumnFamily(new ColumnFamilyDescriptor(b2));
} finally {
if (cf1 != null) {
cf1.close();
}
if (cf2 != null) {
cf2.close();
}
if (cf3 != null) {
cf3.close();
}
}
} }
} }
@ -690,22 +533,13 @@ public class ColumnFamilyTest {
final RocksDB db = RocksDB.open(options, final RocksDB db = RocksDB.open(options,
dbFolder.getRoot().getAbsolutePath()); dbFolder.getRoot().getAbsolutePath());
) { ) {
try { final byte[] b0 = new byte[] {0, 0};
final byte[] b0 = new byte[]{0, 0}; final byte[] b1 = new byte[] {0, 1};
final byte[] b1 = new byte[]{0, 1}; cf1 = db.createColumnFamily(new ColumnFamilyDescriptor(b0));
cf1 = db.createColumnFamily(new ColumnFamilyDescriptor(b0)); cf2 = db.createColumnFamily(new ColumnFamilyDescriptor(b1));
cf2 = db.createColumnFamily(new ColumnFamilyDescriptor(b1)); final List<byte[]> families =
final List<byte[]> families = RocksDB.listColumnFamilies(options, RocksDB.listColumnFamilies(options, dbFolder.getRoot().getAbsolutePath());
dbFolder.getRoot().getAbsolutePath()); assertThat(families).contains("default".getBytes(), b0, b1);
assertThat(families).contains("default".getBytes(), b0, b1);
} finally {
if (cf1 != null) {
cf1.close();
}
if (cf2 != null) {
cf2.close();
}
}
} }
} }
@ -716,17 +550,57 @@ public class ColumnFamilyTest {
final RocksDB db = RocksDB.open(options, final RocksDB db = RocksDB.open(options,
dbFolder.getRoot().getAbsolutePath()); dbFolder.getRoot().getAbsolutePath());
) { ) {
try { final String simplifiedChinese = "\u7b80\u4f53\u5b57";
final String simplifiedChinese = "\u7b80\u4f53\u5b57"; columnFamilyHandle =
columnFamilyHandle = db.createColumnFamily( db.createColumnFamily(new ColumnFamilyDescriptor(simplifiedChinese.getBytes()));
new ColumnFamilyDescriptor(simplifiedChinese.getBytes()));
final List<byte[]> families = RocksDB.listColumnFamilies(options, final List<byte[]> families =
dbFolder.getRoot().getAbsolutePath()); RocksDB.listColumnFamilies(options, dbFolder.getRoot().getAbsolutePath());
assertThat(families).contains("default".getBytes(), assertThat(families).contains("default".getBytes(), simplifiedChinese.getBytes());
simplifiedChinese.getBytes()); }
}
@Test
public void testDestroyColumnFamilyHandle() throws RocksDBException {
try (final Options options = new Options().setCreateIfMissing(true);
final RocksDB db = RocksDB.open(options, dbFolder.getRoot().getAbsolutePath());) {
final byte[] name1 = "cf1".getBytes();
final byte[] name2 = "cf2".getBytes();
final ColumnFamilyDescriptor desc1 = new ColumnFamilyDescriptor(name1);
final ColumnFamilyDescriptor desc2 = new ColumnFamilyDescriptor(name2);
final ColumnFamilyHandle cf1 = db.createColumnFamily(desc1);
final ColumnFamilyHandle cf2 = db.createColumnFamily(desc2);
assertTrue(cf1.isOwningHandle());
assertTrue(cf2.isOwningHandle());
assertFalse(cf1.isDefaultColumnFamily());
db.destroyColumnFamilyHandle(cf1);
// At this point cf1 should not be used!
assertFalse(cf1.isOwningHandle());
assertTrue(cf2.isOwningHandle());
}
}
@Test
@Deprecated
/**
* @deprecated Now explicitly closing instances of ColumnFamilyHandle is not required.
* RocksDB instance will take care of closing its associated ColumnFamilyHandle objects.
*/
public void testColumnFamilyCloseBeforeDb() throws RocksDBException {
final List<ColumnFamilyDescriptor> cfNames =
Arrays.asList(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
new ColumnFamilyDescriptor("new_cf".getBytes()));
final List<ColumnFamilyHandle> columnFamilyHandleList = new ArrayList<>();
try (final DBOptions options =
new DBOptions().setCreateIfMissing(true).setCreateMissingColumnFamilies(true);
final RocksDB db = RocksDB.open(
options, dbFolder.getRoot().getAbsolutePath(), cfNames, columnFamilyHandleList)) {
try {
db.put("testKey".getBytes(), "tstValue".getBytes());
// Do something...
} finally { } finally {
if (columnFamilyHandle != null) { for (final ColumnFamilyHandle columnFamilyHandle : columnFamilyHandleList) {
columnFamilyHandle.close(); columnFamilyHandle.close();
} }
} }

View File

@ -39,29 +39,22 @@ public class CompactionFilterFactoryTest {
final List<ColumnFamilyHandle> cfHandles = new ArrayList<>(); final List<ColumnFamilyHandle> cfHandles = new ArrayList<>();
try (final RocksDB rocksDb = RocksDB.open(options, try (final RocksDB rocksDb =
dbFolder.getRoot().getAbsolutePath(), cfNames, cfHandles); RocksDB.open(options, dbFolder.getRoot().getAbsolutePath(), cfNames, cfHandles)) {
) { final byte[] key1 = "key1".getBytes();
try { final byte[] key2 = "key2".getBytes();
final byte[] key1 = "key1".getBytes();
final byte[] key2 = "key2".getBytes();
final byte[] value1 = "value1".getBytes(); final byte[] value1 = "value1".getBytes();
final byte[] value2 = new byte[0]; final byte[] value2 = new byte[0];
rocksDb.put(cfHandles.get(1), key1, value1); rocksDb.put(cfHandles.get(1), key1, value1);
rocksDb.put(cfHandles.get(1), key2, value2); rocksDb.put(cfHandles.get(1), key2, value2);
rocksDb.compactRange(cfHandles.get(1)); rocksDb.compactRange(cfHandles.get(1));
assertThat(rocksDb.get(cfHandles.get(1), key1)).isEqualTo(value1); assertThat(rocksDb.get(cfHandles.get(1), key1)).isEqualTo(value1);
final boolean exists = rocksDb.keyMayExist(cfHandles.get(1), key2, null); final boolean exists = rocksDb.keyMayExist(cfHandles.get(1), key2, null);
assertThat(exists).isFalse(); assertThat(exists).isFalse();
} finally {
for (final ColumnFamilyHandle cfHandle : cfHandles) {
cfHandle.close();
}
}
} }
} }
} }