-
Notifications
You must be signed in to change notification settings - Fork 3.7k
[Issue 5476]Fix message deduplicate issue while using external sequence id with batch produce #5491
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
Conversation
|
It's hard to handle sequence id in key based batcher, if user set an external sequence id, we can't rewrite it and the sequence ids will distributed into inner batchers of key based batcher. Current implementation will throw exception if client can ensure the message is duplicated, since we do not throws exceptions before, is it ok to throws exception? |
|
run java8 tests |
|
hello @codelipenghui can you fix the test case: |
|
run java8 tests |
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 don't think this is a valid assumption. The logic of dedup is to transparently perform the deduplication, without giving error to the application in case of duplicates.
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 shouldn't trigger an error to the application when there are dups, rather the contract is that it gets an OK. In any case we need to think through the implication of triggering an error just for one message out of band.
This is a very different behavior from the other failure modes where all the messages are failed after 1 failure.
I believe that handling this in broker side is much preferable.
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.
If users use the external sequence id and enable batch on producer, sequence id 1,2,3,1 will happens if we do not the check, we can just throw an exception when using the external sequence id?
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.
If we don't know whether is dup or not, then why are we triggering error here?
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.
Why do we need 2 new fields here? Don't we just need to a new last_sequence_id? isn't lowest_sequence_id the same as sequence_id ?
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.
Yes, we can only add a new last_sequence_id, will fix.
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 don't see how these sequence ids relate to a Topic.
- What's the lowest sequenceId ? I think this only apply to 1 single batch.
- Sequence id are per producer anyway
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.
It is in the inner class named PublishContext of Topic
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.
It is in the inner class named PublishContext of Topic
Which already contains a sequence id field
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.
What does it mean the lowest sequence id of a producer?
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.
It is in the inner class named MessagePublishContext of Producer
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, though as in other comment, there's already a sequenceId field. Is that now ignored? If yes, then it should be removed.
Also, as you can see, there's an originalSequenceId field. This is used in the context of geo-replication and it would have to be accounted for as well.
|
@merlimat Thanks for the review, i have addressed your comments, please take a look again. |
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, though as in other comment, there's already a sequenceId field. Is that now ignored? If yes, then it should be removed.
Also, as you can see, there's an originalSequenceId field. This is used in the context of geo-replication and it would have to be accounted for as well.
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.
It is in the inner class named PublishContext of Topic
Which already contains a sequence id field
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.
This is not correct on 2 levels:
- If
sequenceId <= lastSequenceIdPushedwe don't know yet whether the message is already dup, because the previous attempt might still fail. This has to be disambiguated by the broker which has visibility at the storage level. - As mentioned before, we cannot throw error when there's a duplicate, rather we need to return "ok" to the application.
run java8 tests |
|
run java8 tests |
|
@merlimat Please help take a look, i have addressed your comments |
|
run java8 tests |
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.
can this be simplified with the following statement?
long callbackSequenceId = Math.max(lastSequenceId, sequenceId);
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 will add a common function to get the callback sequenceId
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.
same comment as above
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.
nit:
| if (lowestSequenceId == -1) { | |
| if (lowestSequenceId == -1L) { |
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.
will fix
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.
| private long lowestSequenceId = -1; | |
| private long lowestSequenceId = -1L; |
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.
will fix
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.
| private long highestSequenceId = -1; | |
| private long highestSequenceId = -1L; |
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.
will fix
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.
can you make a common function for this statement here?
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.
will fix
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.
should this be highest sequence id?
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 think we need to keep track highestSequenceId, no?
- We should only assign the highest sequence id, no?
lastSequenceIdPushed = Math.max(lastSequenceIdPushed, op.lastSequenceIdPushed);
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.
It seems to me that we use highest sequence id at the client side, but use last at the wire protocol and broker side. Can we make it consistent by just using highest across the places?
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.
sequenceId => lowestSequenceId
lastSequenceId => highestSequenceId
It is very confusing using sequenceId and lastSequenceId.
|
run java8 tests |
|
run java8 tests |
1 similar comment
|
run java8 tests |
|
run java8 tests |
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.
this should :
lastSequenceIdPublished = Math.max(lastSequenceIdPublished, getHighestSequenceId(op));
because the duplicated publishes will still succeed, right. It can override a larger sequence Id with a smaller one.
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.
expectedSequenceId = getHighestSequenceId(op);
|
run java8 tests |
2 similar comments
|
run java8 tests |
|
run java8 tests |
a29257d to
d982c8d
Compare
|
run java8 tests |
2 similar comments
|
run java8 tests |
|
run java8 tests |
|
run cpp tests |
|
run java8 tests |
|
ping @merlimat PTAL again. |
|
run java8 tests |
2 similar comments
|
run java8 tests |
|
run java8 tests |
|
ping @merlimat PTAL again. |
|
@merlimat PTAL again, thanks |
merlimat
left a 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.
👍 Nice work @codelipenghui
|
@codelipenghui The change is not compatible with 2.4.2, i will move the |
## Motivation Since #5491 merged, while user use new pulsar client to produce batch messages to older version broker(e.g. 2.4.0), send ack error will occur: ``` [pulsar-client-io-8-2] WARN org.apache.pulsar.client.impl.ProducerImpl - [persistent://sandbox/pressure-test/test-A-partition-11] [pulsar-cluster-test-13-294] Got ack for msg. expecting: 13 - got: 224 - queue-size: 9 ``` The problem is client use highest sequence id to match the response sequence id, but in old version broker can not return the highest id. So, this pr is try to fix the problem of produce batch message with new version client and old version broker. ### Modifications Add highest sequence id to CommandSendReceipt. If the response highest sequence id of send receipt > lowest sequence id, it means broker is a new version broker, so we need to verify the highest sequence id, otherwise we only verify the lowest sequence id.
Fixes #5476
Motivation
Fix #5476
Modifications
last_sequence_idin MessageMetadata and CommandSend, use sequence id and last_sequence_id to indicate the batchlowest_sequence_idandhighest_sequence_id.last_sequence_idto client and add message deduplicate check in clientVerifying this change
Added new unit tests to verify this change
Does this pull request potentially affect one of the following parts:
If
yeswas chosen, please highlight the changesDocumentation