consumer.go 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. package sarama
  2. import (
  3. "errors"
  4. "fmt"
  5. "math"
  6. "sync"
  7. "sync/atomic"
  8. "time"
  9. "github.com/rcrowley/go-metrics"
  10. )
  11. // ConsumerMessage encapsulates a Kafka message returned by the consumer.
  12. type ConsumerMessage struct {
  13. Headers []*RecordHeader // only set if kafka is version 0.11+
  14. Timestamp time.Time // only set if kafka is version 0.10+, inner message timestamp
  15. BlockTimestamp time.Time // only set if kafka is version 0.10+, outer (compressed) block timestamp
  16. Key, Value []byte
  17. Topic string
  18. Partition int32
  19. Offset int64
  20. }
  21. // ConsumerError is what is provided to the user when an error occurs.
  22. // It wraps an error and includes the topic and partition.
  23. type ConsumerError struct {
  24. Topic string
  25. Partition int32
  26. Err error
  27. }
  28. func (ce ConsumerError) Error() string {
  29. return fmt.Sprintf("kafka: error while consuming %s/%d: %s", ce.Topic, ce.Partition, ce.Err)
  30. }
  31. func (ce ConsumerError) Unwrap() error {
  32. return ce.Err
  33. }
  34. // ConsumerErrors is a type that wraps a batch of errors and implements the Error interface.
  35. // It can be returned from the PartitionConsumer's Close methods to avoid the need to manually drain errors
  36. // when stopping.
  37. type ConsumerErrors []*ConsumerError
  38. func (ce ConsumerErrors) Error() string {
  39. return fmt.Sprintf("kafka: %d errors while consuming", len(ce))
  40. }
  41. // Consumer manages PartitionConsumers which process Kafka messages from brokers. You MUST call Close()
  42. // on a consumer to avoid leaks, it will not be garbage-collected automatically when it passes out of
  43. // scope.
  44. type Consumer interface {
  45. // Topics returns the set of available topics as retrieved from the cluster
  46. // metadata. This method is the same as Client.Topics(), and is provided for
  47. // convenience.
  48. Topics() ([]string, error)
  49. // Partitions returns the sorted list of all partition IDs for the given topic.
  50. // This method is the same as Client.Partitions(), and is provided for convenience.
  51. Partitions(topic string) ([]int32, error)
  52. // ConsumePartition creates a PartitionConsumer on the given topic/partition with
  53. // the given offset. It will return an error if this Consumer is already consuming
  54. // on the given topic/partition. Offset can be a literal offset, or OffsetNewest
  55. // or OffsetOldest
  56. ConsumePartition(topic string, partition int32, offset int64) (PartitionConsumer, error)
  57. // HighWaterMarks returns the current high water marks for each topic and partition.
  58. // Consistency between partitions is not guaranteed since high water marks are updated separately.
  59. HighWaterMarks() map[string]map[int32]int64
  60. // Close shuts down the consumer. It must be called after all child
  61. // PartitionConsumers have already been closed.
  62. Close() error
  63. }
  64. type consumer struct {
  65. conf *Config
  66. children map[string]map[int32]*partitionConsumer
  67. brokerConsumers map[*Broker]*brokerConsumer
  68. client Client
  69. lock sync.Mutex
  70. }
  71. // NewConsumer creates a new consumer using the given broker addresses and configuration.
  72. func NewConsumer(addrs []string, config *Config) (Consumer, error) {
  73. client, err := NewClient(addrs, config)
  74. if err != nil {
  75. return nil, err
  76. }
  77. return newConsumer(client)
  78. }
  79. // NewConsumerFromClient creates a new consumer using the given client. It is still
  80. // necessary to call Close() on the underlying client when shutting down this consumer.
  81. func NewConsumerFromClient(client Client) (Consumer, error) {
  82. // For clients passed in by the client, ensure we don't
  83. // call Close() on it.
  84. cli := &nopCloserClient{client}
  85. return newConsumer(cli)
  86. }
  87. func newConsumer(client Client) (Consumer, error) {
  88. // Check that we are not dealing with a closed Client before processing any other arguments
  89. if client.Closed() {
  90. return nil, ErrClosedClient
  91. }
  92. c := &consumer{
  93. client: client,
  94. conf: client.Config(),
  95. children: make(map[string]map[int32]*partitionConsumer),
  96. brokerConsumers: make(map[*Broker]*brokerConsumer),
  97. }
  98. return c, nil
  99. }
  100. func (c *consumer) Close() error {
  101. return c.client.Close()
  102. }
  103. func (c *consumer) Topics() ([]string, error) {
  104. return c.client.Topics()
  105. }
  106. func (c *consumer) Partitions(topic string) ([]int32, error) {
  107. return c.client.Partitions(topic)
  108. }
  109. func (c *consumer) ConsumePartition(topic string, partition int32, offset int64) (PartitionConsumer, error) {
  110. child := &partitionConsumer{
  111. consumer: c,
  112. conf: c.conf,
  113. topic: topic,
  114. partition: partition,
  115. messages: make(chan *ConsumerMessage, c.conf.ChannelBufferSize),
  116. errors: make(chan *ConsumerError, c.conf.ChannelBufferSize),
  117. feeder: make(chan *FetchResponse, 1),
  118. preferredReadReplica: invalidPreferredReplicaID,
  119. trigger: make(chan none, 1),
  120. dying: make(chan none),
  121. fetchSize: c.conf.Consumer.Fetch.Default,
  122. }
  123. if err := child.chooseStartingOffset(offset); err != nil {
  124. return nil, err
  125. }
  126. var leader *Broker
  127. var err error
  128. if leader, err = c.client.Leader(child.topic, child.partition); err != nil {
  129. return nil, err
  130. }
  131. if err := c.addChild(child); err != nil {
  132. return nil, err
  133. }
  134. go withRecover(child.dispatcher)
  135. go withRecover(child.responseFeeder)
  136. child.broker = c.refBrokerConsumer(leader)
  137. child.broker.input <- child
  138. return child, nil
  139. }
  140. func (c *consumer) HighWaterMarks() map[string]map[int32]int64 {
  141. c.lock.Lock()
  142. defer c.lock.Unlock()
  143. hwms := make(map[string]map[int32]int64)
  144. for topic, p := range c.children {
  145. hwm := make(map[int32]int64, len(p))
  146. for partition, pc := range p {
  147. hwm[partition] = pc.HighWaterMarkOffset()
  148. }
  149. hwms[topic] = hwm
  150. }
  151. return hwms
  152. }
  153. func (c *consumer) addChild(child *partitionConsumer) error {
  154. c.lock.Lock()
  155. defer c.lock.Unlock()
  156. topicChildren := c.children[child.topic]
  157. if topicChildren == nil {
  158. topicChildren = make(map[int32]*partitionConsumer)
  159. c.children[child.topic] = topicChildren
  160. }
  161. if topicChildren[child.partition] != nil {
  162. return ConfigurationError("That topic/partition is already being consumed")
  163. }
  164. topicChildren[child.partition] = child
  165. return nil
  166. }
  167. func (c *consumer) removeChild(child *partitionConsumer) {
  168. c.lock.Lock()
  169. defer c.lock.Unlock()
  170. delete(c.children[child.topic], child.partition)
  171. }
  172. func (c *consumer) refBrokerConsumer(broker *Broker) *brokerConsumer {
  173. c.lock.Lock()
  174. defer c.lock.Unlock()
  175. bc := c.brokerConsumers[broker]
  176. if bc == nil {
  177. bc = c.newBrokerConsumer(broker)
  178. c.brokerConsumers[broker] = bc
  179. }
  180. bc.refs++
  181. return bc
  182. }
  183. func (c *consumer) unrefBrokerConsumer(brokerWorker *brokerConsumer) {
  184. c.lock.Lock()
  185. defer c.lock.Unlock()
  186. brokerWorker.refs--
  187. if brokerWorker.refs == 0 {
  188. close(brokerWorker.input)
  189. if c.brokerConsumers[brokerWorker.broker] == brokerWorker {
  190. delete(c.brokerConsumers, brokerWorker.broker)
  191. }
  192. }
  193. }
  194. func (c *consumer) abandonBrokerConsumer(brokerWorker *brokerConsumer) {
  195. c.lock.Lock()
  196. defer c.lock.Unlock()
  197. delete(c.brokerConsumers, brokerWorker.broker)
  198. }
  199. // PartitionConsumer
  200. // PartitionConsumer processes Kafka messages from a given topic and partition. You MUST call one of Close() or
  201. // AsyncClose() on a PartitionConsumer to avoid leaks; it will not be garbage-collected automatically when it passes out
  202. // of scope.
  203. //
  204. // The simplest way of using a PartitionConsumer is to loop over its Messages channel using a for/range
  205. // loop. The PartitionConsumer will only stop itself in one case: when the offset being consumed is reported
  206. // as out of range by the brokers. In this case you should decide what you want to do (try a different offset,
  207. // notify a human, etc) and handle it appropriately. For all other error cases, it will just keep retrying.
  208. // By default, it logs these errors to sarama.Logger; if you want to be notified directly of all errors, set
  209. // your config's Consumer.Return.Errors to true and read from the Errors channel, using a select statement
  210. // or a separate goroutine. Check out the Consumer examples to see implementations of these different approaches.
  211. //
  212. // To terminate such a for/range loop while the loop is executing, call AsyncClose. This will kick off the process of
  213. // consumer tear-down & return immediately. Continue to loop, servicing the Messages channel until the teardown process
  214. // AsyncClose initiated closes it (thus terminating the for/range loop). If you've already ceased reading Messages, call
  215. // Close; this will signal the PartitionConsumer's goroutines to begin shutting down (just like AsyncClose), but will
  216. // also drain the Messages channel, harvest all errors & return them once cleanup has completed.
  217. type PartitionConsumer interface {
  218. // AsyncClose initiates a shutdown of the PartitionConsumer. This method will return immediately, after which you
  219. // should continue to service the 'Messages' and 'Errors' channels until they are empty. It is required to call this
  220. // function, or Close before a consumer object passes out of scope, as it will otherwise leak memory. You must call
  221. // this before calling Close on the underlying client.
  222. AsyncClose()
  223. // Close stops the PartitionConsumer from fetching messages. It will initiate a shutdown just like AsyncClose, drain
  224. // the Messages channel, harvest any errors & return them to the caller. Note that if you are continuing to service
  225. // the Messages channel when this function is called, you will be competing with Close for messages; consider
  226. // calling AsyncClose, instead. It is required to call this function (or AsyncClose) before a consumer object passes
  227. // out of scope, as it will otherwise leak memory. You must call this before calling Close on the underlying client.
  228. Close() error
  229. // Messages returns the read channel for the messages that are returned by
  230. // the broker.
  231. Messages() <-chan *ConsumerMessage
  232. // Errors returns a read channel of errors that occurred during consuming, if
  233. // enabled. By default, errors are logged and not returned over this channel.
  234. // If you want to implement any custom error handling, set your config's
  235. // Consumer.Return.Errors setting to true, and read from this channel.
  236. Errors() <-chan *ConsumerError
  237. // HighWaterMarkOffset returns the high water mark offset of the partition,
  238. // i.e. the offset that will be used for the next message that will be produced.
  239. // You can use this to determine how far behind the processing is.
  240. HighWaterMarkOffset() int64
  241. }
  242. type partitionConsumer struct {
  243. highWaterMarkOffset int64 // must be at the top of the struct because https://golang.org/pkg/sync/atomic/#pkg-note-BUG
  244. consumer *consumer
  245. conf *Config
  246. broker *brokerConsumer
  247. messages chan *ConsumerMessage
  248. errors chan *ConsumerError
  249. feeder chan *FetchResponse
  250. preferredReadReplica int32
  251. trigger, dying chan none
  252. closeOnce sync.Once
  253. topic string
  254. partition int32
  255. responseResult error
  256. fetchSize int32
  257. offset int64
  258. retries int32
  259. }
  260. var errTimedOut = errors.New("timed out feeding messages to the user") // not user-facing
  261. func (child *partitionConsumer) sendError(err error) {
  262. cErr := &ConsumerError{
  263. Topic: child.topic,
  264. Partition: child.partition,
  265. Err: err,
  266. }
  267. if child.conf.Consumer.Return.Errors {
  268. child.errors <- cErr
  269. } else {
  270. Logger.Println(cErr)
  271. }
  272. }
  273. func (child *partitionConsumer) computeBackoff() time.Duration {
  274. if child.conf.Consumer.Retry.BackoffFunc != nil {
  275. retries := atomic.AddInt32(&child.retries, 1)
  276. return child.conf.Consumer.Retry.BackoffFunc(int(retries))
  277. }
  278. return child.conf.Consumer.Retry.Backoff
  279. }
  280. func (child *partitionConsumer) dispatcher() {
  281. for range child.trigger {
  282. select {
  283. case <-child.dying:
  284. close(child.trigger)
  285. case <-time.After(child.computeBackoff()):
  286. if child.broker != nil {
  287. child.consumer.unrefBrokerConsumer(child.broker)
  288. child.broker = nil
  289. }
  290. Logger.Printf("consumer/%s/%d finding new broker\n", child.topic, child.partition)
  291. if err := child.dispatch(); err != nil {
  292. child.sendError(err)
  293. child.trigger <- none{}
  294. }
  295. }
  296. }
  297. if child.broker != nil {
  298. child.consumer.unrefBrokerConsumer(child.broker)
  299. }
  300. child.consumer.removeChild(child)
  301. close(child.feeder)
  302. }
  303. func (child *partitionConsumer) preferredBroker() (*Broker, error) {
  304. if child.preferredReadReplica >= 0 {
  305. broker, err := child.consumer.client.Broker(child.preferredReadReplica)
  306. if err == nil {
  307. return broker, nil
  308. }
  309. }
  310. // if preferred replica cannot be found fallback to leader
  311. return child.consumer.client.Leader(child.topic, child.partition)
  312. }
  313. func (child *partitionConsumer) dispatch() error {
  314. if err := child.consumer.client.RefreshMetadata(child.topic); err != nil {
  315. return err
  316. }
  317. broker, err := child.preferredBroker()
  318. if err != nil {
  319. return err
  320. }
  321. child.broker = child.consumer.refBrokerConsumer(broker)
  322. child.broker.input <- child
  323. return nil
  324. }
  325. func (child *partitionConsumer) chooseStartingOffset(offset int64) error {
  326. newestOffset, err := child.consumer.client.GetOffset(child.topic, child.partition, OffsetNewest)
  327. if err != nil {
  328. return err
  329. }
  330. oldestOffset, err := child.consumer.client.GetOffset(child.topic, child.partition, OffsetOldest)
  331. if err != nil {
  332. return err
  333. }
  334. switch {
  335. case offset == OffsetNewest:
  336. child.offset = newestOffset
  337. case offset == OffsetOldest:
  338. child.offset = oldestOffset
  339. case offset >= oldestOffset && offset <= newestOffset:
  340. child.offset = offset
  341. default:
  342. return ErrOffsetOutOfRange
  343. }
  344. return nil
  345. }
  346. func (child *partitionConsumer) Messages() <-chan *ConsumerMessage {
  347. return child.messages
  348. }
  349. func (child *partitionConsumer) Errors() <-chan *ConsumerError {
  350. return child.errors
  351. }
  352. func (child *partitionConsumer) AsyncClose() {
  353. // this triggers whatever broker owns this child to abandon it and close its trigger channel, which causes
  354. // the dispatcher to exit its loop, which removes it from the consumer then closes its 'messages' and
  355. // 'errors' channel (alternatively, if the child is already at the dispatcher for some reason, that will
  356. // also just close itself)
  357. child.closeOnce.Do(func() {
  358. close(child.dying)
  359. })
  360. }
  361. func (child *partitionConsumer) Close() error {
  362. child.AsyncClose()
  363. var consumerErrors ConsumerErrors
  364. for err := range child.errors {
  365. consumerErrors = append(consumerErrors, err)
  366. }
  367. if len(consumerErrors) > 0 {
  368. return consumerErrors
  369. }
  370. return nil
  371. }
  372. func (child *partitionConsumer) HighWaterMarkOffset() int64 {
  373. return atomic.LoadInt64(&child.highWaterMarkOffset)
  374. }
  375. func (child *partitionConsumer) responseFeeder() {
  376. var msgs []*ConsumerMessage
  377. expiryTicker := time.NewTicker(child.conf.Consumer.MaxProcessingTime)
  378. firstAttempt := true
  379. feederLoop:
  380. for response := range child.feeder {
  381. msgs, child.responseResult = child.parseResponse(response)
  382. if child.responseResult == nil {
  383. atomic.StoreInt32(&child.retries, 0)
  384. }
  385. for i, msg := range msgs {
  386. child.interceptors(msg)
  387. messageSelect:
  388. select {
  389. case <-child.dying:
  390. child.broker.acks.Done()
  391. continue feederLoop
  392. case child.messages <- msg:
  393. firstAttempt = true
  394. case <-expiryTicker.C:
  395. if !firstAttempt {
  396. child.responseResult = errTimedOut
  397. child.broker.acks.Done()
  398. remainingLoop:
  399. for _, msg = range msgs[i:] {
  400. child.interceptors(msg)
  401. select {
  402. case child.messages <- msg:
  403. case <-child.dying:
  404. break remainingLoop
  405. }
  406. }
  407. child.broker.input <- child
  408. continue feederLoop
  409. } else {
  410. // current message has not been sent, return to select
  411. // statement
  412. firstAttempt = false
  413. goto messageSelect
  414. }
  415. }
  416. }
  417. child.broker.acks.Done()
  418. }
  419. expiryTicker.Stop()
  420. close(child.messages)
  421. close(child.errors)
  422. }
  423. func (child *partitionConsumer) parseMessages(msgSet *MessageSet) ([]*ConsumerMessage, error) {
  424. var messages []*ConsumerMessage
  425. for _, msgBlock := range msgSet.Messages {
  426. for _, msg := range msgBlock.Messages() {
  427. offset := msg.Offset
  428. timestamp := msg.Msg.Timestamp
  429. if msg.Msg.Version >= 1 {
  430. baseOffset := msgBlock.Offset - msgBlock.Messages()[len(msgBlock.Messages())-1].Offset
  431. offset += baseOffset
  432. if msg.Msg.LogAppendTime {
  433. timestamp = msgBlock.Msg.Timestamp
  434. }
  435. }
  436. if offset < child.offset {
  437. continue
  438. }
  439. messages = append(messages, &ConsumerMessage{
  440. Topic: child.topic,
  441. Partition: child.partition,
  442. Key: msg.Msg.Key,
  443. Value: msg.Msg.Value,
  444. Offset: offset,
  445. Timestamp: timestamp,
  446. BlockTimestamp: msgBlock.Msg.Timestamp,
  447. })
  448. child.offset = offset + 1
  449. }
  450. }
  451. if len(messages) == 0 {
  452. child.offset++
  453. }
  454. return messages, nil
  455. }
  456. func (child *partitionConsumer) parseRecords(batch *RecordBatch) ([]*ConsumerMessage, error) {
  457. messages := make([]*ConsumerMessage, 0, len(batch.Records))
  458. for _, rec := range batch.Records {
  459. offset := batch.FirstOffset + rec.OffsetDelta
  460. if offset < child.offset {
  461. continue
  462. }
  463. timestamp := batch.FirstTimestamp.Add(rec.TimestampDelta)
  464. if batch.LogAppendTime {
  465. timestamp = batch.MaxTimestamp
  466. }
  467. messages = append(messages, &ConsumerMessage{
  468. Topic: child.topic,
  469. Partition: child.partition,
  470. Key: rec.Key,
  471. Value: rec.Value,
  472. Offset: offset,
  473. Timestamp: timestamp,
  474. Headers: rec.Headers,
  475. })
  476. child.offset = offset + 1
  477. }
  478. if len(messages) == 0 {
  479. child.offset++
  480. }
  481. return messages, nil
  482. }
  483. func (child *partitionConsumer) parseResponse(response *FetchResponse) ([]*ConsumerMessage, error) {
  484. var (
  485. metricRegistry = child.conf.MetricRegistry
  486. consumerBatchSizeMetric metrics.Histogram
  487. )
  488. if metricRegistry != nil {
  489. consumerBatchSizeMetric = getOrRegisterHistogram("consumer-batch-size", metricRegistry)
  490. }
  491. // If request was throttled and empty we log and return without error
  492. if response.ThrottleTime != time.Duration(0) && len(response.Blocks) == 0 {
  493. Logger.Printf(
  494. "consumer/broker/%d FetchResponse throttled %v\n",
  495. child.broker.broker.ID(), response.ThrottleTime)
  496. return nil, nil
  497. }
  498. block := response.GetBlock(child.topic, child.partition)
  499. if block == nil {
  500. return nil, ErrIncompleteResponse
  501. }
  502. if block.Err != ErrNoError {
  503. return nil, block.Err
  504. }
  505. nRecs, err := block.numRecords()
  506. if err != nil {
  507. return nil, err
  508. }
  509. consumerBatchSizeMetric.Update(int64(nRecs))
  510. if block.PreferredReadReplica != invalidPreferredReplicaID {
  511. child.preferredReadReplica = block.PreferredReadReplica
  512. }
  513. if nRecs == 0 {
  514. partialTrailingMessage, err := block.isPartial()
  515. if err != nil {
  516. return nil, err
  517. }
  518. // We got no messages. If we got a trailing one then we need to ask for more data.
  519. // Otherwise we just poll again and wait for one to be produced...
  520. if partialTrailingMessage {
  521. if child.conf.Consumer.Fetch.Max > 0 && child.fetchSize == child.conf.Consumer.Fetch.Max {
  522. // we can't ask for more data, we've hit the configured limit
  523. child.sendError(ErrMessageTooLarge)
  524. child.offset++ // skip this one so we can keep processing future messages
  525. } else {
  526. child.fetchSize *= 2
  527. // check int32 overflow
  528. if child.fetchSize < 0 {
  529. child.fetchSize = math.MaxInt32
  530. }
  531. if child.conf.Consumer.Fetch.Max > 0 && child.fetchSize > child.conf.Consumer.Fetch.Max {
  532. child.fetchSize = child.conf.Consumer.Fetch.Max
  533. }
  534. }
  535. } else if block.LastRecordsBatchOffset != nil && *block.LastRecordsBatchOffset < block.HighWaterMarkOffset {
  536. // check last record offset to avoid stuck if high watermark was not reached
  537. Logger.Printf("consumer/broker/%d received batch with zero records but high watermark was not reached, topic %s, partition %d, offset %d\n", child.broker.broker.ID(), child.topic, child.partition, *block.LastRecordsBatchOffset)
  538. child.offset = *block.LastRecordsBatchOffset + 1
  539. }
  540. return nil, nil
  541. }
  542. // we got messages, reset our fetch size in case it was increased for a previous request
  543. child.fetchSize = child.conf.Consumer.Fetch.Default
  544. atomic.StoreInt64(&child.highWaterMarkOffset, block.HighWaterMarkOffset)
  545. // abortedProducerIDs contains producerID which message should be ignored as uncommitted
  546. // - producerID are added when the partitionConsumer iterate over the offset at which an aborted transaction begins (abortedTransaction.FirstOffset)
  547. // - producerID are removed when partitionConsumer iterate over an aborted controlRecord, meaning the aborted transaction for this producer is over
  548. abortedProducerIDs := make(map[int64]struct{}, len(block.AbortedTransactions))
  549. abortedTransactions := block.getAbortedTransactions()
  550. var messages []*ConsumerMessage
  551. for _, records := range block.RecordsSet {
  552. switch records.recordsType {
  553. case legacyRecords:
  554. messageSetMessages, err := child.parseMessages(records.MsgSet)
  555. if err != nil {
  556. return nil, err
  557. }
  558. messages = append(messages, messageSetMessages...)
  559. case defaultRecords:
  560. // Consume remaining abortedTransaction up to last offset of current batch
  561. for _, txn := range abortedTransactions {
  562. if txn.FirstOffset > records.RecordBatch.LastOffset() {
  563. break
  564. }
  565. abortedProducerIDs[txn.ProducerID] = struct{}{}
  566. // Pop abortedTransactions so that we never add it again
  567. abortedTransactions = abortedTransactions[1:]
  568. }
  569. recordBatchMessages, err := child.parseRecords(records.RecordBatch)
  570. if err != nil {
  571. return nil, err
  572. }
  573. // Parse and commit offset but do not expose messages that are:
  574. // - control records
  575. // - part of an aborted transaction when set to `ReadCommitted`
  576. // control record
  577. isControl, err := records.isControl()
  578. if err != nil {
  579. // I don't know why there is this continue in case of error to begin with
  580. // Safe bet is to ignore control messages if ReadUncommitted
  581. // and block on them in case of error and ReadCommitted
  582. if child.conf.Consumer.IsolationLevel == ReadCommitted {
  583. return nil, err
  584. }
  585. continue
  586. }
  587. if isControl {
  588. controlRecord, err := records.getControlRecord()
  589. if err != nil {
  590. return nil, err
  591. }
  592. if controlRecord.Type == ControlRecordAbort {
  593. delete(abortedProducerIDs, records.RecordBatch.ProducerID)
  594. }
  595. continue
  596. }
  597. // filter aborted transactions
  598. if child.conf.Consumer.IsolationLevel == ReadCommitted {
  599. _, isAborted := abortedProducerIDs[records.RecordBatch.ProducerID]
  600. if records.RecordBatch.IsTransactional && isAborted {
  601. continue
  602. }
  603. }
  604. messages = append(messages, recordBatchMessages...)
  605. default:
  606. return nil, fmt.Errorf("unknown records type: %v", records.recordsType)
  607. }
  608. }
  609. return messages, nil
  610. }
  611. func (child *partitionConsumer) interceptors(msg *ConsumerMessage) {
  612. for _, interceptor := range child.conf.Consumer.Interceptors {
  613. msg.safelyApplyInterceptor(interceptor)
  614. }
  615. }
  616. type brokerConsumer struct {
  617. consumer *consumer
  618. broker *Broker
  619. input chan *partitionConsumer
  620. newSubscriptions chan []*partitionConsumer
  621. subscriptions map[*partitionConsumer]none
  622. wait chan none
  623. acks sync.WaitGroup
  624. refs int
  625. }
  626. func (c *consumer) newBrokerConsumer(broker *Broker) *brokerConsumer {
  627. bc := &brokerConsumer{
  628. consumer: c,
  629. broker: broker,
  630. input: make(chan *partitionConsumer),
  631. newSubscriptions: make(chan []*partitionConsumer),
  632. wait: make(chan none),
  633. subscriptions: make(map[*partitionConsumer]none),
  634. refs: 0,
  635. }
  636. go withRecover(bc.subscriptionManager)
  637. go withRecover(bc.subscriptionConsumer)
  638. return bc
  639. }
  640. // The subscriptionManager constantly accepts new subscriptions on `input` (even when the main subscriptionConsumer
  641. // goroutine is in the middle of a network request) and batches it up. The main worker goroutine picks
  642. // up a batch of new subscriptions between every network request by reading from `newSubscriptions`, so we give
  643. // it nil if no new subscriptions are available. We also write to `wait` only when new subscriptions is available,
  644. // so the main goroutine can block waiting for work if it has none.
  645. func (bc *brokerConsumer) subscriptionManager() {
  646. var buffer []*partitionConsumer
  647. for {
  648. if len(buffer) > 0 {
  649. select {
  650. case event, ok := <-bc.input:
  651. if !ok {
  652. goto done
  653. }
  654. buffer = append(buffer, event)
  655. case bc.newSubscriptions <- buffer:
  656. buffer = nil
  657. case bc.wait <- none{}:
  658. }
  659. } else {
  660. select {
  661. case event, ok := <-bc.input:
  662. if !ok {
  663. goto done
  664. }
  665. buffer = append(buffer, event)
  666. case bc.newSubscriptions <- nil:
  667. }
  668. }
  669. }
  670. done:
  671. close(bc.wait)
  672. if len(buffer) > 0 {
  673. bc.newSubscriptions <- buffer
  674. }
  675. close(bc.newSubscriptions)
  676. }
  677. // subscriptionConsumer ensures we will get nil right away if no new subscriptions is available
  678. func (bc *brokerConsumer) subscriptionConsumer() {
  679. <-bc.wait // wait for our first piece of work
  680. for newSubscriptions := range bc.newSubscriptions {
  681. bc.updateSubscriptions(newSubscriptions)
  682. if len(bc.subscriptions) == 0 {
  683. // We're about to be shut down or we're about to receive more subscriptions.
  684. // Either way, the signal just hasn't propagated to our goroutine yet.
  685. <-bc.wait
  686. continue
  687. }
  688. response, err := bc.fetchNewMessages()
  689. if err != nil {
  690. Logger.Printf("consumer/broker/%d disconnecting due to error processing FetchRequest: %s\n", bc.broker.ID(), err)
  691. bc.abort(err)
  692. return
  693. }
  694. bc.acks.Add(len(bc.subscriptions))
  695. for child := range bc.subscriptions {
  696. child.feeder <- response
  697. }
  698. bc.acks.Wait()
  699. bc.handleResponses()
  700. }
  701. }
  702. func (bc *brokerConsumer) updateSubscriptions(newSubscriptions []*partitionConsumer) {
  703. for _, child := range newSubscriptions {
  704. bc.subscriptions[child] = none{}
  705. Logger.Printf("consumer/broker/%d added subscription to %s/%d\n", bc.broker.ID(), child.topic, child.partition)
  706. }
  707. for child := range bc.subscriptions {
  708. select {
  709. case <-child.dying:
  710. Logger.Printf("consumer/broker/%d closed dead subscription to %s/%d\n", bc.broker.ID(), child.topic, child.partition)
  711. close(child.trigger)
  712. delete(bc.subscriptions, child)
  713. default:
  714. // no-op
  715. }
  716. }
  717. }
  718. // handleResponses handles the response codes left for us by our subscriptions, and abandons ones that have been closed
  719. func (bc *brokerConsumer) handleResponses() {
  720. for child := range bc.subscriptions {
  721. result := child.responseResult
  722. child.responseResult = nil
  723. if result == nil {
  724. if preferredBroker, err := child.preferredBroker(); err == nil {
  725. if bc.broker.ID() != preferredBroker.ID() {
  726. // not an error but needs redispatching to consume from preferred replica
  727. child.trigger <- none{}
  728. delete(bc.subscriptions, child)
  729. }
  730. }
  731. continue
  732. }
  733. // Discard any replica preference.
  734. child.preferredReadReplica = -1
  735. switch result {
  736. case errTimedOut:
  737. Logger.Printf("consumer/broker/%d abandoned subscription to %s/%d because consuming was taking too long\n",
  738. bc.broker.ID(), child.topic, child.partition)
  739. delete(bc.subscriptions, child)
  740. case ErrOffsetOutOfRange:
  741. // there's no point in retrying this it will just fail the same way again
  742. // shut it down and force the user to choose what to do
  743. child.sendError(result)
  744. Logger.Printf("consumer/%s/%d shutting down because %s\n", child.topic, child.partition, result)
  745. close(child.trigger)
  746. delete(bc.subscriptions, child)
  747. case ErrUnknownTopicOrPartition, ErrNotLeaderForPartition, ErrLeaderNotAvailable, ErrReplicaNotAvailable:
  748. // not an error, but does need redispatching
  749. Logger.Printf("consumer/broker/%d abandoned subscription to %s/%d because %s\n",
  750. bc.broker.ID(), child.topic, child.partition, result)
  751. child.trigger <- none{}
  752. delete(bc.subscriptions, child)
  753. default:
  754. // dunno, tell the user and try redispatching
  755. child.sendError(result)
  756. Logger.Printf("consumer/broker/%d abandoned subscription to %s/%d because %s\n",
  757. bc.broker.ID(), child.topic, child.partition, result)
  758. child.trigger <- none{}
  759. delete(bc.subscriptions, child)
  760. }
  761. }
  762. }
  763. func (bc *brokerConsumer) abort(err error) {
  764. bc.consumer.abandonBrokerConsumer(bc)
  765. _ = bc.broker.Close() // we don't care about the error this might return, we already have one
  766. for child := range bc.subscriptions {
  767. child.sendError(err)
  768. child.trigger <- none{}
  769. }
  770. for newSubscriptions := range bc.newSubscriptions {
  771. if len(newSubscriptions) == 0 {
  772. <-bc.wait
  773. continue
  774. }
  775. for _, child := range newSubscriptions {
  776. child.sendError(err)
  777. child.trigger <- none{}
  778. }
  779. }
  780. }
  781. func (bc *brokerConsumer) fetchNewMessages() (*FetchResponse, error) {
  782. request := &FetchRequest{
  783. MinBytes: bc.consumer.conf.Consumer.Fetch.Min,
  784. MaxWaitTime: int32(bc.consumer.conf.Consumer.MaxWaitTime / time.Millisecond),
  785. }
  786. if bc.consumer.conf.Version.IsAtLeast(V0_9_0_0) {
  787. request.Version = 1
  788. }
  789. if bc.consumer.conf.Version.IsAtLeast(V0_10_0_0) {
  790. request.Version = 2
  791. }
  792. if bc.consumer.conf.Version.IsAtLeast(V0_10_1_0) {
  793. request.Version = 3
  794. request.MaxBytes = MaxResponseSize
  795. }
  796. if bc.consumer.conf.Version.IsAtLeast(V0_11_0_0) {
  797. request.Version = 4
  798. request.Isolation = bc.consumer.conf.Consumer.IsolationLevel
  799. }
  800. if bc.consumer.conf.Version.IsAtLeast(V1_1_0_0) {
  801. request.Version = 7
  802. // We do not currently implement KIP-227 FetchSessions. Setting the id to 0
  803. // and the epoch to -1 tells the broker not to generate as session ID we're going
  804. // to just ignore anyway.
  805. request.SessionID = 0
  806. request.SessionEpoch = -1
  807. }
  808. if bc.consumer.conf.Version.IsAtLeast(V2_1_0_0) {
  809. request.Version = 10
  810. }
  811. if bc.consumer.conf.Version.IsAtLeast(V2_3_0_0) {
  812. request.Version = 11
  813. request.RackID = bc.consumer.conf.RackID
  814. }
  815. for child := range bc.subscriptions {
  816. request.AddBlock(child.topic, child.partition, child.offset, child.fetchSize)
  817. }
  818. return bc.broker.Fetch(request)
  819. }