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

GEODE-9641: Remove unnecessary allocation of HeapDataOutputStream #6943

Merged
merged 3 commits into from
Oct 8, 2021

Conversation

kamilla1201
Copy link
Contributor

@kamilla1201 kamilla1201 commented Oct 6, 2021

GEODE-9641 was introduced by the fix for GEODE-9204.
This change removes the unnecessary allocation of HeapDataOutputStream in ReplyMessage.toData().
I ran P2pPartitionedPutBytesBenchmark at least 15 times to test this fix and all runs passed.

For all changes:

  • Is there a JIRA ticket associated with this PR? Is it referenced in the commit message?

  • Has your PR been rebased against the latest commit within the target branch (typically develop)?

  • Is your initial contribution a single, squashed commit?

  • Does gradlew build run cleanly?

  • Have you written or updated unit tests to verify your changes?

  • If adding new dependencies to the code, are these dependencies licensed in a way that is compatible for inclusion under ASF 2.0?

Copy link
Contributor

@nonbinaryprogrammer nonbinaryprogrammer left a comment

Choose a reason for hiding this comment

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

nice performance improvement

if (this.returnValueIsException || this.returnValue != null) {
final boolean hasReturnValue = this.returnValueIsException || this.returnValue != null;
if (hasReturnValue) {
hdos = new HeapDataOutputStream(context.getSerializationVersion());
Copy link
Contributor

Choose a reason for hiding this comment

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

[comment] (Just a comment, not a request for changes) It would be best to close hdos in a finally-block. The try-with-resource syntax is generally the preferred syntax. If you make this change, you would probably have to do some refactoring to extract methods or it won't be very clean.

It's safest to do refactoring like the following example if there's a decent unit test. ReplyMessageTest exists but unfortunately, it doesn't cover much.

You could try something like this which also seems to be more readable. In order: extracted writeStatus, extracted writeProcessProcessorId, moved hdos into a try-with-resource block, polished up the try-with-resource block, extracted writeReturnValue which pulled the nested try-catch block out:

  @Override
  public void toData(DataOutput out,
      SerializationContext context) throws IOException {
    super.toData(out, context);

    boolean failedSerialization = false;
    final boolean hasReturnValue = this.returnValueIsException || this.returnValue != null;
    if (hasReturnValue) {
      try (HeapDataOutputStream hdos = new HeapDataOutputStream(context.getSerializationVersion())) {
        writeReturnValue(out, context, failedSerialization, hdos);
      }
    } else {
      writeStatus(out);
      writeProcessorId(out);
    }
  }

  private void writeReturnValue(DataOutput out, SerializationContext context, 
      boolean failedSerialization, HeapDataOutputStream hdos) throws IOException {
    try {
      context.getSerializer().writeObject(this.returnValue, hdos);
    } catch (NotSerializableException e) {
      logger.warn("Unable to serialize a reply to " + getRecipientsDescription(), e);
      failedSerialization = true;
      this.returnValue = new ReplyException(e);
      this.returnValueIsException = true;
    }
    writeStatus(out);
    writeProcessorId(out);
    if (failedSerialization) {
      context.getSerializer().writeObject(this.returnValue, out);
    } else {
      hdos.sendTo(out);
    }
  }

  private void writeProcessorId(DataOutput out) throws IOException {
    if (this.processorId != 0) {
      out.writeInt(processorId);
    }
  }

  private void writeStatus(DataOutput out) throws IOException {
    byte status = 0;
    if (this.ignored) {
      status |= IGNORED_FLAG;
    }
    if (this.returnValueIsException) {
      status |= EXCEPTION_FLAG;
    } else if (this.returnValue != null) {
      status |= OBJECT_FLAG;
    }
    if (this.processorId != 0) {
      status |= PROCESSOR_ID_FLAG;
    }
    if (this.closed) {
      status |= CLOSED_FLAG;
    }
    if (this.internal) {
      status |= INTERNAL_FLAG;
    }
    out.writeByte(status);
  }

Copy link
Contributor

Choose a reason for hiding this comment

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

something else to consider in this method is should you even call hdos.close? If you look at it all it is doing is freeing up some heap resources and reseting the BufferDataOutputStream which actually mean you could continue to use it (which seems a bit off since close usually ends the life of an object). If you had a HDOS that was stored in something that was going to live longer than close could help free some memory up early. But in this case HDOS becomes garbage at the end of this method. The best practice in that case is to just let the garbage collector free it up and to not class close. But I'd be okay with you leaving the close call in but if it is going to cause a major refactor you might want to instead not call it.

if (this.returnValueIsException || this.returnValue != null) {
final boolean hasReturnValue = this.returnValueIsException || this.returnValue != null;
if (hasReturnValue) {
hdos = new HeapDataOutputStream(context.getSerializationVersion());
Copy link
Contributor

Choose a reason for hiding this comment

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

something else to consider in this method is should you even call hdos.close? If you look at it all it is doing is freeing up some heap resources and reseting the BufferDataOutputStream which actually mean you could continue to use it (which seems a bit off since close usually ends the life of an object). If you had a HDOS that was stored in something that was going to live longer than close could help free some memory up early. But in this case HDOS becomes garbage at the end of this method. The best practice in that case is to just let the garbage collector free it up and to not class close. But I'd be okay with you leaving the close call in but if it is going to cause a major refactor you might want to instead not call it.

@lgtm-com
Copy link

lgtm-com bot commented Oct 8, 2021

This pull request introduces 1 alert when merging 4d9027e into 3514680 - view on LGTM.com

new alerts:

  • 1 for Potential output resource leak

@kamilla1201 kamilla1201 merged commit 72455fc into apache:develop Oct 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
6 participants