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:
parent
64b6452e0c
commit
044a71e27e
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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>
|
||||||
|
@ -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);
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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`
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user