Skip to content

Remove protective copies in Thrift APIs#6457

Closed
ctubbsii wants to merge 1 commit into
apache:mainfrom
ctubbsii:unsafe-binary-api-thrift
Closed

Remove protective copies in Thrift APIs#6457
ctubbsii wants to merge 1 commit into
apache:mainfrom
ctubbsii:unsafe-binary-api-thrift

Conversation

@ctubbsii

Copy link
Copy Markdown
Member

Generate thrift with unsafe_binaries flag to remove unwanted protective copies of ByteBuffer types (we will copy as needed ourselves).

Generate thrift with unsafe_binaries flag to remove unwanted protective
copies of ByteBuffer types (we will copy as needed ourselves).
@ctubbsii ctubbsii added this to the 4.0.0 milestone Jun 30, 2026
@ctubbsii ctubbsii self-assigned this Jun 30, 2026
@ctubbsii

ctubbsii commented Jun 30, 2026

Copy link
Copy Markdown
Member Author

There are a few other options we may want to look at for performance, too. In particular, the option_type=jdk8 (which I intend to do in a subsequent PR), the future_iface (which may be interesting to use), and the reuse_objects (which might require turning back on the protective copies in the APIs) options.

thrift -help 2>&1 | sed -n '/^\s\sjava\s/,/^\s\s\w/p'

@dlmarion

dlmarion commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

These changes look good

@DomGarguilo DomGarguilo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM as long as it addresses the issue outlined in #6455

@dlmarion

dlmarion commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

So, these changes look good, but there is still an issue. Take this code for example:

for (TKeyValue tkv : sr.getResults()) {
results.add(new KeyValue(new Key(tkv.getKey()), tkv.getValue()));
}

The code before #6430 looked like:

for (TKeyValue tkv : sr.results) {
results.add(new KeyValue(new Key(tkv.key), tkv.value));
}

The code in #6430 made the members private, so when accessing TKeyValue.value the calling code had to call either TKeyValue.getValue() or TKeyValue.bufferForValue() as below.

public byte[] getValue() {
setValue(org.apache.thrift.TBaseHelper.rightSize(value));
return value == null ? null : value.array();
}
public java.nio.ByteBuffer bufferForValue() {
return org.apache.thrift.TBaseHelper.copyBinary(value);
}

The changes in this PR only removes the defensive copy from bufferForValue, so the calling code needs to change as well.

@dlmarion dlmarion left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Either the code calling the Thrift objects needs to be checked / corrected, or maybe some other Thrift compilation flag needs to be used so that copies are not made in the getX methods.

@ctubbsii

ctubbsii commented Jul 1, 2026

Copy link
Copy Markdown
Member Author

I'm abandoning this change. I don't think it's a good idea anymore for the following reasons:

  1. This also removes the protective copies from the setters/constructors, which existed prior to Use private Thrift members #6430 and we didn't have a problem with those. I don't think it's worth removing those along with the ones for the getter.
  2. The .getValue() method that returns a byte[] does not change with this PR, and that's the one that we are now using, not the .bufferForValue() method, which is affected by this change. This means that our code isn't really impacted by this change. The bigger impact from Use private Thrift members #6430 is just the switch from direct access to the internal ByteBuffer to the method that returns a byte array, rather than one that makes a protective copy of the ByteBuffer. The equivalent to before this PR would be to use the .bufferForValue() version of methods, but I don't necessarily think that's a good idea either.
  3. The method we are using, .getValue(), does not actually make copies, except in the case where a copy is needed to truncate the underlying byte buffer's array to size. Other than that, there's just a very lightweight object allocation to wrap with a ByteBuffer interface, which I suspect is not really the cause of any performance issues.
  4. I think ultimately, any issues with the RPC layer are really best addressed in calling code, but I think the protective copies and the use of
  5. It should be noted that the GarbageCollector IT that started failing after Use private Thrift members #6430 is already flaky, and sometimes times out even after reverting Use private Thrift members #6430. While Use private Thrift members #6430 may have made the situation slightly more reliably fail, I still think that the best fixes are probably in the calling code, and that test probably needs to tuning anyway and should not be considered a reliable indicator that the RPC changes are a problem overall.

We can continue the discussion on #6455 for possible next steps.

@ctubbsii ctubbsii removed this from the 4.0.0 milestone Jul 1, 2026
@ctubbsii ctubbsii closed this Jul 1, 2026
@ctubbsii ctubbsii deleted the unsafe-binary-api-thrift branch July 1, 2026 23:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants