Add iterator's SeekForPrev functionality to the java-api

Summary:
As discussed in #2742 , this pull-requests brings the iterator's [SeekForPrev()](https://github.com/facebook/rocksdb/wiki/SeekForPrev) functionality to the java-api. It affects all locations in the code where previously only Seek() was supported.

All code changes are essentially a copy & paste of the already existing implementations for Seek().
**Please Note**: the changes to the C++ code were applied without fully understanding its effect, so please take a closer look. However, since Seek() and SeekForPrev() provide exactly the same signature, I do not expect any mistake here.

The java-tests are extended by new tests for the additional functionality.

Compilation (`make rocksdbjavastatic`) and test (`java/make test`) run without errors.
Closes https://github.com/facebook/rocksdb/pull/2747

Differential Revision: D5721011

Pulled By: sagar0

fbshipit-source-id: c1f951cddc321592c70dd2d32bc04892f3f119f8
This commit is contained in:
zawlazaw 2017-09-12 10:41:48 -07:00 committed by Facebook Github Bot
parent 64b6452e0c
commit 044a71e27e
8 changed files with 138 additions and 14 deletions

View File

@ -99,6 +99,29 @@ void Java_org_rocksdb_RocksIterator_seek0(
env->ReleaseByteArrayElements(jtarget, target, JNI_ABORT); env->ReleaseByteArrayElements(jtarget, target, JNI_ABORT);
} }
/*
* Class: org_rocksdb_RocksIterator
* Method: seekForPrev0
* Signature: (J[BI)V
*/
void Java_org_rocksdb_RocksIterator_seekForPrev0(
JNIEnv* env, jobject jobj, jlong handle,
jbyteArray jtarget, jint jtarget_len) {
jbyte* target = env->GetByteArrayElements(jtarget, nullptr);
if(target == nullptr) {
// exception thrown: OutOfMemoryError
return;
}
rocksdb::Slice target_slice(
reinterpret_cast<char*>(target), jtarget_len);
auto* it = reinterpret_cast<rocksdb::Iterator*>(handle);
it->SeekForPrev(target_slice);
env->ReleaseByteArrayElements(jtarget, target, JNI_ABORT);
}
/* /*
* Class: org_rocksdb_RocksIterator * Class: org_rocksdb_RocksIterator
* Method: status0 * Method: status0

View File

@ -491,6 +491,29 @@ void Java_org_rocksdb_WBWIRocksIterator_seek0(
env->ReleaseByteArrayElements(jtarget, target, JNI_ABORT); env->ReleaseByteArrayElements(jtarget, target, JNI_ABORT);
} }
/*
* Class: org_rocksdb_WBWIRocksIterator
* Method: seekForPrev0
* Signature: (J[BI)V
*/
void Java_org_rocksdb_WBWIRocksIterator_seekForPrev0(
JNIEnv* env, jobject jobj, jlong handle, jbyteArray jtarget,
jint jtarget_len) {
auto* it = reinterpret_cast<rocksdb::WBWIIterator*>(handle);
jbyte* target = env->GetByteArrayElements(jtarget, nullptr);
if(target == nullptr) {
// exception thrown: OutOfMemoryError
return;
}
rocksdb::Slice target_slice(
reinterpret_cast<char*>(target), jtarget_len);
it->SeekForPrev(target_slice);
env->ReleaseByteArrayElements(jtarget, target, JNI_ABORT);
}
/* /*
* Class: org_rocksdb_WBWIRocksIterator * Class: org_rocksdb_WBWIRocksIterator
* Method: status0 * Method: status0

View File

@ -58,6 +58,12 @@ public abstract class AbstractRocksIterator<P extends RocksObject>
seek0(nativeHandle_, target, target.length); seek0(nativeHandle_, target, target.length);
} }
@Override
public void seekForPrev(byte[] target) {
assert (isOwningHandle());
seekForPrev0(nativeHandle_, target, target.length);
}
@Override @Override
public void next() { public void next() {
assert (isOwningHandle()); assert (isOwningHandle());
@ -97,5 +103,6 @@ public abstract class AbstractRocksIterator<P extends RocksObject>
abstract void next0(long handle); abstract void next0(long handle);
abstract void prev0(long handle); abstract void prev0(long handle);
abstract void seek0(long handle, byte[] target, int targetLen); abstract void seek0(long handle, byte[] target, int targetLen);
abstract void seekForPrev0(long handle, byte[] target, int targetLen);
abstract void status0(long handle) throws RocksDBException; abstract void status0(long handle) throws RocksDBException;
} }

View File

@ -57,6 +57,7 @@ public class RocksIterator extends AbstractRocksIterator<RocksDB> {
@Override final native void next0(long handle); @Override final native void next0(long handle);
@Override final native void prev0(long handle); @Override final native void prev0(long handle);
@Override final native void seek0(long handle, byte[] target, int targetLen); @Override final native void seek0(long handle, byte[] target, int targetLen);
@Override final native void seekForPrev0(long handle, byte[] target, int targetLen);
@Override final native void status0(long handle) throws RocksDBException; @Override final native void status0(long handle) throws RocksDBException;
private native byte[] key0(long handle); private native byte[] key0(long handle);

View File

@ -52,6 +52,18 @@ public interface RocksIteratorInterface {
*/ */
void seek(byte[] target); void seek(byte[] target);
/**
* <p>Position at the first entry in the source whose key is that or
* before target.</p>
*
* <p>The iterator is valid after this call if the source contains
* a key that comes at or before target.</p>
*
* @param target byte array describing a key or a
* key prefix to seek for.
*/
void seekForPrev(byte[] target);
/** /**
* <p>Moves to the next entry in the source. After this call, Valid() is * <p>Moves to the next entry in the source. After this call, Valid() is
* true if the iterator was not positioned at the last entry in the source.</p> * true if the iterator was not positioned at the last entry in the source.</p>

View File

@ -45,6 +45,7 @@ public class WBWIRocksIterator
@Override final native void next0(long handle); @Override final native void next0(long handle);
@Override final native void prev0(long handle); @Override final native void prev0(long handle);
@Override final native void seek0(long handle, byte[] target, int targetLen); @Override final native void seek0(long handle, byte[] target, int targetLen);
@Override final native void seekForPrev0(long handle, byte[] target, int targetLen);
@Override final native void status0(long handle) throws RocksDBException; @Override final native void status0(long handle) throws RocksDBException;
private native long[] entry1(final long handle); private native long[] entry1(final long handle);

View File

@ -53,6 +53,48 @@ public class RocksIteratorTest {
assertThat(iterator.value()).isEqualTo("value2".getBytes()); assertThat(iterator.value()).isEqualTo("value2".getBytes());
iterator.status(); iterator.status();
} }
try (final RocksIterator iterator = db.newIterator()) {
iterator.seek("key0".getBytes());
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo("key1".getBytes());
iterator.seek("key1".getBytes());
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo("key1".getBytes());
iterator.seek("key1.5".getBytes());
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo("key2".getBytes());
iterator.seek("key2".getBytes());
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo("key2".getBytes());
iterator.seek("key3".getBytes());
assertThat(iterator.isValid()).isFalse();
}
try (final RocksIterator iterator = db.newIterator()) {
iterator.seekForPrev("key0".getBytes());
assertThat(iterator.isValid()).isFalse();
iterator.seekForPrev("key1".getBytes());
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo("key1".getBytes());
iterator.seekForPrev("key1.5".getBytes());
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo("key1".getBytes());
iterator.seekForPrev("key2".getBytes());
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo("key2".getBytes());
iterator.seekForPrev("key3".getBytes());
assertThat(iterator.isValid()).isTrue();
assertThat(iterator.key()).isEqualTo("key2".getBytes());
}
} }
} }
} }

View File

@ -27,6 +27,9 @@ import static org.junit.Assert.*;
*/ */
public class BytewiseComparatorTest { public class BytewiseComparatorTest {
private List<String> source_strings = Arrays.asList("b", "d", "f", "h", "j", "l");
private List<String> interleaving_strings = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m");
/** /**
* Open the database using the C++ BytewiseComparatorImpl * Open the database using the C++ BytewiseComparatorImpl
* and test the results against our Java BytewiseComparator * and test the results against our Java BytewiseComparator
@ -42,7 +45,6 @@ public class BytewiseComparatorTest {
doRandomIterationTest( doRandomIterationTest(
db, db,
toJavaComparator(new BytewiseComparator(new ComparatorOptions())), toJavaComparator(new BytewiseComparator(new ComparatorOptions())),
Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i"),
rnd, rnd,
8, 100, 3 8, 100, 3
); );
@ -67,7 +69,6 @@ public class BytewiseComparatorTest {
doRandomIterationTest( doRandomIterationTest(
db, db,
toJavaComparator(new BytewiseComparator(new ComparatorOptions())), toJavaComparator(new BytewiseComparator(new ComparatorOptions())),
Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i"),
rnd, rnd,
8, 100, 3 8, 100, 3
); );
@ -94,7 +95,6 @@ public class BytewiseComparatorTest {
toJavaComparator(new DirectBytewiseComparator( toJavaComparator(new DirectBytewiseComparator(
new ComparatorOptions()) new ComparatorOptions())
), ),
Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i"),
rnd, rnd,
8, 100, 3 8, 100, 3
); );
@ -121,7 +121,6 @@ public class BytewiseComparatorTest {
toJavaComparator(new DirectBytewiseComparator( toJavaComparator(new DirectBytewiseComparator(
new ComparatorOptions()) new ComparatorOptions())
), ),
Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i"),
rnd, rnd,
8, 100, 3 8, 100, 3
); );
@ -148,7 +147,6 @@ public class BytewiseComparatorTest {
toJavaComparator( toJavaComparator(
new ReverseBytewiseComparator(new ComparatorOptions()) new ReverseBytewiseComparator(new ComparatorOptions())
), ),
Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i"),
rnd, rnd,
8, 100, 3 8, 100, 3
); );
@ -176,7 +174,6 @@ public class BytewiseComparatorTest {
toJavaComparator( toJavaComparator(
new ReverseBytewiseComparator(new ComparatorOptions()) new ReverseBytewiseComparator(new ComparatorOptions())
), ),
Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i"),
rnd, rnd,
8, 100, 3 8, 100, 3
); );
@ -188,7 +185,7 @@ public class BytewiseComparatorTest {
private void doRandomIterationTest( private void doRandomIterationTest(
final RocksDB db, final java.util.Comparator<String> javaComparator, final RocksDB db, final java.util.Comparator<String> javaComparator,
final List<String> source_strings, final Random rnd, final Random rnd,
final int num_writes, final int num_iter_ops, final int num_writes, final int num_iter_ops,
final int num_trigger_flush) throws RocksDBException { final int num_trigger_flush) throws RocksDBException {
@ -228,7 +225,7 @@ public class BytewiseComparatorTest {
for (int i = 0; i < num_iter_ops; i++) { for (int i = 0; i < num_iter_ops; i++) {
// Random walk and make sure iter and result_iter returns the // Random walk and make sure iter and result_iter returns the
// same key and value // same key and value
final int type = rnd.nextInt(6); final int type = rnd.nextInt(7);
iter.status(); iter.status();
switch (type) { switch (type) {
case 0: case 0:
@ -242,14 +239,22 @@ public class BytewiseComparatorTest {
result_iter.seekToLast(); result_iter.seekToLast();
break; break;
case 2: { case 2: {
// Seek to random key // Seek to random (existing or non-existing) key
final int key_idx = rnd.nextInt(source_strings.size()); final int key_idx = rnd.nextInt(interleaving_strings.size());
final String key = source_strings.get(key_idx); final String key = interleaving_strings.get(key_idx);
iter.seek(bytes(key)); iter.seek(bytes(key));
result_iter.seek(bytes(key)); result_iter.seek(bytes(key));
break; break;
} }
case 3: case 3: {
// SeekForPrev to random (existing or non-existing) key
final int key_idx = rnd.nextInt(interleaving_strings.size());
final String key = interleaving_strings.get(key_idx);
iter.seekForPrev(bytes(key));
result_iter.seekForPrev(bytes(key));
break;
}
case 4:
// Next // Next
if (is_valid) { if (is_valid) {
iter.next(); iter.next();
@ -258,7 +263,7 @@ public class BytewiseComparatorTest {
continue; continue;
} }
break; break;
case 4: case 5:
// Prev // Prev
if (is_valid) { if (is_valid) {
iter.prev(); iter.prev();
@ -268,7 +273,7 @@ public class BytewiseComparatorTest {
} }
break; break;
default: { default: {
assert (type == 5); assert (type == 6);
final int key_idx = rnd.nextInt(source_strings.size()); final int key_idx = rnd.nextInt(source_strings.size());
final String key = source_strings.get(key_idx); final String key = source_strings.get(key_idx);
final byte[] result = db.get(new ReadOptions(), bytes(key)); final byte[] result = db.get(new ReadOptions(), bytes(key));
@ -413,6 +418,16 @@ public class BytewiseComparatorTest {
} }
} }
@Override
public void seekForPrev(final byte[] target) {
for(offset = entries.size()-1; offset >= 0; offset--) {
if(comparator.compare(entries.get(offset).getKey(),
(K)new String(target, StandardCharsets.UTF_8)) <= 0) {
return;
}
}
}
/** /**
* Is `a` a prefix of `b` * Is `a` a prefix of `b`
* *