diff --git a/src/app.rs b/src/app.rs index e41f227e7..c71237443 100644 --- a/src/app.rs +++ b/src/app.rs @@ -137,7 +137,7 @@ pub async fn start(config: &Configuration, app_container: &AppContainer) -> Vec< // Start runners to remove torrents without peers, every interval if config.core.inactive_peer_cleanup_interval > 0 { - jobs.push(torrent_cleanup::start_job(&config.core, &app_container.tracker)); + jobs.push(torrent_cleanup::start_job(&config.core, &app_container.torrents_manager)); } // Start Health Check API diff --git a/src/app_test.rs b/src/app_test.rs index 929a23418..5f189f391 100644 --- a/src/app_test.rs +++ b/src/app_test.rs @@ -9,6 +9,9 @@ use crate::core::authentication::key::repository::persisted::DatabaseKeyReposito use crate::core::authentication::service::{self, AuthenticationService}; use crate::core::databases::Database; use crate::core::services::initialize_database; +use crate::core::torrent::manager::TorrentsManager; +use crate::core::torrent::repository::in_memory::InMemoryTorrentRepository; +use crate::core::torrent::repository::persisted::DatabasePersistentTorrentRepository; use crate::core::whitelist; use crate::core::whitelist::repository::in_memory::InMemoryWhitelist; @@ -22,6 +25,9 @@ pub fn initialize_tracker_dependencies( Arc, Arc, Arc, + Arc, + Arc, + Arc, ) { let database = initialize_database(config); let in_memory_whitelist = Arc::new(InMemoryWhitelist::default()); @@ -36,6 +42,21 @@ pub fn initialize_tracker_dependencies( &db_key_repository.clone(), &in_memory_key_repository.clone(), )); + let in_memory_torrent_repository = Arc::new(InMemoryTorrentRepository::default()); + let db_torrent_repository = Arc::new(DatabasePersistentTorrentRepository::new(&database)); + let torrents_manager = Arc::new(TorrentsManager::new( + &config.core, + &in_memory_torrent_repository, + &db_torrent_repository, + )); - (database, in_memory_whitelist, whitelist_authorization, authentication_service) + ( + database, + in_memory_whitelist, + whitelist_authorization, + authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + torrents_manager, + ) } diff --git a/src/bootstrap/app.rs b/src/bootstrap/app.rs index a0c7887cf..294b2ca73 100644 --- a/src/bootstrap/app.rs +++ b/src/bootstrap/app.rs @@ -27,6 +27,9 @@ use crate::core::authentication::key::repository::in_memory::InMemoryKeyReposito use crate::core::authentication::key::repository::persisted::DatabaseKeyRepository; use crate::core::authentication::service; use crate::core::services::{initialize_database, initialize_tracker, initialize_whitelist_manager, statistics}; +use crate::core::torrent::manager::TorrentsManager; +use crate::core::torrent::repository::in_memory::InMemoryTorrentRepository; +use crate::core::torrent::repository::persisted::DatabasePersistentTorrentRepository; use crate::core::whitelist; use crate::core::whitelist::repository::in_memory::InMemoryWhitelist; use crate::servers::udp::server::banning::BanService; @@ -103,10 +106,23 @@ pub fn initialize_app_container(configuration: &Configuration) -> AppContainer { &db_key_repository.clone(), &in_memory_key_repository.clone(), )); + let in_memory_torrent_repository = Arc::new(InMemoryTorrentRepository::default()); + let db_torrent_repository = Arc::new(DatabasePersistentTorrentRepository::new(&database)); + let torrents_manager = Arc::new(TorrentsManager::new( + &configuration.core, + &in_memory_torrent_repository, + &db_torrent_repository, + )); - let tracker = Arc::new(initialize_tracker(configuration, &database, &whitelist_authorization)); + let tracker = Arc::new(initialize_tracker( + configuration, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + )); AppContainer { + database, tracker, keys_handler, authentication_service, @@ -115,6 +131,9 @@ pub fn initialize_app_container(configuration: &Configuration) -> AppContainer { stats_event_sender, stats_repository, whitelist_manager, + in_memory_torrent_repository, + db_torrent_repository, + torrents_manager, } } diff --git a/src/bootstrap/jobs/torrent_cleanup.rs b/src/bootstrap/jobs/torrent_cleanup.rs index 6abb4f26b..45e6e9e68 100644 --- a/src/bootstrap/jobs/torrent_cleanup.rs +++ b/src/bootstrap/jobs/torrent_cleanup.rs @@ -17,7 +17,7 @@ use tokio::task::JoinHandle; use torrust_tracker_configuration::Core; use tracing::instrument; -use crate::core; +use crate::core::torrent::manager::TorrentsManager; /// It starts a jobs for cleaning up the torrent data in the tracker. /// @@ -25,9 +25,9 @@ use crate::core; /// /// Refer to [`torrust-tracker-configuration documentation`](https://docs.rs/torrust-tracker-configuration) for more info about that option. #[must_use] -#[instrument(skip(config, tracker))] -pub fn start_job(config: &Core, tracker: &Arc) -> JoinHandle<()> { - let weak_tracker = std::sync::Arc::downgrade(tracker); +#[instrument(skip(config, torrents_manager))] +pub fn start_job(config: &Core, torrents_manager: &Arc) -> JoinHandle<()> { + let weak_torrents_manager = std::sync::Arc::downgrade(torrents_manager); let interval = config.inactive_peer_cleanup_interval; tokio::spawn(async move { @@ -42,10 +42,10 @@ pub fn start_job(config: &Core, tracker: &Arc) -> JoinHandle<()> break; } _ = interval.tick() => { - if let Some(tracker) = weak_tracker.upgrade() { + if let Some(torrents_manager) = weak_torrents_manager.upgrade() { let start_time = Utc::now().time(); tracing::info!("Cleaning up torrents.."); - tracker.cleanup_torrents(); + torrents_manager.cleanup_torrents(); tracing::info!("Cleaned up torrents in: {}ms", (Utc::now().time() - start_time).num_milliseconds()); } else { break; diff --git a/src/container.rs b/src/container.rs index 14c4b5d7b..8407d0b69 100644 --- a/src/container.rs +++ b/src/container.rs @@ -4,13 +4,18 @@ use tokio::sync::RwLock; use crate::core::authentication::handler::KeysHandler; use crate::core::authentication::service::AuthenticationService; +use crate::core::databases::Database; use crate::core::statistics::event::sender::Sender; use crate::core::statistics::repository::Repository; +use crate::core::torrent::manager::TorrentsManager; +use crate::core::torrent::repository::in_memory::InMemoryTorrentRepository; +use crate::core::torrent::repository::persisted::DatabasePersistentTorrentRepository; use crate::core::whitelist::manager::WhiteListManager; use crate::core::{whitelist, Tracker}; use crate::servers::udp::server::banning::BanService; pub struct AppContainer { + pub database: Arc>, pub tracker: Arc, pub keys_handler: Arc, pub authentication_service: Arc, @@ -19,4 +24,7 @@ pub struct AppContainer { pub stats_event_sender: Arc>>, pub stats_repository: Arc, pub whitelist_manager: Arc, + pub in_memory_torrent_repository: Arc, + pub db_torrent_repository: Arc, + pub torrents_manager: Arc, } diff --git a/src/core/mod.rs b/src/core/mod.rs index 26ef69bfa..f2483b21e 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -449,24 +449,17 @@ pub mod whitelist; pub mod peer_tests; -use std::cmp::max; use std::net::IpAddr; use std::sync::Arc; -use std::time::Duration; use bittorrent_primitives::info_hash::InfoHash; -use torrust_tracker_clock::clock::Time; +use torrent::repository::in_memory::InMemoryTorrentRepository; +use torrent::repository::persisted::DatabasePersistentTorrentRepository; use torrust_tracker_configuration::{AnnouncePolicy, Core, TORRENT_PEERS_LIMIT}; use torrust_tracker_primitives::core::{AnnounceData, ScrapeData}; use torrust_tracker_primitives::peer; use torrust_tracker_primitives::swarm_metadata::SwarmMetadata; use torrust_tracker_primitives::torrent_metrics::TorrentsMetrics; -use torrust_tracker_torrent_repository::entry::EntrySync; -use torrust_tracker_torrent_repository::repository::Repository; - -use self::torrent::Torrents; -use crate::core::databases::Database; -use crate::CurrentClock; /// The domain layer tracker service. /// @@ -481,15 +474,14 @@ pub struct Tracker { /// The tracker configuration. config: Core, - /// A database driver implementation: [`Sqlite3`](crate::core::databases::sqlite) - /// or [`MySQL`](crate::core::databases::mysql) - database: Arc>, - /// The service to check is a torrent is whitelisted. pub whitelist_authorization: Arc, /// The in-memory torrents repository. - torrents: Arc, + in_memory_torrent_repository: Arc, + + /// The persistent torrents repository. + db_torrent_repository: Arc, } /// How many peers the peer announcing wants in the announce response. @@ -542,14 +534,15 @@ impl Tracker { /// Will return a `databases::error::Error` if unable to connect to database. The `Tracker` is responsible for the persistence. pub fn new( config: &Core, - database: &Arc>, whitelist_authorization: &Arc, + in_memory_torrent_repository: &Arc, + db_torrent_repository: &Arc, ) -> Result { Ok(Tracker { config: config.clone(), - database: database.clone(), whitelist_authorization: whitelist_authorization.clone(), - torrents: Arc::default(), + in_memory_torrent_repository: in_memory_torrent_repository.clone(), + db_torrent_repository: db_torrent_repository.clone(), }) } @@ -654,53 +647,6 @@ impl Tracker { scrape_data } - /// It returns the data for a `scrape` response. - fn get_swarm_metadata(&self, info_hash: &InfoHash) -> SwarmMetadata { - match self.torrents.get(info_hash) { - Some(torrent_entry) => torrent_entry.get_swarm_metadata(), - None => SwarmMetadata::default(), - } - } - - /// It loads the torrents from database into memory. It only loads the torrent entry list with the number of seeders for each torrent. - /// Peers data is not persisted. - /// - /// # Context: Tracker - /// - /// # Errors - /// - /// Will return a `database::Error` if unable to load the list of `persistent_torrents` from the database. - pub fn load_torrents_from_database(&self) -> Result<(), databases::error::Error> { - let persistent_torrents = self.database.load_persistent_torrents()?; - - self.torrents.import_persistent(&persistent_torrents); - - Ok(()) - } - - /// # Context: Tracker - /// - /// Get torrent peers for a given torrent and client. - /// - /// It filters out the client making the request. - fn get_peers_for(&self, info_hash: &InfoHash, peer: &peer::Peer, limit: usize) -> Vec> { - match self.torrents.get(info_hash) { - None => vec![], - Some(entry) => entry.get_peers_for_client(&peer.peer_addr, Some(max(limit, TORRENT_PEERS_LIMIT))), - } - } - - /// # Context: Tracker - /// - /// Get torrent peers for a given torrent. - #[must_use] - pub fn get_torrent_peers(&self, info_hash: &InfoHash) -> Vec> { - match self.torrents.get(info_hash) { - None => vec![], - Some(entry) => entry.get_peers(Some(TORRENT_PEERS_LIMIT)), - } - } - /// It updates the torrent entry in memory, it also stores in the database /// the torrent info data which is persistent, and finally return the data /// needed for a `announce` request response. @@ -708,14 +654,14 @@ impl Tracker { /// # Context: Tracker #[must_use] pub fn upsert_peer_and_get_stats(&self, info_hash: &InfoHash, peer: &peer::Peer) -> SwarmMetadata { - let swarm_metadata_before = match self.torrents.get_swarm_metadata(info_hash) { + let swarm_metadata_before = match self.in_memory_torrent_repository.get_opt_swarm_metadata(info_hash) { Some(swarm_metadata) => swarm_metadata, None => SwarmMetadata::zeroed(), }; - self.torrents.upsert_peer(info_hash, peer); + self.in_memory_torrent_repository.upsert_peer(info_hash, peer); - let swarm_metadata_after = match self.torrents.get_swarm_metadata(info_hash) { + let swarm_metadata_after = match self.in_memory_torrent_repository.get_opt_swarm_metadata(info_hash) { Some(swarm_metadata) => swarm_metadata, None => SwarmMetadata::zeroed(), }; @@ -735,45 +681,42 @@ impl Tracker { let completed = swarm_metadata.downloaded; let info_hash = *info_hash; - drop(self.database.save_persistent_torrent(&info_hash, completed)); + drop(self.db_torrent_repository.save(&info_hash, completed)); } } - /// It calculates and returns the general `Tracker` - /// [`TorrentsMetrics`] - /// + /// It returns the data for a `scrape` response. + fn get_swarm_metadata(&self, info_hash: &InfoHash) -> SwarmMetadata { + self.in_memory_torrent_repository.get_swarm_metadata(info_hash) + } + /// # Context: Tracker /// - /// # Panics - /// Panics if unable to get the torrent metrics. - #[must_use] - pub fn get_torrents_metrics(&self) -> TorrentsMetrics { - self.torrents.get_metrics() + /// Get torrent peers for a given torrent and client. + /// + /// It filters out the client making the request. + fn get_peers_for(&self, info_hash: &InfoHash, peer: &peer::Peer, limit: usize) -> Vec> { + self.in_memory_torrent_repository.get_peers_for(info_hash, peer, limit) } - /// Remove inactive peers and (optionally) peerless torrents. - /// /// # Context: Tracker - pub fn cleanup_torrents(&self) { - let current_cutoff = CurrentClock::now_sub(&Duration::from_secs(u64::from(self.config.tracker_policy.max_peer_timeout))) - .unwrap_or_default(); - - self.torrents.remove_inactive_peers(current_cutoff); - - if self.config.tracker_policy.remove_peerless_torrents { - self.torrents.remove_peerless_torrents(&self.config.tracker_policy); - } + /// + /// Get torrent peers for a given torrent. + #[must_use] + pub fn get_torrent_peers(&self, info_hash: &InfoHash) -> Vec> { + self.in_memory_torrent_repository.get_torrent_peers(info_hash) } - /// It drops the database tables. + /// It calculates and returns the general `Tracker` + /// [`TorrentsMetrics`] /// - /// # Errors + /// # Context: Tracker /// - /// Will return `Err` if unable to drop tables. - pub fn drop_database_tables(&self) -> Result<(), databases::error::Error> { - // todo: this is only used for testing. WE have to pass the database - // reference directly to the tests instead of via the tracker. - self.database.drop_database_tables() + /// # Panics + /// Panics if unable to get the torrent metrics. + #[must_use] + pub fn get_torrents_metrics(&self) -> TorrentsMetrics { + self.in_memory_torrent_repository.get_torrents_metrics() } } @@ -805,39 +748,78 @@ mod tests { use crate::app_test::initialize_tracker_dependencies; use crate::core::peer::Peer; use crate::core::services::{initialize_tracker, initialize_whitelist_manager}; + use crate::core::torrent::manager::TorrentsManager; use crate::core::whitelist::manager::WhiteListManager; use crate::core::{whitelist, TorrentsMetrics, Tracker}; fn public_tracker() -> Tracker { let config = configuration::ephemeral_public(); - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); - - initialize_tracker(&config, &database, &whitelist_authorization) + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + _authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(&config); + + initialize_tracker( + &config, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + ) } fn whitelisted_tracker() -> (Tracker, Arc, Arc) { let config = configuration::ephemeral_listed(); - let (database, in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); + let ( + database, + in_memory_whitelist, + whitelist_authorization, + _authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(&config); let whitelist_manager = initialize_whitelist_manager(database.clone(), in_memory_whitelist.clone()); - let tracker = initialize_tracker(&config, &database, &whitelist_authorization); + let tracker = initialize_tracker( + &config, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + ); (tracker, whitelist_authorization, whitelist_manager) } - pub fn tracker_persisting_torrents_in_database() -> Tracker { + pub fn tracker_persisting_torrents_in_database() -> (Tracker, Arc) { let mut config = configuration::ephemeral_listed(); config.core.tracker_policy.persistent_torrent_completed_stat = true; - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + _authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + torrents_manager, + ) = initialize_tracker_dependencies(&config); + + let tracker = initialize_tracker( + &config, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + ); - initialize_tracker(&config, &database, &whitelist_authorization) + (tracker, torrents_manager) } fn sample_info_hash() -> InfoHash { @@ -1492,13 +1474,12 @@ mod tests { use aquatic_udp_protocol::AnnounceEvent; use torrust_tracker_torrent_repository::entry::EntrySync; - use torrust_tracker_torrent_repository::repository::Repository; use crate::core::tests::the_tracker::{sample_info_hash, sample_peer, tracker_persisting_torrents_in_database}; #[tokio::test] async fn it_should_persist_the_number_of_completed_peers_for_all_torrents_into_the_database() { - let tracker = tracker_persisting_torrents_in_database(); + let (tracker, torrents_manager) = tracker_persisting_torrents_in_database(); let info_hash = sample_info_hash(); @@ -1513,11 +1494,14 @@ mod tests { assert_eq!(swarm_stats.downloaded, 1); // Remove the newly updated torrent from memory - tracker.torrents.remove(&info_hash); + let _unused = tracker.in_memory_torrent_repository.remove(&info_hash); - tracker.load_torrents_from_database().unwrap(); + torrents_manager.load_torrents_from_database().unwrap(); - let torrent_entry = tracker.torrents.get(&info_hash).expect("it should be able to get entry"); + let torrent_entry = tracker + .in_memory_torrent_repository + .get(&info_hash) + .expect("it should be able to get entry"); // It persists the number of completed peers. assert_eq!(torrent_entry.get_swarm_metadata().downloaded, 1); diff --git a/src/core/services/mod.rs b/src/core/services/mod.rs index 611ea24d2..a9bca2df7 100644 --- a/src/core/services/mod.rs +++ b/src/core/services/mod.rs @@ -14,6 +14,8 @@ use torrust_tracker_configuration::v2_0_0::database; use torrust_tracker_configuration::Configuration; use super::databases::{self, Database}; +use super::torrent::repository::in_memory::InMemoryTorrentRepository; +use super::torrent::repository::persisted::DatabasePersistentTorrentRepository; use super::whitelist; use super::whitelist::manager::WhiteListManager; use super::whitelist::repository::in_memory::InMemoryWhitelist; @@ -28,10 +30,16 @@ use crate::core::Tracker; #[must_use] pub fn initialize_tracker( config: &Configuration, - database: &Arc>, whitelist_authorization: &Arc, + in_memory_torrent_repository: &Arc, + db_torrent_repository: &Arc, ) -> Tracker { - match Tracker::new(&Arc::new(config).core, database, whitelist_authorization) { + match Tracker::new( + &Arc::new(config).core, + whitelist_authorization, + in_memory_torrent_repository, + db_torrent_repository, + ) { Ok(tracker) => tracker, Err(error) => { panic!("{}", error) diff --git a/src/core/services/statistics/mod.rs b/src/core/services/statistics/mod.rs index cc59bcf12..7c2233efd 100644 --- a/src/core/services/statistics/mod.rs +++ b/src/core/services/statistics/mod.rs @@ -132,12 +132,25 @@ mod tests { async fn the_statistics_service_should_return_the_tracker_metrics() { let config = tracker_configuration(); - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + _authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(&config); + let (_stats_event_sender, stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics); let stats_repository = Arc::new(stats_repository); - let tracker = Arc::new(initialize_tracker(&config, &database, &whitelist_authorization)); + let tracker = Arc::new(initialize_tracker( + &config, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + )); let ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP))); diff --git a/src/core/services/torrent.rs b/src/core/services/torrent.rs index 9b7254098..c2ffa05aa 100644 --- a/src/core/services/torrent.rs +++ b/src/core/services/torrent.rs @@ -10,7 +10,6 @@ use bittorrent_primitives::info_hash::InfoHash; use torrust_tracker_primitives::pagination::Pagination; use torrust_tracker_primitives::peer; use torrust_tracker_torrent_repository::entry::EntrySync; -use torrust_tracker_torrent_repository::repository::Repository; use crate::core::Tracker; @@ -46,7 +45,7 @@ pub struct BasicInfo { /// It returns all the information the tracker has about one torrent in a [Info] struct. pub async fn get_torrent_info(tracker: Arc, info_hash: &InfoHash) -> Option { - let torrent_entry_option = tracker.torrents.get(info_hash); + let torrent_entry_option = tracker.in_memory_torrent_repository.get(info_hash); let torrent_entry = torrent_entry_option?; @@ -69,7 +68,7 @@ pub async fn get_torrent_info(tracker: Arc, info_hash: &InfoHash) -> Op pub async fn get_torrents_page(tracker: Arc, pagination: Option<&Pagination>) -> Vec { let mut basic_infos: Vec = vec![]; - for (info_hash, torrent_entry) in tracker.torrents.get_paginated(pagination) { + for (info_hash, torrent_entry) in tracker.in_memory_torrent_repository.get_paginated(pagination) { let stats = torrent_entry.get_swarm_metadata(); basic_infos.push(BasicInfo { @@ -88,7 +87,11 @@ pub async fn get_torrents(tracker: Arc, info_hashes: &[InfoHash]) -> Ve let mut basic_infos: Vec = vec![]; for info_hash in info_hashes { - if let Some(stats) = tracker.torrents.get(info_hash).map(|t| t.get_swarm_metadata()) { + if let Some(stats) = tracker + .in_memory_torrent_repository + .get(info_hash) + .map(|t| t.get_swarm_metadata()) + { basic_infos.push(BasicInfo { info_hash: *info_hash, seeders: u64::from(stats.complete), @@ -104,10 +107,35 @@ pub async fn get_torrents(tracker: Arc, info_hashes: &[InfoHash]) -> Ve #[cfg(test)] mod tests { use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + use std::sync::Arc; use aquatic_udp_protocol::{AnnounceEvent, NumberOfBytes, PeerId}; + use torrust_tracker_configuration::Configuration; use torrust_tracker_primitives::{peer, DurationSinceUnixEpoch}; + use crate::app_test::initialize_tracker_dependencies; + use crate::core::services::initialize_tracker; + use crate::core::Tracker; + + fn initialize_tracker_and_deps(config: &Configuration) -> Arc { + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + _authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(config); + + Arc::new(initialize_tracker( + config, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + )) + } + fn sample_peer() -> peer::Peer { peer::Peer { peer_id: PeerId(*b"-qB00000000000000000"), @@ -131,7 +159,7 @@ mod tests { use crate::app_test::initialize_tracker_dependencies; use crate::core::services::initialize_tracker; - use crate::core::services::torrent::tests::sample_peer; + use crate::core::services::torrent::tests::{initialize_tracker_and_deps, sample_peer}; use crate::core::services::torrent::{get_torrent_info, Info}; pub fn tracker_configuration() -> Configuration { @@ -142,10 +170,22 @@ mod tests { async fn should_return_none_if_the_tracker_does_not_have_the_torrent() { let config = tracker_configuration(); - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); - - let tracker = initialize_tracker(&config, &database, &whitelist_authorization); + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + _authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(&config); + + let tracker = initialize_tracker( + &config, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + ); let tracker = Arc::new(tracker); @@ -162,10 +202,7 @@ mod tests { async fn should_return_the_torrent_info_if_the_tracker_has_the_torrent() { let config = tracker_configuration(); - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); - - let tracker = Arc::new(initialize_tracker(&config, &database, &whitelist_authorization)); + let tracker = initialize_tracker_and_deps(&config); let hash = "9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d".to_owned(); let info_hash = InfoHash::from_str(&hash).unwrap(); @@ -189,15 +226,12 @@ mod tests { mod searching_for_torrents { use std::str::FromStr; - use std::sync::Arc; use bittorrent_primitives::info_hash::InfoHash; use torrust_tracker_configuration::Configuration; use torrust_tracker_test_helpers::configuration; - use crate::app_test::initialize_tracker_dependencies; - use crate::core::services::initialize_tracker; - use crate::core::services::torrent::tests::sample_peer; + use crate::core::services::torrent::tests::{initialize_tracker_and_deps, sample_peer}; use crate::core::services::torrent::{get_torrents_page, BasicInfo, Pagination}; pub fn tracker_configuration() -> Configuration { @@ -208,10 +242,7 @@ mod tests { async fn should_return_an_empty_result_if_the_tracker_does_not_have_any_torrent() { let config = tracker_configuration(); - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); - - let tracker = Arc::new(initialize_tracker(&config, &database, &whitelist_authorization)); + let tracker = initialize_tracker_and_deps(&config); let torrents = get_torrents_page(tracker.clone(), Some(&Pagination::default())).await; @@ -222,10 +253,7 @@ mod tests { async fn should_return_a_summarized_info_for_all_torrents() { let config = tracker_configuration(); - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); - - let tracker = Arc::new(initialize_tracker(&config, &database, &whitelist_authorization)); + let tracker = initialize_tracker_and_deps(&config); let hash = "9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d".to_owned(); let info_hash = InfoHash::from_str(&hash).unwrap(); @@ -249,10 +277,7 @@ mod tests { async fn should_allow_limiting_the_number_of_torrents_in_the_result() { let config = tracker_configuration(); - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); - - let tracker = Arc::new(initialize_tracker(&config, &database, &whitelist_authorization)); + let tracker = initialize_tracker_and_deps(&config); let hash1 = "9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d".to_owned(); let info_hash1 = InfoHash::from_str(&hash1).unwrap(); @@ -274,10 +299,7 @@ mod tests { async fn should_allow_using_pagination_in_the_result() { let config = tracker_configuration(); - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); - - let tracker = Arc::new(initialize_tracker(&config, &database, &whitelist_authorization)); + let tracker = initialize_tracker_and_deps(&config); let hash1 = "9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d".to_owned(); let info_hash1 = InfoHash::from_str(&hash1).unwrap(); @@ -308,10 +330,7 @@ mod tests { async fn should_return_torrents_ordered_by_info_hash() { let config = tracker_configuration(); - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); - - let tracker = Arc::new(initialize_tracker(&config, &database, &whitelist_authorization)); + let tracker = initialize_tracker_and_deps(&config); let hash1 = "9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d".to_owned(); let info_hash1 = InfoHash::from_str(&hash1).unwrap(); diff --git a/src/core/torrent/manager.rs b/src/core/torrent/manager.rs new file mode 100644 index 000000000..261376755 --- /dev/null +++ b/src/core/torrent/manager.rs @@ -0,0 +1,64 @@ +use std::sync::Arc; +use std::time::Duration; + +use torrust_tracker_clock::clock::Time; +use torrust_tracker_configuration::Core; + +use super::repository::in_memory::InMemoryTorrentRepository; +use super::repository::persisted::DatabasePersistentTorrentRepository; +use crate::core::databases; +use crate::CurrentClock; + +pub struct TorrentsManager { + /// The tracker configuration. + config: Core, + + /// The in-memory torrents repository. + in_memory_torrent_repository: Arc, + + /// The persistent torrents repository. + db_torrent_repository: Arc, +} + +impl TorrentsManager { + #[must_use] + pub fn new( + config: &Core, + in_memory_torrent_repository: &Arc, + db_torrent_repository: &Arc, + ) -> Self { + Self { + config: config.clone(), + in_memory_torrent_repository: in_memory_torrent_repository.clone(), + db_torrent_repository: db_torrent_repository.clone(), + } + } + + /// It loads the torrents from database into memory. It only loads the + /// torrent entry list with the number of seeders for each torrent. Peers + /// data is not persisted. + /// + /// # Errors + /// + /// Will return a `database::Error` if unable to load the list of `persistent_torrents` from the database. + pub fn load_torrents_from_database(&self) -> Result<(), databases::error::Error> { + let persistent_torrents = self.db_torrent_repository.load_all()?; + + self.in_memory_torrent_repository.import_persistent(&persistent_torrents); + + Ok(()) + } + + /// Remove inactive peers and (optionally) peerless torrents. + pub fn cleanup_torrents(&self) { + let current_cutoff = CurrentClock::now_sub(&Duration::from_secs(u64::from(self.config.tracker_policy.max_peer_timeout))) + .unwrap_or_default(); + + self.in_memory_torrent_repository.remove_inactive_peers(current_cutoff); + + if self.config.tracker_policy.remove_peerless_torrents { + self.in_memory_torrent_repository + .remove_peerless_torrents(&self.config.tracker_policy); + } + } +} diff --git a/src/core/torrent/mod.rs b/src/core/torrent/mod.rs index 38311864b..95a5ff1eb 100644 --- a/src/core/torrent/mod.rs +++ b/src/core/torrent/mod.rs @@ -25,6 +25,9 @@ //! - The number of peers that have NOT completed downloading the torrent and are still active, that means they are actively participating in the network. //! Peer that don not have a full copy of the torrent data are called "leechers". //! +pub mod manager; +pub mod repository; + use torrust_tracker_torrent_repository::TorrentsSkipMapMutexStd; pub type Torrents = TorrentsSkipMapMutexStd; // Currently Used diff --git a/src/core/torrent/repository/in_memory.rs b/src/core/torrent/repository/in_memory.rs new file mode 100644 index 000000000..6b1902d95 --- /dev/null +++ b/src/core/torrent/repository/in_memory.rs @@ -0,0 +1,103 @@ +use std::cmp::max; +use std::sync::Arc; + +use bittorrent_primitives::info_hash::InfoHash; +use torrust_tracker_configuration::{TrackerPolicy, TORRENT_PEERS_LIMIT}; +use torrust_tracker_primitives::pagination::Pagination; +use torrust_tracker_primitives::swarm_metadata::SwarmMetadata; +use torrust_tracker_primitives::torrent_metrics::TorrentsMetrics; +use torrust_tracker_primitives::{peer, DurationSinceUnixEpoch, PersistentTorrents}; +use torrust_tracker_torrent_repository::entry::EntrySync; +use torrust_tracker_torrent_repository::repository::Repository; +use torrust_tracker_torrent_repository::EntryMutexStd; + +use crate::core::torrent::Torrents; + +/// The in-memory torrents repository. +/// +/// There are many implementations of the repository trait. We tried with +/// different types of data structures, but the best performance was with +/// the one we use for production. We kept the other implementations for +/// reference. +#[derive(Debug, Default)] +pub struct InMemoryTorrentRepository { + /// The in-memory torrents repository implementation. + torrents: Arc, +} + +impl InMemoryTorrentRepository { + /// It inserts (or updates if it's already in the list) the peer in the + /// torrent entry. + pub fn upsert_peer(&self, info_hash: &InfoHash, peer: &peer::Peer) { + self.torrents.upsert_peer(info_hash, peer); + } + + #[must_use] + pub fn remove(&self, key: &InfoHash) -> Option { + self.torrents.remove(key) + } + + pub fn remove_inactive_peers(&self, current_cutoff: DurationSinceUnixEpoch) { + self.torrents.remove_inactive_peers(current_cutoff); + } + + pub fn remove_peerless_torrents(&self, policy: &TrackerPolicy) { + self.torrents.remove_peerless_torrents(policy); + } + + #[must_use] + pub fn get(&self, key: &InfoHash) -> Option { + self.torrents.get(key) + } + + #[must_use] + pub fn get_paginated(&self, pagination: Option<&Pagination>) -> Vec<(InfoHash, EntryMutexStd)> { + self.torrents.get_paginated(pagination) + } + + /// It returns the data for a `scrape` response or empty if the torrent is + /// not found. + #[must_use] + pub fn get_swarm_metadata(&self, info_hash: &InfoHash) -> SwarmMetadata { + match self.torrents.get(info_hash) { + Some(torrent_entry) => torrent_entry.get_swarm_metadata(), + None => SwarmMetadata::default(), + } + } + + /// It returns the data for a `scrape` response if the torrent is found. + #[must_use] + pub fn get_opt_swarm_metadata(&self, info_hash: &InfoHash) -> Option { + self.torrents.get_swarm_metadata(info_hash) + } + + /// Get torrent peers for a given torrent and client. + /// + /// It filters out the client making the request. + #[must_use] + pub fn get_peers_for(&self, info_hash: &InfoHash, peer: &peer::Peer, limit: usize) -> Vec> { + match self.torrents.get(info_hash) { + None => vec![], + Some(entry) => entry.get_peers_for_client(&peer.peer_addr, Some(max(limit, TORRENT_PEERS_LIMIT))), + } + } + + /// Get torrent peers for a given torrent. + #[must_use] + pub fn get_torrent_peers(&self, info_hash: &InfoHash) -> Vec> { + match self.torrents.get(info_hash) { + None => vec![], + Some(entry) => entry.get_peers(Some(TORRENT_PEERS_LIMIT)), + } + } + + /// It calculates and returns the general [`TorrentsMetrics`]. + #[must_use] + pub fn get_torrents_metrics(&self) -> TorrentsMetrics { + self.torrents.get_metrics() + } + + pub fn import_persistent(&self, persistent_torrents: &PersistentTorrents) { + self.torrents.import_persistent(persistent_torrents); + } +} diff --git a/src/core/torrent/repository/mod.rs b/src/core/torrent/repository/mod.rs new file mode 100644 index 000000000..51723b68d --- /dev/null +++ b/src/core/torrent/repository/mod.rs @@ -0,0 +1,2 @@ +pub mod in_memory; +pub mod persisted; diff --git a/src/core/torrent/repository/persisted.rs b/src/core/torrent/repository/persisted.rs new file mode 100644 index 000000000..86a3db0e3 --- /dev/null +++ b/src/core/torrent/repository/persisted.rs @@ -0,0 +1,44 @@ +use std::sync::Arc; + +use bittorrent_primitives::info_hash::InfoHash; +use torrust_tracker_primitives::PersistentTorrents; + +use crate::core::databases::error::Error; +use crate::core::databases::Database; + +/// Torrent repository implementation that persists the torrents in a database. +/// +/// Not all the torrent in-memory data is persisted. For now only some of the +/// torrent metrics are persisted. +pub struct DatabasePersistentTorrentRepository { + /// A database driver implementation: [`Sqlite3`](crate::core::databases::sqlite) + /// or [`MySQL`](crate::core::databases::mysql) + database: Arc>, +} + +impl DatabasePersistentTorrentRepository { + #[must_use] + pub fn new(database: &Arc>) -> DatabasePersistentTorrentRepository { + Self { + database: database.clone(), + } + } + + /// It loads the persistent torrents from the database. + /// + /// # Errors + /// + /// Will return a database `Err` if unable to load. + pub fn load_all(&self) -> Result { + self.database.load_persistent_torrents() + } + + /// It saves the persistent torrent into the database. + /// + /// # Errors + /// + /// Will return a database `Err` if unable to save. + pub fn save(&self, info_hash: &InfoHash, downloaded: u32) -> Result<(), Error> { + self.database.save_persistent_torrent(info_hash, downloaded) + } +} diff --git a/src/servers/http/v1/handlers/announce.rs b/src/servers/http/v1/handlers/announce.rs index c42981d4c..a9567fb81 100644 --- a/src/servers/http/v1/handlers/announce.rs +++ b/src/servers/http/v1/handlers/announce.rs @@ -274,12 +274,25 @@ mod tests { /// Initialize tracker's dependencies and tracker. fn initialize_tracker_and_deps(config: &Configuration) -> TrackerAndDeps { - let (database, _in_memory_whitelist, whitelist_authorization, authentication_service) = - initialize_tracker_dependencies(config); + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(config); + let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics); let stats_event_sender = Arc::new(stats_event_sender); - let tracker = Arc::new(initialize_tracker(config, &database, &whitelist_authorization)); + let tracker = Arc::new(initialize_tracker( + config, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + )); (tracker, stats_event_sender, whitelist_authorization, authentication_service) } diff --git a/src/servers/http/v1/handlers/scrape.rs b/src/servers/http/v1/handlers/scrape.rs index de4610a61..116d717a1 100644 --- a/src/servers/http/v1/handlers/scrape.rs +++ b/src/servers/http/v1/handlers/scrape.rs @@ -151,13 +151,25 @@ mod tests { ) { let config = configuration::ephemeral_private(); - let (database, _in_memory_whitelist, whitelist_authorization, authentication_service) = - initialize_tracker_dependencies(&config); + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(&config); let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics); ( - initialize_tracker(&config, &database, &whitelist_authorization), + initialize_tracker( + &config, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + ), stats_event_sender, authentication_service, ) @@ -170,13 +182,25 @@ mod tests { ) { let config = configuration::ephemeral_listed(); - let (database, _in_memory_whitelist, whitelist_authorization, authentication_service) = - initialize_tracker_dependencies(&config); + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(&config); let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics); ( - initialize_tracker(&config, &database, &whitelist_authorization), + initialize_tracker( + &config, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + ), stats_event_sender, authentication_service, ) @@ -189,13 +213,25 @@ mod tests { ) { let config = configuration::ephemeral_with_reverse_proxy(); - let (database, _in_memory_whitelist, whitelist_authorization, authentication_service) = - initialize_tracker_dependencies(&config); + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(&config); let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics); ( - initialize_tracker(&config, &database, &whitelist_authorization), + initialize_tracker( + &config, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + ), stats_event_sender, authentication_service, ) @@ -208,13 +244,25 @@ mod tests { ) { let config = configuration::ephemeral_without_reverse_proxy(); - let (database, _in_memory_whitelist, whitelist_authorization, authentication_service) = - initialize_tracker_dependencies(&config); + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(&config); let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics); ( - initialize_tracker(&config, &database, &whitelist_authorization), + initialize_tracker( + &config, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + ), stats_event_sender, authentication_service, ) diff --git a/src/servers/http/v1/services/announce.rs b/src/servers/http/v1/services/announce.rs index 018348d7e..322bc80eb 100644 --- a/src/servers/http/v1/services/announce.rs +++ b/src/servers/http/v1/services/announce.rs @@ -73,12 +73,24 @@ mod tests { fn public_tracker() -> (Tracker, Arc>>) { let config = configuration::ephemeral_public(); - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + _authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(&config); let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics); let stats_event_sender = Arc::new(stats_event_sender); - let tracker = initialize_tracker(&config, &database, &whitelist_authorization); + let tracker = initialize_tracker( + &config, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + ); (tracker, stats_event_sender) } @@ -132,10 +144,23 @@ mod tests { fn test_tracker_factory() -> Tracker { let config = configuration::ephemeral(); - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); - - Tracker::new(&config.core, &database, &whitelist_authorization).unwrap() + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + _authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(&config); + + Tracker::new( + &config.core, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + ) + .unwrap() } #[tokio::test] diff --git a/src/servers/http/v1/services/scrape.rs b/src/servers/http/v1/services/scrape.rs index 9ad741234..299938f84 100644 --- a/src/servers/http/v1/services/scrape.rs +++ b/src/servers/http/v1/services/scrape.rs @@ -87,10 +87,22 @@ mod tests { fn public_tracker() -> Tracker { let config = configuration::ephemeral_public(); - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); - - initialize_tracker(&config, &database, &whitelist_authorization) + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + _authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(&config); + + initialize_tracker( + &config, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + ) } fn sample_info_hashes() -> Vec { @@ -116,10 +128,23 @@ mod tests { fn test_tracker_factory() -> Tracker { let config = configuration::ephemeral(); - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); - - Tracker::new(&config.core, &database, &whitelist_authorization).unwrap() + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + _authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(&config); + + Tracker::new( + &config.core, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + ) + .unwrap() } mod with_real_data { diff --git a/src/servers/udp/handlers.rs b/src/servers/udp/handlers.rs index feeca4e40..5584c167b 100644 --- a/src/servers/udp/handlers.rs +++ b/src/servers/udp/handlers.rs @@ -516,13 +516,26 @@ mod tests { } fn initialize_tracker_and_deps(config: &Configuration) -> TrackerAndDeps { - let (database, in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(config); + let ( + database, + in_memory_whitelist, + whitelist_authorization, + _authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(config); + let (stats_event_sender, _stats_repository) = statistics::setup::factory(config.core.tracker_usage_statistics); let stats_event_sender = Arc::new(stats_event_sender); let whitelist_manager = initialize_whitelist_manager(database.clone(), in_memory_whitelist.clone()); - let tracker = Arc::new(initialize_tracker(config, &database, &whitelist_authorization)); + let tracker = Arc::new(initialize_tracker( + config, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + )); ( tracker, @@ -630,10 +643,25 @@ mod tests { fn test_tracker_factory() -> (Arc, Arc) { let config = tracker_configuration(); - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); - - let tracker = Arc::new(Tracker::new(&config.core, &database, &whitelist_authorization).unwrap()); + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + _authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(&config); + + let tracker = Arc::new( + Tracker::new( + &config.core, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + ) + .unwrap(), + ); (tracker, whitelist_authorization) } @@ -1378,8 +1406,15 @@ mod tests { async fn the_peer_ip_should_be_changed_to_the_external_ip_in_the_tracker_configuration() { let config = Arc::new(TrackerConfigurationBuilder::default().with_external_ip("::126.0.0.1").into()); - let (database, _in_memory_whitelist, whitelist_authorization, _authentication_service) = - initialize_tracker_dependencies(&config); + let ( + _database, + _in_memory_whitelist, + whitelist_authorization, + _authentication_service, + in_memory_torrent_repository, + db_torrent_repository, + _torrents_manager, + ) = initialize_tracker_dependencies(&config); let mut stats_event_sender_mock = statistics::event::sender::MockSender::new(); stats_event_sender_mock @@ -1390,7 +1425,15 @@ mod tests { let stats_event_sender: Arc>> = Arc::new(Some(Box::new(stats_event_sender_mock))); - let tracker = Arc::new(core::Tracker::new(&config.core, &database, &whitelist_authorization).unwrap()); + let tracker = Arc::new( + core::Tracker::new( + &config.core, + &whitelist_authorization, + &in_memory_torrent_repository, + &db_torrent_repository, + ) + .unwrap(), + ); let loopback_ipv4 = Ipv4Addr::new(127, 0, 0, 1); let loopback_ipv6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); diff --git a/tests/servers/api/environment.rs b/tests/servers/api/environment.rs index f014df36f..8967ff830 100644 --- a/tests/servers/api/environment.rs +++ b/tests/servers/api/environment.rs @@ -10,6 +10,7 @@ use torrust_tracker_lib::bootstrap::app::{initialize_app_container, initialize_g use torrust_tracker_lib::bootstrap::jobs::make_rust_tls; use torrust_tracker_lib::core::authentication::handler::KeysHandler; use torrust_tracker_lib::core::authentication::service::AuthenticationService; +use torrust_tracker_lib::core::databases::Database; use torrust_tracker_lib::core::statistics::event::sender::Sender; use torrust_tracker_lib::core::statistics::repository::Repository; use torrust_tracker_lib::core::whitelist::manager::WhiteListManager; @@ -24,6 +25,7 @@ where S: std::fmt::Debug + std::fmt::Display, { pub config: Arc, + pub database: Arc>, pub tracker: Arc, pub keys_handler: Arc, pub authentication_service: Arc, @@ -61,6 +63,7 @@ impl Environment { Self { config, + database: app_container.database.clone(), tracker: app_container.tracker.clone(), keys_handler: app_container.keys_handler.clone(), authentication_service: app_container.authentication_service.clone(), @@ -78,6 +81,7 @@ impl Environment { Environment { config: self.config, + database: self.database.clone(), tracker: self.tracker.clone(), keys_handler: self.keys_handler.clone(), authentication_service: self.authentication_service.clone(), @@ -112,6 +116,7 @@ impl Environment { pub async fn stop(self) -> Environment { Environment { config: self.config, + database: self.database, tracker: self.tracker, keys_handler: self.keys_handler, authentication_service: self.authentication_service, diff --git a/tests/servers/api/mod.rs b/tests/servers/api/mod.rs index 278fd869d..92bc19a5f 100644 --- a/tests/servers/api/mod.rs +++ b/tests/servers/api/mod.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use torrust_tracker_lib::core::Tracker; +use torrust_tracker_lib::core::databases::Database; use torrust_tracker_lib::servers::apis::server; pub mod connection_info; @@ -9,12 +9,15 @@ pub mod v1; pub type Started = environment::Environment; -/// It forces a database error by dropping all tables. -/// That makes any query fail. +/// It forces a database error by dropping all tables. That makes all queries +/// fail. +/// /// code-review: +/// /// Alternatively we could: +/// /// - Inject a database mock in the future. /// - Inject directly the database reference passed to the Tracker type. -pub fn force_database_error(tracker: &Arc) { +pub fn force_database_error(tracker: &Arc>) { tracker.drop_database_tables().unwrap(); } diff --git a/tests/servers/api/v1/contract/context/auth_key.rs b/tests/servers/api/v1/contract/context/auth_key.rs index 73860c9c2..3b7d2d6ba 100644 --- a/tests/servers/api/v1/contract/context/auth_key.rs +++ b/tests/servers/api/v1/contract/context/auth_key.rs @@ -126,7 +126,7 @@ async fn should_fail_when_the_auth_key_cannot_be_generated() { let env = Started::new(&configuration::ephemeral().into()).await; - force_database_error(&env.tracker); + force_database_error(&env.database); let request_id = Uuid::new_v4(); @@ -297,7 +297,7 @@ async fn should_fail_when_the_auth_key_cannot_be_deleted() { .await .unwrap(); - force_database_error(&env.tracker); + force_database_error(&env.database); let request_id = Uuid::new_v4(); @@ -403,7 +403,7 @@ async fn should_fail_when_keys_cannot_be_reloaded() { .await .unwrap(); - force_database_error(&env.tracker); + force_database_error(&env.database); let response = Client::new(env.get_connection_info()) .reload_keys(Some(headers_with_request_id(request_id))) @@ -556,7 +556,7 @@ mod deprecated_generate_key_endpoint { let env = Started::new(&configuration::ephemeral().into()).await; - force_database_error(&env.tracker); + force_database_error(&env.database); let request_id = Uuid::new_v4(); let seconds_valid = 60; diff --git a/tests/servers/api/v1/contract/context/whitelist.rs b/tests/servers/api/v1/contract/context/whitelist.rs index aef1db4f1..78850d3bf 100644 --- a/tests/servers/api/v1/contract/context/whitelist.rs +++ b/tests/servers/api/v1/contract/context/whitelist.rs @@ -111,7 +111,7 @@ async fn should_fail_when_the_torrent_cannot_be_whitelisted() { let info_hash = "9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d".to_owned(); - force_database_error(&env.tracker); + force_database_error(&env.database); let request_id = Uuid::new_v4(); @@ -239,7 +239,7 @@ async fn should_fail_when_the_torrent_cannot_be_removed_from_the_whitelist() { let info_hash = InfoHash::from_str(&hash).unwrap(); env.whitelist_manager.add_torrent_to_whitelist(&info_hash).await.unwrap(); - force_database_error(&env.tracker); + force_database_error(&env.database); let request_id = Uuid::new_v4(); @@ -340,7 +340,7 @@ async fn should_fail_when_the_whitelist_cannot_be_reloaded_from_the_database() { let info_hash = InfoHash::from_str(&hash).unwrap(); env.whitelist_manager.add_torrent_to_whitelist(&info_hash).await.unwrap(); - force_database_error(&env.tracker); + force_database_error(&env.database); let request_id = Uuid::new_v4(); diff --git a/tests/servers/http/environment.rs b/tests/servers/http/environment.rs index 81b6a12e2..80c042a21 100644 --- a/tests/servers/http/environment.rs +++ b/tests/servers/http/environment.rs @@ -7,6 +7,7 @@ use torrust_tracker_lib::bootstrap::app::{initialize_app_container, initialize_g use torrust_tracker_lib::bootstrap::jobs::make_rust_tls; use torrust_tracker_lib::core::authentication::handler::KeysHandler; use torrust_tracker_lib::core::authentication::service::AuthenticationService; +use torrust_tracker_lib::core::databases::Database; use torrust_tracker_lib::core::statistics::event::sender::Sender; use torrust_tracker_lib::core::statistics::repository::Repository; use torrust_tracker_lib::core::whitelist::manager::WhiteListManager; @@ -17,6 +18,7 @@ use torrust_tracker_primitives::peer; pub struct Environment { pub config: Arc, + pub database: Arc>, pub tracker: Arc, pub keys_handler: Arc, pub authentication_service: Arc, @@ -57,6 +59,7 @@ impl Environment { Self { config, + database: app_container.database.clone(), tracker: app_container.tracker.clone(), keys_handler: app_container.keys_handler.clone(), authentication_service: app_container.authentication_service.clone(), @@ -73,6 +76,7 @@ impl Environment { pub async fn start(self) -> Environment { Environment { config: self.config, + database: self.database.clone(), tracker: self.tracker.clone(), keys_handler: self.keys_handler.clone(), authentication_service: self.authentication_service.clone(), @@ -104,6 +108,7 @@ impl Environment { pub async fn stop(self) -> Environment { Environment { config: self.config, + database: self.database, tracker: self.tracker, keys_handler: self.keys_handler, authentication_service: self.authentication_service, diff --git a/tests/servers/udp/environment.rs b/tests/servers/udp/environment.rs index b728509c0..c02e35e6e 100644 --- a/tests/servers/udp/environment.rs +++ b/tests/servers/udp/environment.rs @@ -5,6 +5,7 @@ use bittorrent_primitives::info_hash::InfoHash; use tokio::sync::RwLock; use torrust_tracker_configuration::{Configuration, UdpTracker, DEFAULT_TIMEOUT}; use torrust_tracker_lib::bootstrap::app::{initialize_app_container, initialize_global_services}; +use torrust_tracker_lib::core::databases::Database; use torrust_tracker_lib::core::statistics::event::sender::Sender; use torrust_tracker_lib::core::statistics::repository::Repository; use torrust_tracker_lib::core::{whitelist, Tracker}; @@ -20,6 +21,7 @@ where S: std::fmt::Debug + std::fmt::Display, { pub config: Arc, + pub database: Arc>, pub tracker: Arc, pub whitelist_authorization: Arc, pub stats_event_sender: Arc>>, @@ -57,6 +59,7 @@ impl Environment { Self { config, + database: app_container.database.clone(), tracker: app_container.tracker.clone(), whitelist_authorization: app_container.whitelist_authorization.clone(), stats_event_sender: app_container.stats_event_sender.clone(), @@ -72,6 +75,7 @@ impl Environment { let cookie_lifetime = self.config.cookie_lifetime; Environment { config: self.config, + database: self.database.clone(), tracker: self.tracker.clone(), whitelist_authorization: self.whitelist_authorization.clone(), stats_event_sender: self.stats_event_sender.clone(), @@ -109,6 +113,7 @@ impl Environment { Environment { config: self.config, + database: self.database, tracker: self.tracker, whitelist_authorization: self.whitelist_authorization, stats_event_sender: self.stats_event_sender,