Skip to content

Commit f6aca40

Browse files
committed
Merge #1176: Add more metrics
903d47f feat: [#1145] add UDP avg processing time to stats (Jose Celano) 08a862a refactor: [#1145] add type and processing time to UDP response events (Jose Celano) 1ce2e33 feat: [#1145] add banned ips total for UDP to stats (Jose Celano) 1299f17 feat: make ban service generic for all trackers (Jose Celano) 6f9b44c feat: [#1145] add banned reqs counter to stats (Jose Celano) 2ff476b refactor: rename enum variand Udp4RequestAborted (Jose Celano) Pull request description: Add more metrics useful for detecting tracker errors and load level. ### UDP - [x] `udp_requests_banned`: the total number of UDP requests that have been banned. - [x] `udp_banned_ips_total`: the total number of IPs that have been banned for sending wrong connection IDs. - [x] `udp_avg_connect_processing_time_ns`: the average time processing a UDP connect request. - [x] `udp_avg_announce_processing_time_ns`: the average time processing a UDP announce request. - [x] `udp_avg_scrape_processing_time_ns`: the average time processing a UDP scrape request. ### Important refactor I needed to pass the Ban Service to the stats handler to get some values. I did not want to add the ban service to the tracker because the tracker is already to "fat". It has many responsibilities. In fact, I want to extract new services out of the tracker like whitelist, authorization, etc. My plan was to extract them and leave the tracker as the application services container. However I think it will be easier if we: - We pass new services like `BanService` directly to handlers instead of using the tracker as a facade. - Move other services out of the `Tracker` and also pass them directly to handlers. At the end, the `Tracker` should have only a couple of methods like `announce` and `scrape`. ACKs for top commit: josecelano: ACK 903d47f Tree-SHA512: 31a8436466ea04608558e603fa8e60fd242dd44c5faae890db1234da31ac245d9eea824aa18d6f5ce3a84b94825909239d965d8f6ac62fddf0de3ac8bfd6b228
2 parents c9638f3 + 903d47f commit f6aca40

28 files changed

+448
-97
lines changed

src/app.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@
2323
//! - Tracker REST API: the tracker API can be enabled/disabled.
2424
use std::sync::Arc;
2525

26+
use tokio::sync::RwLock;
2627
use tokio::task::JoinHandle;
2728
use torrust_tracker_configuration::Configuration;
2829
use tracing::instrument;
2930

3031
use crate::bootstrap::jobs::{health_check_api, http_tracker, torrent_cleanup, tracker_apis, udp_tracker};
3132
use crate::servers::registar::Registar;
33+
use crate::servers::udp::server::banning::BanService;
3234
use crate::{core, servers};
3335

3436
/// # Panics
@@ -37,8 +39,12 @@ use crate::{core, servers};
3739
///
3840
/// - Can't retrieve tracker keys from database.
3941
/// - Can't load whitelist from database.
40-
#[instrument(skip(config, tracker))]
41-
pub async fn start(config: &Configuration, tracker: Arc<core::Tracker>) -> Vec<JoinHandle<()>> {
42+
#[instrument(skip(config, tracker, ban_service))]
43+
pub async fn start(
44+
config: &Configuration,
45+
tracker: Arc<core::Tracker>,
46+
ban_service: Arc<RwLock<BanService>>,
47+
) -> Vec<JoinHandle<()>> {
4248
if config.http_api.is_none()
4349
&& (config.udp_trackers.is_none() || config.udp_trackers.as_ref().map_or(true, std::vec::Vec::is_empty))
4450
&& (config.http_trackers.is_none() || config.http_trackers.as_ref().map_or(true, std::vec::Vec::is_empty))
@@ -75,7 +81,9 @@ pub async fn start(config: &Configuration, tracker: Arc<core::Tracker>) -> Vec<J
7581
udp_tracker_config.bind_address
7682
);
7783
} else {
78-
jobs.push(udp_tracker::start_job(udp_tracker_config, tracker.clone(), registar.give_form()).await);
84+
jobs.push(
85+
udp_tracker::start_job(udp_tracker_config, tracker.clone(), ban_service.clone(), registar.give_form()).await,
86+
);
7987
}
8088
}
8189
} else {
@@ -105,6 +113,7 @@ pub async fn start(config: &Configuration, tracker: Arc<core::Tracker>) -> Vec<J
105113
if let Some(job) = tracker_apis::start_job(
106114
http_api_config,
107115
tracker.clone(),
116+
ban_service.clone(),
108117
registar.give_form(),
109118
servers::apis::Version::V1,
110119
)

src/bootstrap/app.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//! 4. Initialize the domain tracker.
1414
use std::sync::Arc;
1515

16+
use tokio::sync::RwLock;
1617
use torrust_tracker_clock::static_time;
1718
use torrust_tracker_configuration::validator::Validator;
1819
use torrust_tracker_configuration::Configuration;
@@ -22,6 +23,8 @@ use super::config::initialize_configuration;
2223
use crate::bootstrap;
2324
use crate::core::services::tracker_factory;
2425
use crate::core::Tracker;
26+
use crate::servers::udp::server::banning::BanService;
27+
use crate::servers::udp::server::launcher::MAX_CONNECTION_ID_ERRORS_PER_IP;
2528
use crate::shared::crypto::ephemeral_instance_keys;
2629
use crate::shared::crypto::keys::{self, Keeper as _};
2730

@@ -32,7 +35,7 @@ use crate::shared::crypto::keys::{self, Keeper as _};
3235
/// Setup can file if the configuration is invalid.
3336
#[must_use]
3437
#[instrument(skip())]
35-
pub fn setup() -> (Configuration, Arc<Tracker>) {
38+
pub fn setup() -> (Configuration, Arc<Tracker>, Arc<RwLock<BanService>>) {
3639
#[cfg(not(test))]
3740
check_seed();
3841

@@ -44,9 +47,11 @@ pub fn setup() -> (Configuration, Arc<Tracker>) {
4447

4548
let tracker = initialize_with_configuration(&configuration);
4649

50+
let udp_ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
51+
4752
tracing::info!("Configuration:\n{}", configuration.clone().mask_secrets().to_json());
4853

49-
(configuration, tracker)
54+
(configuration, tracker, udp_ban_service)
5055
}
5156

5257
/// checks if the seed is the instance seed in production.

src/bootstrap/jobs/tracker_apis.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use std::net::SocketAddr;
2424
use std::sync::Arc;
2525

2626
use axum_server::tls_rustls::RustlsConfig;
27+
use tokio::sync::RwLock;
2728
use tokio::task::JoinHandle;
2829
use torrust_tracker_configuration::{AccessTokens, HttpApi};
2930
use tracing::instrument;
@@ -33,6 +34,7 @@ use crate::core;
3334
use crate::servers::apis::server::{ApiServer, Launcher};
3435
use crate::servers::apis::Version;
3536
use crate::servers::registar::ServiceRegistrationForm;
37+
use crate::servers::udp::server::banning::BanService;
3638

3739
/// This is the message that the "launcher" spawned task sends to the main
3840
/// application process to notify the API server was successfully started.
@@ -54,10 +56,11 @@ pub struct ApiServerJobStarted();
5456
/// It would panic if unable to send the `ApiServerJobStarted` notice.
5557
///
5658
///
57-
#[instrument(skip(config, tracker, form))]
59+
#[instrument(skip(config, tracker, ban_service, form))]
5860
pub async fn start_job(
5961
config: &HttpApi,
6062
tracker: Arc<core::Tracker>,
63+
ban_service: Arc<RwLock<BanService>>,
6164
form: ServiceRegistrationForm,
6265
version: Version,
6366
) -> Option<JoinHandle<()>> {
@@ -70,21 +73,22 @@ pub async fn start_job(
7073
let access_tokens = Arc::new(config.access_tokens.clone());
7174

7275
match version {
73-
Version::V1 => Some(start_v1(bind_to, tls, tracker.clone(), form, access_tokens).await),
76+
Version::V1 => Some(start_v1(bind_to, tls, tracker.clone(), ban_service.clone(), form, access_tokens).await),
7477
}
7578
}
7679

7780
#[allow(clippy::async_yields_async)]
78-
#[instrument(skip(socket, tls, tracker, form, access_tokens))]
81+
#[instrument(skip(socket, tls, tracker, ban_service, form, access_tokens))]
7982
async fn start_v1(
8083
socket: SocketAddr,
8184
tls: Option<RustlsConfig>,
8285
tracker: Arc<core::Tracker>,
86+
ban_service: Arc<RwLock<BanService>>,
8387
form: ServiceRegistrationForm,
8488
access_tokens: Arc<AccessTokens>,
8589
) -> JoinHandle<()> {
8690
let server = ApiServer::new(Launcher::new(socket, tls))
87-
.start(tracker, form, access_tokens)
91+
.start(tracker, ban_service, form, access_tokens)
8892
.await
8993
.expect("it should be able to start to the tracker api");
9094

@@ -98,21 +102,25 @@ async fn start_v1(
98102
mod tests {
99103
use std::sync::Arc;
100104

105+
use tokio::sync::RwLock;
101106
use torrust_tracker_test_helpers::configuration::ephemeral_public;
102107

103108
use crate::bootstrap::app::initialize_with_configuration;
104109
use crate::bootstrap::jobs::tracker_apis::start_job;
105110
use crate::servers::apis::Version;
106111
use crate::servers::registar::Registar;
112+
use crate::servers::udp::server::banning::BanService;
113+
use crate::servers::udp::server::launcher::MAX_CONNECTION_ID_ERRORS_PER_IP;
107114

108115
#[tokio::test]
109116
async fn it_should_start_http_tracker() {
110117
let cfg = Arc::new(ephemeral_public());
111118
let config = &cfg.http_api.clone().unwrap();
112119
let tracker = initialize_with_configuration(&cfg);
120+
let ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
113121
let version = Version::V1;
114122

115-
start_job(config, tracker, Registar::default().give_form(), version)
123+
start_job(config, tracker, ban_service, Registar::default().give_form(), version)
116124
.await
117125
.expect("it should be able to join to the tracker api start-job");
118126
}

src/bootstrap/jobs/udp_tracker.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
//! > for the configuration options.
99
use std::sync::Arc;
1010

11+
use tokio::sync::RwLock;
1112
use tokio::task::JoinHandle;
1213
use torrust_tracker_configuration::UdpTracker;
1314
use tracing::instrument;
1415

1516
use crate::core;
1617
use crate::servers::registar::ServiceRegistrationForm;
18+
use crate::servers::udp::server::banning::BanService;
1719
use crate::servers::udp::server::spawner::Spawner;
1820
use crate::servers::udp::server::Server;
1921
use crate::servers::udp::UDP_TRACKER_LOG_TARGET;
@@ -29,13 +31,18 @@ use crate::servers::udp::UDP_TRACKER_LOG_TARGET;
2931
/// It will panic if the task did not finish successfully.
3032
#[must_use]
3133
#[allow(clippy::async_yields_async)]
32-
#[instrument(skip(config, tracker, form))]
33-
pub async fn start_job(config: &UdpTracker, tracker: Arc<core::Tracker>, form: ServiceRegistrationForm) -> JoinHandle<()> {
34+
#[instrument(skip(config, tracker, ban_service, form))]
35+
pub async fn start_job(
36+
config: &UdpTracker,
37+
tracker: Arc<core::Tracker>,
38+
ban_service: Arc<RwLock<BanService>>,
39+
form: ServiceRegistrationForm,
40+
) -> JoinHandle<()> {
3441
let bind_to = config.bind_address;
3542
let cookie_lifetime = config.cookie_lifetime;
3643

3744
let server = Server::new(Spawner::new(bind_to))
38-
.start(tracker, form, cookie_lifetime)
45+
.start(tracker, ban_service, form, cookie_lifetime)
3946
.await
4047
.expect("it should be able to start the udp tracker");
4148

src/console/profiling.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,9 @@ pub async fn run() {
179179
return;
180180
};
181181

182-
let (config, tracker) = bootstrap::app::setup();
182+
let (config, tracker, ban_service) = bootstrap::app::setup();
183183

184-
let jobs = app::start(&config, tracker).await;
184+
let jobs = app::start(&config, tracker, ban_service).await;
185185

186186
// Run the tracker for a fixed duration
187187
let run_duration = sleep(Duration::from_secs(duration_secs));

src/core/services/statistics/mod.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ pub mod setup;
4040

4141
use std::sync::Arc;
4242

43+
use tokio::sync::RwLock;
4344
use torrust_tracker_primitives::torrent_metrics::TorrentsMetrics;
4445

4546
use crate::core::statistics::metrics::Metrics;
4647
use crate::core::Tracker;
48+
use crate::servers::udp::server::banning::BanService;
4749

4850
/// All the metrics collected by the tracker.
4951
#[derive(Debug, PartialEq)]
@@ -60,28 +62,37 @@ pub struct TrackerMetrics {
6062
}
6163

6264
/// It returns all the [`TrackerMetrics`]
63-
pub async fn get_metrics(tracker: Arc<Tracker>) -> TrackerMetrics {
65+
pub async fn get_metrics(tracker: Arc<Tracker>, ban_service: Arc<RwLock<BanService>>) -> TrackerMetrics {
6466
let torrents_metrics = tracker.get_torrents_metrics();
6567
let stats = tracker.get_stats().await;
68+
let udp_banned_ips_total = ban_service.read().await.get_banned_ips_total();
6669

6770
TrackerMetrics {
6871
torrents_metrics,
6972
protocol_metrics: Metrics {
70-
// TCP
73+
// TCPv4
7174
tcp4_connections_handled: stats.tcp4_connections_handled,
7275
tcp4_announces_handled: stats.tcp4_announces_handled,
7376
tcp4_scrapes_handled: stats.tcp4_scrapes_handled,
77+
// TCPv6
7478
tcp6_connections_handled: stats.tcp6_connections_handled,
7579
tcp6_announces_handled: stats.tcp6_announces_handled,
7680
tcp6_scrapes_handled: stats.tcp6_scrapes_handled,
7781
// UDP
7882
udp_requests_aborted: stats.udp_requests_aborted,
83+
udp_requests_banned: stats.udp_requests_banned,
84+
udp_banned_ips_total: udp_banned_ips_total as u64,
85+
udp_avg_connect_processing_time_ns: stats.udp_avg_connect_processing_time_ns,
86+
udp_avg_announce_processing_time_ns: stats.udp_avg_announce_processing_time_ns,
87+
udp_avg_scrape_processing_time_ns: stats.udp_avg_scrape_processing_time_ns,
88+
// UDPv4
7989
udp4_requests: stats.udp4_requests,
8090
udp4_connections_handled: stats.udp4_connections_handled,
8191
udp4_announces_handled: stats.udp4_announces_handled,
8292
udp4_scrapes_handled: stats.udp4_scrapes_handled,
8393
udp4_responses: stats.udp4_responses,
8494
udp4_errors_handled: stats.udp4_errors_handled,
95+
// UDPv6
8596
udp6_requests: stats.udp6_requests,
8697
udp6_connections_handled: stats.udp6_connections_handled,
8798
udp6_announces_handled: stats.udp6_announces_handled,
@@ -96,13 +107,16 @@ pub async fn get_metrics(tracker: Arc<Tracker>) -> TrackerMetrics {
96107
mod tests {
97108
use std::sync::Arc;
98109

110+
use tokio::sync::RwLock;
99111
use torrust_tracker_configuration::Configuration;
100112
use torrust_tracker_primitives::torrent_metrics::TorrentsMetrics;
101113
use torrust_tracker_test_helpers::configuration;
102114

103115
use crate::core;
104116
use crate::core::services::statistics::{get_metrics, TrackerMetrics};
105117
use crate::core::services::tracker_factory;
118+
use crate::servers::udp::server::banning::BanService;
119+
use crate::servers::udp::server::launcher::MAX_CONNECTION_ID_ERRORS_PER_IP;
106120

107121
pub fn tracker_configuration() -> Configuration {
108122
configuration::ephemeral()
@@ -111,8 +125,9 @@ mod tests {
111125
#[tokio::test]
112126
async fn the_statistics_service_should_return_the_tracker_metrics() {
113127
let tracker = Arc::new(tracker_factory(&tracker_configuration()));
128+
let ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
114129

115-
let tracker_metrics = get_metrics(tracker.clone()).await;
130+
let tracker_metrics = get_metrics(tracker.clone(), ban_service.clone()).await;
116131

117132
assert_eq!(
118133
tracker_metrics,

src/core/statistics/event/handler.rs

+32-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::core::statistics::event::Event;
1+
use crate::core::statistics::event::{Event, UdpResponseKind};
22
use crate::core::statistics::repository::Repository;
33

44
pub async fn handle_event(event: Event, stats_repository: &Repository) {
@@ -24,9 +24,12 @@ pub async fn handle_event(event: Event, stats_repository: &Repository) {
2424
}
2525

2626
// UDP
27-
Event::Udp4RequestAborted => {
27+
Event::UdpRequestAborted => {
2828
stats_repository.increase_udp_requests_aborted().await;
2929
}
30+
Event::UdpRequestBanned => {
31+
stats_repository.increase_udp_requests_banned().await;
32+
}
3033

3134
// UDP4
3235
Event::Udp4Request => {
@@ -41,8 +44,30 @@ pub async fn handle_event(event: Event, stats_repository: &Repository) {
4144
Event::Udp4Scrape => {
4245
stats_repository.increase_udp4_scrapes().await;
4346
}
44-
Event::Udp4Response => {
47+
Event::Udp4Response {
48+
kind,
49+
req_processing_time,
50+
} => {
4551
stats_repository.increase_udp4_responses().await;
52+
53+
match kind {
54+
UdpResponseKind::Connect => {
55+
stats_repository
56+
.recalculate_udp_avg_connect_processing_time_ns(req_processing_time)
57+
.await;
58+
}
59+
UdpResponseKind::Announce => {
60+
stats_repository
61+
.recalculate_udp_avg_announce_processing_time_ns(req_processing_time)
62+
.await;
63+
}
64+
UdpResponseKind::Scrape => {
65+
stats_repository
66+
.recalculate_udp_avg_scrape_processing_time_ns(req_processing_time)
67+
.await;
68+
}
69+
UdpResponseKind::Error => {}
70+
}
4671
}
4772
Event::Udp4Error => {
4873
stats_repository.increase_udp4_errors().await;
@@ -61,7 +86,10 @@ pub async fn handle_event(event: Event, stats_repository: &Repository) {
6186
Event::Udp6Scrape => {
6287
stats_repository.increase_udp6_scrapes().await;
6388
}
64-
Event::Udp6Response => {
89+
Event::Udp6Response {
90+
kind: _,
91+
req_processing_time: _,
92+
} => {
6593
stats_repository.increase_udp6_responses().await;
6694
}
6795
Event::Udp6Error => {

0 commit comments

Comments
 (0)