1use super::options::{BatchingOptions, HedgingOptions};
16use crate::publisher::actor::BundledMessage;
17use crate::publisher::actor::ToDispatcher;
18use crate::publisher::builder::PublisherBuilder;
19
20use tokio::sync::mpsc::UnboundedSender;
21use tokio::sync::oneshot;
22
23pub use super::base_publisher::BasePublisher;
24
25#[derive(Debug, Clone)]
67pub struct Publisher {
68 #[cfg_attr(not(test), expect(dead_code))]
71 pub(crate) batching_options: BatchingOptions,
72 #[cfg_attr(not(test), expect(dead_code))]
75 pub(crate) hedging_options: Option<HedgingOptions>,
76 pub(crate) tx: UnboundedSender<ToDispatcher>,
77}
78
79impl Publisher {
80 pub fn builder<T: Into<String>>(topic: T) -> PublisherBuilder {
92 PublisherBuilder::new(topic.into())
93 }
94
95 #[must_use = "ignoring the publish result may lead to undetected delivery failures"]
109 pub fn publish(&self, msg: crate::model::Message) -> crate::publisher::PublishFuture {
110 let (tx, rx) = tokio::sync::oneshot::channel();
111
112 if self
116 .tx
117 .send(ToDispatcher::Publish(BundledMessage { msg, tx }))
118 .is_err()
119 {
120 }
122 crate::publisher::PublishFuture { rx }
123 }
124
125 pub async fn flush(&self) {
150 let (tx, rx) = oneshot::channel();
151 if self.tx.send(ToDispatcher::Flush(tx)).is_ok() {
152 let _ = rx.await;
153 }
154 }
155
156 pub fn resume_publish<T: std::convert::Into<std::string::String>>(&self, ordering_key: T) {
174 let _ = self
175 .tx
176 .send(ToDispatcher::ResumePublish(ordering_key.into()));
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183 use crate::publisher::builder::PublisherPartialBuilder;
184 use crate::publisher::client::BasePublisher;
185 use crate::publisher::constants::*;
186 use crate::publisher::options::BatchingOptions;
187 use crate::{
188 generated::gapic_dataplane::client::Publisher as GapicPublisher,
189 model::{Message, PublishResponse},
190 };
191 use google_cloud_test_macros::tokio_test_no_panics;
192 use mockall::Sequence;
193 use rand::{RngExt, distr::Alphanumeric};
194 use std::error::Error;
195 use std::time::Duration;
196
197 static TOPIC: &str = "my-topic";
198
199 mockall::mock! {
200 #[derive(Debug)]
201 GapicPublisher {}
202 impl crate::generated::gapic_dataplane::stub::Publisher for GapicPublisher {
203 async fn publish(&self, req: crate::model::PublishRequest, _options: crate::RequestOptions) -> crate::Result<crate::Response<crate::model::PublishResponse>>;
204 }
205 }
206
207 mockall::mock! {
214 #[derive(Debug)]
215 GapicPublisherWithFuture {}
216 impl crate::generated::gapic_dataplane::stub::Publisher for GapicPublisherWithFuture {
217 fn publish(&self, req: crate::model::PublishRequest, _options: crate::RequestOptions) -> impl Future<Output=crate::Result<crate::Response<crate::model::PublishResponse>>> + Send;
218 }
219 }
220
221 fn publish_ok(
222 req: crate::model::PublishRequest,
223 _options: crate::RequestOptions,
224 ) -> crate::Result<crate::Response<crate::model::PublishResponse>> {
225 let ids = req
226 .messages
227 .iter()
228 .map(|m| String::from_utf8(m.data.to_vec()).unwrap());
229 Ok(crate::Response::from(
230 PublishResponse::new().set_message_ids(ids),
231 ))
232 }
233
234 fn publish_err(
235 _req: crate::model::PublishRequest,
236 _options: crate::RequestOptions,
237 ) -> crate::Result<crate::Response<crate::model::PublishResponse>> {
238 Err(crate::Error::service(
239 google_cloud_gax::error::rpc::Status::default()
240 .set_code(google_cloud_gax::error::rpc::Code::Unknown)
241 .set_message("unknown error has occurred"),
242 ))
243 }
244
245 #[track_caller]
246 fn assert_publish_err(got_err: crate::error::PublishError) {
247 assert!(
248 matches!(got_err, crate::error::PublishError::Rpc(_)),
249 "{got_err:?}"
250 );
251 let source = got_err
252 .source()
253 .and_then(|e| e.downcast_ref::<std::sync::Arc<crate::Error>>())
254 .expect("send error should contain a source");
255 assert!(source.status().is_some(), "{got_err:?}");
256 assert_eq!(
257 source.status().unwrap().code,
258 google_cloud_gax::error::rpc::Code::Unknown,
259 "{got_err:?}"
260 );
261 }
262
263 fn generate_random_data() -> String {
264 rand::rng()
265 .sample_iter(&Alphanumeric)
266 .take(16)
267 .map(char::from)
268 .collect()
269 }
270
271 macro_rules! assert_publishing_is_ok {
272 ($publisher:ident, $($ordering_key:expr),+) => {
273 $(
274 let msg = generate_random_data();
275 let got = $publisher
276 .publish(
277 Message::new()
278 .set_ordering_key($ordering_key)
279 .set_data(msg.clone()),
280 )
281 .await;
282 assert_eq!(got?, msg);
283 )+
284 };
285 }
286
287 macro_rules! assert_publishing_is_paused {
288 ($publisher:ident, $($ordering_key:expr),+) => {
289 $(
290 let got_err = $publisher
291 .publish(
292 Message::new()
293 .set_ordering_key($ordering_key)
294 .set_data(generate_random_data()),
295 )
296 .await;
297 assert!(
298 matches!(got_err, Err(crate::error::PublishError::OrderingKeyPaused)),
299 "{got_err:?}"
300 );
301 )+
302 };
303 }
304
305 #[tokio_test_no_panics]
306 async fn publisher_publish_successfully() -> anyhow::Result<()> {
307 let mut mock = MockGapicPublisher::new();
308 mock.expect_publish()
309 .times(2)
310 .withf(|req, _o| req.topic == TOPIC)
311 .returning(publish_ok);
312
313 let client = GapicPublisher::from_stub(mock);
314 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
315 .set_message_count_threshold(1_u32)
316 .build();
317
318 let messages = [
319 Message::new().set_data("hello"),
320 Message::new().set_data("world"),
321 ];
322 let mut handles = Vec::new();
323 for msg in messages {
324 let handle = publisher.publish(msg.clone());
325 handles.push((msg, handle));
326 }
327
328 for (id, rx) in handles.into_iter() {
329 let got = rx.await?;
330 let id = String::from_utf8(id.data.to_vec())?;
331 assert_eq!(got, id);
332 }
333
334 Ok(())
335 }
336
337 #[tokio_test_no_panics]
338 async fn publisher_publish_successfully_with_arc() -> anyhow::Result<()> {
339 let mut mock = MockGapicPublisher::new();
340 mock.expect_publish()
341 .times(2)
342 .withf(|req, _o| req.topic == TOPIC)
343 .returning(publish_ok);
344
345 let mock_arc = std::sync::Arc::new(mock);
346 let client = GapicPublisher::from_stub::<MockGapicPublisher>(mock_arc);
347 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
348 .set_message_count_threshold(1_u32)
349 .build();
350
351 let messages = [
352 Message::new().set_data("hello"),
353 Message::new().set_data("world"),
354 ];
355 let mut handles = Vec::new();
356 for msg in messages {
357 let handle = publisher.publish(msg.clone());
358 handles.push((msg, handle));
359 }
360
361 for (id, rx) in handles.into_iter() {
362 let got = rx.await?;
363 let id = String::from_utf8(id.data.to_vec())?;
364 assert_eq!(got, id);
365 }
366
367 Ok(())
368 }
369
370 #[tokio::test]
371 async fn publisher_publish_large_message() -> anyhow::Result<()> {
372 let mut mock = MockGapicPublisher::new();
373 mock.expect_publish()
374 .withf(|req, _o| req.topic == TOPIC)
375 .returning(publish_ok);
376
377 let client = GapicPublisher::from_stub(mock);
378 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
379 .set_byte_threshold(1_u32)
380 .build();
381 assert_publishing_is_ok!(publisher, "");
382 assert_publishing_is_ok!(publisher, "key");
383
384 Ok(())
385 }
386
387 #[tokio::test(start_paused = true)]
388 async fn worker_handles_forced_shutdown_gracefully() -> anyhow::Result<()> {
389 let mock = MockGapicPublisher::new();
390
391 let client = GapicPublisher::from_stub(mock);
392 let (publisher, background_task_handle) =
393 PublisherPartialBuilder::new(client, TOPIC.to_string())
394 .set_message_count_threshold(100_u32)
395 .build_return_handle();
396
397 let messages = [
398 Message::new().set_data("hello"),
399 Message::new().set_data("world"),
400 ];
401 let mut handles = Vec::new();
402 for msg in messages {
403 let handle = publisher.publish(msg);
404 handles.push(handle);
405 }
406
407 background_task_handle.abort();
408
409 for rx in handles.into_iter() {
410 rx.await
411 .expect_err("expected error when background task canceled");
412 }
413
414 Ok(())
415 }
416
417 #[tokio_test_no_panics(start_paused = true)]
418 async fn dropping_publisher_flushes_pending_messages() -> anyhow::Result<()> {
419 let mut mock = MockGapicPublisher::new();
422 mock.expect_publish()
423 .withf(|req, _o| req.topic == TOPIC)
424 .times(2)
425 .returning(publish_ok);
426
427 let client = GapicPublisher::from_stub(mock);
428 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
429 .set_message_count_threshold(1000_u32)
430 .set_delay_threshold(Duration::from_secs(60))
431 .build();
432
433 let start = tokio::time::Instant::now();
434 let messages = [
435 Message::new().set_data("hello"),
436 Message::new().set_data("world"),
437 Message::new().set_data("hello").set_ordering_key("key"),
438 Message::new().set_data("world").set_ordering_key("key"),
439 ];
440 let mut handles = Vec::new();
441 for msg in messages {
442 let handle = publisher.publish(msg.clone());
443 handles.push((msg, handle));
444 }
445 drop(publisher); for (id, rx) in handles.into_iter() {
448 let got = rx.await?;
449 let id = String::from_utf8(id.data.to_vec())?;
450 assert_eq!(got, id);
451 assert_eq!(start.elapsed(), Duration::ZERO);
452 }
453
454 Ok(())
455 }
456
457 #[tokio_test_no_panics]
458 async fn publisher_handles_publish_errors() -> anyhow::Result<()> {
459 let mut mock = MockGapicPublisher::new();
460 mock.expect_publish()
461 .times(2)
462 .withf(|req, _o| req.topic == TOPIC)
463 .returning(publish_err);
464
465 let client = GapicPublisher::from_stub(mock);
466 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
467 .set_message_count_threshold(1_u32)
468 .build();
469
470 let messages = [
471 Message::new().set_data("hello"),
472 Message::new().set_data("world"),
473 ];
474
475 let mut handles = Vec::new();
476 for msg in messages {
477 let handle = publisher.publish(msg.clone());
478 handles.push(handle);
479 }
480
481 for rx in handles.into_iter() {
482 let got = rx.await;
483 assert!(got.is_err(), "{got:?}");
484 }
485
486 Ok(())
487 }
488
489 #[tokio_test_no_panics(start_paused = true)]
490 async fn flush_sends_pending_messages_immediately() -> anyhow::Result<()> {
491 let mut mock = MockGapicPublisher::new();
492 mock.expect_publish()
493 .withf(|req, _o| req.topic == TOPIC)
494 .returning(publish_ok);
495
496 let client = GapicPublisher::from_stub(mock);
497 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
498 .set_message_count_threshold(1000_u32)
500 .set_delay_threshold(Duration::from_secs(60))
501 .build();
502
503 let start = tokio::time::Instant::now();
504 let messages = [
505 Message::new().set_data("hello"),
506 Message::new().set_data("world"),
507 ];
508 let mut handles = Vec::new();
509 for msg in messages {
510 let handle = publisher.publish(msg.clone());
511 handles.push((msg, handle));
512 }
513
514 publisher.flush().await;
515 assert_eq!(start.elapsed(), Duration::ZERO);
516
517 let post = publisher.publish(Message::new().set_data("after"));
518 for (id, rx) in handles.into_iter() {
519 let got = rx.await?;
520 let id = String::from_utf8(id.data.to_vec())?;
521 assert_eq!(got, id);
522 assert_eq!(start.elapsed(), Duration::ZERO);
523 }
524
525 let got = post.await?;
528 assert_eq!(got, "after");
529 assert_eq!(start.elapsed(), Duration::from_secs(60));
530
531 Ok(())
532 }
533
534 #[tokio_test_no_panics(start_paused = true)]
535 async fn dropping_handles_does_not_prevent_publishing() -> anyhow::Result<()> {
537 let mut mock = MockGapicPublisher::new();
538 mock.expect_publish()
539 .withf(|r, _| {
540 r.messages.len() == 2
541 && r.messages[0].data == "hello"
542 && r.messages[1].data == "world"
543 })
544 .return_once(publish_ok);
545
546 let client = GapicPublisher::from_stub(mock);
547 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
548 .set_message_count_threshold(1000_u32)
550 .set_delay_threshold(Duration::from_secs(60))
551 .build();
552
553 let start = tokio::time::Instant::now();
554 let messages = [
555 Message::new().set_data("hello"),
556 Message::new().set_data("world"),
557 ];
558 for msg in messages {
559 let handle = publisher.publish(msg.clone());
560 drop(handle);
561 }
562
563 publisher.flush().await;
564 assert_eq!(start.elapsed(), Duration::ZERO);
565
566 Ok(())
567 }
568
569 #[tokio::test(start_paused = true)]
570 async fn flush_with_no_messages_is_noop() -> anyhow::Result<()> {
571 let mock = MockGapicPublisher::new();
572
573 let client = GapicPublisher::from_stub(mock);
574 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string()).build();
575
576 let start = tokio::time::Instant::now();
577 publisher.flush().await;
578 assert_eq!(start.elapsed(), Duration::ZERO);
579
580 Ok(())
581 }
582
583 #[tokio_test_no_panics]
584 async fn batch_sends_on_message_count_threshold_success() -> anyhow::Result<()> {
585 let mut mock = MockGapicPublisher::new();
587 mock.expect_publish()
588 .withf(|r, _| r.messages.len() == 2)
589 .return_once(publish_ok);
590
591 let client = GapicPublisher::from_stub(mock);
592 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
593 .set_message_count_threshold(2_u32)
594 .set_byte_threshold(MAX_BYTES)
595 .set_delay_threshold(std::time::Duration::MAX)
596 .build();
597
598 let messages = [
599 Message::new().set_data("hello"),
600 Message::new().set_data("world"),
601 ];
602 let mut handles = Vec::new();
603 for msg in messages {
604 let handle = publisher.publish(msg.clone());
605 handles.push((msg, handle));
606 }
607
608 for (id, rx) in handles.into_iter() {
609 let got = rx.await?;
610 let id = String::from_utf8(id.data.to_vec())?;
611 assert_eq!(got, id);
612 }
613
614 Ok(())
615 }
616
617 #[tokio_test_no_panics]
618 async fn batch_sends_on_message_count_threshold_error() -> anyhow::Result<()> {
619 let mut mock = MockGapicPublisher::new();
621 mock.expect_publish()
622 .withf(|r, _| r.messages.len() == 2)
623 .return_once(publish_err);
624
625 let client = GapicPublisher::from_stub(mock);
626 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
627 .set_message_count_threshold(2_u32)
628 .set_byte_threshold(MAX_BYTES)
629 .set_delay_threshold(std::time::Duration::MAX)
630 .build();
631
632 let messages = [
633 Message::new().set_data("hello"),
634 Message::new().set_data("world"),
635 ];
636 let mut handles = Vec::new();
637 for msg in messages {
638 let handle = publisher.publish(msg.clone());
639 handles.push(handle);
640 }
641
642 for rx in handles.into_iter() {
643 let got = rx.await;
644 assert!(got.is_err(), "{got:?}");
645 }
646
647 Ok(())
648 }
649
650 #[tokio_test_no_panics(start_paused = true)]
651 async fn batch_sends_on_byte_threshold() -> anyhow::Result<()> {
652 let mut mock = MockGapicPublisher::new();
654 mock.expect_publish()
655 .withf(|r, _| r.messages.len() == 1)
656 .times(2)
657 .returning(publish_ok);
658
659 let client = GapicPublisher::from_stub(mock);
660 let byte_threshold = TOPIC.len() + "hello".len() + "key".len() + 1;
662 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
663 .set_message_count_threshold(MAX_MESSAGES)
664 .set_byte_threshold(byte_threshold as u32)
665 .set_delay_threshold(std::time::Duration::MAX)
666 .build();
667
668 let handle = publisher.publish(Message::new().set_data("hello"));
670 let _handle = publisher.publish(Message::new().set_data("world"));
672 assert_eq!(handle.await?, "hello");
673
674 let handle = publisher.publish(Message::new().set_data("hello").set_ordering_key("key"));
676 let _handle = publisher.publish(Message::new().set_data("world").set_ordering_key("key"));
678 assert_eq!(handle.await?, "hello");
679
680 Ok(())
681 }
682
683 #[tokio_test_no_panics(start_paused = true)]
684 async fn batch_sends_on_delay_threshold() -> anyhow::Result<()> {
685 let mut mock = MockGapicPublisher::new();
686 mock.expect_publish()
687 .withf(|req, _| req.topic == TOPIC)
688 .returning(publish_ok);
689
690 let client = GapicPublisher::from_stub(mock);
691 let delay = std::time::Duration::from_millis(10);
692 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
693 .set_message_count_threshold(u32::MAX)
694 .set_byte_threshold(MAX_BYTES)
695 .set_delay_threshold(delay)
696 .build();
697
698 for _ in 0..3 {
700 let start = tokio::time::Instant::now();
701 let messages = [
702 Message::new().set_data("hello 0"),
703 Message::new().set_data("hello 1"),
704 Message::new()
705 .set_data("hello 2")
706 .set_ordering_key("ordering key 1"),
707 Message::new()
708 .set_data("hello 3")
709 .set_ordering_key("ordering key 2"),
710 ];
711 let mut handles = Vec::new();
712 for msg in messages {
713 let handle = publisher.publish(msg.clone());
714 handles.push((msg, handle));
715 }
716
717 for (id, rx) in handles.into_iter() {
718 let got = rx.await?;
719 let id = String::from_utf8(id.data.to_vec())?;
720 assert_eq!(got, id);
721 assert_eq!(
722 start.elapsed(),
723 delay,
724 "batch of messages should have sent after {:?}",
725 delay
726 )
727 }
728 }
729
730 Ok(())
731 }
732
733 #[tokio::test(start_paused = true)]
734 #[allow(clippy::get_first)]
735 async fn batching_separates_by_ordering_key() -> anyhow::Result<()> {
736 let mut mock = MockGapicPublisher::new();
738 mock.expect_publish()
739 .withf(|r, _| {
740 r.messages.len() == 2 && r.messages[0].ordering_key == r.messages[1].ordering_key
741 })
742 .returning(publish_ok);
743
744 let client = GapicPublisher::from_stub(mock);
745 let message_count_threshold = 2_u32;
747 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
748 .set_message_count_threshold(message_count_threshold)
749 .set_byte_threshold(MAX_BYTES)
750 .set_delay_threshold(std::time::Duration::MAX)
751 .build();
752
753 let num_ordering_keys = 3;
754 let mut messages = Vec::new();
755 for i in 0..(2 * message_count_threshold * num_ordering_keys) {
759 messages.push(
760 Message::new()
761 .set_data(format!("test message {}", i))
762 .set_ordering_key(format!("ordering key: {}", i % num_ordering_keys)),
763 );
764 }
765 let mut handles = Vec::new();
766 for msg in messages {
767 let handle = publisher.publish(msg.clone());
768 handles.push((msg, handle));
769 }
770
771 for (id, rx) in handles.into_iter() {
772 let got = rx.await?;
773 let id = String::from_utf8(id.data.to_vec())?;
774 assert_eq!(got, id);
775 }
776
777 Ok(())
778 }
779
780 #[tokio_test_no_panics(start_paused = true)]
781 #[allow(clippy::get_first)]
782 async fn batching_handles_empty_ordering_key() -> anyhow::Result<()> {
783 let mut mock = MockGapicPublisher::new();
785 mock.expect_publish()
786 .withf(|r, _| {
787 r.messages.len() == 2 && r.messages[0].ordering_key == r.messages[1].ordering_key
788 })
789 .returning(publish_ok);
790
791 let client = GapicPublisher::from_stub(mock);
792 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
794 .set_message_count_threshold(2_u32)
795 .set_byte_threshold(MAX_BYTES)
796 .set_delay_threshold(std::time::Duration::MAX)
797 .build();
798
799 let messages = [
800 Message::new().set_data("hello 1"),
801 Message::new().set_data("hello 2").set_ordering_key(""),
802 Message::new()
803 .set_data("hello 3")
804 .set_ordering_key("ordering key :1"),
805 Message::new()
806 .set_data("hello 4")
807 .set_ordering_key("ordering key :1"),
808 ];
809
810 let mut handles = Vec::new();
811 for msg in messages {
812 let handle = publisher.publish(msg.clone());
813 handles.push((msg, handle));
814 }
815
816 for (id, rx) in handles.into_iter() {
817 let got = rx.await?;
818 let id = String::from_utf8(id.data.to_vec())?;
819 assert_eq!(got, id);
820 }
821
822 Ok(())
823 }
824
825 #[tokio_test_no_panics(start_paused = true)]
826 #[allow(clippy::get_first)]
827 async fn ordering_key_limits_to_one_outstanding_batch() -> anyhow::Result<()> {
828 let mut seq = Sequence::new();
832 let mut mock = MockGapicPublisherWithFuture::new();
833 mock.expect_publish()
834 .times(1)
835 .in_sequence(&mut seq)
836 .withf(|r, _| r.messages.len() == 1)
837 .returning({
838 |r, o| {
839 Box::pin(async move {
840 tokio::time::sleep(Duration::from_millis(10)).await;
841 publish_ok(r, o)
842 })
843 }
844 });
845
846 mock.expect_publish()
847 .times(1)
848 .in_sequence(&mut seq)
849 .withf(|r, _| r.messages.len() == 1)
850 .returning(|r, o| Box::pin(async move { publish_ok(r, o) }));
851
852 let client = GapicPublisher::from_stub(mock);
853 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
855 .set_message_count_threshold(1_u32)
856 .set_byte_threshold(MAX_BYTES)
857 .set_delay_threshold(std::time::Duration::MAX)
858 .build();
859
860 let messages = [
861 Message::new()
862 .set_data("hello 1")
863 .set_ordering_key("ordering key"),
864 Message::new()
865 .set_data("hello 2")
866 .set_ordering_key("ordering key"),
867 ];
868
869 let start = tokio::time::Instant::now();
870 let msg1_handle = publisher.publish(messages.get(0).unwrap().clone());
871 let msg2_handle = publisher.publish(messages.get(1).unwrap().clone());
872 assert_eq!(msg2_handle.await?, "hello 2");
873 assert_eq!(
874 start.elapsed(),
875 Duration::from_millis(10),
876 "the second batch of messages should have sent after the first which is has been delayed by {:?}",
877 Duration::from_millis(10)
878 );
879 assert_eq!(msg1_handle.await?, "hello 1");
881
882 Ok(())
883 }
884
885 #[tokio_test_no_panics(start_paused = true)]
886 #[allow(clippy::get_first)]
887 async fn empty_ordering_key_allows_concurrent_batches() -> anyhow::Result<()> {
888 let mut seq = Sequence::new();
893 let mut mock = MockGapicPublisherWithFuture::new();
894 mock.expect_publish()
895 .times(1)
896 .in_sequence(&mut seq)
897 .withf(|r, _| r.messages.len() == 1)
898 .returning(|r, o| {
899 Box::pin(async move {
900 tokio::time::sleep(Duration::from_millis(10)).await;
901 publish_ok(r, o)
902 })
903 });
904
905 mock.expect_publish()
906 .times(1)
907 .in_sequence(&mut seq)
908 .withf(|r, _| r.topic == TOPIC && r.messages.len() == 1)
909 .returning(|r, o| Box::pin(async move { publish_ok(r, o) }));
910
911 let client = GapicPublisher::from_stub(mock);
912 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
914 .set_message_count_threshold(1_u32)
915 .set_byte_threshold(MAX_BYTES)
916 .set_delay_threshold(std::time::Duration::MAX)
917 .build();
918
919 let messages = [
920 Message::new().set_data("hello 1").set_ordering_key(""),
921 Message::new().set_data("hello 2").set_ordering_key(""),
922 ];
923
924 let start = tokio::time::Instant::now();
925 let msg1_handle = publisher.publish(messages.get(0).unwrap().clone());
926 let msg2_handle = publisher.publish(messages.get(1).unwrap().clone());
927 assert_eq!(msg2_handle.await?, "hello 2");
928 assert_eq!(
929 start.elapsed(),
930 Duration::from_millis(0),
931 "the second batch of messages should have sent without any delay"
932 );
933 assert_eq!(msg1_handle.await?, "hello 1");
935
936 Ok(())
937 }
938
939 #[tokio_test_no_panics(start_paused = true)]
940 async fn ordering_key_error_pauses_publisher() -> anyhow::Result<()> {
941 let mut seq = Sequence::new();
943 let mut mock = MockGapicPublisher::new();
944 mock.expect_publish()
945 .withf(|req, _o| req.topic == TOPIC)
946 .times(1)
947 .in_sequence(&mut seq)
948 .returning(publish_err);
949
950 mock.expect_publish()
951 .withf(|req, _o| req.topic == TOPIC)
952 .times(2)
953 .in_sequence(&mut seq)
954 .returning(publish_ok);
955
956 let client = GapicPublisher::from_stub(mock);
957 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
958 .set_message_count_threshold(1_u32)
959 .build();
960
961 let key = "ordering_key";
962 let msg_0_handle =
963 publisher.publish(Message::new().set_ordering_key(key).set_data("msg 0"));
964 let msg_1_handle =
966 publisher.publish(Message::new().set_ordering_key(key).set_data("msg 1"));
967
968 let mut got_err = msg_0_handle.await.unwrap_err();
970 assert_publish_err(got_err);
971
972 got_err = msg_1_handle.await.unwrap_err();
974 assert!(
975 matches!(got_err, crate::error::PublishError::OrderingKeyPaused),
976 "{got_err:?}"
977 );
978
979 for _ in 0..3 {
981 assert_publishing_is_paused!(publisher, key);
982 }
983
984 assert_publishing_is_ok!(publisher, "", "without_error");
986
987 Ok(())
988 }
989
990 #[tokio_test_no_panics(start_paused = true)]
991 async fn batch_error_pauses_ordering_key() -> anyhow::Result<()> {
992 let mut mock = MockGapicPublisher::new();
994 mock.expect_publish()
995 .times(1)
996 .withf(|r, _| r.topic == TOPIC && r.messages.len() == 2)
997 .returning(publish_err);
998
999 let client = GapicPublisher::from_stub(mock);
1000 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string())
1001 .set_message_count_threshold(2_u32)
1002 .build();
1003
1004 let key = "ordering_key";
1005 let msg_0_handle =
1007 publisher.publish(Message::new().set_ordering_key(key).set_data("msg 0"));
1008 let msg_1_handle =
1009 publisher.publish(Message::new().set_ordering_key(key).set_data("msg 1"));
1010
1011 let mut got_err = msg_0_handle.await.unwrap_err();
1013 assert_publish_err(got_err);
1014 got_err = msg_1_handle.await.unwrap_err();
1015 assert_publish_err(got_err);
1016
1017 assert_publishing_is_paused!(publisher, key);
1019
1020 Ok(())
1021 }
1022
1023 #[tokio_test_no_panics(start_paused = true)]
1024 async fn flush_on_paused_ordering_key_returns_error() -> anyhow::Result<()> {
1025 let mut seq = Sequence::new();
1027 let mut mock = MockGapicPublisher::new();
1028 mock.expect_publish()
1029 .withf(|req, _o| req.topic == TOPIC)
1030 .times(1)
1031 .in_sequence(&mut seq)
1032 .returning(publish_err);
1033
1034 mock.expect_publish()
1035 .withf(|req, _o| req.topic == TOPIC)
1036 .times(2)
1037 .in_sequence(&mut seq)
1038 .returning(publish_ok);
1039
1040 let client = GapicPublisher::from_stub(mock);
1041 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string()).build();
1042
1043 let key = "ordering_key";
1044 let handle = publisher.publish(Message::new().set_ordering_key(key).set_data("msg 0"));
1046 publisher.flush().await;
1047 let got_err = handle.await.unwrap_err();
1049 assert_publish_err(got_err);
1050
1051 assert_publishing_is_paused!(publisher, key);
1053
1054 assert_publishing_is_ok!(publisher, "", "without_error");
1056
1057 Ok(())
1058 }
1059
1060 #[tokio_test_no_panics(start_paused = true)]
1061 async fn resuming_non_paused_ordering_key_is_noop() -> anyhow::Result<()> {
1062 let mut mock = MockGapicPublisher::new();
1063 mock.expect_publish()
1064 .withf(|req, _o| req.topic == TOPIC)
1065 .times(4)
1066 .returning(publish_ok);
1067
1068 let client = GapicPublisher::from_stub(mock);
1069 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string()).build();
1070
1071 publisher.resume_publish("");
1073 assert_publishing_is_ok!(publisher, "");
1074
1075 publisher.resume_publish("");
1077 assert_publishing_is_ok!(publisher, "");
1078
1079 let key = "without_error";
1081 publisher.resume_publish(key);
1082 assert_publishing_is_ok!(publisher, key);
1083
1084 publisher.resume_publish(key);
1086 assert_publishing_is_ok!(publisher, key);
1087
1088 Ok(())
1089 }
1090
1091 #[tokio_test_no_panics(start_paused = true)]
1092 async fn resuming_paused_ordering_key_allows_publishing() -> anyhow::Result<()> {
1093 let mut seq = Sequence::new();
1094 let mut mock = MockGapicPublisher::new();
1095 mock.expect_publish()
1096 .withf(|req, _o| req.topic == TOPIC)
1097 .times(1)
1098 .in_sequence(&mut seq)
1099 .returning(publish_err);
1100
1101 mock.expect_publish()
1102 .withf(|req, _o| req.topic == TOPIC)
1103 .times(3)
1104 .in_sequence(&mut seq)
1105 .returning(publish_ok);
1106
1107 let client = GapicPublisher::from_stub(mock);
1108 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string()).build();
1109
1110 let key = "ordering_key";
1111 let handle = publisher.publish(Message::new().set_ordering_key(key).set_data("msg 0"));
1113 let got_err = handle.await.unwrap_err();
1115 assert_publish_err(got_err);
1116
1117 assert_publishing_is_paused!(publisher, key);
1119
1120 publisher.resume_publish(key);
1122 assert_publishing_is_ok!(publisher, key);
1123
1124 assert_publishing_is_ok!(publisher, "", "without_error");
1126
1127 Ok(())
1128 }
1129
1130 #[tokio_test_no_panics(start_paused = true)]
1131 async fn resuming_ordering_key_twice_is_safe() -> anyhow::Result<()> {
1132 let mut seq = Sequence::new();
1134 let mut mock = MockGapicPublisher::new();
1135 mock.expect_publish()
1136 .withf(|req, _o| req.topic == TOPIC)
1137 .in_sequence(&mut seq)
1138 .times(1)
1139 .returning(publish_err);
1140
1141 mock.expect_publish()
1142 .withf(|req, _o| req.topic == TOPIC)
1143 .in_sequence(&mut seq)
1144 .return_once(publish_ok);
1145
1146 let client = GapicPublisher::from_stub(mock);
1147 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string()).build();
1148
1149 let key = "ordering_key";
1150 let handle = publisher.publish(Message::new().set_ordering_key(key).set_data("msg 0"));
1152 publisher.flush().await;
1153 let got_err = handle.await.unwrap_err();
1155 assert_publish_err(got_err);
1156
1157 assert_publishing_is_paused!(publisher, key);
1159
1160 publisher.resume_publish(key);
1162 publisher.resume_publish(key);
1163 assert_publishing_is_ok!(publisher, key);
1164
1165 Ok(())
1166 }
1167
1168 #[tokio_test_no_panics(start_paused = true)]
1169 async fn resuming_one_ordering_key_does_not_resume_others() -> anyhow::Result<()> {
1170 let mut seq = Sequence::new();
1172 let mut mock = MockGapicPublisher::new();
1173 mock.expect_publish()
1174 .withf(|req, _o| req.topic == TOPIC)
1175 .times(2)
1176 .in_sequence(&mut seq)
1177 .returning(publish_err);
1178
1179 mock.expect_publish()
1180 .withf(|req, _o| req.topic == TOPIC)
1181 .times(1)
1182 .in_sequence(&mut seq)
1183 .returning(publish_ok);
1184
1185 let client = GapicPublisher::from_stub(mock);
1186 let publisher = PublisherPartialBuilder::new(client, TOPIC.to_string()).build();
1187
1188 let key_0 = "ordering_key_0";
1189 let key_1 = "ordering_key_1";
1190 let handle_0 = publisher.publish(Message::new().set_ordering_key(key_0).set_data("msg 0"));
1192 let handle_1 = publisher.publish(Message::new().set_ordering_key(key_1).set_data("msg 1"));
1193 publisher.flush().await;
1194 let mut got_err = handle_0.await.unwrap_err();
1195 assert_publish_err(got_err);
1196 got_err = handle_1.await.unwrap_err();
1197 assert_publish_err(got_err);
1198
1199 assert_publishing_is_paused!(publisher, key_0, key_1);
1201
1202 publisher.resume_publish(key_0);
1204
1205 assert_publishing_is_ok!(publisher, key_0);
1207
1208 assert_publishing_is_paused!(publisher, key_1);
1210
1211 Ok(())
1212 }
1213
1214 #[tokio::test]
1215 async fn publisher_builder_clamps_batching_options() -> anyhow::Result<()> {
1216 let oversized_options = BatchingOptions::new()
1218 .set_delay_threshold(MAX_DELAY + Duration::from_secs(1))
1219 .set_message_count_threshold(MAX_MESSAGES + 1)
1220 .set_byte_threshold(MAX_BYTES + 1);
1221
1222 let publishers = vec![
1223 BasePublisher::builder()
1224 .build()
1225 .await?
1226 .publisher("projects/my-project/topics/my-topic")
1227 .set_delay_threshold(oversized_options.delay_threshold)
1228 .set_message_count_threshold(oversized_options.message_count_threshold)
1229 .set_byte_threshold(oversized_options.byte_threshold)
1230 .build(),
1231 Publisher::builder("projects/my-project/topics/my-topic".to_string())
1232 .set_delay_threshold(oversized_options.delay_threshold)
1233 .set_message_count_threshold(oversized_options.message_count_threshold)
1234 .set_byte_threshold(oversized_options.byte_threshold)
1235 .build()
1236 .await?,
1237 ];
1238
1239 for publisher in publishers {
1240 let got = publisher.batching_options;
1241 assert_eq!(got.delay_threshold, MAX_DELAY);
1242 assert_eq!(got.message_count_threshold, MAX_MESSAGES);
1243 assert_eq!(got.byte_threshold, MAX_BYTES);
1244 }
1245
1246 let normal_options = BatchingOptions::new()
1248 .set_delay_threshold(Duration::from_secs(10))
1249 .set_message_count_threshold(10_u32)
1250 .set_byte_threshold(100_u32);
1251
1252 let publishers = vec![
1253 BasePublisher::builder()
1254 .build()
1255 .await?
1256 .publisher("projects/my-project/topics/my-topic")
1257 .set_delay_threshold(normal_options.delay_threshold)
1258 .set_message_count_threshold(normal_options.message_count_threshold)
1259 .set_byte_threshold(normal_options.byte_threshold)
1260 .build(),
1261 Publisher::builder("projects/my-project/topics/my-topic".to_string())
1262 .set_delay_threshold(normal_options.delay_threshold)
1263 .set_message_count_threshold(normal_options.message_count_threshold)
1264 .set_byte_threshold(normal_options.byte_threshold)
1265 .build()
1266 .await?,
1267 ];
1268
1269 for publisher in publishers {
1270 let got = publisher.batching_options;
1271
1272 assert_eq!(got.delay_threshold, normal_options.delay_threshold);
1273 assert_eq!(
1274 got.message_count_threshold,
1275 normal_options.message_count_threshold
1276 );
1277 assert_eq!(got.byte_threshold, normal_options.byte_threshold);
1278 }
1279 Ok(())
1280 }
1281}