1use std::{
2 future::Future,
3 pin::Pin,
4 task::{Context, Poll},
5 time::Duration,
6};
7
8use futures::FutureExt;
9use slog::{Drain, OwnedKVList, Record};
10
11use crate::{
12 batch::BatchBuilder,
13 client::Client,
14 queue::{BatchMessageQueue, MessageQueue, MessageQueueSender},
15 sender::SenderTask,
16 serializer::LogMessageSerializer,
17};
18
19pub struct CloudDrainBuilder {
21 queue_capacity: Option<usize>,
22 max_concurrency: usize,
23 initial_retry_delay: Duration,
24 max_retry_delay: Duration,
25 max_retries: Option<u32>,
26 debug: bool,
27}
28
29impl CloudDrainBuilder {
30 #[inline]
32 pub const fn new() -> Self {
33 Self {
34 queue_capacity: None,
35 max_concurrency: 16,
36 initial_retry_delay: Duration::from_secs(1),
37 max_retry_delay: Duration::from_secs(60),
38 max_retries: None,
39 debug: false,
40 }
41 }
42
43 #[inline]
46 pub fn queue_capacity(mut self, queue_capacity: Option<usize>) -> Self {
47 self.queue_capacity = queue_capacity;
48 self
49 }
50
51 #[inline]
53 pub fn max_concurrency(mut self, max_concurrency: usize) -> Self {
54 assert!(max_concurrency > 0);
55
56 self.max_concurrency = max_concurrency;
57 self
58 }
59
60 #[inline]
62 pub fn initial_retry_delay(mut self, initial_retry_delay: Duration) -> Self {
63 self.initial_retry_delay = initial_retry_delay;
64 self
65 }
66
67 #[inline]
69 pub fn max_retry_delay(mut self, max_retry_delay: Duration) -> Self {
70 self.max_retry_delay = max_retry_delay;
71 self
72 }
73
74 #[inline]
77 pub fn max_retries(mut self, max_retries: Option<u32>) -> Self {
78 self.max_retries = max_retries;
79 self
80 }
81
82 #[inline]
88 pub fn debug(mut self, debug: bool) -> Self {
89 self.debug = debug;
90 self
91 }
92
93 pub fn build<C, S, M, B, BB>(
100 self,
101 client: C,
102 serializer: S,
103 batch_builder: BB,
104 ) -> (CloudDrain<S, M>, CloudDrainTask, CloudDrainHandle<M>)
105 where
106 C: Client<Message = B> + Send + Sync + 'static,
107 B: Clone + Send + Sync + 'static,
108 BB: BatchBuilder<Item = M, Batch = B> + Send + 'static,
109 M: Send + 'static,
110 {
111 let (messages, message_sender) = MessageQueue::new(self.queue_capacity);
112
113 let messages = messages.batch(batch_builder);
114
115 let sender_task = SenderTask::builder()
116 .max_concurrency(self.max_concurrency)
117 .initial_retry_delay(self.initial_retry_delay)
118 .max_retry_delay(self.max_retry_delay)
119 .max_retries(self.max_retries)
120 .build(client, messages);
121
122 let task = CloudDrainTask { inner: sender_task };
123
124 let handle = CloudDrainHandle {
125 sender: message_sender.clone(),
126 };
127
128 let drain = CloudDrain {
129 serializer,
130 sender: message_sender,
131 debug: self.debug,
132 };
133
134 (drain, task, handle)
135 }
136}
137
138impl Default for CloudDrainBuilder {
139 #[inline]
140 fn default() -> Self {
141 Self::new()
142 }
143}
144
145pub struct CloudDrain<S, M> {
147 serializer: S,
148 sender: MessageQueueSender<M>,
149 debug: bool,
150}
151
152impl<S, M> Drain for CloudDrain<S, M>
153where
154 S: LogMessageSerializer<Serialized = M>,
155{
156 type Ok = ();
157 type Err = ();
158
159 fn log(&self, record: &Record, logger_values: &OwnedKVList) -> Result<(), ()> {
160 match self.serializer.serialize(record, logger_values) {
161 Ok(msg) => {
162 let res = self.sender.send(msg);
163
164 if self.debug && res.is_err() {
165 eprintln!("unable to send a log message: the background sender is not running");
166 }
167 }
168 Err(err) if self.debug => {
169 eprintln!("unable to serialize a log message: {err}");
170 }
171 Err(_) => (),
172 }
173
174 Ok(())
175 }
176}
177
178pub struct CloudDrainTask {
183 inner: SenderTask,
184}
185
186impl Future for CloudDrainTask {
187 type Output = ();
188
189 #[inline]
190 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
191 self.inner.poll_unpin(cx)
192 }
193}
194
195pub struct CloudDrainHandle<M> {
197 sender: MessageQueueSender<M>,
198}
199
200impl<M> CloudDrainHandle<M> {
201 pub async fn flush(&self) {
203 self.sender.flush().await
204 }
205
206 pub fn blocking_flush(&self) {
211 self.sender.blocking_flush();
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use std::{future::Future, time::Duration};
218
219 use bytes::Bytes;
220 use futures::poll;
221 use slog::{Drain, Logger, OwnedKVList, Record, info, o};
222 use tokio::sync::{mpsc, oneshot};
223
224 use crate::{
225 batch::BatchBuilder,
226 client::{Client, HttpClientError, SendError, StatusCode},
227 serializer::LogMessageSerializer,
228 };
229
230 use super::{CloudDrainBuilder, CloudDrainHandle, CloudDrainTask};
231
232 struct MessageSerializer;
234
235 impl LogMessageSerializer for MessageSerializer {
236 type Serialized = String;
237
238 fn serialize(&self, record: &Record, _: &OwnedKVList) -> slog::Result<String> {
239 Ok(record.msg().to_string())
240 }
241 }
242
243 struct VecBatchBuilder {
245 batch: Vec<String>,
246 max_len: usize,
247 }
248
249 impl BatchBuilder for VecBatchBuilder {
250 type Item = String;
251 type Batch = Vec<String>;
252
253 fn push(&mut self, item: String) -> Option<Vec<String>> {
254 let res = if self.batch.len() < self.max_len {
255 None
256 } else {
257 Some(std::mem::take(&mut self.batch))
258 };
259
260 self.batch.push(item);
261
262 res
263 }
264
265 fn flush(&mut self) -> Option<Vec<String>> {
266 if self.batch.is_empty() {
267 None
268 } else {
269 Some(std::mem::take(&mut self.batch))
270 }
271 }
272 }
273
274 struct Request {
276 batch: Vec<String>,
277 response: oneshot::Sender<Result<(), SendError>>,
278 }
279
280 impl Request {
281 fn respond_ok(self) {
283 let _ = self.response.send(Ok(()));
284 }
285
286 fn respond_status(self, status: StatusCode) {
288 let err = HttpClientError::UnexpectedStatusCode(status, Bytes::new());
289
290 let _ = self.response.send(Err(err.into()));
291 }
292 }
293
294 struct MockClient {
296 requests: mpsc::UnboundedSender<Request>,
297 }
298
299 impl Client for MockClient {
300 type Message = Vec<String>;
301
302 async fn send(&self, batch: Vec<String>) -> Result<(), SendError> {
303 let (tx, rx) = oneshot::channel();
304
305 let _ = self.requests.send(Request {
306 batch,
307 response: tx,
308 });
309
310 rx.await.unwrap_or(Ok(()))
312 }
313 }
314
315 struct TestDrain {
317 logger: Logger,
318 task: CloudDrainTask,
319 handle: CloudDrainHandle<String>,
320 requests: mpsc::UnboundedReceiver<Request>,
321 }
322
323 fn builder() -> CloudDrainBuilder {
325 CloudDrainBuilder::new()
326 .initial_retry_delay(Duration::from_millis(1))
327 .max_retry_delay(Duration::from_millis(1))
328 }
329
330 fn build(builder: CloudDrainBuilder, max_batch_len: usize) -> TestDrain {
332 let (tx, rx) = mpsc::unbounded_channel();
333
334 let client = MockClient { requests: tx };
335
336 let batch_builder = VecBatchBuilder {
337 batch: Vec::new(),
338 max_len: max_batch_len,
339 };
340
341 let (drain, task, handle) = builder.build(client, MessageSerializer, batch_builder);
342
343 TestDrain {
344 logger: Logger::root(drain.fuse(), o!()),
345 task,
346 handle,
347 requests: rx,
348 }
349 }
350
351 async fn within<F>(fut: F) -> F::Output
353 where
354 F: Future,
355 {
356 tokio::time::timeout(Duration::from_secs(5), fut)
357 .await
358 .expect("timed out")
359 }
360
361 async fn next_request(requests: &mut mpsc::UnboundedReceiver<Request>) -> Request {
363 within(requests.recv())
364 .await
365 .expect("request channel closed")
366 }
367
368 async fn assert_no_request(requests: &mut mpsc::UnboundedReceiver<Request>) {
370 tokio::time::sleep(Duration::from_millis(50)).await;
371
372 assert!(requests.try_recv().is_err(), "unexpected request");
373 }
374
375 #[tokio::test]
376 async fn batches_queued_messages() {
377 let TestDrain {
378 logger,
379 task,
380 handle: _handle,
381 mut requests,
382 } = build(builder(), 2);
383
384 for msg in ["a", "b", "c", "d", "e"] {
385 info!(logger, "{}", msg);
386 }
387
388 tokio::spawn(task);
389
390 let mut batches = Vec::new();
391
392 for _ in 0..3 {
393 let req = next_request(&mut requests).await;
394
395 batches.push(req.batch.clone());
396
397 req.respond_ok();
398 }
399
400 batches.sort();
402
403 assert_eq!(batches, [vec!["a", "b"], vec!["c", "d"], vec!["e"]]);
404 }
405
406 #[tokio::test]
407 async fn sends_partial_batch_without_waiting() {
408 let TestDrain {
409 logger,
410 task,
411 handle: _handle,
412 mut requests,
413 } = build(builder(), 10);
414
415 tokio::spawn(task);
416
417 info!(logger, "a");
418
419 assert_eq!(next_request(&mut requests).await.batch, ["a"]);
420 }
421
422 #[tokio::test]
423 async fn flush_waits_for_message_delivery() {
424 let TestDrain {
425 logger,
426 task,
427 handle,
428 mut requests,
429 } = build(builder(), 10);
430
431 tokio::spawn(task);
432
433 info!(logger, "a");
434
435 let mut flush = std::pin::pin!(handle.flush());
436
437 assert!(poll!(flush.as_mut()).is_pending());
438
439 let req = next_request(&mut requests).await;
440
441 tokio::time::sleep(Duration::from_millis(50)).await;
442
443 assert!(poll!(flush.as_mut()).is_pending());
444
445 req.respond_ok();
446
447 within(flush).await;
448 }
449
450 #[tokio::test]
451 async fn flush_does_not_wait_for_messages_logged_after_it() {
452 let TestDrain {
453 logger,
454 task,
455 handle,
456 mut requests,
457 } = build(builder(), 10);
458
459 tokio::spawn(task);
460
461 info!(logger, "a");
462
463 let mut flush = std::pin::pin!(handle.flush());
464
465 assert!(poll!(flush.as_mut()).is_pending());
466
467 info!(logger, "b");
468
469 let req = next_request(&mut requests).await;
470
471 assert_eq!(req.batch, ["a"]);
472
473 req.respond_ok();
474
475 let req = next_request(&mut requests).await;
476
477 assert_eq!(req.batch, ["b"]);
478
479 within(flush).await;
481 }
482
483 #[tokio::test]
484 async fn retries_failed_sends() {
485 let TestDrain {
486 logger,
487 task,
488 handle,
489 mut requests,
490 } = build(builder(), 10);
491
492 tokio::spawn(task);
493
494 info!(logger, "a");
495
496 next_request(&mut requests)
497 .await
498 .respond_status(StatusCode::SERVICE_UNAVAILABLE);
499
500 next_request(&mut requests)
501 .await
502 .respond_status(StatusCode::TOO_MANY_REQUESTS);
503
504 let req = next_request(&mut requests).await;
505
506 assert_eq!(req.batch, ["a"]);
507
508 req.respond_ok();
509
510 within(handle.flush()).await;
511
512 assert_no_request(&mut requests).await;
513 }
514
515 #[tokio::test]
516 async fn gives_up_after_max_retries() {
517 let TestDrain {
518 logger,
519 task,
520 handle,
521 mut requests,
522 } = build(builder().max_retries(Some(2)), 10);
523
524 tokio::spawn(task);
525
526 info!(logger, "a");
527
528 for _ in 0..3 {
529 next_request(&mut requests)
530 .await
531 .respond_status(StatusCode::SERVICE_UNAVAILABLE);
532 }
533
534 within(handle.flush()).await;
535
536 assert_no_request(&mut requests).await;
537 }
538
539 #[tokio::test]
540 async fn does_not_retry_non_retryable_errors() {
541 let TestDrain {
542 logger,
543 task,
544 handle,
545 mut requests,
546 } = build(builder(), 10);
547
548 tokio::spawn(task);
549
550 info!(logger, "a");
551
552 next_request(&mut requests)
553 .await
554 .respond_status(StatusCode::BAD_REQUEST);
555
556 within(handle.flush()).await;
557
558 assert_no_request(&mut requests).await;
559 }
560
561 #[tokio::test]
562 async fn limits_concurrent_sends() {
563 let TestDrain {
564 logger,
565 task,
566 handle: _handle,
567 mut requests,
568 } = build(builder().max_concurrency(2), 1);
569
570 for msg in ["a", "b", "c"] {
571 info!(logger, "{}", msg);
572 }
573
574 tokio::spawn(task);
575
576 let first = next_request(&mut requests).await;
577 let second = next_request(&mut requests).await;
578
579 assert_no_request(&mut requests).await;
580
581 let mut batches = vec![first.batch.clone(), second.batch.clone()];
582
583 first.respond_ok();
584
585 batches.push(next_request(&mut requests).await.batch);
586 batches.sort();
587
588 assert_eq!(batches, [["a"], ["b"], ["c"]]);
589 }
590
591 #[tokio::test]
592 async fn bounded_queue_drops_oldest_messages_but_keeps_flushes() {
593 let TestDrain {
594 logger,
595 task,
596 handle,
597 mut requests,
598 } = build(builder().queue_capacity(Some(1)), 10);
599
600 let mut flush = std::pin::pin!(handle.flush());
601
602 assert!(poll!(flush.as_mut()).is_pending());
603
604 info!(logger, "a");
606 info!(logger, "b");
607
608 tokio::spawn(task);
609
610 let req = next_request(&mut requests).await;
611
612 assert_eq!(req.batch, ["b"]);
613
614 within(flush).await;
616 }
617
618 #[tokio::test]
619 async fn flush_completes_when_task_is_dropped() {
620 let TestDrain {
621 logger,
622 task,
623 handle,
624 requests: _requests,
625 } = build(builder(), 10);
626
627 info!(logger, "a");
628
629 let mut flush = std::pin::pin!(handle.flush());
630
631 assert!(poll!(flush.as_mut()).is_pending());
632
633 std::mem::drop(task);
634
635 within(flush).await;
636 within(handle.flush()).await;
637 }
638
639 #[tokio::test]
640 async fn task_delivers_pending_messages_before_finishing() {
641 let TestDrain {
642 logger,
643 task,
644 handle,
645 mut requests,
646 } = build(builder(), 10);
647
648 info!(logger, "a");
649
650 std::mem::drop(logger);
651 std::mem::drop(handle);
652
653 let task = tokio::spawn(task);
654
655 let req = next_request(&mut requests).await;
656
657 assert_eq!(req.batch, ["a"]);
658
659 tokio::time::sleep(Duration::from_millis(50)).await;
660
661 assert!(!task.is_finished());
662
663 req.respond_ok();
664
665 within(task).await.unwrap();
666 }
667}