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

Add support for Wide columns in Java #12334

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

Conversation

rhubner
Copy link
Contributor

@rhubner rhubner commented Feb 7, 2024

This PR adds support for wide columns in Java. First implementation is primarily focused on correctness rather than performance. We would like to merge this first and hear feedback from RocksDB Java community before refining it.

Fix #12113

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.

I have done a partial review... There are three main things that you might want to look at IMHO:

  1. Whether you really need std::make_unique for a pointer that never escapes its function scope. It seems superfluous to me.

  2. Clean up of JNI resources you have allocated before you return from a function when an error condition occurs (or even when the function completes normally)

  3. String conversion from JNI types to C++ standard types. There are functions in portal.h to assist in doing this correctly. If they don't suit, we can consider adding an additional one for your use-case.

java/rocksjni/portal.h Outdated Show resolved Hide resolved
"ByteBuffer)");
return;
}
auto name = reinterpret_cast<char*>(_name) + namesOffset[i];
Copy link
Collaborator

Choose a reason for hiding this comment

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

There are utility methods in portal.h to go from jstring to std::string or char* - could we use those here instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We are not working with jstring but with void* here. I cast it to char* so that I can add offset to it.


const auto columns_len = static_cast<int>(env->GetArrayLength(jNames));

auto namesOffset = std::make_unique<jint[]>(columns_len);
Copy link
Collaborator

Choose a reason for hiding this comment

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

make_unique seems an unnecessary overhead here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that's true, but it allows me to automatically release all the memory allocated here and simplify return statements.

auto j_value =
static_cast<jbyteArray>(env->GetObjectArrayElement(jvalues, i));
if (env->ExceptionCheck()) {
return;
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 to free the JNI local references for name/jname here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No we don't need to. doc

Copy link
Collaborator

Choose a reason for hiding this comment

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

As I understand it, GetObjectArrayElement returns a jobject and when you are finished with this (on either success or error paths), you should call DeleteLocalRef on that object. There are other examples of this pattern already in the existing code-base, for example: https://github.com/facebook/rocksdb/blob/main/java/rocksjni/rocksjni.cc#L462

auto value = std::make_unique<jbyte[]>(jvalue_len);
env->GetByteArrayRegion(j_value, 0, jvalue_len, value.get());
if (env->ExceptionCheck()) {
return;
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 to free the JNI local references for name/jname and value/jvalue here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No we don't need to. GetByteArrayRegion only copies data from java arrays to provide memory. This memory is allocated as a smart pointer and is automatically released when we exit the method.

auto value = reinterpret_cast<char*>(_value) + valuesOffset[i];
valuesRequiredSize[i] = static_cast<jint>(column.value().size());
max_data_len = std::min(valuesRequiredSize[i], (valuesLen[i]));
std::memcpy(value, column.value().data(), max_data_len);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is explicit memcpy needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, See previous comment

env->SetIntArrayRegion(jnamesRequiredSize, 0, max_items,
namesRequiredSize.get());
if (env->ExceptionCheck()) {
return nullptr;
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 to free the previous JNI array fetches and local-references here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No we don't need to. See previous comment

env->SetIntArrayRegion(jvaluesRequiredSize, 0, max_items,
valuesRequiredSize.get());
if (env->ExceptionCheck()) {
return nullptr;
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 to free the previous JNI array fetches and local-references here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No we don't need to. See previous comment

"Invalid key argument (argument is not a valid direct ByteBuffer)");
return nullptr;
}
auto key = reinterpret_cast<char*>(_key) + key_offset;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could this use portal.h for string conversion

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No. See previous comment

jlong jcf_handle) {
auto* db = reinterpret_cast<ROCKSDB_NAMESPACE::DB*>(jdb_handle);

auto key = std::make_unique<jbyte[]>(keyLength);
Copy link
Collaborator

Choose a reason for hiding this comment

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

std::make_unique seems superfluous here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, See previous comment

@rhubner rhubner force-pushed the eb/wide-columns branch 5 times, most recently from 239f9df to a9f7753 Compare March 20, 2024 08:22
@rhubner rhubner marked this pull request as ready for review March 25, 2024 17:16
@test-wangxiaoyu
Copy link

Hello, after I finished compiling, the run encountered the following error:
librocksdbjni-osx-x86_64.jnilib was not found inside JAR.
My machine environment is macbook air 2022,The compiled command is as follows:
make clean jclean
DEBUG_LEVEL=0 make -j12 rocksdbjava

@rhubner
Copy link
Contributor Author

rhubner commented Mar 27, 2024

Hello @test-wangxiaoyu

librocksdbjni-osx-x86_64.jnilib was not found inside JAR.

This is a very strange error. Considering you have a macbook air 2022, these models have ARM CPU (
Mac computers with Apple silicon ) and Java is trying to search for Intel version of native library. My suggestion is to check that you are running native ARM version of Java. Sometimes also called aarch64. From the error message it looks like you are running Intel version of Java via Rosetta emulation layer. You can download correct version from Adoptium project or Liberica

You can also check what native library is inside the jar archive. For example this is how it looks like on my linux machine.

$ jar -t --file java/target/rocksdbjni-9.2.0-linux64.jar | grep librocksdbjni
librocksdbjni-linux64.so

Radek

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.

LGTM

@adamretter
Copy link
Collaborator

@pdillinger @ajkr Can we get this one merged please?

@nomed
Copy link

nomed commented Sep 18, 2024

Hi,

Thank you for the great work on this feature! I am very interested in using wide columns in RocksDB. Could you please let me know if this feature is planned to be merged? Also, do you have an estimated timeline for when it might be available in a release?

Thank you!

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.

Is there a plan to launch the wide column functionality of the java api
5 participants