@@ -482,9 +482,10 @@ class CPPKAFKA_API BufferedProducer {
482
482
#endif
483
483
484
484
private:
485
- enum class MessagePriority { Low, High };
486
485
enum class SenderType { Sync, Async };
487
-
486
+ enum class QueueKind { Retry, Regular };
487
+ enum class FlushAction { DontFlush, DoFlush };
488
+
488
489
template <typename T>
489
490
struct CounterGuard {
490
491
CounterGuard (std::atomic<T>& counter) : counter_(counter) { ++counter_; }
@@ -519,18 +520,21 @@ class CPPKAFKA_API BufferedProducer {
519
520
return nullptr ;
520
521
}
521
522
template <typename BuilderType>
522
- void do_add_message (BuilderType&& builder, MessagePriority priority, bool do_flush );
523
+ void do_add_message (BuilderType&& builder, QueueKind queue_kind, FlushAction flush_action );
523
524
template <typename BuilderType>
524
525
void produce_message (BuilderType&& builder);
525
526
Configuration prepare_configuration (Configuration config);
526
527
void on_delivery_report (const Message& message);
527
528
template <typename BuilderType>
528
529
void async_produce (BuilderType&& message, bool throw_on_error);
529
-
530
+ static void swap_queues (QueueType & queue1, QueueType & queue2, std::mutex & mutex);
531
+
530
532
// Members
531
533
Producer producer_;
532
534
QueueType messages_;
535
+ QueueType retry_messages_;
533
536
mutable std::mutex mutex_;
537
+ mutable std::mutex retry_mutex_;
534
538
ProduceSuccessCallback produce_success_callback_;
535
539
ProduceFailureCallback produce_failure_callback_;
536
540
ProduceTerminationCallback produce_termination_callback_;
@@ -565,7 +569,8 @@ template <typename BufferType, typename Allocator>
565
569
BufferedProducer<BufferType, Allocator>::BufferedProducer(Configuration config,
566
570
const Allocator& alloc)
567
571
: producer_(prepare_configuration(std::move(config))),
568
- messages_ (alloc) {
572
+ messages_ (alloc),
573
+ retry_messages_(alloc) {
569
574
producer_.set_payload_policy (get_default_payload_policy<BufferType>());
570
575
#ifdef KAFKA_TEST_INSTANCE
571
576
test_params_ = nullptr ;
@@ -580,7 +585,7 @@ void BufferedProducer<BufferType, Allocator>::add_message(const MessageBuilder&
580
585
template <typename BufferType, typename Allocator>
581
586
void BufferedProducer<BufferType, Allocator>::add_message(Builder builder) {
582
587
add_tracker (SenderType::Async, builder);
583
- do_add_message (move (builder), MessagePriority::Low, true );
588
+ do_add_message (move (builder), QueueKind::Regular, FlushAction::DoFlush );
584
589
}
585
590
586
591
template <typename BufferType, typename Allocator>
@@ -624,30 +629,36 @@ void BufferedProducer<BufferType, Allocator>::produce(const Message& message) {
624
629
template <typename BufferType, typename Allocator>
625
630
void BufferedProducer<BufferType, Allocator>::async_flush() {
626
631
CounterGuard<size_t > counter_guard (flushes_in_progress_);
627
- QueueType flush_queue; // flush from temporary queue
632
+ auto queue_flusher = [ this ](QueueType& queue, std::mutex & mutex)-> void
628
633
{
629
- std::lock_guard<std::mutex> lock (mutex_);
630
- std::swap (messages_, flush_queue);
631
- }
632
- while (!flush_queue.empty ()) {
633
- async_produce (std::move (flush_queue.front ()), false );
634
- flush_queue.pop_front ();
635
- }
634
+ QueueType flush_queue; // flush from temporary queue
635
+ swap_queues (queue, flush_queue, mutex);
636
+
637
+ while (!flush_queue.empty ()) {
638
+ async_produce (std::move (flush_queue.front ()), false );
639
+ flush_queue.pop_front ();
640
+ }
641
+ };
642
+ queue_flusher (retry_messages_, retry_mutex_);
643
+ queue_flusher (messages_, mutex_);
636
644
}
637
645
638
646
template <typename BufferType, typename Allocator>
639
647
void BufferedProducer<BufferType, Allocator>::flush(bool preserve_order) {
640
648
if (preserve_order) {
641
649
CounterGuard<size_t > counter_guard (flushes_in_progress_);
642
- QueueType flush_queue; // flush from temporary queue
650
+ auto queue_flusher = [ this ](QueueType& queue, std::mutex & mutex)-> void
643
651
{
644
- std::lock_guard<std::mutex> lock (mutex_);
645
- std::swap (messages_, flush_queue);
646
- }
647
- while (!flush_queue.empty ()) {
648
- sync_produce (flush_queue.front ());
649
- flush_queue.pop_front ();
650
- }
652
+ QueueType flush_queue; // flush from temporary queue
653
+ swap_queues (queue, flush_queue, mutex);
654
+
655
+ while (!flush_queue.empty ()) {
656
+ sync_produce (flush_queue.front ());
657
+ flush_queue.pop_front ();
658
+ }
659
+ };
660
+ queue_flusher (retry_messages_, retry_mutex_);
661
+ queue_flusher (messages_, mutex_);
651
662
}
652
663
else {
653
664
async_flush ();
@@ -661,25 +672,42 @@ bool BufferedProducer<BufferType, Allocator>::flush(std::chrono::milliseconds ti
661
672
if (preserve_order) {
662
673
CounterGuard<size_t > counter_guard (flushes_in_progress_);
663
674
QueueType flush_queue; // flush from temporary queue
675
+ swap_queues (messages_, flush_queue, mutex_);
676
+ QueueType retry_flush_queue; // flush from temporary retry queue
677
+ swap_queues (retry_messages_, retry_flush_queue, retry_mutex_);
678
+
679
+ auto queue_flusher = [this ](QueueType& queue)->bool
664
680
{
665
- std::lock_guard<std::mutex> lock (mutex_);
666
- std::swap (messages_, flush_queue);
667
- }
681
+ if (!queue.empty ()) {
682
+ sync_produce (queue.front ());
683
+ queue.pop_front ();
684
+ return true ;
685
+ }
686
+ return false ;
687
+ };
668
688
auto remaining = timeout;
669
689
auto start_time = std::chrono::high_resolution_clock::now ();
670
690
do {
671
- sync_produce (flush_queue.front ());
672
- flush_queue.pop_front ();
691
+ if (!queue_flusher (retry_flush_queue) && !queue_flusher (flush_queue)) {
692
+ break ;
693
+ }
673
694
// calculate remaining time
674
695
remaining = timeout - std::chrono::duration_cast<std::chrono::milliseconds>
675
696
(std::chrono::high_resolution_clock::now () - start_time);
676
- } while (!flush_queue. empty () && ( remaining.count () > 0 ) );
697
+ } while (remaining.count () > 0 );
677
698
678
699
// Re-enqueue remaining messages in original order
679
- if (!flush_queue.empty ()) {
680
- std::lock_guard<std::mutex> lock (mutex_);
681
- messages_.insert (messages_.begin (), std::make_move_iterator (flush_queue.begin ()), std::make_move_iterator (flush_queue.end ()));
682
- }
700
+ auto re_enqueuer = [this ](QueueType& src_queue, QueueType& dst_queue, std::mutex & mutex)->void
701
+ {
702
+ if (!src_queue.empty ()) {
703
+ std::lock_guard<std::mutex> lock (mutex);
704
+ dst_queue.insert (dst_queue.begin (),
705
+ std::make_move_iterator (src_queue.begin ()),
706
+ std::make_move_iterator (src_queue.end ()));
707
+ }
708
+ };
709
+ re_enqueuer (retry_flush_queue, retry_messages_, retry_mutex_);
710
+ re_enqueuer (flush_queue, messages_, mutex_);
683
711
}
684
712
else {
685
713
async_flush ();
@@ -732,14 +760,15 @@ bool BufferedProducer<BufferType, Allocator>::wait_for_acks(std::chrono::millise
732
760
733
761
template <typename BufferType, typename Allocator>
734
762
void BufferedProducer<BufferType, Allocator>::clear() {
735
- std::lock_guard<std::mutex> lock (mutex_);
736
763
QueueType tmp;
737
- std::swap (tmp, messages_);
764
+ swap_queues (messages_, tmp, mutex_);
765
+ QueueType retry_tmp;
766
+ swap_queues (retry_messages_, retry_tmp, retry_mutex_);
738
767
}
739
768
740
769
template <typename BufferType, typename Allocator>
741
770
size_t BufferedProducer<BufferType, Allocator>::get_buffer_size() const {
742
- return messages_.size ();
771
+ return messages_.size () + retry_messages_. size () ;
743
772
}
744
773
745
774
template <typename BufferType, typename Allocator>
@@ -769,18 +798,20 @@ BufferedProducer<BufferType, Allocator>::get_flush_method() const {
769
798
template <typename BufferType, typename Allocator>
770
799
template <typename BuilderType>
771
800
void BufferedProducer<BufferType, Allocator>::do_add_message(BuilderType&& builder,
772
- MessagePriority priority,
773
- bool do_flush) {
774
- {
801
+ QueueKind queue_kind,
802
+ FlushAction flush_action) {
803
+ if (queue_kind == QueueKind::Retry) {
804
+ std::lock_guard<std::mutex> lock (retry_mutex_);
805
+ retry_messages_.emplace_back (std::forward<BuilderType>(builder));
806
+ }
807
+ else {
775
808
std::lock_guard<std::mutex> lock (mutex_);
776
- if (priority == MessagePriority::High) {
777
- messages_.emplace_front (std::forward<BuilderType>(builder));
778
- }
779
- else {
780
- messages_.emplace_back (std::forward<BuilderType>(builder));
781
- }
809
+ messages_.emplace_back (std::forward<BuilderType>(builder));
782
810
}
783
- if (do_flush && (max_buffer_size_ >= 0 ) && (max_buffer_size_ <= (ssize_t )messages_.size ())) {
811
+
812
+ // Flush the queues only if a regular message is added. Retry messages may be added
813
+ // from rdkafka callbacks, and flush/async_flush is a user-level call
814
+ if (queue_kind == QueueKind::Regular && flush_action == FlushAction::DoFlush && (max_buffer_size_ >= 0 ) && (max_buffer_size_ <= get_buffer_size ())) {
784
815
if (flush_method_ == FlushMethod::Sync) {
785
816
flush ();
786
817
}
@@ -928,7 +959,7 @@ void BufferedProducer<BufferType, Allocator>::async_produce(BuilderType&& builde
928
959
TrackerPtr tracker = std::static_pointer_cast<Tracker>(builder.internal ());
929
960
if (tracker && tracker->num_retries_ > 0 ) {
930
961
--tracker->num_retries_ ;
931
- do_add_message (std::forward<BuilderType>(builder), MessagePriority::High, false );
962
+ do_add_message (std::forward<BuilderType>(builder), QueueKind::Retry, FlushAction::DontFlush );
932
963
return ;
933
964
}
934
965
}
@@ -967,7 +998,7 @@ void BufferedProducer<BufferType, Allocator>::on_delivery_report(const Message&
967
998
--tracker->num_retries_ ;
968
999
if (tracker->sender_ == SenderType::Async) {
969
1000
// Re-enqueue for later retransmission with higher priority (i.e. front of the queue)
970
- do_add_message (Builder (message), MessagePriority::High, false );
1001
+ do_add_message (Builder (message), QueueKind::Retry, FlushAction::DontFlush );
971
1002
}
972
1003
should_retry = true ;
973
1004
}
@@ -999,6 +1030,13 @@ void BufferedProducer<BufferType, Allocator>::on_delivery_report(const Message&
999
1030
}
1000
1031
}
1001
1032
1033
+ template <typename BufferType, typename Allocator>
1034
+ void BufferedProducer<BufferType, Allocator>::swap_queues(BufferedProducer<BufferType, Allocator>::QueueType & queue1, BufferedProducer<BufferType, Allocator>::QueueType & queue2, std::mutex & mutex)
1035
+ {
1036
+ std::lock_guard<std::mutex> lock (mutex);
1037
+ std::swap (queue1, queue2);
1038
+ }
1039
+
1002
1040
} // cppkafka
1003
1041
1004
1042
#endif // CPPKAFKA_BUFFERED_PRODUCER_H
0 commit comments