-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[FIXED] Only deliver replicated message after quorum #6792
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -436,6 +436,7 @@ type consumer struct { | |
| rdqi avl.SequenceSet | ||
| rdc map[uint64]uint64 | ||
| replies map[uint64]string | ||
| pendingDeliveries map[uint64]*jsPubMsg // Messages that can be delivered after achieving quorum. | ||
| maxdc uint64 | ||
| waiting *waitQueue | ||
| cfg ConsumerConfig | ||
|
|
@@ -1533,6 +1534,7 @@ func (o *consumer) setLeader(isLeader bool) { | |
| o.rdq = nil | ||
| o.rdqi.Empty() | ||
| o.pending = nil | ||
| o.resetPendingDeliveries() | ||
| // ok if they are nil, we protect inside unsubscribe() | ||
| o.unsubscribe(o.ackSub) | ||
| o.unsubscribe(o.reqSub) | ||
|
|
@@ -2170,6 +2172,11 @@ func (o *consumer) updateConfig(cfg *ConsumerConfig) error { | |
| if cfg.MaxAckPending != o.cfg.MaxAckPending { | ||
| o.maxp = cfg.MaxAckPending | ||
| o.signalNewMessages() | ||
| // If MaxAckPending is lowered, we could have allocated a pending deliveries map of larger size. | ||
| // Reset it here, so we can shrink the map. | ||
| if cfg.MaxAckPending < o.cfg.MaxAckPending { | ||
| o.resetPendingDeliveries() | ||
| } | ||
| } | ||
| // AckWait | ||
| if cfg.AckWait != o.cfg.AckWait { | ||
|
|
@@ -2529,6 +2536,16 @@ func (o *consumer) addAckReply(sseq uint64, reply string) { | |
| o.replies[sseq] = reply | ||
| } | ||
|
|
||
| // Used to remember messages that need to be sent for a replicated consumer, after delivered quorum. | ||
| // Lock should be held. | ||
| func (o *consumer) addReplicatedQueuedMsg(pmsg *jsPubMsg) { | ||
| // Is not explicitly limited in size, but will at maximum hold maximum ack pending. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we sure this comment is true?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The stream sequence is used as a key, which is the same for
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok so when new message comes into stream and we kick the consumer we just bail because we hit max pending yes?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Understood but I am wondering if we should short circuit in stream's signalConsumers()? WDYT?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could potentially be an additional optimization, although can't say how large the gain would be.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's do that PR after this one that short circuits. |
||
| if o.pendingDeliveries == nil { | ||
| o.pendingDeliveries = make(map[uint64]*jsPubMsg) | ||
| } | ||
| o.pendingDeliveries[pmsg.seq] = pmsg | ||
| } | ||
|
|
||
| // Lock should be held. | ||
| func (o *consumer) updateAcks(dseq, sseq uint64, reply string) { | ||
| if o.node != nil { | ||
|
|
@@ -3089,7 +3106,6 @@ func (o *consumer) processAckMsg(sseq, dseq, dc uint64, reply string, doSample b | |
| } | ||
|
|
||
| // Check if this ack is above the current pointer to our next to deliver. | ||
| // This could happen on a cooperative takeover with high speed deliveries. | ||
| if sseq >= o.sseq { | ||
| // Let's make sure this is valid. | ||
| // This is only received on the consumer leader, so should never be higher | ||
|
|
@@ -4842,7 +4858,14 @@ func (o *consumer) deliverMsg(dsubj, ackReply string, pmsg *jsPubMsg, dc uint64, | |
| } | ||
|
|
||
| // Send message. | ||
| o.outq.send(pmsg) | ||
| // If we're replicated we MUST only send the message AFTER we've got quorum for updating | ||
| // delivered state. Otherwise, we could be in an invalid state after a leader change. | ||
| // We can send immediately if not replicated, not using acks, or using flow control (incompatible). | ||
| if o.node == nil || ap == AckNone || o.cfg.FlowControl { | ||
| o.outq.send(pmsg) | ||
| } else { | ||
| o.addReplicatedQueuedMsg(pmsg) | ||
| } | ||
|
|
||
| // Flow control. | ||
| if o.maxpb > 0 && o.needFlowControl(psz) { | ||
|
|
@@ -6104,3 +6127,10 @@ func (o *consumer) stopAndClearPtmr() { | |
| stopAndClearTimer(&o.ptmr) | ||
| o.ptmrEnd = time.Time{} | ||
| } | ||
|
|
||
| func (o *consumer) resetPendingDeliveries() { | ||
| for _, pmsg := range o.pendingDeliveries { | ||
| pmsg.returnToPool() | ||
| } | ||
| o.pendingDeliveries = nil | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.