|
| 1 | +//! Statistics services. |
| 2 | +//! |
| 3 | +//! It includes: |
| 4 | +//! |
| 5 | +//! - A [`factory`](crate::packages::udp_tracker_core::statistics::setup::factory) function to build the structs needed to collect the tracker metrics. |
| 6 | +//! - A [`get_metrics`] service to get the tracker [`metrics`](crate::packages::udp_tracker_core::statistics::metrics::Metrics). |
| 7 | +//! |
| 8 | +//! Tracker metrics are collected using a Publisher-Subscribe pattern. |
| 9 | +//! |
| 10 | +//! The factory function builds two structs: |
| 11 | +//! |
| 12 | +//! - An statistics event [`Sender`](crate::packages::udp_tracker_core::statistics::event::sender::Sender) |
| 13 | +//! - An statistics [`Repository`] |
| 14 | +//! |
| 15 | +//! ```text |
| 16 | +//! let (stats_event_sender, stats_repository) = factory(tracker_usage_statistics); |
| 17 | +//! ``` |
| 18 | +//! |
| 19 | +//! The statistics repository is responsible for storing the metrics in memory. |
| 20 | +//! The statistics event sender allows sending events related to metrics. |
| 21 | +//! There is an event listener that is receiving all the events and processing them with an event handler. |
| 22 | +//! Then, the event handler updates the metrics depending on the received event. |
| 23 | +//! |
| 24 | +//! For example, if you send the event [`Event::Udp4Connect`](crate::packages::udp_tracker_core::statistics::event::Event::Udp4Connect): |
| 25 | +//! |
| 26 | +//! ```text |
| 27 | +//! let result = event_sender.send_event(Event::Udp4Connect).await; |
| 28 | +//! ``` |
| 29 | +//! |
| 30 | +//! Eventually the counter for UDP connections from IPv4 peers will be increased. |
| 31 | +//! |
| 32 | +//! ```rust,no_run |
| 33 | +//! pub struct Metrics { |
| 34 | +//! // ... |
| 35 | +//! pub udp4_connections_handled: u64, // This will be incremented |
| 36 | +//! // ... |
| 37 | +//! } |
| 38 | +//! ``` |
| 39 | +use std::sync::Arc; |
| 40 | + |
| 41 | +use bittorrent_tracker_core::torrent::repository::in_memory::InMemoryTorrentRepository; |
| 42 | +use packages::udp_tracker_core::statistics::metrics::Metrics; |
| 43 | +use packages::udp_tracker_core::statistics::repository::Repository; |
| 44 | +use tokio::sync::RwLock; |
| 45 | +use torrust_tracker_primitives::torrent_metrics::TorrentsMetrics; |
| 46 | + |
| 47 | +use crate::packages; |
| 48 | +use crate::servers::udp::server::banning::BanService; |
| 49 | + |
| 50 | +/// All the metrics collected by the tracker. |
| 51 | +#[derive(Debug, PartialEq)] |
| 52 | +pub struct TrackerMetrics { |
| 53 | + /// Domain level metrics. |
| 54 | + /// |
| 55 | + /// General metrics for all torrents (number of seeders, leechers, etcetera) |
| 56 | + pub torrents_metrics: TorrentsMetrics, |
| 57 | + |
| 58 | + /// Application level metrics. Usage statistics/metrics. |
| 59 | + /// |
| 60 | + /// Metrics about how the tracker is been used (number of udp announce requests, etcetera) |
| 61 | + pub protocol_metrics: Metrics, |
| 62 | +} |
| 63 | + |
| 64 | +/// It returns all the [`TrackerMetrics`] |
| 65 | +pub async fn get_metrics( |
| 66 | + in_memory_torrent_repository: Arc<InMemoryTorrentRepository>, |
| 67 | + ban_service: Arc<RwLock<BanService>>, |
| 68 | + stats_repository: Arc<Repository>, |
| 69 | +) -> TrackerMetrics { |
| 70 | + let torrents_metrics = in_memory_torrent_repository.get_torrents_metrics(); |
| 71 | + let stats = stats_repository.get_stats().await; |
| 72 | + let udp_banned_ips_total = ban_service.read().await.get_banned_ips_total(); |
| 73 | + |
| 74 | + TrackerMetrics { |
| 75 | + torrents_metrics, |
| 76 | + protocol_metrics: Metrics { |
| 77 | + // UDP |
| 78 | + udp_requests_aborted: stats.udp_requests_aborted, |
| 79 | + udp_requests_banned: stats.udp_requests_banned, |
| 80 | + udp_banned_ips_total: udp_banned_ips_total as u64, |
| 81 | + udp_avg_connect_processing_time_ns: stats.udp_avg_connect_processing_time_ns, |
| 82 | + udp_avg_announce_processing_time_ns: stats.udp_avg_announce_processing_time_ns, |
| 83 | + udp_avg_scrape_processing_time_ns: stats.udp_avg_scrape_processing_time_ns, |
| 84 | + // UDPv4 |
| 85 | + udp4_requests: stats.udp4_requests, |
| 86 | + udp4_connections_handled: stats.udp4_connections_handled, |
| 87 | + udp4_announces_handled: stats.udp4_announces_handled, |
| 88 | + udp4_scrapes_handled: stats.udp4_scrapes_handled, |
| 89 | + udp4_responses: stats.udp4_responses, |
| 90 | + udp4_errors_handled: stats.udp4_errors_handled, |
| 91 | + // UDPv6 |
| 92 | + udp6_requests: stats.udp6_requests, |
| 93 | + udp6_connections_handled: stats.udp6_connections_handled, |
| 94 | + udp6_announces_handled: stats.udp6_announces_handled, |
| 95 | + udp6_scrapes_handled: stats.udp6_scrapes_handled, |
| 96 | + udp6_responses: stats.udp6_responses, |
| 97 | + udp6_errors_handled: stats.udp6_errors_handled, |
| 98 | + }, |
| 99 | + } |
| 100 | +} |
| 101 | + |
| 102 | +#[cfg(test)] |
| 103 | +mod tests { |
| 104 | + use std::sync::Arc; |
| 105 | + |
| 106 | + use bittorrent_tracker_core::torrent::repository::in_memory::InMemoryTorrentRepository; |
| 107 | + use bittorrent_tracker_core::{self}; |
| 108 | + use tokio::sync::RwLock; |
| 109 | + use torrust_tracker_configuration::Configuration; |
| 110 | + use torrust_tracker_primitives::torrent_metrics::TorrentsMetrics; |
| 111 | + use torrust_tracker_test_helpers::configuration; |
| 112 | + |
| 113 | + use crate::packages::udp_tracker_core::statistics; |
| 114 | + use crate::packages::udp_tracker_core::statistics::services::{get_metrics, TrackerMetrics}; |
| 115 | + use crate::servers::udp::server::banning::BanService; |
| 116 | + use crate::servers::udp::server::launcher::MAX_CONNECTION_ID_ERRORS_PER_IP; |
| 117 | + |
| 118 | + pub fn tracker_configuration() -> Configuration { |
| 119 | + configuration::ephemeral() |
| 120 | + } |
| 121 | + |
| 122 | + #[tokio::test] |
| 123 | + async fn the_statistics_service_should_return_the_tracker_metrics() { |
| 124 | + let config = tracker_configuration(); |
| 125 | + |
| 126 | + let in_memory_torrent_repository = Arc::new(InMemoryTorrentRepository::default()); |
| 127 | + let (_stats_event_sender, stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics); |
| 128 | + let stats_repository = Arc::new(stats_repository); |
| 129 | + let ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP))); |
| 130 | + |
| 131 | + let tracker_metrics = get_metrics( |
| 132 | + in_memory_torrent_repository.clone(), |
| 133 | + ban_service.clone(), |
| 134 | + stats_repository.clone(), |
| 135 | + ) |
| 136 | + .await; |
| 137 | + |
| 138 | + assert_eq!( |
| 139 | + tracker_metrics, |
| 140 | + TrackerMetrics { |
| 141 | + torrents_metrics: TorrentsMetrics::default(), |
| 142 | + protocol_metrics: statistics::metrics::Metrics::default(), |
| 143 | + } |
| 144 | + ); |
| 145 | + } |
| 146 | +} |
0 commit comments