1use std::collections::HashMap;
16use std::collections::VecDeque;
17use std::mem;
18use std::path::PathBuf;
19use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
20use std::sync::{Arc, RwLock, RwLockWriteGuard};
21use std::time::{Duration, Instant};
22
23use chrono::Utc;
24use malloc_size_of::MallocSizeOf;
25use malloc_size_of_derive::MallocSizeOf;
26
27use crate::error::ErrorKind;
28use crate::TimerId;
29use crate::{internal_metrics::UploadMetrics, Glean};
30pub use directory::process_metadata;
31use directory::{PingDirectoryManager, PingPayloadsByDirectory};
32use policy::Policy;
33use request::create_date_header_value;
34
35pub use directory::{PingMetadata, PingPayload};
36pub use request::{HeaderMap, PingRequest};
37pub use result::{UploadResult, UploadTaskAction};
38
39mod directory;
40mod policy;
41mod request;
42mod result;
43
44const WAIT_TIME_FOR_PING_PROCESSING: u64 = 1000; #[derive(Debug, MallocSizeOf)]
47struct RateLimiter {
48 started: Option<Instant>,
50 count: u32,
52 interval: Duration,
54 max_count: u32,
56}
57
58#[derive(PartialEq)]
60enum RateLimiterState {
61 Incrementing,
63 Throttled(u64),
68}
69
70impl RateLimiter {
71 pub fn new(interval: Duration, max_count: u32) -> Self {
72 Self {
73 started: None,
74 count: 0,
75 interval,
76 max_count,
77 }
78 }
79
80 fn reset(&mut self) {
81 self.started = Some(Instant::now());
82 self.count = 0;
83 }
84
85 fn elapsed(&self) -> Duration {
86 self.started.unwrap().elapsed()
87 }
88
89 fn should_reset(&self) -> bool {
95 if self.started.is_none() {
96 return true;
97 }
98
99 if self.elapsed() > self.interval {
101 return true;
102 }
103
104 false
105 }
106
107 pub fn get_state(&mut self) -> RateLimiterState {
113 if self.should_reset() {
114 self.reset();
115 }
116
117 if self.count == self.max_count {
118 let remaining = self.interval.as_millis() - self.elapsed().as_millis();
121 return RateLimiterState::Throttled(
122 remaining
123 .try_into()
124 .unwrap_or(self.interval.as_secs() * 1000),
125 );
126 }
127
128 self.count += 1;
129 RateLimiterState::Incrementing
130 }
131}
132
133#[derive(PartialEq, Eq, Debug)]
138pub enum PingUploadTask {
139 Upload {
141 request: PingRequest,
144 },
145
146 Wait {
149 time: u64,
152 },
153
154 Done {
167 #[doc(hidden)]
168 unused: i8,
170 },
171}
172
173impl PingUploadTask {
174 pub fn is_upload(&self) -> bool {
176 matches!(self, PingUploadTask::Upload { .. })
177 }
178
179 pub fn is_wait(&self) -> bool {
181 matches!(self, PingUploadTask::Wait { .. })
182 }
183
184 pub(crate) fn done() -> Self {
185 PingUploadTask::Done { unused: 0 }
186 }
187}
188
189#[derive(Debug)]
191pub struct PingUploadManager {
192 queue: RwLock<VecDeque<PingRequest>>,
194 directory_manager: PingDirectoryManager,
196 processed_pending_pings: Arc<AtomicBool>,
198 cached_pings: Arc<RwLock<PingPayloadsByDirectory>>,
200 recoverable_failure_count: AtomicU32,
202 wait_attempt_count: AtomicU32,
204 rate_limiter: Option<RwLock<RateLimiter>>,
209 language_binding_name: String,
213 upload_metrics: UploadMetrics,
215 policy: Policy,
217
218 in_flight: RwLock<HashMap<String, (TimerId, TimerId)>>,
219}
220
221impl MallocSizeOf for PingUploadManager {
222 fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
223 let shallow_size = {
224 let queue = self.queue.read().unwrap();
225 if ops.has_malloc_enclosing_size_of() {
226 if let Some(front) = queue.front() {
227 unsafe { ops.malloc_enclosing_size_of(front) }
230 } else {
231 0
233 }
234 } else {
235 queue.capacity() * mem::size_of::<PingRequest>()
239 }
240 };
241
242 let mut n = shallow_size
243 + self.directory_manager.size_of(ops)
244 + mem::size_of::<AtomicBool>() + self.cached_pings.read().unwrap().size_of(ops)
246 + self.rate_limiter.as_ref().map(|rl| {
247 let lock = rl.read().unwrap();
248 (*lock).size_of(ops)
249 }).unwrap_or(0)
250 + self.language_binding_name.size_of(ops)
251 + self.upload_metrics.size_of(ops)
252 + self.policy.size_of(ops);
253
254 let in_flight = self.in_flight.read().unwrap();
255 n += in_flight.size_of(ops);
256
257 n
258 }
259}
260
261impl PingUploadManager {
262 pub fn new<P: Into<PathBuf>>(data_path: P, language_binding_name: &str) -> Self {
273 Self {
274 queue: RwLock::new(VecDeque::new()),
275 directory_manager: PingDirectoryManager::new(data_path),
276 processed_pending_pings: Arc::new(AtomicBool::new(false)),
277 cached_pings: Arc::new(RwLock::new(PingPayloadsByDirectory::default())),
278 recoverable_failure_count: AtomicU32::new(0),
279 wait_attempt_count: AtomicU32::new(0),
280 rate_limiter: None,
281 language_binding_name: language_binding_name.into(),
282 upload_metrics: UploadMetrics::new(),
283 policy: Policy::default(),
284 in_flight: RwLock::new(HashMap::default()),
285 }
286 }
287
288 pub fn scan_pending_pings_directories(
295 &self,
296 trigger_upload: bool,
297 ) -> std::thread::JoinHandle<()> {
298 let local_manager = self.directory_manager.clone();
299 let local_cached_pings = self.cached_pings.clone();
300 let local_flag = self.processed_pending_pings.clone();
301 crate::thread::spawn("glean.ping_directory_manager.process_dir", move || {
302 {
303 let mut local_cached_pings = local_cached_pings
305 .write()
306 .expect("Can't write to pending pings cache.");
307 local_cached_pings.extend(local_manager.process_dirs());
308 local_flag.store(true, Ordering::SeqCst);
309 }
310 if trigger_upload {
311 crate::dispatcher::launch(|| {
312 if let Some(state) = crate::maybe_global_state().and_then(|s| s.lock().ok()) {
313 if let Err(e) = state.callbacks.trigger_upload() {
314 log::error!(
315 "Triggering upload after pending ping scan failed. Error: {}",
316 e
317 );
318 }
319 }
320 });
321 }
322 })
323 .expect("Unable to spawn thread to process pings directories.")
324 }
325
326 #[cfg(test)]
328 pub fn no_policy<P: Into<PathBuf>>(data_path: P) -> Self {
329 let mut upload_manager = Self::new(data_path, "Test");
330
331 upload_manager.policy.set_max_recoverable_failures(None);
333 upload_manager.policy.set_max_wait_attempts(None);
334 upload_manager.policy.set_max_ping_body_size(None);
335 upload_manager
336 .policy
337 .set_max_pending_pings_directory_size(None);
338 upload_manager.policy.set_max_pending_pings_count(None);
339
340 upload_manager
342 .scan_pending_pings_directories(false)
343 .join()
344 .unwrap();
345
346 upload_manager
347 }
348
349 fn processed_pending_pings(&self) -> bool {
350 self.processed_pending_pings.load(Ordering::SeqCst)
351 }
352
353 fn recoverable_failure_count(&self) -> u32 {
354 self.recoverable_failure_count.load(Ordering::SeqCst)
355 }
356
357 fn wait_attempt_count(&self) -> u32 {
358 self.wait_attempt_count.load(Ordering::SeqCst)
359 }
360
361 fn build_ping_request(&self, glean: &Glean, ping: PingPayload) -> Option<PingRequest> {
366 let PingPayload {
367 document_id,
368 upload_path: path,
369 json_body: body,
370 headers,
371 body_has_info_sections,
372 ping_name,
373 uploader_capabilities,
374 } = ping;
375 let mut request = PingRequest::builder(
376 &self.language_binding_name,
377 self.policy.max_ping_body_size(),
378 )
379 .document_id(&document_id)
380 .path(path)
381 .body(body)
382 .body_has_info_sections(body_has_info_sections)
383 .ping_name(ping_name)
384 .uploader_capabilities(uploader_capabilities);
385
386 if let Some(headers) = headers {
387 request = request.headers(headers);
388 }
389
390 match request.build() {
391 Ok(request) => Some(request),
392 Err(e) => {
393 log::warn!("Error trying to build ping request: {}", e);
394 self.directory_manager.delete_file(&document_id);
395
396 if let ErrorKind::PingBodyOverflow(s) = e.kind() {
399 self.upload_metrics
400 .discarded_exceeding_pings_size
401 .accumulate_sync(glean, *s as i64 / 1024);
402 }
403
404 None
405 }
406 }
407 }
408
409 pub fn enqueue_ping(&self, glean: &Glean, ping: PingPayload) {
411 let mut queue = self
412 .queue
413 .write()
414 .expect("Can't write to pending pings queue.");
415
416 let PingPayload {
417 ref document_id,
418 upload_path: ref path,
419 ..
420 } = ping;
421 if queue
423 .iter()
424 .any(|request| request.document_id.as_str() == document_id)
425 {
426 log::warn!(
427 "Attempted to enqueue a duplicate ping {} at {}.",
428 document_id,
429 path
430 );
431 return;
432 }
433
434 {
435 let in_flight = self.in_flight.read().unwrap();
436 if in_flight.contains_key(document_id) {
437 log::warn!(
438 "Attempted to enqueue an in-flight ping {} at {}.",
439 document_id,
440 path
441 );
442 self.upload_metrics
443 .in_flight_pings_dropped
444 .add_sync(glean, 0);
445 return;
446 }
447 }
448
449 log::trace!("Enqueuing ping {} at {}", document_id, path);
450 if let Some(request) = self.build_ping_request(glean, ping) {
451 queue.push_back(request)
452 }
453 }
454
455 fn enqueue_cached_pings(&self, glean: &Glean) {
472 let mut cached_pings = self
473 .cached_pings
474 .write()
475 .expect("Can't write to pending pings cache.");
476
477 if cached_pings.len() > 0 {
478 let mut pending_pings_directory_size: u64 = 0;
479 let mut pending_pings_count = 0;
480 let mut deleting = false;
481 let mut delete_reason: Option<&'static str> = None;
482
483 let total = cached_pings.pending_pings.len() as u64;
484 self.upload_metrics
485 .pending_pings
486 .add_sync(glean, total.try_into().unwrap_or(0));
487
488 if total > self.policy.max_pending_pings_count() {
489 log::warn!(
490 "More than {} pending pings in the directory, will delete {} old pings.",
491 self.policy.max_pending_pings_count(),
492 total - self.policy.max_pending_pings_count()
493 );
494 }
495
496 cached_pings.pending_pings.reverse();
502 cached_pings.pending_pings.retain(|(file_size, PingPayload {document_id, ..})| {
503 pending_pings_count += 1;
504 pending_pings_directory_size += file_size;
505
506 if !deleting && pending_pings_directory_size > self.policy.max_pending_pings_directory_size() {
510 log::warn!(
511 "Pending pings directory has reached the size quota of {} bytes, outstanding pings will be deleted.",
512 self.policy.max_pending_pings_directory_size()
513 );
514 deleting = true;
515 delete_reason = Some("size_quota");
516 }
517
518 if !deleting && pending_pings_count > self.policy.max_pending_pings_count() {
522 deleting = true;
523 delete_reason = Some("count_quota");
524 }
525
526 if deleting && self.directory_manager.delete_file(document_id) {
527 self.upload_metrics
528 .deleted_pings_after_quota_hit
529 .add_sync(glean, 1);
530 if let Some(reason) = delete_reason {
531 self.upload_metrics
532 .pending_pings_deleted
533 .get(reason)
534 .add_sync(glean, 1);
535 }
536 return false;
537 }
538
539 true
540 });
541 cached_pings.pending_pings.reverse();
544 self.upload_metrics
545 .pending_pings_directory_size
546 .accumulate_sync(glean, pending_pings_directory_size as i64 / 1024);
547
548 cached_pings
551 .deletion_request_pings
552 .drain(..)
553 .for_each(|(_, ping)| self.enqueue_ping(glean, ping));
554 cached_pings
555 .pending_pings
556 .drain(..)
557 .for_each(|(_, ping)| self.enqueue_ping(glean, ping));
558 }
559 }
560
561 pub fn set_rate_limiter(&mut self, interval: u64, max_tasks: u32) {
574 self.rate_limiter = Some(RwLock::new(RateLimiter::new(
575 Duration::from_secs(interval),
576 max_tasks,
577 )));
578 }
579
580 pub(crate) fn set_max_pending_pings_count(&mut self, n: u64) {
581 self.policy.set_max_pending_pings_count(Some(n));
582 }
583
584 pub(crate) fn set_max_pending_pings_directory_size(&mut self, n: u64) {
585 self.policy.set_max_pending_pings_directory_size(Some(n));
586 }
587
588 pub fn enqueue_ping_from_file(&self, glean: &Glean, document_id: &str) {
597 if let Some(ping) = self.directory_manager.process_file(document_id) {
598 self.enqueue_ping(glean, ping);
599 }
600 }
601
602 pub fn clear_ping_queue(&self) -> RwLockWriteGuard<'_, VecDeque<PingRequest>> {
604 log::trace!("Clearing ping queue");
605 let mut queue = self
606 .queue
607 .write()
608 .expect("Can't write to pending pings queue.");
609
610 queue.retain(|ping| ping.is_deletion_request());
611 log::trace!(
612 "{} pings left in the queue (only deletion-request expected)",
613 queue.len()
614 );
615 queue
616 }
617
618 fn get_upload_task_internal(&self, glean: &Glean, log_ping: bool) -> PingUploadTask {
619 let wait_or_done = |time: u64| {
624 self.wait_attempt_count.fetch_add(1, Ordering::SeqCst);
625 if self.wait_attempt_count() > self.policy.max_wait_attempts() {
626 PingUploadTask::done()
627 } else {
628 PingUploadTask::Wait { time }
629 }
630 };
631
632 if !self.processed_pending_pings() {
633 log::info!(
634 "Tried getting an upload task, but processing is ongoing. Will come back later."
635 );
636 return wait_or_done(WAIT_TIME_FOR_PING_PROCESSING);
637 }
638
639 self.enqueue_cached_pings(glean);
641
642 if self.recoverable_failure_count() >= self.policy.max_recoverable_failures() {
643 log::warn!(
644 "Reached maximum recoverable failures for the current uploading window. You are done."
645 );
646 return PingUploadTask::done();
647 }
648
649 let mut queue = self
650 .queue
651 .write()
652 .expect("Can't write to pending pings queue.");
653 match queue.front() {
654 Some(request) => {
655 if let Some(rate_limiter) = &self.rate_limiter {
656 let mut rate_limiter = rate_limiter
657 .write()
658 .expect("Can't write to the rate limiter.");
659 if let RateLimiterState::Throttled(remaining) = rate_limiter.get_state() {
660 log::info!(
661 "Tried getting an upload task, but we are throttled at the moment."
662 );
663 return wait_or_done(remaining);
664 }
665 }
666
667 log::info!(
668 "New upload task with id {} (path: {})",
669 request.document_id,
670 request.path
671 );
672
673 if log_ping {
674 if let Some(body) = request.pretty_body() {
675 chunked_log_info(&request.path, &body);
676 } else {
677 chunked_log_info(&request.path, "<invalid ping payload>");
678 }
679 }
680
681 {
682 let mut in_flight = self.in_flight.write().unwrap();
686 let success_id = self.upload_metrics.send_success.start_sync();
687 let failure_id = self.upload_metrics.send_failure.start_sync();
688 in_flight.insert(request.document_id.clone(), (success_id, failure_id));
689 }
690
691 let mut request = queue.pop_front().unwrap();
692
693 request
695 .headers
696 .insert("Date".to_string(), create_date_header_value(Utc::now()));
697
698 PingUploadTask::Upload { request }
699 }
700 None => {
701 log::info!("No more pings to upload! You are done.");
702 PingUploadTask::done()
703 }
704 }
705 }
706
707 pub fn get_upload_task(&self, glean: &Glean, log_ping: bool) -> PingUploadTask {
718 let task = self.get_upload_task_internal(glean, log_ping);
719
720 if !task.is_wait() && self.wait_attempt_count() > 0 {
721 self.wait_attempt_count.store(0, Ordering::SeqCst);
722 }
723
724 if !task.is_upload() && self.recoverable_failure_count() > 0 {
725 self.recoverable_failure_count.store(0, Ordering::SeqCst);
726 }
727
728 task
729 }
730
731 pub fn process_ping_upload_response(
770 &self,
771 glean: &Glean,
772 document_id: &str,
773 status: UploadResult,
774 ) -> UploadTaskAction {
775 use UploadResult::*;
776
777 let stop_time = zeitstempel::now_awake();
778
779 if let Some(label) = status.get_label() {
780 let metric = self.upload_metrics.ping_upload_failure.get(label);
781 metric.add_sync(glean, 1);
782 }
783
784 let send_ids = {
785 let mut lock = self.in_flight.write().unwrap();
786 lock.remove(document_id)
787 };
788
789 if send_ids.is_none() {
790 self.upload_metrics.missing_send_ids.add_sync(glean, 1);
791 }
792
793 match status {
794 HttpStatus { code } if (200..=299).contains(&code) => {
795 log::info!("Ping {} successfully sent {}.", document_id, code);
796 if let Some((success_id, failure_id)) = send_ids {
797 self.upload_metrics
798 .send_success
799 .set_stop_and_accumulate(glean, success_id, stop_time);
800 self.upload_metrics.send_failure.cancel_sync(failure_id);
801 }
802 if glean.store_submitted_pings_enabled {
803 glean
804 .storage()
805 .mark_ping_as_uploaded(document_id, Utc::now());
806 }
807 self.directory_manager.delete_file(document_id);
808 }
809
810 UnrecoverableFailure { .. } | HttpStatus { code: 400..=499 } | Incapable { .. } => {
811 log::warn!(
812 "Unrecoverable upload failure while attempting to send ping {}. Error was {:?}",
813 document_id,
814 status
815 );
816 if let Some((success_id, failure_id)) = send_ids {
817 self.upload_metrics.send_success.cancel_sync(success_id);
818 self.upload_metrics
819 .send_failure
820 .set_stop_and_accumulate(glean, failure_id, stop_time);
821 }
822 if glean.store_submitted_pings_enabled {
823 glean.storage().mark_ping_as_upload_failed(document_id);
824 }
825 self.directory_manager.delete_file(document_id);
826 }
827
828 RecoverableFailure { .. } | HttpStatus { .. } => {
829 log::warn!(
830 "Recoverable upload failure while attempting to send ping {}, will retry. Error was {:?}",
831 document_id,
832 status
833 );
834 if let Some((success_id, failure_id)) = send_ids {
835 self.upload_metrics.send_success.cancel_sync(success_id);
836 self.upload_metrics
837 .send_failure
838 .set_stop_and_accumulate(glean, failure_id, stop_time);
839 }
840 self.enqueue_ping_from_file(glean, document_id);
841 self.recoverable_failure_count
842 .fetch_add(1, Ordering::SeqCst);
843 }
844
845 Done { .. } => {
846 log::debug!("Uploader signaled Done. Exiting.");
847 if let Some((success_id, failure_id)) = send_ids {
848 self.upload_metrics.send_success.cancel_sync(success_id);
849 self.upload_metrics.send_failure.cancel_sync(failure_id);
850 }
851 return UploadTaskAction::End;
852 }
853 };
854
855 UploadTaskAction::Next
856 }
857}
858
859#[cfg(target_os = "android")]
861pub fn chunked_log_info(path: &str, payload: &str) {
862 const MAX_LOG_PAYLOAD_SIZE_BYTES: usize = 4000;
866
867 if path.len() + payload.len() <= MAX_LOG_PAYLOAD_SIZE_BYTES {
871 log::info!("Glean ping to URL: {}\n{}", path, payload);
872 return;
873 }
874
875 let mut start = 0;
878 let mut end = MAX_LOG_PAYLOAD_SIZE_BYTES;
879 let mut chunk_idx = 1;
880 let total_chunks = payload.len() / MAX_LOG_PAYLOAD_SIZE_BYTES + 1;
882
883 while end < payload.len() {
884 for _ in 0..4 {
887 if payload.is_char_boundary(end) {
888 break;
889 }
890 end -= 1;
891 }
892
893 log::info!(
894 "Glean ping to URL: {} [Part {} of {}]\n{}",
895 path,
896 chunk_idx,
897 total_chunks,
898 &payload[start..end]
899 );
900
901 start = end;
903 end = end + MAX_LOG_PAYLOAD_SIZE_BYTES;
904 chunk_idx += 1;
905 }
906
907 if start < payload.len() {
909 log::info!(
910 "Glean ping to URL: {} [Part {} of {}]\n{}",
911 path,
912 chunk_idx,
913 total_chunks,
914 &payload[start..]
915 );
916 }
917}
918
919#[cfg(not(target_os = "android"))]
921pub fn chunked_log_info(_path: &str, payload: &str) {
922 log::info!("{}", payload)
923}
924
925#[cfg(test)]
926mod test {
927 use std::thread;
928 use uuid::Uuid;
929
930 use super::*;
931 use crate::metrics::PingType;
932 use crate::{tests::new_glean, PENDING_PINGS_DIRECTORY};
933
934 const PATH: &str = "/submit/app_id/ping_name/schema_version/doc_id";
935
936 #[test]
937 fn doesnt_error_when_there_are_no_pending_pings() {
938 let (glean, _t) = new_glean(None);
939
940 assert_eq!(glean.get_upload_task(), PingUploadTask::done());
943 }
944
945 #[test]
946 fn returns_ping_request_when_there_is_one() {
947 let (glean, dir) = new_glean(None);
948
949 let upload_manager = PingUploadManager::no_policy(dir.path());
950
951 upload_manager.enqueue_ping(
953 &glean,
954 PingPayload {
955 document_id: Uuid::new_v4().to_string(),
956 upload_path: PATH.into(),
957 json_body: "".into(),
958 headers: None,
959 body_has_info_sections: true,
960 ping_name: "ping-name".into(),
961 uploader_capabilities: vec![],
962 },
963 );
964
965 let task = upload_manager.get_upload_task(&glean, false);
968 assert!(task.is_upload());
969 }
970
971 #[test]
972 fn returns_as_many_ping_requests_as_there_are() {
973 let (glean, dir) = new_glean(None);
974
975 let upload_manager = PingUploadManager::no_policy(dir.path());
976
977 let n = 10;
979 for _ in 0..n {
980 upload_manager.enqueue_ping(
981 &glean,
982 PingPayload {
983 document_id: Uuid::new_v4().to_string(),
984 upload_path: PATH.into(),
985 json_body: "".into(),
986 headers: None,
987 body_has_info_sections: true,
988 ping_name: "ping-name".into(),
989 uploader_capabilities: vec![],
990 },
991 );
992 }
993
994 for _ in 0..n {
996 let task = upload_manager.get_upload_task(&glean, false);
997 assert!(task.is_upload());
998 }
999
1000 assert_eq!(
1002 upload_manager.get_upload_task(&glean, false),
1003 PingUploadTask::done()
1004 );
1005 }
1006
1007 #[test]
1008 fn limits_the_number_of_pings_when_there_is_rate_limiting() {
1009 let (glean, dir) = new_glean(None);
1010
1011 let mut upload_manager = PingUploadManager::no_policy(dir.path());
1012
1013 let max_pings_per_interval = 10;
1015 upload_manager.set_rate_limiter(3, 10);
1016
1017 for _ in 0..max_pings_per_interval {
1019 upload_manager.enqueue_ping(
1020 &glean,
1021 PingPayload {
1022 document_id: Uuid::new_v4().to_string(),
1023 upload_path: PATH.into(),
1024 json_body: "".into(),
1025 headers: None,
1026 body_has_info_sections: true,
1027 ping_name: "ping-name".into(),
1028 uploader_capabilities: vec![],
1029 },
1030 );
1031 }
1032
1033 for _ in 0..max_pings_per_interval {
1035 let task = upload_manager.get_upload_task(&glean, false);
1036 assert!(task.is_upload());
1037 }
1038
1039 upload_manager.enqueue_ping(
1041 &glean,
1042 PingPayload {
1043 document_id: Uuid::new_v4().to_string(),
1044 upload_path: PATH.into(),
1045 json_body: "".into(),
1046 headers: None,
1047 body_has_info_sections: true,
1048 ping_name: "ping-name".into(),
1049 uploader_capabilities: vec![],
1050 },
1051 );
1052
1053 match upload_manager.get_upload_task(&glean, false) {
1055 PingUploadTask::Wait { time } => {
1056 thread::sleep(Duration::from_millis(time));
1058 }
1059 _ => panic!("Expected upload manager to return a wait task!"),
1060 };
1061
1062 let task = upload_manager.get_upload_task(&glean, false);
1063 assert!(task.is_upload());
1064 }
1065
1066 #[test]
1067 fn clearing_the_queue_works_correctly() {
1068 let (glean, dir) = new_glean(None);
1069
1070 let upload_manager = PingUploadManager::no_policy(dir.path());
1071
1072 for _ in 0..10 {
1074 upload_manager.enqueue_ping(
1075 &glean,
1076 PingPayload {
1077 document_id: Uuid::new_v4().to_string(),
1078 upload_path: PATH.into(),
1079 json_body: "".into(),
1080 headers: None,
1081 body_has_info_sections: true,
1082 ping_name: "ping-name".into(),
1083 uploader_capabilities: vec![],
1084 },
1085 );
1086 }
1087
1088 drop(upload_manager.clear_ping_queue());
1090
1091 assert_eq!(
1093 upload_manager.get_upload_task(&glean, false),
1094 PingUploadTask::done()
1095 );
1096 }
1097
1098 #[test]
1099 fn clearing_the_queue_doesnt_clear_deletion_request_pings() {
1100 let (mut glean, _t) = new_glean(None);
1101
1102 let ping_type = PingType::new(
1104 "test",
1105 true,
1106 true,
1107 true,
1108 true,
1109 true,
1110 vec![],
1111 vec![],
1112 true,
1113 vec![],
1114 );
1115 glean.register_ping_type(&ping_type);
1116
1117 let n = 10;
1119 for _ in 0..n {
1120 ping_type.submit_sync(&glean, None);
1121 }
1122
1123 glean
1124 .internal_pings
1125 .deletion_request
1126 .submit_sync(&glean, None);
1127
1128 drop(glean.upload_manager.clear_ping_queue());
1130
1131 let upload_task = glean.get_upload_task();
1132 match upload_task {
1133 PingUploadTask::Upload { request } => assert!(request.is_deletion_request()),
1134 _ => panic!("Expected upload manager to return the next request!"),
1135 }
1136
1137 assert_eq!(glean.get_upload_task(), PingUploadTask::done());
1139 }
1140
1141 #[test]
1142 fn fills_up_queue_successfully_from_disk() {
1143 let (mut glean, dir) = new_glean(None);
1144
1145 let ping_type = PingType::new(
1147 "test",
1148 true,
1149 true,
1150 true,
1151 true,
1152 true,
1153 vec![],
1154 vec![],
1155 true,
1156 vec![],
1157 );
1158 glean.register_ping_type(&ping_type);
1159
1160 let n = 10;
1162 for _ in 0..n {
1163 ping_type.submit_sync(&glean, None);
1164 }
1165
1166 let upload_manager = PingUploadManager::no_policy(dir.path());
1168
1169 for _ in 0..n {
1171 let task = upload_manager.get_upload_task(&glean, false);
1172 assert!(task.is_upload());
1173 }
1174
1175 assert_eq!(
1177 upload_manager.get_upload_task(&glean, false),
1178 PingUploadTask::done()
1179 );
1180 }
1181
1182 #[test]
1183 fn processes_correctly_success_upload_response() {
1184 let (mut glean, dir) = new_glean(None);
1185
1186 let ping_type = PingType::new(
1188 "test",
1189 true,
1190 true,
1191 true,
1192 true,
1193 true,
1194 vec![],
1195 vec![],
1196 true,
1197 vec![],
1198 );
1199 glean.register_ping_type(&ping_type);
1200
1201 ping_type.submit_sync(&glean, None);
1203
1204 let pending_pings_dir = dir.path().join(PENDING_PINGS_DIRECTORY);
1206
1207 match glean.get_upload_task() {
1209 PingUploadTask::Upload { request } => {
1210 let document_id = request.document_id;
1212 glean.process_ping_upload_response(&document_id, UploadResult::http_status(200));
1213 assert!(!pending_pings_dir.join(document_id).exists());
1215 }
1216 _ => panic!("Expected upload manager to return the next request!"),
1217 }
1218
1219 assert_eq!(glean.get_upload_task(), PingUploadTask::done());
1221 }
1222
1223 #[test]
1224 fn processes_correctly_client_error_upload_response() {
1225 let (mut glean, dir) = new_glean(None);
1226
1227 let ping_type = PingType::new(
1229 "test",
1230 true,
1231 true,
1232 true,
1233 true,
1234 true,
1235 vec![],
1236 vec![],
1237 true,
1238 vec![],
1239 );
1240 glean.register_ping_type(&ping_type);
1241
1242 ping_type.submit_sync(&glean, None);
1244
1245 let pending_pings_dir = dir.path().join(PENDING_PINGS_DIRECTORY);
1247
1248 match glean.get_upload_task() {
1250 PingUploadTask::Upload { request } => {
1251 let document_id = request.document_id;
1253 glean.process_ping_upload_response(&document_id, UploadResult::http_status(404));
1254 assert!(!pending_pings_dir.join(document_id).exists());
1256 }
1257 _ => panic!("Expected upload manager to return the next request!"),
1258 }
1259
1260 assert_eq!(glean.get_upload_task(), PingUploadTask::done());
1262 }
1263
1264 #[test]
1265 fn processes_correctly_server_error_upload_response() {
1266 let (mut glean, _t) = new_glean(None);
1267
1268 let ping_type = PingType::new(
1270 "test",
1271 true,
1272 true,
1273 true,
1274 true,
1275 true,
1276 vec![],
1277 vec![],
1278 true,
1279 vec![],
1280 );
1281 glean.register_ping_type(&ping_type);
1282
1283 ping_type.submit_sync(&glean, None);
1285
1286 match glean.get_upload_task() {
1288 PingUploadTask::Upload { request } => {
1289 let document_id = request.document_id;
1291 glean.process_ping_upload_response(&document_id, UploadResult::http_status(500));
1292 match glean.get_upload_task() {
1294 PingUploadTask::Upload { request } => {
1295 assert_eq!(document_id, request.document_id);
1296 }
1297 _ => panic!("Expected upload manager to return the next request!"),
1298 }
1299 }
1300 _ => panic!("Expected upload manager to return the next request!"),
1301 }
1302
1303 assert_eq!(glean.get_upload_task(), PingUploadTask::done());
1305 }
1306
1307 #[test]
1308 fn processes_correctly_unrecoverable_upload_response() {
1309 let (mut glean, dir) = new_glean(None);
1310
1311 let ping_type = PingType::new(
1313 "test",
1314 true,
1315 true,
1316 true,
1317 true,
1318 true,
1319 vec![],
1320 vec![],
1321 true,
1322 vec![],
1323 );
1324 glean.register_ping_type(&ping_type);
1325
1326 ping_type.submit_sync(&glean, None);
1328
1329 let pending_pings_dir = dir.path().join(PENDING_PINGS_DIRECTORY);
1331
1332 match glean.get_upload_task() {
1334 PingUploadTask::Upload { request } => {
1335 let document_id = request.document_id;
1337 glean.process_ping_upload_response(
1338 &document_id,
1339 UploadResult::unrecoverable_failure(),
1340 );
1341 assert!(!pending_pings_dir.join(document_id).exists());
1343 }
1344 _ => panic!("Expected upload manager to return the next request!"),
1345 }
1346
1347 assert_eq!(glean.get_upload_task(), PingUploadTask::done());
1349 }
1350
1351 #[test]
1352 fn new_pings_are_added_while_upload_in_progress() {
1353 let (glean, dir) = new_glean(None);
1354
1355 let upload_manager = PingUploadManager::no_policy(dir.path());
1356
1357 let doc1 = Uuid::new_v4().to_string();
1358 let path1 = format!("/submit/app_id/test-ping/1/{}", doc1);
1359
1360 let doc2 = Uuid::new_v4().to_string();
1361 let path2 = format!("/submit/app_id/test-ping/1/{}", doc2);
1362
1363 upload_manager.enqueue_ping(
1365 &glean,
1366 PingPayload {
1367 document_id: doc1.clone(),
1368 upload_path: path1,
1369 json_body: "".into(),
1370 headers: None,
1371 body_has_info_sections: true,
1372 ping_name: "test-ping".into(),
1373 uploader_capabilities: vec![],
1374 },
1375 );
1376
1377 let req = match upload_manager.get_upload_task(&glean, false) {
1379 PingUploadTask::Upload { request } => request,
1380 _ => panic!("Expected upload manager to return the next request!"),
1381 };
1382 assert_eq!(doc1, req.document_id);
1383
1384 upload_manager.enqueue_ping(
1386 &glean,
1387 PingPayload {
1388 document_id: doc2.clone(),
1389 upload_path: path2,
1390 json_body: "".into(),
1391 headers: None,
1392 body_has_info_sections: true,
1393 ping_name: "test-ping".into(),
1394 uploader_capabilities: vec![],
1395 },
1396 );
1397
1398 upload_manager.process_ping_upload_response(
1400 &glean,
1401 &req.document_id,
1402 UploadResult::http_status(200),
1403 );
1404
1405 let req = match upload_manager.get_upload_task(&glean, false) {
1407 PingUploadTask::Upload { request } => request,
1408 _ => panic!("Expected upload manager to return the next request!"),
1409 };
1410 assert_eq!(doc2, req.document_id);
1411
1412 upload_manager.process_ping_upload_response(
1414 &glean,
1415 &req.document_id,
1416 UploadResult::http_status(200),
1417 );
1418
1419 assert_eq!(
1421 upload_manager.get_upload_task(&glean, false),
1422 PingUploadTask::done()
1423 );
1424 }
1425
1426 #[test]
1427 fn adds_debug_view_header_to_requests_when_tag_is_set() {
1428 let (mut glean, _t) = new_glean(None);
1429
1430 glean.set_debug_view_tag("valid-tag");
1431
1432 let ping_type = PingType::new(
1434 "test",
1435 true,
1436 true,
1437 true,
1438 true,
1439 true,
1440 vec![],
1441 vec![],
1442 true,
1443 vec![],
1444 );
1445 glean.register_ping_type(&ping_type);
1446
1447 ping_type.submit_sync(&glean, None);
1449
1450 match glean.get_upload_task() {
1452 PingUploadTask::Upload { request } => {
1453 assert_eq!(request.headers.get("X-Debug-ID").unwrap(), "valid-tag")
1454 }
1455 _ => panic!("Expected upload manager to return the next request!"),
1456 }
1457 }
1458
1459 #[test]
1460 fn duplicates_are_not_enqueued() {
1461 let (glean, dir) = new_glean(None);
1462
1463 let upload_manager = PingUploadManager::no_policy(dir.path());
1466
1467 let doc_id = Uuid::new_v4().to_string();
1468 let path = format!("/submit/app_id/test-ping/1/{}", doc_id);
1469
1470 upload_manager.enqueue_ping(
1472 &glean,
1473 PingPayload {
1474 document_id: doc_id.clone(),
1475 upload_path: path.clone(),
1476 json_body: "".into(),
1477 headers: None,
1478 body_has_info_sections: true,
1479 ping_name: "test-ping".into(),
1480 uploader_capabilities: vec![],
1481 },
1482 );
1483 upload_manager.enqueue_ping(
1484 &glean,
1485 PingPayload {
1486 document_id: doc_id,
1487 upload_path: path,
1488 json_body: "".into(),
1489 headers: None,
1490 body_has_info_sections: true,
1491 ping_name: "test-ping".into(),
1492 uploader_capabilities: vec![],
1493 },
1494 );
1495
1496 let task = upload_manager.get_upload_task(&glean, false);
1498 assert!(task.is_upload());
1499
1500 assert_eq!(
1502 upload_manager.get_upload_task(&glean, false),
1503 PingUploadTask::done()
1504 );
1505 }
1506
1507 #[test]
1508 fn maximum_of_recoverable_errors_is_enforced_for_uploading_window() {
1509 let (mut glean, dir) = new_glean(None);
1510
1511 let ping_type = PingType::new(
1513 "test",
1514 true,
1515 true,
1516 true,
1517 true,
1518 true,
1519 vec![],
1520 vec![],
1521 true,
1522 vec![],
1523 );
1524 glean.register_ping_type(&ping_type);
1525
1526 let n = 5;
1528 for _ in 0..n {
1529 ping_type.submit_sync(&glean, None);
1530 }
1531
1532 let mut upload_manager = PingUploadManager::no_policy(dir.path());
1533
1534 let max_recoverable_failures = 3;
1536 upload_manager
1537 .policy
1538 .set_max_recoverable_failures(Some(max_recoverable_failures));
1539
1540 for _ in 0..max_recoverable_failures {
1542 match upload_manager.get_upload_task(&glean, false) {
1543 PingUploadTask::Upload { request } => {
1544 upload_manager.process_ping_upload_response(
1545 &glean,
1546 &request.document_id,
1547 UploadResult::recoverable_failure(),
1548 );
1549 }
1550 _ => panic!("Expected upload manager to return the next request!"),
1551 }
1552 }
1553
1554 assert_eq!(
1557 upload_manager.get_upload_task(&glean, false),
1558 PingUploadTask::done()
1559 );
1560
1561 for _ in 0..n {
1563 let task = upload_manager.get_upload_task(&glean, false);
1564 assert!(task.is_upload());
1565 }
1566 }
1567
1568 #[test]
1569 fn quota_is_enforced_when_enqueueing_cached_pings() {
1570 let (mut glean, dir) = new_glean(None);
1571
1572 let ping_type = PingType::new(
1574 "test",
1575 true,
1576 true,
1577 true,
1578 true,
1579 true,
1580 vec![],
1581 vec![],
1582 true,
1583 vec![],
1584 );
1585 glean.register_ping_type(&ping_type);
1586
1587 let n = 10;
1589 for _ in 0..n {
1590 ping_type.submit_sync(&glean, None);
1591 }
1592
1593 let directory_manager = PingDirectoryManager::new(dir.path());
1594 let pending_pings = directory_manager.process_dirs().pending_pings;
1595 let (_, newest_ping) = &pending_pings.last().unwrap();
1598 let PingPayload {
1599 document_id: newest_ping_id,
1600 ..
1601 } = &newest_ping;
1602
1603 let mut upload_manager = PingUploadManager::no_policy(dir.path());
1605
1606 upload_manager
1613 .policy
1614 .set_max_pending_pings_directory_size(Some(500));
1615
1616 match upload_manager.get_upload_task(&glean, false) {
1620 PingUploadTask::Upload { request } => assert_eq!(&request.document_id, newest_ping_id),
1621 _ => panic!("Expected upload manager to return the next request!"),
1622 }
1623
1624 assert_eq!(
1627 upload_manager.get_upload_task(&glean, false),
1628 PingUploadTask::done()
1629 );
1630
1631 assert_eq!(
1633 n - 1,
1634 upload_manager
1635 .upload_metrics
1636 .deleted_pings_after_quota_hit
1637 .get_value(&glean, Some("metrics"))
1638 .unwrap()
1639 );
1640 assert_eq!(
1641 n,
1642 upload_manager
1643 .upload_metrics
1644 .pending_pings
1645 .get_value(&glean, Some("metrics"))
1646 .unwrap()
1647 );
1648 }
1649
1650 #[test]
1651 fn number_quota_is_enforced_when_enqueueing_cached_pings() {
1652 let (mut glean, dir) = new_glean(None);
1653
1654 let ping_type = PingType::new(
1656 "test",
1657 true,
1658 true,
1659 true,
1660 true,
1661 true,
1662 vec![],
1663 vec![],
1664 true,
1665 vec![],
1666 );
1667 glean.register_ping_type(&ping_type);
1668
1669 let count_quota = 3;
1671 let n = 10;
1673
1674 for _ in 0..n {
1676 ping_type.submit_sync(&glean, None);
1677 }
1678
1679 let directory_manager = PingDirectoryManager::new(dir.path());
1680 let pending_pings = directory_manager.process_dirs().pending_pings;
1681 let expected_pings = pending_pings
1684 .iter()
1685 .rev()
1686 .take(count_quota)
1687 .map(|(_, ping)| ping.document_id.clone())
1688 .collect::<Vec<_>>();
1689
1690 let mut upload_manager = PingUploadManager::no_policy(dir.path());
1692
1693 upload_manager
1694 .policy
1695 .set_max_pending_pings_count(Some(count_quota as u64));
1696
1697 for ping_id in expected_pings.iter().rev() {
1701 match upload_manager.get_upload_task(&glean, false) {
1702 PingUploadTask::Upload { request } => assert_eq!(&request.document_id, ping_id),
1703 _ => panic!("Expected upload manager to return the next request!"),
1704 }
1705 }
1706
1707 assert_eq!(
1710 upload_manager.get_upload_task(&glean, false),
1711 PingUploadTask::done()
1712 );
1713
1714 assert_eq!(
1716 (n - count_quota) as i32,
1717 upload_manager
1718 .upload_metrics
1719 .deleted_pings_after_quota_hit
1720 .get_value(&glean, Some("metrics"))
1721 .unwrap()
1722 );
1723 assert_eq!(
1724 n as i32,
1725 upload_manager
1726 .upload_metrics
1727 .pending_pings
1728 .get_value(&glean, Some("metrics"))
1729 .unwrap()
1730 );
1731 }
1732
1733 #[test]
1734 fn size_and_count_quota_work_together_size_first() {
1735 let (mut glean, dir) = new_glean(None);
1736
1737 let ping_type = PingType::new(
1739 "test",
1740 true,
1741 true,
1742 true,
1743 true,
1744 true,
1745 vec![],
1746 vec![],
1747 true,
1748 vec![],
1749 );
1750 glean.register_ping_type(&ping_type);
1751
1752 let expected_number_of_pings = 3;
1753 let n = 10;
1755
1756 for _ in 0..n {
1758 ping_type.submit_sync(&glean, None);
1759 }
1760
1761 let directory_manager = PingDirectoryManager::new(dir.path());
1762 let pending_pings = directory_manager.process_dirs().pending_pings;
1763 let expected_pings = pending_pings
1766 .iter()
1767 .rev()
1768 .take(expected_number_of_pings)
1769 .map(|(_, ping)| ping.document_id.clone())
1770 .collect::<Vec<_>>();
1771
1772 let mut upload_manager = PingUploadManager::no_policy(dir.path());
1774
1775 upload_manager
1778 .policy
1779 .set_max_pending_pings_directory_size(Some(1300));
1780 upload_manager.policy.set_max_pending_pings_count(Some(5));
1781
1782 for ping_id in expected_pings.iter().rev() {
1786 match upload_manager.get_upload_task(&glean, false) {
1787 PingUploadTask::Upload { request } => assert_eq!(&request.document_id, ping_id),
1788 _ => panic!("Expected upload manager to return the next request!"),
1789 }
1790 }
1791
1792 assert_eq!(
1795 upload_manager.get_upload_task(&glean, false),
1796 PingUploadTask::done()
1797 );
1798
1799 assert_eq!(
1801 (n - expected_number_of_pings) as i32,
1802 upload_manager
1803 .upload_metrics
1804 .deleted_pings_after_quota_hit
1805 .get_value(&glean, Some("metrics"))
1806 .unwrap()
1807 );
1808 assert_eq!(
1809 n as i32,
1810 upload_manager
1811 .upload_metrics
1812 .pending_pings
1813 .get_value(&glean, Some("metrics"))
1814 .unwrap()
1815 );
1816 assert_eq!(
1818 (n - expected_number_of_pings) as i32,
1819 upload_manager
1820 .upload_metrics
1821 .pending_pings_deleted
1822 .get("size_quota")
1823 .get_value(&glean, Some("health"))
1824 .unwrap()
1825 );
1826 assert!(upload_manager
1827 .upload_metrics
1828 .pending_pings_deleted
1829 .get("count_quota")
1830 .get_value(&glean, Some("health"))
1831 .is_none());
1832 }
1833
1834 #[test]
1835 fn size_and_count_quota_work_together_count_first() {
1836 let (mut glean, dir) = new_glean(None);
1837
1838 let ping_type = PingType::new(
1840 "test",
1841 true,
1842 true,
1843 true,
1844 true,
1845 true,
1846 vec![],
1847 vec![],
1848 true,
1849 vec![],
1850 );
1851 glean.register_ping_type(&ping_type);
1852
1853 let expected_number_of_pings = 2;
1854 let n = 10;
1856
1857 for _ in 0..n {
1859 ping_type.submit_sync(&glean, None);
1860 }
1861
1862 let directory_manager = PingDirectoryManager::new(dir.path());
1863 let pending_pings = directory_manager.process_dirs().pending_pings;
1864 let expected_pings = pending_pings
1867 .iter()
1868 .rev()
1869 .take(expected_number_of_pings)
1870 .map(|(_, ping)| ping.document_id.clone())
1871 .collect::<Vec<_>>();
1872
1873 let mut upload_manager = PingUploadManager::no_policy(dir.path());
1875
1876 upload_manager
1878 .policy
1879 .set_max_pending_pings_directory_size(Some(100_000));
1880 upload_manager.policy.set_max_pending_pings_count(Some(2));
1881
1882 for ping_id in expected_pings.iter().rev() {
1886 match upload_manager.get_upload_task(&glean, false) {
1887 PingUploadTask::Upload { request } => assert_eq!(&request.document_id, ping_id),
1888 _ => panic!("Expected upload manager to return the next request!"),
1889 }
1890 }
1891
1892 assert_eq!(
1895 upload_manager.get_upload_task(&glean, false),
1896 PingUploadTask::done()
1897 );
1898
1899 assert_eq!(
1901 (n - expected_number_of_pings) as i32,
1902 upload_manager
1903 .upload_metrics
1904 .deleted_pings_after_quota_hit
1905 .get_value(&glean, Some("metrics"))
1906 .unwrap()
1907 );
1908 assert_eq!(
1909 n as i32,
1910 upload_manager
1911 .upload_metrics
1912 .pending_pings
1913 .get_value(&glean, Some("metrics"))
1914 .unwrap()
1915 );
1916 assert_eq!(
1918 (n - expected_number_of_pings) as i32,
1919 upload_manager
1920 .upload_metrics
1921 .pending_pings_deleted
1922 .get("count_quota")
1923 .get_value(&glean, Some("health"))
1924 .unwrap()
1925 );
1926 assert!(upload_manager
1927 .upload_metrics
1928 .pending_pings_deleted
1929 .get("size_quota")
1930 .get_value(&glean, Some("health"))
1931 .is_none());
1932 }
1933
1934 #[test]
1935 fn pending_pings_deleted_is_not_recorded_when_quota_not_hit() {
1936 let (mut glean, dir) = new_glean(None);
1937
1938 let ping_type = PingType::new(
1939 "test",
1940 true,
1941 true,
1942 true,
1943 true,
1944 true,
1945 vec![],
1946 vec![],
1947 true,
1948 vec![],
1949 );
1950 glean.register_ping_type(&ping_type);
1951
1952 for _ in 0..3 {
1954 ping_type.submit_sync(&glean, None);
1955 }
1956
1957 let mut upload_manager = PingUploadManager::no_policy(dir.path());
1958 upload_manager.policy.set_max_pending_pings_count(Some(10));
1959 upload_manager
1960 .policy
1961 .set_max_pending_pings_directory_size(Some(1024 * 1024));
1962
1963 upload_manager.get_upload_task(&glean, false);
1964
1965 assert!(upload_manager
1966 .upload_metrics
1967 .pending_pings_deleted
1968 .get("count_quota")
1969 .get_value(&glean, Some("health"))
1970 .is_none());
1971 assert!(upload_manager
1972 .upload_metrics
1973 .pending_pings_deleted
1974 .get("size_quota")
1975 .get_value(&glean, Some("health"))
1976 .is_none());
1977 }
1978
1979 #[test]
1980 fn pending_pings_config_overrides_are_applied() {
1981 let (_, dir) = new_glean(None);
1982
1983 let mut upload_manager = PingUploadManager::new(dir.path(), "test");
1984
1985 let custom_count: u64 = 42;
1986 let custom_size: u64 = 999_999;
1987 upload_manager.set_max_pending_pings_count(custom_count);
1988 upload_manager.set_max_pending_pings_directory_size(custom_size);
1989
1990 assert_eq!(
1991 custom_count,
1992 upload_manager.policy.max_pending_pings_count()
1993 );
1994 assert_eq!(
1995 custom_size,
1996 upload_manager.policy.max_pending_pings_directory_size()
1997 );
1998 }
1999
2000 #[test]
2001 fn maximum_wait_attemps_is_enforced() {
2002 let (glean, dir) = new_glean(None);
2003
2004 let mut upload_manager = PingUploadManager::no_policy(dir.path());
2005
2006 let max_wait_attempts = 3;
2008 upload_manager
2009 .policy
2010 .set_max_wait_attempts(Some(max_wait_attempts));
2011
2012 let secs_per_interval = 5;
2018 let max_pings_per_interval = 1;
2019 upload_manager.set_rate_limiter(secs_per_interval, max_pings_per_interval);
2020
2021 upload_manager.enqueue_ping(
2023 &glean,
2024 PingPayload {
2025 document_id: Uuid::new_v4().to_string(),
2026 upload_path: PATH.into(),
2027 json_body: "".into(),
2028 headers: None,
2029 body_has_info_sections: true,
2030 ping_name: "ping-name".into(),
2031 uploader_capabilities: vec![],
2032 },
2033 );
2034 upload_manager.enqueue_ping(
2035 &glean,
2036 PingPayload {
2037 document_id: Uuid::new_v4().to_string(),
2038 upload_path: PATH.into(),
2039 json_body: "".into(),
2040 headers: None,
2041 body_has_info_sections: true,
2042 ping_name: "ping-name".into(),
2043 uploader_capabilities: vec![],
2044 },
2045 );
2046
2047 match upload_manager.get_upload_task(&glean, false) {
2049 PingUploadTask::Upload { .. } => {}
2050 _ => panic!("Expected upload manager to return the next request!"),
2051 }
2052
2053 for _ in 0..max_wait_attempts {
2057 let task = upload_manager.get_upload_task(&glean, false);
2058 assert!(task.is_wait());
2059 }
2060
2061 assert_eq!(
2064 upload_manager.get_upload_task(&glean, false),
2065 PingUploadTask::done()
2066 );
2067
2068 thread::sleep(Duration::from_secs(secs_per_interval));
2070
2071 let task = upload_manager.get_upload_task(&glean, false);
2073 assert!(task.is_upload());
2074
2075 assert_eq!(
2077 upload_manager.get_upload_task(&glean, false),
2078 PingUploadTask::done()
2079 );
2080 }
2081
2082 #[test]
2083 fn wait_task_contains_expected_wait_time_when_pending_pings_dir_not_processed_yet() {
2084 let (glean, dir) = new_glean(None);
2085 let upload_manager = PingUploadManager::new(dir.path(), "test");
2086 match upload_manager.get_upload_task(&glean, false) {
2087 PingUploadTask::Wait { time } => {
2088 assert_eq!(time, WAIT_TIME_FOR_PING_PROCESSING);
2089 }
2090 _ => panic!("Expected upload manager to return a wait task!"),
2091 };
2092 }
2093
2094 #[test]
2095 fn cannot_enqueue_ping_while_its_being_processed() {
2096 let (glean, dir) = new_glean(None);
2097
2098 let upload_manager = PingUploadManager::no_policy(dir.path());
2099
2100 let identifier = &Uuid::new_v4();
2102 let ping = PingPayload {
2103 document_id: identifier.to_string(),
2104 upload_path: PATH.into(),
2105 json_body: "".into(),
2106 headers: None,
2107 body_has_info_sections: true,
2108 ping_name: "ping-name".into(),
2109 uploader_capabilities: vec![],
2110 };
2111 upload_manager.enqueue_ping(&glean, ping);
2112 assert!(upload_manager.get_upload_task(&glean, false).is_upload());
2113
2114 let ping = PingPayload {
2116 document_id: identifier.to_string(),
2117 upload_path: PATH.into(),
2118 json_body: "".into(),
2119 headers: None,
2120 body_has_info_sections: true,
2121 ping_name: "ping-name".into(),
2122 uploader_capabilities: vec![],
2123 };
2124 upload_manager.enqueue_ping(&glean, ping);
2125
2126 assert_eq!(
2128 upload_manager.get_upload_task(&glean, false),
2129 PingUploadTask::done()
2130 );
2131
2132 upload_manager.process_ping_upload_response(
2134 &glean,
2135 &identifier.to_string(),
2136 UploadResult::http_status(200),
2137 );
2138 }
2139
2140 #[test]
2141 fn stores_pings_during_submission_and_upload_if_enabled() {
2142 let (mut glean, _t) = new_glean(None);
2143 glean.set_store_submitted_pings_enabled(true);
2144
2145 let ping_type = PingType::new(
2147 "test",
2148 true,
2149 true,
2150 true,
2151 true,
2152 true,
2153 vec![],
2154 vec![],
2155 true,
2156 vec![],
2157 );
2158 glean.register_ping_type(&ping_type);
2159
2160 ping_type.submit_sync(&glean, None);
2162
2163 let pings = glean.storage().get_all_submitted_pings();
2164 assert_eq!(pings.len(), 1);
2165 let ping = pings.first().unwrap();
2166 assert!(ping.submitted_date.0 <= Utc::now());
2167 assert!(ping.uploaded_date.is_none());
2168
2169 match glean.get_upload_task() {
2171 PingUploadTask::Upload { request } => {
2172 let document_id = request.document_id;
2174 glean.process_ping_upload_response(&document_id, UploadResult::http_status(200));
2175 }
2176 _ => panic!("Expected upload manager to return the next request!"),
2177 }
2178
2179 let pings = glean.storage().get_all_submitted_pings();
2180 assert_eq!(pings.len(), 1);
2181 let ping = pings.first().unwrap();
2182 assert!(ping.submitted_date.0 <= Utc::now());
2183 assert!(ping.uploaded_date.is_some());
2184
2185 assert_eq!(glean.get_upload_task(), PingUploadTask::done());
2187 }
2188
2189 #[test]
2190 fn stores_pings_during_submission_and_marks_as_upload_failed_when_appropriate() {
2191 let (mut glean, _t) = new_glean(None);
2192 glean.set_store_submitted_pings_enabled(true);
2193
2194 let ping_type = PingType::new(
2196 "test",
2197 true,
2198 true,
2199 true,
2200 true,
2201 true,
2202 vec![],
2203 vec![],
2204 true,
2205 vec![],
2206 );
2207 glean.register_ping_type(&ping_type);
2208
2209 ping_type.submit_sync(&glean, None);
2211
2212 let pings = glean.storage().get_all_submitted_pings();
2213 assert_eq!(pings.len(), 1);
2214 let ping = pings.first().unwrap();
2215 assert!(ping.submitted_date.0 <= Utc::now());
2216 assert!(ping.uploaded_date.is_none());
2217
2218 match glean.get_upload_task() {
2220 PingUploadTask::Upload { request } => {
2221 let document_id = request.document_id;
2223 glean.process_ping_upload_response(&document_id, UploadResult::http_status(400));
2224 }
2225 _ => panic!("Expected upload manager to return the next request!"),
2226 }
2227
2228 let pings = glean.storage().get_all_submitted_pings();
2229 assert_eq!(pings.len(), 1);
2230 let ping = pings.first().unwrap();
2231 assert!(ping.submitted_date.0 <= Utc::now());
2232 assert!(ping.upload_failed.is_some());
2233 assert!(ping.uploaded_date.is_none());
2234
2235 assert_eq!(glean.get_upload_task(), PingUploadTask::done());
2237 }
2238}