Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JNI Reader for Table Iterator #12511

Open
wants to merge 28 commits into
base: main
Choose a base branch
from

Conversation

swamirishi
Copy link

Adding a Jni layer for table Iterator. Also changing the table iterator's interface preparse internal key so that SSTFileReaderIterator can be reused.

@swamirishi
Copy link
Author

swamirishi commented Apr 5, 2024

@cbi42 @jowlyzhang Can you please review this? In ozone we depend on jni for rocksdb apis. I have just added JNI layer for the table_iterator written in your PR.

@adamretter adamretter self-requested a review April 5, 2024 07:03
Copy link
Collaborator

@adamretter adamretter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. I have left some feedback on the Java changes which need some amendment - also a Java test needs to be added please for this new API. I will leave @ajkr @pdillinger and the RocksDB team to comment on the internal C++ changes to RocksDB table... as I cannot myself say if they are desirable or not for the RocksDB team.

java/rocksjni/raw_sst_file_reader_iterator.cc Outdated Show resolved Hide resolved
*/
class RawSstFileReaderIterator extends SstFileReaderIterator {

protected RawSstFileReaderIterator(SstFileReader reader, long nativeHandle) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please set all arguments and variables to final where possible.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

*/
class RawSstFileReaderIterator extends SstFileReaderIterator {

protected RawSstFileReaderIterator(SstFileReader reader, long nativeHandle) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class needs to implement some distinct form of disposeInternal to avoid leaking memory when it is finished with.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It extends SstFileReaderIterator which clears off the required handle.

@@ -113,7 +113,7 @@ std::unique_ptr<Iterator> SstFileReader::NewTableIterator() {
// InternalIterator.
return nullptr;
}
return std::make_unique<TableIterator>(internal_iter);
return std::make_unique<TableIterator>(internal_iter, &rep_->options);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ajkr @pdillinger Can you comment on this change in the internals for RocksDB please?

@@ -24,7 +25,7 @@ class TableIterator : public Iterator {
}

public:
explicit TableIterator(InternalIterator* iter) : iter_(iter) {}
explicit TableIterator(InternalIterator* iter, Options* options) : iter_(iter), options_(options) {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ajkr @pdillinger Can you comment on this change in the internals for RocksDB please?

@@ -48,22 +49,65 @@ class TableIterator : public Iterator {
bool Valid() const override { return iter_->Valid(); }
void SeekToFirst() override { return iter_->SeekToFirst(); }
void SeekToLast() override { return iter_->SeekToLast(); }
void Seek(const Slice& target) override { return iter_->Seek(target); }
void Seek(const Slice& target) override {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ajkr @pdillinger Can you comment on this change in the internals for RocksDB please?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will review this. Thanks!

void SeekForPrev(const Slice& target) override {
return iter_->SeekForPrev(target);
std::string seek_key_buf;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ajkr @pdillinger Can you comment on this change in the internals for RocksDB please?

@@ -48,22 +49,65 @@ class TableIterator : public Iterator {
bool Valid() const override { return iter_->Valid(); }
void SeekToFirst() override { return iter_->SeekToFirst(); }
void SeekToLast() override { return iter_->SeekToLast(); }
void Seek(const Slice& target) override { return iter_->Seek(target); }
void Seek(const Slice& target) override {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will review this. Thanks!

@@ -48,22 +49,65 @@ class TableIterator : public Iterator {
bool Valid() const override { return iter_->Valid(); }
void SeekToFirst() override { return iter_->SeekToFirst(); }
void SeekToLast() override { return iter_->SeekToLast(); }
void Seek(const Slice& target) override { return iter_->Seek(target); }
void Seek(const Slice& target) override {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type of key taken as input for Seek and SeekForPrev should be consistent with the type of key produced by Iterator::key. Can you add jni APIs for the util methods GetInternalKeyForSeek etc instead?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we change the definition of the TableIterator to seek and get only on the user_key instead? Isn't this a better way to define this class?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name TableIterator indicates it's iterating the raw table, and that makes internal key a better format of key for these APIs.

Status GetProperty(std::string /*prop_name*/,
std::string* /*prop*/) override {
assert(false);
return Status::NotSupported("TableIterator does not support GetProperty.");
}

uint64_t SequenceNumber() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The public API is Iterator in rocksdb/includes/iterator.h, the TableIterator is an internal class that is not supposed to be publicly accessible. These SequenceNumber, and type APIs are not defined for Iterator.

We have added ParseEntry util methods to parse sequence number and type out of an internal key. You can have you own wrapper iterator class wrapping a RocksDB Iterator, and add the parsing logic in to the wrapper iterator.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need all of these infos separately and storing the parsed entry would be much more optimal. Otherwise for each and every call we would have to parse the key over and over again. All I am doing here is changing the definition of the table iter which would seek over the same user key and return user key when getKey() is called. I have added 2 more functions on top of iterator, I believe that should be fine, since JNI would be called only when TableIterator is initialized.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jowlyzhang TableIterator itself is a wrapper for InternalIterator why have 2 wrappers? Should we consider making this public?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth having 2 wrappers because it will be your own wrapper iterator that you don't need to check into RocksDB codebase, you own those logic and you can modify it however you want. Parsing an entry every time over and over again isn't computationally heavy or performance wise concerning, you can just wrap it in your own wrapper iterator. It's beset for the RocksDB TableIterator to just do the most vanilla things of iterating raw entries. Others wishing to add their own iterating constraints can just add on top of it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jowlyzhang I have updated the code to add another jni functionality to convert internal Key to user key and vice versa

@swamirishi
Copy link
Author

@jowlyzhang Can you check this PR?

@jowlyzhang
Copy link
Contributor

@jowlyzhang Can you check this PR?

Thanks for making the changes. Since this PR now mostly have java changes, I'll leave it to @adamretter to make the call here.

Copy link
Collaborator

@adamretter adamretter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issues I think I have identified repeat throughout and should be addressed please

kEntryWideColumnEntity((byte)0x7),
kEntryOther((byte)0x8);
private final byte value;
private static Map<Byte, EntryType> reverseLookupMap = new HashMap<>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a map here? The collection is small enough to iterate I would have thought?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the map and falling back to iterating through the values.

private final byte value;
private static Map<Byte, EntryType> reverseLookupMap = new HashMap<>();

EntryType(byte value) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be final param.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

jint len) {
const std::unique_ptr<char[]> target(new char[len]);
if (target == nullptr) {
jclass oom_class = env->FindClass("/lang/java/OutOfMemoryError");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this has been tested as the class name is incorrect. Also there are util methods in portal.h for throwing errors that could be used.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added util method to also do operations on indirect byte array buffers

"Memory allocation failed in RocksDB JNI function");
return;
}
env->GetByteArrayRegion(jtarget, off, len,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs error checking

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

auto slice_size = key_slice.size();
jsize copy_size = std::min(static_cast<uint32_t>(slice_size),
static_cast<uint32_t>(jlen));
env->SetByteArrayRegion(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs error checking

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

auto slice_size = key_slice.size();
jsize copy_size = std::min(static_cast<uint32_t>(slice_size),
static_cast<uint32_t>(int_key_len));
env->SetByteArrayRegion(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs error checking.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

reinterpret_cast<const ROCKSDB_NAMESPACE::Options *>(options_handle);
const std::unique_ptr<char[]> target(new char[user_key_len]);
if (target == nullptr) {
jclass oom_class = env->FindClass("/lang/java/OutOfMemoryError");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class name and testing?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

"Memory allocation failed in RocksDB JNI function");
return static_cast<jsize>(0);
}
env->GetByteArrayRegion(user_key, user_key_off, user_key_len,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs error checking.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

auto slice_size = key_slice.size();
jsize copy_size = std::min(static_cast<uint32_t>(slice_size),
static_cast<uint32_t>(int_key_len));
env->SetByteArrayRegion(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs error checking.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

// exception thrown: OutOfMemoryError
return nullptr;
}
env->SetByteArrayRegion(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs error checking.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@swamirishi
Copy link
Author

@adamretter Apologies for the late response and not following through addressing review comments on time. I have addressed all the review comments. Can you please take a look at it? I have added a Java test for the same as well and also added required util functions as well in portal.h. FYI these util functions can be used in other JNI classes as well.

@swamirishi
Copy link
Author

@adamretter can you please take a look at this?

@swamirishi
Copy link
Author

@adamretter @jowlyzhang Can you please take a look at this patch, whenever you find time?

java/src/main/java/org/rocksdb/EntryType.java Outdated Show resolved Hide resolved
java/src/main/java/org/rocksdb/EntryType.java Outdated Show resolved Hide resolved
return result;
}

public void parseEntry(Options options, byte[] internalKey) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please mark parameters as final. (and methods/functions below)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

java/rocksjni/portal.h Outdated Show resolved Hide resolved
case ROCKSDB_NAMESPACE::EntryType::kEntryOther:
return 0x8;
default:
return 0x9;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There does not appear to be a 0x9 defined in the Java enum, so this will raise an exception. Is that intentional?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah

jlong jhandle) {
auto *sst_file_reader =
reinterpret_cast<ROCKSDB_NAMESPACE::SstFileReader *>(jhandle);
return GET_CPLUSPLUS_POINTER(sst_file_reader->NewTableIterator().release());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit worried about the release() here? What is the ownership model for the iterator in your code please?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is the same as sstFileReaderIterator. So release should be triggered manually irrespective of sstFileReader class

java/rocksjni/type_util.cc Outdated Show resolved Hide resolved
java/rocksjni/type_util.cc Show resolved Hide resolved
@swamirishi
Copy link
Author

@adamretter I have addressed all the review comments. Can you please take a look at the patch again?

@swamirishi
Copy link
Author

@adamretter Can you take a look at this?

@adamretter
Copy link
Collaborator

@swamirishi Not all of my comments have been addressed.

@swamirishi swamirishi force-pushed the JniReaderForTableIterator branch 2 times, most recently from 08d06eb to 8a82a9e Compare June 22, 2024 02:44
@swamirishi
Copy link
Author

@swamirishi Not all of my comments have been addressed.

@adamretter Yeah sorry I missed one of the comments. I have changed the method name and set the parameters to final. Can you check if this is fine now?

@adamretter adamretter changed the title Jni reader for table iterator JNI Reader for Table Iterator Jul 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants