@@ -155,8 +155,17 @@ type partitionConsumer struct {
155155 availablePermits * availablePermits
156156
157157 // the size of the queue channel for buffering messages
158- maxQueueSize int32
159- queueCh * unboundedChannel [* message ]
158+ maxQueueSize int32
159+
160+ // pendingMessages queues all messages received from the broker but not delivered to the user via Chan() or
161+ // Receive() methods.
162+ // There is a background goroutine that sends messages from the connection to `pendingMessages` via `queueInCh` and
163+ // reads messages from `pendingMessages` via `queueOutCh` so that the `dispatcher` goroutine can read messages from
164+ // the `queueOutCh`.
165+ pendingMessages * list.List
166+ queueInCh chan * message
167+ queueOutCh chan * message
168+
160169 startMessageID atomicMessageID
161170 lastDequeuedMsg * trackingMessageID
162171
@@ -354,7 +363,6 @@ func newPartitionConsumer(parent Consumer, client *client, options *partitionCon
354363 partitionIdx : int32 (options .partitionIdx ),
355364 eventsCh : make (chan interface {}, 10 ),
356365 maxQueueSize : int32 (options .receiverQueueSize ),
357- queueCh : newUnboundedChannel [* message ](),
358366 startMessageID : atomicMessageID {msgID : options .startMessageID },
359367 connectedCh : make (chan struct {}),
360368 messageCh : messageCh ,
@@ -419,6 +427,7 @@ func newPartitionConsumer(parent Consumer, client *client, options *partitionCon
419427 }
420428 pc .log .Info ("Created consumer" )
421429 pc .setConsumerState (consumerReady )
430+ pc .startQueueMessagesFromBroker ()
422431
423432 startingMessageID := pc .startMessageID .get ()
424433 if pc .options .startMessageIDInclusive && startingMessageID != nil && startingMessageID .equal (latestMessageID ) {
@@ -949,11 +958,6 @@ func (pc *partitionConsumer) Close() {
949958
950959 // wait for request to finish
951960 <- req .doneCh
952-
953- // It will close `queueCh.in`. If `MessageReceived` was called after that, it will panic because new messages
954- // will be sent to a closed channel. However, generally it's impossible because the broker will not be able to
955- // dispatch messages to this consumer after receiving the close request.
956- pc .queueCh .stop ()
957961}
958962
959963func (pc * partitionConsumer ) Seek (msgID MessageID ) error {
@@ -1176,7 +1180,7 @@ func (pc *partitionConsumer) MessageReceived(response *pb.CommandMessage, header
11761180 pc .markScaleIfNeed ()
11771181 }
11781182
1179- pc .queueCh . inCh <- & message {
1183+ pc .queueInCh <- & message {
11801184 publishTime : timeFromUnixTimestampMillis (msgMeta .GetPublishTime ()),
11811185 eventTime : timeFromUnixTimestampMillis (msgMeta .GetEventTime ()),
11821186 key : msgMeta .GetPartitionKey (),
@@ -1378,7 +1382,7 @@ func (pc *partitionConsumer) MessageReceived(response *pb.CommandMessage, header
13781382 pc .markScaleIfNeed ()
13791383 }
13801384
1381- pc .queueCh . inCh <- msg
1385+ pc .queueInCh <- msg
13821386 }
13831387
13841388 if skippedMessages > 0 {
@@ -1542,12 +1546,14 @@ func (pc *partitionConsumer) dispatcher() {
15421546 }()
15431547 var queueMsg * message
15441548 for {
1545- var queueCh <- chan * message
1549+ queueMsgCh := pc . queueOutCh
15461550 var messageCh chan ConsumerMessage
15471551 var nextMessage ConsumerMessage
15481552 var nextMessageSize int
15491553
15501554 if queueMsg != nil {
1555+ // Do not read from the queued message channel since there is already a message polled in the last loop
1556+ queueMsgCh = nil
15511557 nextMessage = ConsumerMessage {
15521558 Consumer : pc .parentConsumer ,
15531559 Message : queueMsg ,
@@ -1568,8 +1574,6 @@ func (pc *partitionConsumer) dispatcher() {
15681574 } else {
15691575 pc .log .Debug ("skip dispatching messages when seeking" )
15701576 }
1571- } else {
1572- queueCh = pc .queueCh .outCh
15731577 }
15741578
15751579 select {
@@ -1607,7 +1611,7 @@ func (pc *partitionConsumer) dispatcher() {
16071611 pc .log .Debug ("received dispatcherSeekingControlCh, set isSeek to true" )
16081612 pc .isSeeking .Store (true )
16091613
1610- case msg , ok := <- queueCh :
1614+ case msg , ok := <- queueMsgCh :
16111615 if ! ok {
16121616 return
16131617 }
@@ -1630,9 +1634,9 @@ func (pc *partitionConsumer) dispatcher() {
16301634 // drain the message queue on any new connection by sending a
16311635 // special nil message to the channel so we know when to stop dropping messages
16321636 var nextMessageInQueue * trackingMessageID
1633- pc .queueCh . inCh <- nil
1637+ pc .queueInCh <- nil
16341638
1635- for m := range pc .queueCh . outCh {
1639+ for m := range pc .queueOutCh {
16361640 // the queue has been drained
16371641 if m == nil {
16381642 break
@@ -2080,7 +2084,7 @@ func (pc *partitionConsumer) expectMoreIncomingMessages() {
20802084}
20812085
20822086func (pc * partitionConsumer ) markScaleIfNeed () {
2083- // availablePermits + incomingMessages (messages in queueCh ) is the number of prefetched messages
2087+ // availablePermits + incomingMessages (messages in pendingMessages ) is the number of prefetched messages
20842088 // The result of auto-scale we expected is currentQueueSize is slightly bigger than prefetched messages
20852089 prev := pc .scaleReceiverQueueHint .Swap (pc .availablePermits .get ()+ pc .incomingMessages .Load () >=
20862090 pc .currentQueueSize .Load ())
@@ -2220,6 +2224,42 @@ func (pc *partitionConsumer) _getConn() internal.Connection {
22202224 return * pc .conn .Load ()
22212225}
22222226
2227+ func (pc * partitionConsumer ) startQueueMessagesFromBroker () {
2228+ pc .queueInCh = make (chan * message )
2229+ pc .queueOutCh = make (chan * message )
2230+ pc .pendingMessages = list .New ()
2231+
2232+ go func () {
2233+ defer func () {
2234+ close (pc .queueInCh )
2235+ close (pc .queueOutCh )
2236+ pc .log .Debug ("exiting queueMessagesFromBroker" )
2237+ }()
2238+
2239+ for {
2240+ front := pc .pendingMessages .Front ()
2241+ if front == nil {
2242+ select {
2243+ case msg := <- pc .queueInCh :
2244+ pc .pendingMessages .PushBack (msg )
2245+ case <- pc .closeCh :
2246+ return
2247+ }
2248+ } else {
2249+ msg := front .Value .(* message )
2250+ select {
2251+ case pc .queueOutCh <- msg :
2252+ pc .pendingMessages .Remove (front )
2253+ case msg := <- pc .queueInCh :
2254+ pc .pendingMessages .PushBack (msg )
2255+ case <- pc .closeCh :
2256+ return
2257+ }
2258+ }
2259+ }
2260+ }()
2261+ }
2262+
22232263func convertToMessageIDData (msgID * trackingMessageID ) * pb.MessageIdData {
22242264 if msgID == nil {
22252265 return nil
0 commit comments