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