66 "fmt"
77 "math"
88 "sync"
9+ "sync/atomic"
910 "time"
1011
1112 "github.com/eapache/go-resiliency/breaker"
@@ -93,6 +94,9 @@ type asyncProducer struct {
9394 input , successes , retries chan * ProducerMessage
9495 inFlight sync.WaitGroup
9596
97+ done chan struct {}
98+ closed atomic.Bool
99+
96100 brokers map [* Broker ]* brokerProducer
97101 brokerRefs map [* brokerProducer ]int
98102 brokerLock sync.Mutex
@@ -303,6 +307,7 @@ func newAsyncProducer(client Client) (AsyncProducer, error) {
303307 input : make (chan * ProducerMessage ),
304308 successes : make (chan * ProducerMessage ),
305309 retries : make (chan * ProducerMessage ),
310+ done : make (chan struct {}),
306311 brokers : make (map [* Broker ]* brokerProducer ),
307312 brokerRefs : make (map [* brokerProducer ]int ),
308313 txnmgr : txnmgr ,
@@ -781,12 +786,16 @@ func (p *asyncProducer) newPartitionProducer(topic string, partition int32) chan
781786}
782787
783788func (pp * partitionProducer ) backoff (retries int ) {
789+ pp .parent .retryBackoff (retries )
790+ }
791+
792+ func (p * asyncProducer ) retryBackoff (retries int ) {
784793 var backoff time.Duration
785- if pp . parent .conf .Producer .Retry .BackoffFunc != nil {
786- maxRetries := pp . parent .conf .Producer .Retry .Max
787- backoff = pp . parent .conf .Producer .Retry .BackoffFunc (retries , maxRetries )
794+ if p .conf .Producer .Retry .BackoffFunc != nil {
795+ maxRetries := p .conf .Producer .Retry .Max
796+ backoff = p .conf .Producer .Retry .BackoffFunc (retries , maxRetries )
788797 } else {
789- backoff = pp . parent .conf .Producer .Retry .Backoff
798+ backoff = p .conf .Producer .Retry .Backoff
790799 }
791800 if backoff > 0 {
792801 time .Sleep (backoff )
@@ -821,12 +830,19 @@ func (pp *partitionProducer) dispatch() {
821830 }()
822831
823832 for msg := range pp .input {
824- if pp .brokerProducer != nil && pp . brokerProducer . abandoned != nil {
833+ if pp .brokerProducer != nil {
825834 select {
826835 case <- pp .brokerProducer .abandoned :
827836 // a message on the abandoned channel means that our current broker selection is out of date
828- pp .parent .unrefBrokerProducer (pp .leader , pp .brokerProducer )
837+ abandonedLeader := pp .leader
838+ pp .parent .unrefBrokerProducer (abandonedLeader , pp .brokerProducer )
829839 pp .brokerProducer = nil
840+ if leader , _ := pp .parent .client .Leader (pp .topic , pp .partition ); leader != nil && leader .ID () != abandonedLeader .ID () {
841+ pp .leader = leader
842+ pp .brokerProducer = pp .parent .getBrokerProducer (pp .leader )
843+ pp .parent .inFlight .Add (1 ) // we're generating a syn message; track it so we don't shut down while it's still inflight
844+ pp .brokerProducer .input <- & ProducerMessage {Topic : pp .topic , Partition : pp .partition , flags : syn }
845+ }
830846 time .Sleep (pp .parent .conf .Producer .Retry .Backoff )
831847 default :
832848 // producer connection is still open.
@@ -956,6 +972,7 @@ func (p *asyncProducer) newBrokerProducer(broker *Broker) *brokerProducer {
956972 input : input ,
957973 output : bridge ,
958974 responses : responses ,
975+ abandoned : make (chan struct {}),
959976 accumulatingBatch : newProduceSet (p ),
960977 currentRetries : make (map [string ]map [int32 ]error ),
961978 }
@@ -1046,10 +1063,6 @@ func (p *asyncProducer) newBrokerProducer(broker *Broker) *brokerProducer {
10461063 }
10471064 })
10481065
1049- if p .conf .Producer .Retry .Max <= 0 {
1050- bp .abandoned = make (chan struct {})
1051- }
1052-
10531066 return bp
10541067}
10551068
@@ -1092,10 +1105,16 @@ func (bp *brokerProducer) run() {
10921105 timerChan = bp .timer .C
10931106 }
10941107
1108+ var unmuteCh <- chan struct {}
10951109 if bp .flushingBatch != nil {
10961110 output = bp .output
10971111 } else {
10981112 output = nil
1113+ if bp .accumulatingBatch .readyToFlush () {
1114+ if ch , blocked := bp .parent .muter .awaitUnmuteChan (bp .accumulatingBatch ); blocked {
1115+ unmuteCh = ch
1116+ }
1117+ }
10991118 }
11001119
11011120 select {
@@ -1138,7 +1157,6 @@ func (bp *brokerProducer) run() {
11381157 }
11391158
11401159 if bp .accumulatingBatch .wouldOverflow (msg ) {
1141- DebugLogger .Printf ("producer/broker/%d maximum request accumulated, waiting for space\n " , bp .broker .ID ())
11421160 if err := bp .waitForSpace (msg , false ); err != nil {
11431161 bp .parent .retryMessage (msg , err )
11441162 continue
@@ -1169,6 +1187,7 @@ func (bp *brokerProducer) run() {
11691187 if ok {
11701188 bp .handleResponse (response )
11711189 }
1190+ case <- unmuteCh :
11721191 }
11731192 }
11741193}
@@ -1350,32 +1369,28 @@ func (bp *brokerProducer) handleSuccess(sent *produceSet, response *ProduceRespo
13501369 case ErrInvalidMessage , ErrUnknownTopicOrPartition , ErrLeaderNotAvailable , ErrNotLeaderForPartition ,
13511370 ErrRequestTimedOut , ErrNotEnoughReplicas , ErrNotEnoughReplicasAfterAppend , ErrKafkaStorageError :
13521371 if bp .parent .conf .Producer .Retry .Max <= 0 {
1353- bp .parent .abandonBrokerConnection (bp . broker )
1372+ bp .parent .abandonBrokerConnection (bp )
13541373 bp .parent .returnErrors (pSet .msgs , block .Err )
13551374 } else {
13561375 retryTopics = append (retryTopics , topic )
1357- if bp .parent .conf .Producer .Idempotent {
1358- if keepMuted [topic ] == nil {
1359- keepMuted [topic ] = make (map [int32 ]struct {})
1360- }
1361- keepMuted [topic ][partition ] = struct {}{}
1376+ if keepMuted [topic ] == nil {
1377+ keepMuted [topic ] = make (map [int32 ]struct {})
13621378 }
1379+ keepMuted [topic ][partition ] = struct {}{}
13631380 }
13641381 // Other non-retriable errors
13651382 default :
13661383 if bp .parent .conf .Producer .Retry .Max <= 0 {
1367- bp .parent .abandonBrokerConnection (bp . broker )
1384+ bp .parent .abandonBrokerConnection (bp )
13681385 }
13691386 bp .parent .returnErrors (pSet .msgs , block .Err )
13701387 }
13711388 })
13721389
13731390 if len (retryTopics ) > 0 {
1374- if bp .parent .conf .Producer .Idempotent {
1375- err := bp .parent .client .RefreshMetadata (retryTopics ... )
1376- if err != nil {
1377- Logger .Printf ("Failed refreshing metadata because of %v\n " , err )
1378- }
1391+ err := bp .parent .client .RefreshMetadata (retryTopics ... )
1392+ if err != nil {
1393+ Logger .Printf ("Failed refreshing metadata because of %v\n " , err )
13791394 }
13801395
13811396 sent .eachPartition (func (topic string , partition int32 , pSet * partitionSet ) {
@@ -1390,17 +1405,25 @@ func (bp *brokerProducer) handleSuccess(sent *produceSet, response *ProduceRespo
13901405 ErrRequestTimedOut , ErrNotEnoughReplicas , ErrNotEnoughReplicasAfterAppend , ErrKafkaStorageError :
13911406 Logger .Printf ("producer/broker/%d state change to [retrying] on %s/%d because %v\n " ,
13921407 bp .broker .ID (), topic , partition , block .Err )
1393- if bp .currentRetries [topic ] == nil {
1394- bp .currentRetries [topic ] = make (map [int32 ]error )
1408+ leaderChanged := false
1409+ if ! bp .parent .conf .Producer .Idempotent {
1410+ leader , leaderErr := bp .parent .client .Leader (topic , partition )
1411+ leaderChanged = leaderErr == nil && leader != nil && leader .ID () != bp .broker .ID ()
13951412 }
1396- bp.currentRetries [topic ][partition ] = block .Err
1413+ if bp .parent .conf .Producer .Idempotent || leaderChanged {
1414+ if bp .currentRetries [topic ] == nil {
1415+ bp .currentRetries [topic ] = make (map [int32 ]error )
1416+ }
1417+ bp.currentRetries [topic ][partition ] = block .Err
1418+ }
1419+ if leaderChanged {
1420+ bp .parent .abandonBrokerConnection (bp )
1421+ }
1422+ go bp .parent .retryBatch (topic , partition , pSet , block .Err , true )
13971423 if bp .parent .conf .Producer .Idempotent {
1398- go bp .parent .retryBatch (topic , partition , pSet , block .Err , true )
1399- } else {
1400- bp .parent .retryMessages (pSet .msgs , block .Err )
1424+ // dropping the following messages has the side effect of incrementing their retry count
1425+ bp .parent .retryMessages (bp .accumulatingBatch .dropPartition (topic , partition ), block .Err )
14011426 }
1402- // dropping the following messages has the side effect of incrementing their retry count
1403- bp .parent .retryMessages (bp .accumulatingBatch .dropPartition (topic , partition ), block .Err )
14041427 }
14051428 })
14061429 }
@@ -1422,6 +1445,7 @@ func (p *asyncProducer) retryBatch(topic string, partition int32, pSet *partitio
14221445 produceSet.msgs [topic ][partition ] = pSet
14231446 produceSet .bufferBytes += pSet .bufferBytes
14241447 produceSet .bufferCount += len (pSet .msgs )
1448+ retryAttempt := 0
14251449 for _ , msg := range pSet .msgs {
14261450 if msg .retries >= p .conf .Producer .Retry .Max {
14271451 p .returnErrors (pSet .msgs , retryErr )
@@ -1431,6 +1455,10 @@ func (p *asyncProducer) retryBatch(topic string, partition int32, pSet *partitio
14311455 return
14321456 }
14331457 msg .retries ++
1458+ retryAttempt = msg .retries
1459+ }
1460+ if ! p .conf .Producer .Idempotent {
1461+ p .retryBackoff (retryAttempt )
14341462 }
14351463
14361464 // it's expected that a metadata refresh has been requested prior to calling retryBatch
@@ -1454,8 +1482,23 @@ func (p *asyncProducer) retryBatch(topic string, partition int32, pSet *partitio
14541482 }
14551483 }
14561484 bp := p .getBrokerProducer (leader )
1457- bp .output <- produceSet
1458- p .unrefBrokerProducer (leader , bp )
1485+ defer p .unrefBrokerProducer (leader , bp )
1486+
1487+ select {
1488+ case bp .output <- produceSet :
1489+ return
1490+ default :
1491+ }
1492+
1493+ select {
1494+ case bp .output <- produceSet :
1495+ return
1496+ case <- p .done :
1497+ for _ , msg := range pSet .msgs {
1498+ p .returnError (msg , ErrShuttingDown )
1499+ }
1500+ p .muter .unmute (produceSet )
1501+ }
14591502}
14601503
14611504func (bp * brokerProducer ) handleError (sent * produceSet , err error ) {
@@ -1467,7 +1510,7 @@ func (bp *brokerProducer) handleError(sent *produceSet, err error) {
14671510 bp .parent .muter .unmute (sent )
14681511 } else {
14691512 Logger .Printf ("producer/broker/%d state change to [closing] because %s\n " , bp .broker .ID (), err )
1470- bp .parent .abandonBrokerConnection (bp . broker )
1513+ bp .parent .abandonBrokerConnection (bp )
14711514 _ = bp .broker .Close ()
14721515 bp .closing = err
14731516 var retryTopics []string
@@ -1479,7 +1522,7 @@ func (bp *brokerProducer) handleError(sent *produceSet, err error) {
14791522 retryTopicSeen [topic ] = struct {}{}
14801523 retryTopics = append (retryTopics , topic )
14811524 })
1482- if bp . parent . conf . Producer . Idempotent && len (retryTopics ) > 0 {
1525+ if len (retryTopics ) > 0 {
14831526 refreshErr := bp .parent .client .RefreshMetadata (retryTopics ... )
14841527 if refreshErr != nil {
14851528 Logger .Printf ("Failed refreshing metadata because of %v\n " , refreshErr )
@@ -1492,15 +1535,11 @@ func (bp *brokerProducer) handleError(sent *produceSet, err error) {
14921535 bp .currentRetries [topic ] = make (map [int32 ]error )
14931536 }
14941537 bp.currentRetries [topic ][partition ] = err
1495- if bp .parent .conf .Producer .Idempotent {
1496- if keepMuted [topic ] == nil {
1497- keepMuted [topic ] = make (map [int32 ]struct {})
1498- }
1499- keepMuted [topic ][partition ] = struct {}{}
1500- go bp .parent .retryBatch (topic , partition , pSet , err , true )
1501- } else {
1502- bp .parent .retryMessages (pSet .msgs , err )
1538+ if keepMuted [topic ] == nil {
1539+ keepMuted [topic ] = make (map [int32 ]struct {})
15031540 }
1541+ keepMuted [topic ][partition ] = struct {}{}
1542+ go bp .parent .retryBatch (topic , partition , pSet , err , true )
15041543 })
15051544 bp .accumulatingBatch .eachPartition (func (topic string , partition int32 , pSet * partitionSet ) {
15061545 bp .parent .retryMessages (pSet .msgs , err )
@@ -1583,6 +1622,9 @@ func (p *asyncProducer) retryHandler() {
15831622// utility functions
15841623
15851624func (p * asyncProducer ) shutdown () {
1625+ if p .closed .CompareAndSwap (false , true ) {
1626+ close (p .done )
1627+ }
15861628 p .inFlight .Add (1 )
15871629 p .input <- & ProducerMessage {flags : shutdown }
15881630
@@ -1716,14 +1758,15 @@ func (p *asyncProducer) unrefBrokerProducer(broker *Broker, bp *brokerProducer)
17161758 }
17171759}
17181760
1719- func (p * asyncProducer ) abandonBrokerConnection (broker * Broker ) {
1761+ func (p * asyncProducer ) abandonBrokerConnection (bp * brokerProducer ) {
17201762 p .brokerLock .Lock ()
17211763 defer p .brokerLock .Unlock ()
17221764
1723- bc , ok := p .brokers [broker ]
1724- if ok && bc . abandoned != nil {
1725- close ( bc . abandoned )
1765+ bc , ok := p .brokers [bp . broker ]
1766+ if ! ok || bc != bp {
1767+ return
17261768 }
17271769
1728- delete (p .brokers , broker )
1770+ close (bc .abandoned )
1771+ delete (p .brokers , bp .broker )
17291772}
0 commit comments