@@ -138,20 +138,56 @@ DeliveryResult ProducerImpl::Send(
138138 HeadersHolder headers_holder
139139) const {
140140 LOG (operation_log_level_) << fmt::format (" Message to topic '{}' is requested to send" , topic_name);
141+ auto deadline = engine::Deadline::FromDuration (delivery_timeout_);
141142 auto delivery_result_future =
142- ScheduleMessageDelivery (topic_name, key, message, partition, std::move (headers_holder));
143+ ScheduleMessageDelivery (topic_name, key, message, partition, std::move (headers_holder), deadline );
143144
144145 WaitUntilDeliveryReported (delivery_result_future);
145146
146147 return delivery_result_future.get ();
147148}
148149
150+ std::vector<DeliveryResult> ProducerImpl::Send (
151+ utils::zstring_view topic_name,
152+ std::string_view key,
153+ const Messages& messages,
154+ std::optional<std::uint32_t > partition,
155+ std::vector<HeadersHolder> headers_holders
156+ ) const {
157+ UASSERT (messages.Size () == headers_holders.size ());
158+
159+ LOG (operation_log_level_
160+ ) << fmt::format (" Messages {} to topic '{}' are requested to send" , messages.Size (), topic_name);
161+
162+ std::vector<engine::Future<DeliveryResult>> delivery_result_futures;
163+ delivery_result_futures.reserve (messages.Size ());
164+
165+ auto deadline = engine::Deadline::FromDuration (delivery_timeout_);
166+ for (std::size_t i = 0 ; i < messages.Size (); ++i) {
167+ delivery_result_futures.emplace_back (
168+ ScheduleMessageDelivery (topic_name, key, messages[i], partition, std::move (headers_holders[i]), deadline)
169+ );
170+ }
171+
172+ std::vector<DeliveryResult> delivery_results;
173+ delivery_results.reserve (messages.Size ());
174+
175+ for (auto & delivery_result_future : delivery_result_futures) {
176+ WaitUntilDeliveryReported (delivery_result_future);
177+
178+ delivery_results.emplace_back (delivery_result_future.get ());
179+ }
180+
181+ return delivery_results;
182+ }
183+
149184engine::Future<DeliveryResult> ProducerImpl::ScheduleMessageDelivery (
150185 utils::zstring_view topic_name,
151186 std::string_view key,
152187 std::string_view message,
153188 std::optional<std::uint32_t > partition,
154- HeadersHolder headers_holder
189+ HeadersHolder headers_holder,
190+ engine::Deadline deadline
155191) const {
156192 auto waiter = std::make_unique<DeliveryWaiter>();
157193 auto wait_handle = waiter->GetFuture ();
@@ -181,34 +217,42 @@ engine::Future<DeliveryResult> ProducerImpl::ScheduleMessageDelivery(
181217 // /
182218 // / Headers holder **must** be released if `rd_kafka_producev` succeeded.
183219
220+ while (!deadline.IsReached () && !engine::current_task::ShouldCancel ()) {
184221#ifdef __clang__
185222#pragma clang diagnostic push
186223#pragma clang diagnostic ignored "-Wgnu-statement-expression"
187224#endif
188- // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks,cppcoreguidelines-pro-type-const-cast)
189- const rd_kafka_resp_err_t enqueue_error = rd_kafka_producev (
190- producer_.GetHandle (),
191- RD_KAFKA_V_TOPIC (topic_name.c_str ()),
192- RD_KAFKA_V_KEY (key.data (), key.size ()),
193- RD_KAFKA_V_VALUE (const_cast <char *>(message.data ()), message.size ()),
194- RD_KAFKA_V_MSGFLAGS (0 ),
195- RD_KAFKA_V_HEADERS (headers_holder.GetHandle ()),
196- RD_KAFKA_V_PARTITION (partition.value_or (RD_KAFKA_PARTITION_UA)),
197- RD_KAFKA_V_OPAQUE (waiter.get ()),
198- RD_KAFKA_V_END
199- );
200- // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks,cppcoreguidelines-pro-type-const-cast)
225+ // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks,cppcoreguidelines-pro-type-const-cast)
226+ const rd_kafka_resp_err_t enqueue_error = rd_kafka_producev (
227+ producer_.GetHandle (),
228+ RD_KAFKA_V_TOPIC (topic_name.c_str ()),
229+ RD_KAFKA_V_KEY (key.data (), key.size ()),
230+ RD_KAFKA_V_VALUE (const_cast <char *>(message.data ()), message.size ()),
231+ RD_KAFKA_V_MSGFLAGS (0 ),
232+ RD_KAFKA_V_HEADERS (headers_holder.GetHandle ()),
233+ RD_KAFKA_V_PARTITION (partition.value_or (RD_KAFKA_PARTITION_UA)),
234+ RD_KAFKA_V_OPAQUE (waiter.get ()),
235+ RD_KAFKA_V_END
236+ );
237+ // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks,cppcoreguidelines-pro-type-const-cast)
201238
202239#ifdef __clang__
203240#pragma clang diagnostic pop
204241#endif
205242
206- if (enqueue_error == RD_KAFKA_RESP_ERR_NO_ERROR) {
207- [[maybe_unused]] const auto headers_holder_ptr = headers_holder.release ();
208- [[maybe_unused]] const auto waiter_ptr = waiter.release ();
209- } else {
210- LOG_WARNING (" Failed to enqueue message to Kafka local queue: {}" , rd_kafka_err2str (enqueue_error));
211- waiter->SetDeliveryResult (DeliveryResult{enqueue_error});
243+ if (enqueue_error == RD_KAFKA_RESP_ERR_NO_ERROR) {
244+ [[maybe_unused]] const auto headers_holder_ptr = headers_holder.release ();
245+ [[maybe_unused]] const auto waiter_ptr = waiter.release ();
246+ } else if (enqueue_error == RD_KAFKA_RESP_ERR__QUEUE_FULL) {
247+ LOG_LIMITED_WARNING (" Kafka local queue is full" );
248+ // / waiting for a while for the queue to clear up
249+ engine::Yield ();
250+ continue ;
251+ } else {
252+ LOG_WARNING (" Failed to enqueue message to Kafka local queue: {}" , rd_kafka_err2str (enqueue_error));
253+ waiter->SetDeliveryResult (DeliveryResult{enqueue_error});
254+ }
255+ break ;
212256 }
213257
214258 return wait_handle;
0 commit comments