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
[ROCKETMQ-102] When shutdown(), the persisted offet is not the latest consumed message, which may cause repeated messages. #64
Conversation
Can you provide some test data, say before/after applying this patch, how many duplications are found respectively? IMHO, we should make the API as concise as possible. |
The problem exists for long, please review an old issue. The number of duplicated messages are depending on how many messages are being consumed in the thread pool or in the pending queue. This should be very easy to meet when consumptions are takes long and with massive accumulation. The problem should be addressed since rocketmq should prevend duplication as much as possible. The reason I add a new interface is that I want to make it compatible with the previous version, actually, if one shutdown(long awaitTerminations) is enough. Or, if we don't mind, we could use a field to make this in push consumer, if we do this, no more interface is needed but a new field is added. |
I know this issue has been brought up in the past. What I suggest here is providing some testing data to consolidate the rationality of this patch. By doing so, you'll find it easier to get developers convinced. Anyway, thanks a lot for bringing this issue back to attention. |
@lizhanhui please refer to the test case and just switch the |
@lizhanhui @zhouxinyu @vintagewang How do you think my proposol of adding a field called |
1 similar comment
if (awaitTerminateMillis > 0) { | ||
try { | ||
this.consumeExecutor.awaitTermination(awaitTerminateMillis,TimeUnit.MILLISECONDS); | ||
if (!this.consumeExecutor.isTerminated()) log.info("There are messages still being consumed in thread pool, but not going to await them anymore. Have awaited for {} ms",awaitTerminateMillis); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you follow our code code guidelines[1] ? Below if
block is recommended.
if {
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I will update the pr accordingly.
shutdown(0); | ||
} | ||
|
||
public void shutdown(long awaitTerminateMillis) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since MQPushConsumer
only has the method interface for shutdown( )
, so we could consider pull shutdown(long awaitTerminateMillis)
up to the parent interface or make shutdown(long awaitTerminateMillis)
private and give a default input(non-zero) in shutdown()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know and agree to your concern, actually, as I my previous comment, maybe we could add an configuration for awaitMills, what do you think ? The default value could be 0 to keep the same behavior
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, it's ok for me.
Hi @Jaskey , |
1 similar comment
please review the updated pr, which remains the same interface of push consumer. |
1 similar comment
2 similar comments
@@ -52,6 +56,7 @@ | |||
import org.apache.rocketmq.common.protocol.header.PullMessageRequestHeader; | |||
import org.apache.rocketmq.remoting.exception.RemotingException; | |||
import org.junit.After; | |||
import org.junit.Assert; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, let's unify the assert tool and use org.assertj.core.api.Assertions.assertThat
.
if (awaitTerminateMillis > 0) { | ||
try { | ||
this.consumeExecutor.awaitTermination(awaitTerminateMillis,TimeUnit.MILLISECONDS); | ||
if (!this.consumeExecutor.isTerminated()) log.info("There are messages still being consumed in thread pool, but not going to await them anymore. Have awaited for {} ms",awaitTerminateMillis); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be we need a common method to shutdown executor gracefully, like:
public static void shutdownGracefully(ExecutorService executor, long timeout, TimeUnit timeUnit) {
executor.shutdown();
try {
if(!executor.awaitTermination(timeout, timeUnit)) {
executor.shutdownNow();
if(!executor.awaitTermination(timeout, timeUnit)) {
LOG.warn(String.format("%s didn\'t terminate!", new Object[]{executor}));
}
}
} catch (InterruptedException var5) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can, but where do we put this method in, in a Common Util?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a new java file ThreadUtils
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then maybe you merge that commit first and I will merge it later. Also, I don't think shutdownNow is a good choice for consume services, please refer to my comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shutdown
is the first choice, shutdownNow
will be called if timeout.
And develop
will be merged to master
in next release, please refer to our new branching model.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zhouxinyu In my option, rocketmq should have no right to interrupt what dev's business are doing, we may be doing some time-cost job which are doing transcation or inserting database, we should leave the task running if executor is still not terminated.
Besides, since the old version has not termination millis, so 0 of termination millis is the default behavior , shutdown now will cost task being interrupt/cancel immediately which is not proper in my opinion.
I have updated the pr , but still using my old method, please review and let's discuss more about it.
Please check the updated pr. Since BTW, unit test has been updated using |
Changes Unknown when pulling 67cbdc3 on Jaskey:ROCKETMQ-102-shutdown-await into ** on apache:master**. |
2 similar comments
Changes Unknown when pulling 67cbdc3 on Jaskey:ROCKETMQ-102-shutdown-await into ** on apache:master**. |
Changes Unknown when pulling 67cbdc3 on Jaskey:ROCKETMQ-102-shutdown-await into ** on apache:master**. |
… consumed message, which may cause repeated messages. Add configuration to push consumer to accept await termination time to await consuming.
Repeated messages is a big problem for a message queue, and this is a known issue. When can this pr be reviewed and merged? |
@Jaskey I will close the pr, if you happened to the same question, please let me know. |
I request a another issue #2085 since I find this feature is still not supported for the latest release @zhouxinyu @vongosling |
Solution: add interface for push consumer to accept await termination time to await consuming.
JIRA: https://issues.apache.org/jira/browse/ROCKETMQ-102
UnitTest in
DefaultMQPushConsumerTest.java