mirror of
https://github.com/Telecominfraproject/wlan-cloud-lib-cppkafka.git
synced 2025-11-01 02:57:53 +00:00
Bug fixes for sync flush and add_tracker (#91)
* fixes for sync flush and also add_tracker * added flag for flush
This commit is contained in:
committed by
Matias Fontanini
parent
b8f4be5e1b
commit
eb46b8808e
@@ -185,20 +185,28 @@ public:
|
|||||||
* This will send all messages and keep waiting until all of them are acknowledged (this is
|
* This will send all messages and keep waiting until all of them are acknowledged (this is
|
||||||
* done by calling wait_for_acks).
|
* done by calling wait_for_acks).
|
||||||
*
|
*
|
||||||
|
* \param preserve_order If set to True, each message in the queue will be flushed only when the previous
|
||||||
|
* message ack is received. This may result in performance degradation as messages
|
||||||
|
* are sent one at a time. This calls sync_produce() on each message in the buffer.
|
||||||
|
* If set to False, all messages are flushed in one batch before waiting for acks,
|
||||||
|
* however message reordering may occur if librdkafka setting 'messages.sent.max.retries > 0'.
|
||||||
|
*
|
||||||
* \remark Although it is possible to call flush from multiple threads concurrently, better
|
* \remark Although it is possible to call flush from multiple threads concurrently, better
|
||||||
* performance is achieved when called from the same thread or when serialized
|
* performance is achieved when called from the same thread or when serialized
|
||||||
* with respect to other threads.
|
* with respect to other threads.
|
||||||
*/
|
*/
|
||||||
void flush();
|
void flush(bool preserve_order = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Flushes the buffered messages and waits up to 'timeout'
|
* \brief Flushes the buffered messages and waits up to 'timeout'
|
||||||
*
|
*
|
||||||
* \param timeout The maximum time to wait until all acks are received
|
* \param timeout The maximum time to wait until all acks are received
|
||||||
*
|
*
|
||||||
|
* \param preserve_order True to preserve message ordering, False otherwise. See flush above for more details.
|
||||||
|
*
|
||||||
* \return True if the operation completes and all acks have been received.
|
* \return True if the operation completes and all acks have been received.
|
||||||
*/
|
*/
|
||||||
bool flush(std::chrono::milliseconds timeout);
|
bool flush(std::chrono::milliseconds timeout, bool preserve_order = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits for produced message's acknowledgements from the brokers
|
* Waits for produced message's acknowledgements from the brokers
|
||||||
@@ -404,13 +412,15 @@ private:
|
|||||||
};
|
};
|
||||||
using TrackerPtr = std::shared_ptr<Tracker>;
|
using TrackerPtr = std::shared_ptr<Tracker>;
|
||||||
|
|
||||||
|
// Returns existing tracker or creates new one
|
||||||
template <typename BuilderType>
|
template <typename BuilderType>
|
||||||
TrackerPtr add_tracker(BuilderType& builder) {
|
TrackerPtr add_tracker(SenderType sender, BuilderType& builder) {
|
||||||
if (has_internal_data_ && !builder.internal()) {
|
if (has_internal_data_) {
|
||||||
|
if (!builder.internal()) {
|
||||||
// Add message tracker only if it hasn't been added before
|
// Add message tracker only if it hasn't been added before
|
||||||
TrackerPtr tracker = std::make_shared<Tracker>(SenderType::Async, max_number_retries_);
|
builder.internal(std::make_shared<Tracker>(sender, max_number_retries_));
|
||||||
builder.internal(tracker);
|
}
|
||||||
return tracker;
|
return std::static_pointer_cast<Tracker>(builder.internal());
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -469,7 +479,7 @@ void BufferedProducer<BufferType>::add_message(const MessageBuilder& builder) {
|
|||||||
|
|
||||||
template <typename BufferType>
|
template <typename BufferType>
|
||||||
void BufferedProducer<BufferType>::add_message(Builder builder) {
|
void BufferedProducer<BufferType>::add_message(Builder builder) {
|
||||||
add_tracker(builder);
|
add_tracker(SenderType::Async, builder);
|
||||||
do_add_message(move(builder), MessagePriority::Low, true);
|
do_add_message(move(builder), MessagePriority::Low, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,7 +487,7 @@ template <typename BufferType>
|
|||||||
void BufferedProducer<BufferType>::produce(const MessageBuilder& builder) {
|
void BufferedProducer<BufferType>::produce(const MessageBuilder& builder) {
|
||||||
if (has_internal_data_) {
|
if (has_internal_data_) {
|
||||||
MessageBuilder builder_clone(builder.clone());
|
MessageBuilder builder_clone(builder.clone());
|
||||||
add_tracker(builder_clone);
|
add_tracker(SenderType::Async, builder_clone);
|
||||||
async_produce(builder_clone, true);
|
async_produce(builder_clone, true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -489,7 +499,7 @@ template <typename BufferType>
|
|||||||
void BufferedProducer<BufferType>::sync_produce(const MessageBuilder& builder) {
|
void BufferedProducer<BufferType>::sync_produce(const MessageBuilder& builder) {
|
||||||
if (has_internal_data_) {
|
if (has_internal_data_) {
|
||||||
MessageBuilder builder_clone(builder.clone());
|
MessageBuilder builder_clone(builder.clone());
|
||||||
TrackerPtr tracker = add_tracker(builder_clone);
|
TrackerPtr tracker = add_tracker(SenderType::Sync, builder_clone);
|
||||||
// produce until we succeed or we reach max retry limit
|
// produce until we succeed or we reach max retry limit
|
||||||
std::future<bool> should_retry;
|
std::future<bool> should_retry;
|
||||||
do {
|
do {
|
||||||
@@ -526,15 +536,47 @@ void BufferedProducer<BufferType>::async_flush() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename BufferType>
|
template <typename BufferType>
|
||||||
void BufferedProducer<BufferType>::flush() {
|
void BufferedProducer<BufferType>::flush(bool preserve_order) {
|
||||||
|
if (preserve_order) {
|
||||||
|
CounterGuard<size_t> counter_guard(flushes_in_progress_);
|
||||||
|
QueueType flush_queue; // flush from temporary queue
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
std::swap(messages_, flush_queue);
|
||||||
|
}
|
||||||
|
while (!flush_queue.empty()) {
|
||||||
|
sync_produce(flush_queue.front());
|
||||||
|
flush_queue.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
async_flush();
|
async_flush();
|
||||||
wait_for_acks();
|
wait_for_acks();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BufferType>
|
template <typename BufferType>
|
||||||
bool BufferedProducer<BufferType>::flush(std::chrono::milliseconds timeout) {
|
bool BufferedProducer<BufferType>::flush(std::chrono::milliseconds timeout,
|
||||||
|
bool preserve_order) {
|
||||||
|
if (preserve_order) {
|
||||||
|
CounterGuard<size_t> counter_guard(flushes_in_progress_);
|
||||||
|
QueueType flush_queue; // flush from temporary queue
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
std::swap(messages_, flush_queue);
|
||||||
|
}
|
||||||
|
auto start_time = std::chrono::high_resolution_clock::now();
|
||||||
|
while (!flush_queue.empty() &&
|
||||||
|
(std::chrono::duration_cast<std::chrono::milliseconds>
|
||||||
|
(std::chrono::high_resolution_clock::now() - start_time) < timeout)) {
|
||||||
|
sync_produce(flush_queue.front());
|
||||||
|
flush_queue.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
async_flush();
|
async_flush();
|
||||||
return wait_for_acks(timeout);
|
return wait_for_acks(timeout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BufferType>
|
template <typename BufferType>
|
||||||
|
|||||||
@@ -79,6 +79,19 @@ void flusher_run(BufferedProducer<string>& producer,
|
|||||||
producer.flush();
|
producer.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void async_flusher_run(BufferedProducer<string>& producer,
|
||||||
|
int& exit_flag,
|
||||||
|
int num_flush) {
|
||||||
|
while (!exit_flag) {
|
||||||
|
if (producer.get_buffer_size() >= (size_t)num_flush) {
|
||||||
|
producer.async_flush();
|
||||||
|
}
|
||||||
|
this_thread::sleep_for(milliseconds(10));
|
||||||
|
}
|
||||||
|
producer.async_flush();
|
||||||
|
producer.wait_for_acks();
|
||||||
|
}
|
||||||
|
|
||||||
void clear_run(BufferedProducer<string>& producer,
|
void clear_run(BufferedProducer<string>& producer,
|
||||||
condition_variable& clear) {
|
condition_variable& clear) {
|
||||||
mutex m;
|
mutex m;
|
||||||
@@ -377,7 +390,7 @@ TEST_CASE("replay async messages with errors", "[producer][buffered_producer][as
|
|||||||
ErrorProducer<string> producer(make_producer_config(),
|
ErrorProducer<string> producer(make_producer_config(),
|
||||||
BufferedProducer<string>::TestParameters{false, true});
|
BufferedProducer<string>::TestParameters{false, true});
|
||||||
producer.set_max_number_retries(num_retries);
|
producer.set_max_number_retries(num_retries);
|
||||||
thread flusher_thread(flusher_run, ref(producer), ref(exit_flag), 0);
|
thread flusher_thread(async_flusher_run, ref(producer), ref(exit_flag), 0);
|
||||||
string payload = "Hello world";
|
string payload = "Hello world";
|
||||||
producer.produce(MessageBuilder(KAFKA_TOPICS[0]).payload(payload));
|
producer.produce(MessageBuilder(KAFKA_TOPICS[0]).payload(payload));
|
||||||
this_thread::sleep_for(milliseconds(2000));
|
this_thread::sleep_for(milliseconds(2000));
|
||||||
|
|||||||
Reference in New Issue
Block a user