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

[Java Client] Use failPendingMessages to ensure proper cleanup #12259

Merged

Conversation

michaeljmarshall
Copy link
Member

@michaeljmarshall michaeljmarshall commented Oct 1, 2021

Motivation

When calling ProducerImpl#closeAsync, the current cleanup relies on clearPendingMessagesWhenClose. This method does not fail any remaining messages in the batchMessageContainer. After inspecting the failPendingMessages method, it looks like it would be better to call this within the clearPendingMessagesWhenClose method. It also has more robust handling of failing the messages contained in the pendingMessages queue.

Modifications

  • Update the implementation of clearPendingMessagesWhenClose to rely on failPendingMessages.
  • Move already thread safe method calls, like setState outside of the synchronized block

Verifying this change

I added a test that will pass regardless of the outcome of the current mailing list thread discussing whether "close implies flush". There is also already a test that ensures that any outstanding pending messages are failed. Together, these tests ensure correct behavior.

Does this pull request potentially affect one of the following parts:

This is not a breaking change.

Documentation

There is no need to update the docs for this change.

Copy link
Contributor

@eolivelli eolivelli left a comment

Choose a reason for hiding this comment

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

Overall looks good to me,
I left some minor comments

});
pendingMessages.clear();
setState(State.Closed);
synchronized (this) {
Copy link
Contributor

Choose a reason for hiding this comment

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

in the previous code "setState" was inside the synchronized block
so probably is is better to keep it inside that block.

At that point we can simply move the synchronized keyword in the signature of the method

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's okay to move setState outside the synchronized block. I think it's a problem of previous code. See connectionOpened and connectionFailed, setState is all called outside the synchronized block since it's an atomic operation. It's only called inside the synchronized block in closeAsync, which is unnecessary.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's not clear to me why the setState method was ever in a synchronized block. In looking at the git history, it has been that way since the initial import. Since setSet has its own concurrency control and since we call setState outside of synchronized blocks in many other parts of the ProducerImpl, I don't see why this update should be in the synchronized block. Let me know if you think I am missing something, @eolivelli.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't remember the original reasoning, though if the variable state is changed within the sync block, it would appear atomic also with all the other changes in the sync block.

Copy link
Member Author

Choose a reason for hiding this comment

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

it would appear atomic also with all the other changes in the sync block.

Yes, that is true. I'll move it back into the sync block. I wonder if these should be updated too?

if (cause instanceof PulsarClientException.TopicTerminatedException) {
setState(State.Terminated);
synchronized (this) {
failPendingMessages(cnx(), (PulsarClientException) cause);
}
producerCreatedFuture.completeExceptionally(cause);
client.cleanupProducer(this);
} else if (cause instanceof PulsarClientException.ProducerFencedException) {
setState(State.ProducerFenced);
synchronized (this) {
failPendingMessages(cnx(), (PulsarClientException) cause);
}
producerCreatedFuture.completeExceptionally(cause);
client.cleanupProducer(this);

I don't think it will matter too much because once the state is set to a failure/closed state, the producer won't accept new messages anyway.

@BewareMyPower BewareMyPower self-requested a review October 1, 2021 09:38
@BewareMyPower BewareMyPower added area/client type/bug The PR fixed a bug or issue reported a bug release/2.8.2 labels Oct 1, 2021
@BewareMyPower BewareMyPower added this to the 2.9.0 milestone Oct 1, 2021
@michaeljmarshall
Copy link
Member Author

@merlimat @shoothzj - PTAL

client.cleanupProducer(this);
clearPendingMessagesWhenClose();
}
client.cleanupProducer(this);
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it make sense to also move client.cleanupProducer(this); to closeAndClearPendingMessages()?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, we can add that to closeAndClearPendingMessages.

Copy link
Member Author

Choose a reason for hiding this comment

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

I put it in the sync block based on the same reasoning from the discussion on setState.

});
pendingMessages.clear();
setState(State.Closed);
synchronized (this) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't remember the original reasoning, though if the variable state is changed within the sync block, it would appear atomic also with all the other changes in the sync block.

@shoothzj
Copy link
Member

shoothzj commented Oct 2, 2021

Good Catch. LGTM

Copy link
Contributor

@eolivelli eolivelli left a comment

Choose a reason for hiding this comment

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

Lgtm

@michaeljmarshall
Copy link
Member Author

/pulsarbot run-failure-checks

2 similar comments
@michaeljmarshall
Copy link
Member Author

/pulsarbot run-failure-checks

@michaeljmarshall
Copy link
Member Author

/pulsarbot run-failure-checks

@merlimat merlimat merged commit 2ad0e5a into apache:master Oct 6, 2021
codelipenghui pushed a commit that referenced this pull request Oct 6, 2021
* [Java Client] Use failPendingMessages to ensure proper cleanup

* Update method name from code review comments

* Update pulsar-broker/src/test/java/org/apache/pulsar/client/impl/ProducerCloseTest.java

Co-authored-by: Matteo Merli <mmerli@apache.org>

* Move setState into sync block; consolidate client.cleanupProducer call

* Move cleanupProducer into sync block

* Make method closeAndClearPendingMessages synchronized

Co-authored-by: Matteo Merli <mmerli@apache.org>
(cherry picked from commit 2ad0e5a)
@codelipenghui codelipenghui added the cherry-picked/branch-2.8 Archived: 2.8 is end of life label Oct 6, 2021
nicoloboschi pushed a commit to datastax/pulsar that referenced this pull request Oct 27, 2021
…e#12259)

* [Java Client] Use failPendingMessages to ensure proper cleanup

* Update method name from code review comments

* Update pulsar-broker/src/test/java/org/apache/pulsar/client/impl/ProducerCloseTest.java

Co-authored-by: Matteo Merli <mmerli@apache.org>

* Move setState into sync block; consolidate client.cleanupProducer call

* Move cleanupProducer into sync block

* Make method closeAndClearPendingMessages synchronized

Co-authored-by: Matteo Merli <mmerli@apache.org>
(cherry picked from commit 2ad0e5a)
(cherry picked from commit 2e50783)
@michaeljmarshall michaeljmarshall deleted the fail-batch-message-container branch November 14, 2021 04:57
bharanic-dev pushed a commit to bharanic-dev/pulsar that referenced this pull request Mar 18, 2022
…e#12259)

* [Java Client] Use failPendingMessages to ensure proper cleanup

* Update method name from code review comments

* Update pulsar-broker/src/test/java/org/apache/pulsar/client/impl/ProducerCloseTest.java

Co-authored-by: Matteo Merli <mmerli@apache.org>

* Move setState into sync block; consolidate client.cleanupProducer call

* Move cleanupProducer into sync block

* Make method closeAndClearPendingMessages synchronized

Co-authored-by: Matteo Merli <mmerli@apache.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/client cherry-picked/branch-2.8 Archived: 2.8 is end of life release/2.8.2 release/2.9.0 type/bug The PR fixed a bug or issue reported a bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants