From e18cae46e74f2f38bdbd2ee064b3c986c01ed7f6 Mon Sep 17 00:00:00 2001 From: Cameron Garnham Date: Mon, 25 Mar 2024 12:12:46 +0800 Subject: [PATCH] dev: torrent repository cleanups --- cSpell.json | 1 + packages/configuration/src/lib.rs | 2 +- packages/primitives/src/announce_event.rs | 2 +- packages/primitives/src/info_hash.rs | 19 + packages/primitives/src/lib.rs | 5 +- packages/primitives/src/pagination.rs | 8 +- packages/primitives/src/peer.rs | 24 +- packages/primitives/src/torrent_metrics.rs | 12 +- packages/torrent-repository/src/entry/mod.rs | 31 +- .../torrent-repository/src/entry/mutex_std.rs | 17 +- .../src/entry/mutex_tokio.rs | 25 +- .../torrent-repository/src/entry/single.rs | 324 +----------------- .../torrent-repository/src/repository/mod.rs | 38 +- .../src/repository/rw_lock_std.rs | 12 +- .../src/repository/rw_lock_std_mutex_std.rs | 10 +- .../src/repository/rw_lock_std_mutex_tokio.rs | 30 +- .../src/repository/rw_lock_tokio.rs | 10 +- .../src/repository/rw_lock_tokio_mutex_std.rs | 10 +- .../repository/rw_lock_tokio_mutex_tokio.rs | 22 +- src/core/databases/mod.rs | 4 +- src/core/databases/mysql.rs | 6 +- src/core/databases/sqlite.rs | 11 +- src/core/mod.rs | 20 +- src/core/torrent/mod.rs | 3 - .../apis/v1/context/stats/resources.rs | 12 +- src/servers/udp/handlers.rs | 63 ++-- 26 files changed, 259 insertions(+), 462 deletions(-) diff --git a/cSpell.json b/cSpell.json index 1e276dbc2..bbcba98a7 100644 --- a/cSpell.json +++ b/cSpell.json @@ -100,6 +100,7 @@ "ostr", "Pando", "peekable", + "peerlist", "proot", "proto", "Quickstart", diff --git a/packages/configuration/src/lib.rs b/packages/configuration/src/lib.rs index 549c73a31..ca873f3cd 100644 --- a/packages/configuration/src/lib.rs +++ b/packages/configuration/src/lib.rs @@ -246,7 +246,7 @@ use torrust_tracker_primitives::{DatabaseDriver, TrackerMode}; /// The maximum number of returned peers for a torrent. pub const TORRENT_PEERS_LIMIT: usize = 74; -#[derive(Copy, Clone, Debug, PartialEq, Default, Constructor)] +#[derive(Copy, Clone, Debug, PartialEq, Constructor)] pub struct TrackerPolicy { pub remove_peerless_torrents: bool, pub max_peer_timeout: u32, diff --git a/packages/primitives/src/announce_event.rs b/packages/primitives/src/announce_event.rs index 16e47da99..3bd560084 100644 --- a/packages/primitives/src/announce_event.rs +++ b/packages/primitives/src/announce_event.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; /// Announce events. Described on the /// [BEP 3. The `BitTorrent` Protocol Specification](https://www.bittorrent.org/beps/bep_0003.html) -#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(Hash, Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub enum AnnounceEvent { /// The peer has started downloading the torrent. Started, diff --git a/packages/primitives/src/info_hash.rs b/packages/primitives/src/info_hash.rs index 46ae6283e..a07cc41a2 100644 --- a/packages/primitives/src/info_hash.rs +++ b/packages/primitives/src/info_hash.rs @@ -1,3 +1,4 @@ +use std::hash::{DefaultHasher, Hash, Hasher}; use std::panic::Location; use thiserror::Error; @@ -77,6 +78,24 @@ impl std::convert::From<&[u8]> for InfoHash { } } +/// for testing +impl std::convert::From<&DefaultHasher> for InfoHash { + fn from(data: &DefaultHasher) -> InfoHash { + let n = data.finish().to_le_bytes(); + InfoHash([ + n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[0], n[1], n[2], + n[3], + ]) + } +} + +impl std::convert::From<&i32> for InfoHash { + fn from(n: &i32) -> InfoHash { + let n = n.to_le_bytes(); + InfoHash([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, n[0], n[1], n[2], n[3]]) + } +} + impl std::convert::From<[u8; 20]> for InfoHash { fn from(val: [u8; 20]) -> Self { InfoHash(val) diff --git a/packages/primitives/src/lib.rs b/packages/primitives/src/lib.rs index 664c0c82d..aeb4d0d4e 100644 --- a/packages/primitives/src/lib.rs +++ b/packages/primitives/src/lib.rs @@ -4,6 +4,7 @@ //! which is a `BitTorrent` tracker server. These structures are used not only //! by the tracker server crate, but also by other crates in the Torrust //! ecosystem. +use std::collections::BTreeMap; use std::time::Duration; use info_hash::InfoHash; @@ -38,7 +39,7 @@ pub enum IPVersion { } /// Number of bytes downloaded, uploaded or pending to download (left) by the peer. -#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(Hash, Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct NumberOfBytes(pub i64); /// The database management system used by the tracker. @@ -58,7 +59,7 @@ pub enum DatabaseDriver { MySQL, } -pub type PersistentTorrents = Vec<(InfoHash, u32)>; +pub type PersistentTorrents = BTreeMap; /// The mode the tracker will run in. /// diff --git a/packages/primitives/src/pagination.rs b/packages/primitives/src/pagination.rs index ab7dcfe2b..96b5ad662 100644 --- a/packages/primitives/src/pagination.rs +++ b/packages/primitives/src/pagination.rs @@ -1,7 +1,8 @@ +use derive_more::Constructor; use serde::Deserialize; /// A struct to keep information about the page when results are being paginated -#[derive(Deserialize, Copy, Clone, Debug, PartialEq)] +#[derive(Deserialize, Copy, Clone, Debug, PartialEq, Constructor)] pub struct Pagination { /// The page number, starting at 0 pub offset: u32, @@ -10,11 +11,6 @@ pub struct Pagination { } impl Pagination { - #[must_use] - pub fn new(offset: u32, limit: u32) -> Self { - Self { offset, limit } - } - #[must_use] pub fn new_with_options(offset_option: Option, limit_option: Option) -> Self { let offset = match offset_option { diff --git a/packages/primitives/src/peer.rs b/packages/primitives/src/peer.rs index 5fb9e525f..f5b009f2a 100644 --- a/packages/primitives/src/peer.rs +++ b/packages/primitives/src/peer.rs @@ -51,7 +51,7 @@ use crate::{ser_unix_time_value, DurationSinceUnixEpoch, IPVersion, NumberOfByte /// event: AnnounceEvent::Started, /// }; /// ``` -#[derive(PartialEq, Eq, Debug, Clone, Serialize, Copy)] +#[derive(Debug, Clone, Serialize, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Peer { /// ID used by the downloader peer pub peer_id: Id, @@ -173,6 +173,16 @@ impl From<[u8; 20]> for Id { } } +impl From for Id { + fn from(number: i32) -> Self { + let peer_id = number.to_le_bytes(); + Id::from([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, peer_id[0], peer_id[1], peer_id[2], + peer_id[3], + ]) + } +} + impl TryFrom> for Id { type Error = IdConversionError; @@ -332,7 +342,7 @@ impl FromIterator for Vec

{ } pub mod fixture { - use std::net::SocketAddr; + use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use super::{Id, Peer}; use crate::announce_event::AnnounceEvent; @@ -396,8 +406,8 @@ pub mod fixture { impl Default for Peer { fn default() -> Self { Self { - peer_id: Id(*b"-qB00000000000000000"), - peer_addr: std::net::SocketAddr::new(std::net::IpAddr::V4(std::net::Ipv4Addr::new(126, 0, 0, 1)), 8080), + peer_id: Id::default(), + peer_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080), updated: DurationSinceUnixEpoch::new(1_669_397_478_934, 0), uploaded: NumberOfBytes(0), downloaded: NumberOfBytes(0), @@ -406,6 +416,12 @@ pub mod fixture { } } } + + impl Default for Id { + fn default() -> Self { + Self(*b"-qB00000000000000000") + } + } } #[cfg(test)] diff --git a/packages/primitives/src/torrent_metrics.rs b/packages/primitives/src/torrent_metrics.rs index c60507171..02de02954 100644 --- a/packages/primitives/src/torrent_metrics.rs +++ b/packages/primitives/src/torrent_metrics.rs @@ -6,20 +6,20 @@ use std::ops::AddAssign; #[derive(Copy, Clone, Debug, PartialEq, Default)] pub struct TorrentsMetrics { /// Total number of seeders for all torrents - pub seeders: u64, + pub complete: u64, /// Total number of peers that have ever completed downloading for all torrents. - pub completed: u64, + pub downloaded: u64, /// Total number of leechers for all torrents. - pub leechers: u64, + pub incomplete: u64, /// Total number of torrents. pub torrents: u64, } impl AddAssign for TorrentsMetrics { fn add_assign(&mut self, rhs: Self) { - self.seeders += rhs.seeders; - self.completed += rhs.completed; - self.leechers += rhs.leechers; + self.complete += rhs.complete; + self.downloaded += rhs.downloaded; + self.incomplete += rhs.incomplete; self.torrents += rhs.torrents; } } diff --git a/packages/torrent-repository/src/entry/mod.rs b/packages/torrent-repository/src/entry/mod.rs index 11352a8fa..4c39af829 100644 --- a/packages/torrent-repository/src/entry/mod.rs +++ b/packages/torrent-repository/src/entry/mod.rs @@ -1,4 +1,5 @@ use std::fmt::Debug; +use std::net::SocketAddr; use std::sync::Arc; //use serde::{Deserialize, Serialize}; @@ -17,7 +18,7 @@ pub trait Entry { fn get_stats(&self) -> SwarmMetadata; /// Returns True if Still a Valid Entry according to the Tracker Policy - fn is_not_zombie(&self, policy: &TrackerPolicy) -> bool; + fn is_good(&self, policy: &TrackerPolicy) -> bool; /// Returns True if the Peers is Empty fn peers_is_empty(&self) -> bool; @@ -33,7 +34,7 @@ pub trait Entry { /// /// It filters out the input peer, typically because we want to return this /// list of peers to that client peer. - fn get_peers_for_peer(&self, client: &peer::Peer, limit: Option) -> Vec>; + fn get_peers_for_client(&self, client: &SocketAddr, limit: Option) -> Vec>; /// It updates a peer and returns true if the number of complete downloads have increased. /// @@ -51,11 +52,11 @@ pub trait Entry { #[allow(clippy::module_name_repetitions)] pub trait EntrySync { fn get_stats(&self) -> SwarmMetadata; - fn is_not_zombie(&self, policy: &TrackerPolicy) -> bool; + fn is_good(&self, policy: &TrackerPolicy) -> bool; fn peers_is_empty(&self) -> bool; fn get_peers_len(&self) -> usize; fn get_peers(&self, limit: Option) -> Vec>; - fn get_peers_for_peer(&self, client: &peer::Peer, limit: Option) -> Vec>; + fn get_peers_for_client(&self, client: &SocketAddr, limit: Option) -> Vec>; fn insert_or_update_peer(&self, peer: &peer::Peer) -> bool; fn insert_or_update_peer_and_get_stats(&self, peer: &peer::Peer) -> (bool, SwarmMetadata); fn remove_inactive_peers(&self, current_cutoff: DurationSinceUnixEpoch); @@ -63,16 +64,14 @@ pub trait EntrySync { #[allow(clippy::module_name_repetitions)] pub trait EntryAsync { - fn get_stats(self) -> impl std::future::Future + Send; - - #[allow(clippy::wrong_self_convention)] - fn is_not_zombie(self, policy: &TrackerPolicy) -> impl std::future::Future + Send; - fn peers_is_empty(self) -> impl std::future::Future + Send; - fn get_peers_len(self) -> impl std::future::Future + Send; - fn get_peers(self, limit: Option) -> impl std::future::Future>> + Send; - fn get_peers_for_peer( - self, - client: &peer::Peer, + fn get_stats(&self) -> impl std::future::Future + Send; + fn check_good(self, policy: &TrackerPolicy) -> impl std::future::Future + Send; + fn peers_is_empty(&self) -> impl std::future::Future + Send; + fn get_peers_len(&self) -> impl std::future::Future + Send; + fn get_peers(&self, limit: Option) -> impl std::future::Future>> + Send; + fn get_peers_for_client( + &self, + client: &SocketAddr, limit: Option, ) -> impl std::future::Future>> + Send; fn insert_or_update_peer(self, peer: &peer::Peer) -> impl std::future::Future + Send; @@ -88,11 +87,11 @@ pub trait EntryAsync { /// This is the tracker entry for a given torrent and contains the swarm data, /// that's the list of all the peers trying to download the same torrent. /// The tracker keeps one entry like this for every torrent. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Torrent { /// The swarm: a network of peers that are all trying to download the torrent associated to this entry // #[serde(skip)] pub(crate) peers: std::collections::BTreeMap>, /// The number of peers that have ever completed downloading the torrent associated to this entry - pub(crate) completed: u32, + pub(crate) downloaded: u32, } diff --git a/packages/torrent-repository/src/entry/mutex_std.rs b/packages/torrent-repository/src/entry/mutex_std.rs index df6228317..b4b823909 100644 --- a/packages/torrent-repository/src/entry/mutex_std.rs +++ b/packages/torrent-repository/src/entry/mutex_std.rs @@ -1,3 +1,4 @@ +use std::net::SocketAddr; use std::sync::Arc; use torrust_tracker_configuration::TrackerPolicy; @@ -5,15 +6,15 @@ use torrust_tracker_primitives::swarm_metadata::SwarmMetadata; use torrust_tracker_primitives::{peer, DurationSinceUnixEpoch}; use super::{Entry, EntrySync}; -use crate::EntryMutexStd; +use crate::{EntryMutexStd, EntrySingle}; impl EntrySync for EntryMutexStd { fn get_stats(&self) -> SwarmMetadata { self.lock().expect("it should get a lock").get_stats() } - fn is_not_zombie(&self, policy: &TrackerPolicy) -> bool { - self.lock().expect("it should get a lock").is_not_zombie(policy) + fn is_good(&self, policy: &TrackerPolicy) -> bool { + self.lock().expect("it should get a lock").is_good(policy) } fn peers_is_empty(&self) -> bool { @@ -28,8 +29,8 @@ impl EntrySync for EntryMutexStd { self.lock().expect("it should get lock").get_peers(limit) } - fn get_peers_for_peer(&self, client: &peer::Peer, limit: Option) -> Vec> { - self.lock().expect("it should get lock").get_peers_for_peer(client, limit) + fn get_peers_for_client(&self, client: &SocketAddr, limit: Option) -> Vec> { + self.lock().expect("it should get lock").get_peers_for_client(client, limit) } fn insert_or_update_peer(&self, peer: &peer::Peer) -> bool { @@ -48,3 +49,9 @@ impl EntrySync for EntryMutexStd { .remove_inactive_peers(current_cutoff); } } + +impl From for EntryMutexStd { + fn from(entry: EntrySingle) -> Self { + Arc::new(std::sync::Mutex::new(entry)) + } +} diff --git a/packages/torrent-repository/src/entry/mutex_tokio.rs b/packages/torrent-repository/src/entry/mutex_tokio.rs index c4d13fb43..34f4a4e92 100644 --- a/packages/torrent-repository/src/entry/mutex_tokio.rs +++ b/packages/torrent-repository/src/entry/mutex_tokio.rs @@ -1,3 +1,4 @@ +use std::net::SocketAddr; use std::sync::Arc; use torrust_tracker_configuration::TrackerPolicy; @@ -5,31 +6,31 @@ use torrust_tracker_primitives::swarm_metadata::SwarmMetadata; use torrust_tracker_primitives::{peer, DurationSinceUnixEpoch}; use super::{Entry, EntryAsync}; -use crate::EntryMutexTokio; +use crate::{EntryMutexTokio, EntrySingle}; impl EntryAsync for EntryMutexTokio { - async fn get_stats(self) -> SwarmMetadata { + async fn get_stats(&self) -> SwarmMetadata { self.lock().await.get_stats() } - async fn is_not_zombie(self, policy: &TrackerPolicy) -> bool { - self.lock().await.is_not_zombie(policy) + async fn check_good(self, policy: &TrackerPolicy) -> bool { + self.lock().await.is_good(policy) } - async fn peers_is_empty(self) -> bool { + async fn peers_is_empty(&self) -> bool { self.lock().await.peers_is_empty() } - async fn get_peers_len(self) -> usize { + async fn get_peers_len(&self) -> usize { self.lock().await.get_peers_len() } - async fn get_peers(self, limit: Option) -> Vec> { + async fn get_peers(&self, limit: Option) -> Vec> { self.lock().await.get_peers(limit) } - async fn get_peers_for_peer(self, client: &peer::Peer, limit: Option) -> Vec> { - self.lock().await.get_peers_for_peer(client, limit) + async fn get_peers_for_client(&self, client: &SocketAddr, limit: Option) -> Vec> { + self.lock().await.get_peers_for_client(client, limit) } async fn insert_or_update_peer(self, peer: &peer::Peer) -> bool { @@ -44,3 +45,9 @@ impl EntryAsync for EntryMutexTokio { self.lock().await.remove_inactive_peers(current_cutoff); } } + +impl From for EntryMutexTokio { + fn from(entry: EntrySingle) -> Self { + Arc::new(tokio::sync::Mutex::new(entry)) + } +} diff --git a/packages/torrent-repository/src/entry/single.rs b/packages/torrent-repository/src/entry/single.rs index 85fdc6cf0..c1041e9a2 100644 --- a/packages/torrent-repository/src/entry/single.rs +++ b/packages/torrent-repository/src/entry/single.rs @@ -1,3 +1,4 @@ +use std::net::SocketAddr; use std::sync::Arc; use torrust_tracker_configuration::TrackerPolicy; @@ -16,14 +17,14 @@ impl Entry for EntrySingle { let incomplete: u32 = self.peers.len() as u32 - complete; SwarmMetadata { - downloaded: self.completed, + downloaded: self.downloaded, complete, incomplete, } } - fn is_not_zombie(&self, policy: &TrackerPolicy) -> bool { - if policy.persistent_torrent_completed_stat && self.completed > 0 { + fn is_good(&self, policy: &TrackerPolicy) -> bool { + if policy.persistent_torrent_completed_stat && self.downloaded > 0 { return true; } @@ -48,13 +49,13 @@ impl Entry for EntrySingle { } } - fn get_peers_for_peer(&self, client: &peer::Peer, limit: Option) -> Vec> { + fn get_peers_for_client(&self, client: &SocketAddr, limit: Option) -> Vec> { match limit { Some(limit) => self .peers .values() // Take peers which are not the client peer - .filter(|peer| peer::ReadInfo::get_address(peer.as_ref()) != peer::ReadInfo::get_address(client)) + .filter(|peer| peer::ReadInfo::get_address(peer.as_ref()) != *client) // Limit the number of peers on the result .take(limit) .cloned() @@ -63,25 +64,25 @@ impl Entry for EntrySingle { .peers .values() // Take peers which are not the client peer - .filter(|peer| peer::ReadInfo::get_address(peer.as_ref()) != peer::ReadInfo::get_address(client)) + .filter(|peer| peer::ReadInfo::get_address(peer.as_ref()) != *client) .cloned() .collect(), } } fn insert_or_update_peer(&mut self, peer: &peer::Peer) -> bool { - let mut did_torrent_stats_change: bool = false; + let mut downloaded_stats_updated: bool = false; match peer::ReadInfo::get_event(peer) { AnnounceEvent::Stopped => { drop(self.peers.remove(&peer::ReadInfo::get_id(peer))); } AnnounceEvent::Completed => { - let peer_old = self.peers.insert(peer::ReadInfo::get_id(peer), Arc::new(*peer)); + let previous = self.peers.insert(peer::ReadInfo::get_id(peer), Arc::new(*peer)); // Don't count if peer was not previously known and not already completed. - if peer_old.is_some_and(|p| p.event != AnnounceEvent::Completed) { - self.completed += 1; - did_torrent_stats_change = true; + if previous.is_some_and(|p| p.event != AnnounceEvent::Completed) { + self.downloaded += 1; + downloaded_stats_updated = true; } } _ => { @@ -89,7 +90,7 @@ impl Entry for EntrySingle { } } - did_torrent_stats_change + downloaded_stats_updated } fn insert_or_update_peer_and_get_stats(&mut self, peer: &peer::Peer) -> (bool, SwarmMetadata) { @@ -103,302 +104,3 @@ impl Entry for EntrySingle { .retain(|_, peer| peer::ReadInfo::get_updated(peer) > current_cutoff); } } - -#[cfg(test)] -mod tests { - mod torrent_entry { - - use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - use std::ops::Sub; - use std::sync::Arc; - use std::time::Duration; - - use torrust_tracker_clock::clock::stopped::Stopped as _; - use torrust_tracker_clock::clock::{self, Time}; - use torrust_tracker_configuration::TORRENT_PEERS_LIMIT; - use torrust_tracker_primitives::announce_event::AnnounceEvent; - use torrust_tracker_primitives::{peer, DurationSinceUnixEpoch, NumberOfBytes}; - - use crate::entry::Entry; - use crate::{CurrentClock, EntrySingle}; - - struct TorrentPeerBuilder { - peer: peer::Peer, - } - - impl TorrentPeerBuilder { - pub fn default() -> TorrentPeerBuilder { - let default_peer = peer::Peer { - peer_id: peer::Id([0u8; 20]), - peer_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080), - updated: CurrentClock::now(), - uploaded: NumberOfBytes(0), - downloaded: NumberOfBytes(0), - left: NumberOfBytes(0), - event: AnnounceEvent::Started, - }; - TorrentPeerBuilder { peer: default_peer } - } - - pub fn with_event_completed(mut self) -> Self { - self.peer.event = AnnounceEvent::Completed; - self - } - - pub fn with_peer_address(mut self, peer_addr: SocketAddr) -> Self { - self.peer.peer_addr = peer_addr; - self - } - - pub fn with_peer_id(mut self, peer_id: peer::Id) -> Self { - self.peer.peer_id = peer_id; - self - } - - pub fn with_number_of_bytes_left(mut self, left: i64) -> Self { - self.peer.left = NumberOfBytes(left); - self - } - - pub fn updated_at(mut self, updated: DurationSinceUnixEpoch) -> Self { - self.peer.updated = updated; - self - } - - pub fn into(self) -> peer::Peer { - self.peer - } - } - - /// A torrent seeder is a peer with 0 bytes left to download which - /// has not announced it has stopped - fn a_torrent_seeder() -> peer::Peer { - TorrentPeerBuilder::default() - .with_number_of_bytes_left(0) - .with_event_completed() - .into() - } - - /// A torrent leecher is a peer that is not a seeder. - /// Leecher: left > 0 OR event = Stopped - fn a_torrent_leecher() -> peer::Peer { - TorrentPeerBuilder::default() - .with_number_of_bytes_left(1) - .with_event_completed() - .into() - } - - #[test] - fn the_default_torrent_entry_should_contain_an_empty_list_of_peers() { - let torrent_entry = EntrySingle::default(); - - assert_eq!(torrent_entry.get_peers(None).len(), 0); - } - - #[test] - fn a_new_peer_can_be_added_to_a_torrent_entry() { - let mut torrent_entry = EntrySingle::default(); - let torrent_peer = TorrentPeerBuilder::default().into(); - - torrent_entry.insert_or_update_peer(&torrent_peer); // Add the peer - - assert_eq!(*torrent_entry.get_peers(None)[0], torrent_peer); - assert_eq!(torrent_entry.get_peers(None).len(), 1); - } - - #[test] - fn a_torrent_entry_should_contain_the_list_of_peers_that_were_added_to_the_torrent() { - let mut torrent_entry = EntrySingle::default(); - let torrent_peer = TorrentPeerBuilder::default().into(); - - torrent_entry.insert_or_update_peer(&torrent_peer); // Add the peer - - assert_eq!(torrent_entry.get_peers(None), vec![Arc::new(torrent_peer)]); - } - - #[test] - fn a_peer_can_be_updated_in_a_torrent_entry() { - let mut torrent_entry = EntrySingle::default(); - let mut torrent_peer = TorrentPeerBuilder::default().into(); - torrent_entry.insert_or_update_peer(&torrent_peer); // Add the peer - - torrent_peer.event = AnnounceEvent::Completed; // Update the peer - torrent_entry.insert_or_update_peer(&torrent_peer); // Update the peer in the torrent entry - - assert_eq!(torrent_entry.get_peers(None)[0].event, AnnounceEvent::Completed); - } - - #[test] - fn a_peer_should_be_removed_from_a_torrent_entry_when_the_peer_announces_it_has_stopped() { - let mut torrent_entry = EntrySingle::default(); - let mut torrent_peer = TorrentPeerBuilder::default().into(); - torrent_entry.insert_or_update_peer(&torrent_peer); // Add the peer - - torrent_peer.event = AnnounceEvent::Stopped; // Update the peer - torrent_entry.insert_or_update_peer(&torrent_peer); // Update the peer in the torrent entry - - assert_eq!(torrent_entry.get_peers(None).len(), 0); - } - - #[test] - fn torrent_stats_change_when_a_previously_known_peer_announces_it_has_completed_the_torrent() { - let mut torrent_entry = EntrySingle::default(); - let mut torrent_peer = TorrentPeerBuilder::default().into(); - - torrent_entry.insert_or_update_peer(&torrent_peer); // Add the peer - - torrent_peer.event = AnnounceEvent::Completed; // Update the peer - let stats_have_changed = torrent_entry.insert_or_update_peer(&torrent_peer); // Update the peer in the torrent entry - - assert!(stats_have_changed); - } - - #[test] - fn torrent_stats_should_not_change_when_a_peer_announces_it_has_completed_the_torrent_if_it_is_the_first_announce_from_the_peer( - ) { - let mut torrent_entry = EntrySingle::default(); - let torrent_peer_announcing_complete_event = TorrentPeerBuilder::default().with_event_completed().into(); - - // Add a peer that did not exist before in the entry - let torrent_stats_have_not_changed = !torrent_entry.insert_or_update_peer(&torrent_peer_announcing_complete_event); - - assert!(torrent_stats_have_not_changed); - } - - #[test] - fn a_torrent_entry_should_return_the_list_of_peers_for_a_given_peer_filtering_out_the_client_that_is_making_the_request() - { - let mut torrent_entry = EntrySingle::default(); - let peer_socket_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - let torrent_peer = TorrentPeerBuilder::default().with_peer_address(peer_socket_address).into(); - torrent_entry.insert_or_update_peer(&torrent_peer); // Add peer - - // Get peers excluding the one we have just added - let peers = torrent_entry.get_peers_for_peer(&torrent_peer, None); - - assert_eq!(peers.len(), 0); - } - - #[test] - fn two_peers_with_the_same_ip_but_different_port_should_be_considered_different_peers() { - let mut torrent_entry = EntrySingle::default(); - - let peer_ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); - - // Add peer 1 - let torrent_peer_1 = TorrentPeerBuilder::default() - .with_peer_address(SocketAddr::new(peer_ip, 8080)) - .into(); - torrent_entry.insert_or_update_peer(&torrent_peer_1); - - // Add peer 2 - let torrent_peer_2 = TorrentPeerBuilder::default() - .with_peer_address(SocketAddr::new(peer_ip, 8081)) - .into(); - torrent_entry.insert_or_update_peer(&torrent_peer_2); - - // Get peers for peer 1 - let peers = torrent_entry.get_peers_for_peer(&torrent_peer_1, None); - - // The peer 2 using the same IP but different port should be included - assert_eq!(peers[0].peer_addr.ip(), Ipv4Addr::new(127, 0, 0, 1)); - assert_eq!(peers[0].peer_addr.port(), 8081); - } - - fn peer_id_from_i32(number: i32) -> peer::Id { - let peer_id = number.to_le_bytes(); - peer::Id([ - 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, peer_id[0], peer_id[1], - peer_id[2], peer_id[3], - ]) - } - - #[test] - fn the_tracker_should_limit_the_list_of_peers_to_74_when_clients_scrape_torrents() { - let mut torrent_entry = EntrySingle::default(); - - // We add one more peer than the scrape limit - for peer_number in 1..=74 + 1 { - let torrent_peer = TorrentPeerBuilder::default() - .with_peer_id(peer_id_from_i32(peer_number)) - .into(); - torrent_entry.insert_or_update_peer(&torrent_peer); - } - - let peers = torrent_entry.get_peers(Some(TORRENT_PEERS_LIMIT)); - - assert_eq!(peers.len(), 74); - } - - #[test] - fn torrent_stats_should_have_the_number_of_seeders_for_a_torrent() { - let mut torrent_entry = EntrySingle::default(); - let torrent_seeder = a_torrent_seeder(); - - torrent_entry.insert_or_update_peer(&torrent_seeder); // Add seeder - - assert_eq!(torrent_entry.get_stats().complete, 1); - } - - #[test] - fn torrent_stats_should_have_the_number_of_leechers_for_a_torrent() { - let mut torrent_entry = EntrySingle::default(); - let torrent_leecher = a_torrent_leecher(); - - torrent_entry.insert_or_update_peer(&torrent_leecher); // Add leecher - - assert_eq!(torrent_entry.get_stats().incomplete, 1); - } - - #[test] - fn torrent_stats_should_have_the_number_of_peers_that_having_announced_at_least_two_events_the_latest_one_is_the_completed_event( - ) { - let mut torrent_entry = EntrySingle::default(); - let mut torrent_peer = TorrentPeerBuilder::default().into(); - torrent_entry.insert_or_update_peer(&torrent_peer); // Add the peer - - // Announce "Completed" torrent download event. - torrent_peer.event = AnnounceEvent::Completed; - torrent_entry.insert_or_update_peer(&torrent_peer); // Update the peer - - let number_of_previously_known_peers_with_completed_torrent = torrent_entry.get_stats().complete; - - assert_eq!(number_of_previously_known_peers_with_completed_torrent, 1); - } - - #[test] - fn torrent_stats_should_not_include_a_peer_in_the_completed_counter_if_the_peer_has_announced_only_one_event() { - let mut torrent_entry = EntrySingle::default(); - let torrent_peer_announcing_complete_event = TorrentPeerBuilder::default().with_event_completed().into(); - - // Announce "Completed" torrent download event. - // It's the first event announced from this peer. - torrent_entry.insert_or_update_peer(&torrent_peer_announcing_complete_event); // Add the peer - - let number_of_peers_with_completed_torrent = torrent_entry.get_stats().downloaded; - - assert_eq!(number_of_peers_with_completed_torrent, 0); - } - - #[test] - fn a_torrent_entry_should_remove_a_peer_not_updated_after_a_timeout_in_seconds() { - let mut torrent_entry = EntrySingle::default(); - - let timeout = 120u32; - - let now = clock::Working::now(); - clock::Stopped::local_set(&now); - - let timeout_seconds_before_now = now.sub(Duration::from_secs(u64::from(timeout))); - let inactive_peer = TorrentPeerBuilder::default() - .updated_at(timeout_seconds_before_now.sub(Duration::from_secs(1))) - .into(); - torrent_entry.insert_or_update_peer(&inactive_peer); // Add the peer - - let current_cutoff = CurrentClock::now_sub(&Duration::from_secs(u64::from(timeout))).unwrap_or_default(); - torrent_entry.remove_inactive_peers(current_cutoff); - - assert_eq!(torrent_entry.get_peers_len(), 0); - } - } -} diff --git a/packages/torrent-repository/src/repository/mod.rs b/packages/torrent-repository/src/repository/mod.rs index b46771163..494040c9d 100644 --- a/packages/torrent-repository/src/repository/mod.rs +++ b/packages/torrent-repository/src/repository/mod.rs @@ -12,7 +12,9 @@ pub mod rw_lock_tokio; pub mod rw_lock_tokio_mutex_std; pub mod rw_lock_tokio_mutex_tokio; -pub trait Repository: Default + 'static { +use std::fmt::Debug; + +pub trait Repository: Debug + Default + Sized + 'static { fn get(&self, key: &InfoHash) -> Option; fn get_metrics(&self) -> TorrentsMetrics; fn get_paginated(&self, pagination: Option<&Pagination>) -> Vec<(InfoHash, T)>; @@ -24,7 +26,7 @@ pub trait Repository: Default + 'static { } #[allow(clippy::module_name_repetitions)] -pub trait RepositoryAsync: Default + 'static { +pub trait RepositoryAsync: Debug + Default + Sized + 'static { fn get(&self, key: &InfoHash) -> impl std::future::Future> + Send; fn get_metrics(&self) -> impl std::future::Future + Send; fn get_paginated(&self, pagination: Option<&Pagination>) -> impl std::future::Future> + Send; @@ -39,12 +41,36 @@ pub trait RepositoryAsync: Default + 'static { ) -> impl std::future::Future + Send; } -#[derive(Default)] +#[derive(Default, Debug)] +pub struct RwLockStd { + torrents: std::sync::RwLock>, +} + +#[derive(Default, Debug)] pub struct RwLockTokio { torrents: tokio::sync::RwLock>, } -#[derive(Default)] -pub struct RwLockStd { - torrents: std::sync::RwLock>, +impl RwLockStd { + /// # Panics + /// + /// Panics if unable to get a lock. + pub fn write( + &self, + ) -> std::sync::RwLockWriteGuard<'_, std::collections::BTreeMap> { + self.torrents.write().expect("it should get lock") + } +} + +impl RwLockTokio { + pub fn write( + &self, + ) -> impl std::future::Future< + Output = tokio::sync::RwLockWriteGuard< + '_, + std::collections::BTreeMap, + >, + > { + self.torrents.write() + } } diff --git a/packages/torrent-repository/src/repository/rw_lock_std.rs b/packages/torrent-repository/src/repository/rw_lock_std.rs index bacef623d..9d7f29416 100644 --- a/packages/torrent-repository/src/repository/rw_lock_std.rs +++ b/packages/torrent-repository/src/repository/rw_lock_std.rs @@ -49,9 +49,9 @@ where for entry in self.get_torrents().values() { let stats = entry.get_stats(); - metrics.seeders += u64::from(stats.complete); - metrics.completed += u64::from(stats.downloaded); - metrics.leechers += u64::from(stats.incomplete); + metrics.complete += u64::from(stats.complete); + metrics.downloaded += u64::from(stats.downloaded); + metrics.incomplete += u64::from(stats.incomplete); metrics.torrents += 1; } @@ -75,7 +75,7 @@ where fn import_persistent(&self, persistent_torrents: &PersistentTorrents) { let mut torrents = self.get_torrents_mut(); - for (info_hash, completed) in persistent_torrents { + for (info_hash, downloaded) in persistent_torrents { // Skip if torrent entry already exists if torrents.contains_key(info_hash) { continue; @@ -83,7 +83,7 @@ where let entry = EntrySingle { peers: BTreeMap::default(), - completed: *completed, + downloaded: *downloaded, }; torrents.insert(*info_hash, entry); @@ -107,6 +107,6 @@ where fn remove_peerless_torrents(&self, policy: &TrackerPolicy) { let mut db = self.get_torrents_mut(); - db.retain(|_, e| e.is_not_zombie(policy)); + db.retain(|_, e| e.is_good(policy)); } } diff --git a/packages/torrent-repository/src/repository/rw_lock_std_mutex_std.rs b/packages/torrent-repository/src/repository/rw_lock_std_mutex_std.rs index 9fca82ba8..0b65234e3 100644 --- a/packages/torrent-repository/src/repository/rw_lock_std_mutex_std.rs +++ b/packages/torrent-repository/src/repository/rw_lock_std_mutex_std.rs @@ -57,9 +57,9 @@ where for entry in self.get_torrents().values() { let stats = entry.lock().expect("it should get a lock").get_stats(); - metrics.seeders += u64::from(stats.complete); - metrics.completed += u64::from(stats.downloaded); - metrics.leechers += u64::from(stats.incomplete); + metrics.complete += u64::from(stats.complete); + metrics.downloaded += u64::from(stats.downloaded); + metrics.incomplete += u64::from(stats.incomplete); metrics.torrents += 1; } @@ -92,7 +92,7 @@ where let entry = EntryMutexStd::new( EntrySingle { peers: BTreeMap::default(), - completed: *completed, + downloaded: *completed, } .into(), ); @@ -118,6 +118,6 @@ where fn remove_peerless_torrents(&self, policy: &TrackerPolicy) { let mut db = self.get_torrents_mut(); - db.retain(|_, e| e.lock().expect("it should lock entry").is_not_zombie(policy)); + db.retain(|_, e| e.lock().expect("it should lock entry").is_good(policy)); } } diff --git a/packages/torrent-repository/src/repository/rw_lock_std_mutex_tokio.rs b/packages/torrent-repository/src/repository/rw_lock_std_mutex_tokio.rs index b9fb54469..5394abb6a 100644 --- a/packages/torrent-repository/src/repository/rw_lock_std_mutex_tokio.rs +++ b/packages/torrent-repository/src/repository/rw_lock_std_mutex_tokio.rs @@ -1,4 +1,5 @@ use std::collections::BTreeMap; +use std::iter::zip; use std::pin::Pin; use std::sync::Arc; @@ -75,9 +76,9 @@ where for entry in entries { let stats = entry.lock().await.get_stats(); - metrics.seeders += u64::from(stats.complete); - metrics.completed += u64::from(stats.downloaded); - metrics.leechers += u64::from(stats.incomplete); + metrics.complete += u64::from(stats.complete); + metrics.downloaded += u64::from(stats.downloaded); + metrics.incomplete += u64::from(stats.incomplete); metrics.torrents += 1; } @@ -96,7 +97,7 @@ where let entry = EntryMutexTokio::new( EntrySingle { peers: BTreeMap::default(), - completed: *completed, + downloaded: *completed, } .into(), ); @@ -124,8 +125,27 @@ where } async fn remove_peerless_torrents(&self, policy: &TrackerPolicy) { + let handles: Vec> + Send>>>; + + { + let db = self.get_torrents(); + + handles = zip(db.keys().copied(), db.values().cloned()) + .map(|(infohash, torrent)| { + torrent + .check_good(policy) + .map(move |good| if good { None } else { Some(infohash) }) + .boxed() + }) + .collect::>(); + } + + let not_good = join_all(handles).await; + let mut db = self.get_torrents_mut(); - db.retain(|_, e| e.blocking_lock().is_not_zombie(policy)); + for remove in not_good.into_iter().flatten() { + drop(db.remove(&remove)); + } } } diff --git a/packages/torrent-repository/src/repository/rw_lock_tokio.rs b/packages/torrent-repository/src/repository/rw_lock_tokio.rs index d0b7ec751..fa84e2451 100644 --- a/packages/torrent-repository/src/repository/rw_lock_tokio.rs +++ b/packages/torrent-repository/src/repository/rw_lock_tokio.rs @@ -64,9 +64,9 @@ where for entry in self.get_torrents().await.values() { let stats = entry.get_stats(); - metrics.seeders += u64::from(stats.complete); - metrics.completed += u64::from(stats.downloaded); - metrics.leechers += u64::from(stats.incomplete); + metrics.complete += u64::from(stats.complete); + metrics.downloaded += u64::from(stats.downloaded); + metrics.incomplete += u64::from(stats.incomplete); metrics.torrents += 1; } @@ -84,7 +84,7 @@ where let entry = EntrySingle { peers: BTreeMap::default(), - completed: *completed, + downloaded: *completed, }; torrents.insert(*info_hash, entry); @@ -108,6 +108,6 @@ where async fn remove_peerless_torrents(&self, policy: &TrackerPolicy) { let mut db = self.get_torrents_mut().await; - db.retain(|_, e| e.is_not_zombie(policy)); + db.retain(|_, e| e.is_good(policy)); } } diff --git a/packages/torrent-repository/src/repository/rw_lock_tokio_mutex_std.rs b/packages/torrent-repository/src/repository/rw_lock_tokio_mutex_std.rs index f800d2001..fbbc51a09 100644 --- a/packages/torrent-repository/src/repository/rw_lock_tokio_mutex_std.rs +++ b/packages/torrent-repository/src/repository/rw_lock_tokio_mutex_std.rs @@ -72,9 +72,9 @@ where for entry in self.get_torrents().await.values() { let stats = entry.get_stats(); - metrics.seeders += u64::from(stats.complete); - metrics.completed += u64::from(stats.downloaded); - metrics.leechers += u64::from(stats.incomplete); + metrics.complete += u64::from(stats.complete); + metrics.downloaded += u64::from(stats.downloaded); + metrics.incomplete += u64::from(stats.incomplete); metrics.torrents += 1; } @@ -93,7 +93,7 @@ where let entry = EntryMutexStd::new( EntrySingle { peers: BTreeMap::default(), - completed: *completed, + downloaded: *completed, } .into(), ); @@ -119,6 +119,6 @@ where async fn remove_peerless_torrents(&self, policy: &TrackerPolicy) { let mut db = self.get_torrents_mut().await; - db.retain(|_, e| e.lock().expect("it should lock entry").is_not_zombie(policy)); + db.retain(|_, e| e.lock().expect("it should lock entry").is_good(policy)); } } diff --git a/packages/torrent-repository/src/repository/rw_lock_tokio_mutex_tokio.rs b/packages/torrent-repository/src/repository/rw_lock_tokio_mutex_tokio.rs index 7ce2cc74c..bc7fd61e8 100644 --- a/packages/torrent-repository/src/repository/rw_lock_tokio_mutex_tokio.rs +++ b/packages/torrent-repository/src/repository/rw_lock_tokio_mutex_tokio.rs @@ -70,11 +70,11 @@ where async fn get_metrics(&self) -> TorrentsMetrics { let mut metrics = TorrentsMetrics::default(); - for entry in self.get_torrents().await.values().cloned() { + for entry in self.get_torrents().await.values() { let stats = entry.get_stats().await; - metrics.seeders += u64::from(stats.complete); - metrics.completed += u64::from(stats.downloaded); - metrics.leechers += u64::from(stats.incomplete); + metrics.complete += u64::from(stats.complete); + metrics.downloaded += u64::from(stats.downloaded); + metrics.incomplete += u64::from(stats.incomplete); metrics.torrents += 1; } @@ -93,7 +93,7 @@ where let entry = EntryMutexTokio::new( EntrySingle { peers: BTreeMap::default(), - completed: *completed, + downloaded: *completed, } .into(), ); @@ -119,6 +119,16 @@ where async fn remove_peerless_torrents(&self, policy: &TrackerPolicy) { let mut db = self.get_torrents_mut().await; - db.retain(|_, e| e.blocking_lock().is_not_zombie(policy)); + let mut not_good = Vec::::default(); + + for (&infohash, torrent) in db.iter() { + if !torrent.clone().check_good(policy).await { + not_good.push(infohash); + } + } + + for remove in not_good { + drop(db.remove(&remove)); + } } } diff --git a/src/core/databases/mod.rs b/src/core/databases/mod.rs index 20a45cf83..c08aed76a 100644 --- a/src/core/databases/mod.rs +++ b/src/core/databases/mod.rs @@ -117,9 +117,9 @@ pub trait Database: Sync + Send { /// /// It returns an array of tuples with the torrent /// [`InfoHash`] and the - /// [`completed`](torrust_tracker_torrent_repository::entry::Torrent::completed) counter + /// [`downloaded`](torrust_tracker_torrent_repository::entry::Torrent::downloaded) counter /// which is the number of times the torrent has been downloaded. - /// See [`Entry::completed`](torrust_tracker_torrent_repository::entry::Torrent::completed). + /// See [`Entry::downloaded`](torrust_tracker_torrent_repository::entry::Torrent::downloaded). /// /// # Context: Torrent Metrics /// diff --git a/src/core/databases/mysql.rs b/src/core/databases/mysql.rs index e37cdd9bf..ca95fa0b9 100644 --- a/src/core/databases/mysql.rs +++ b/src/core/databases/mysql.rs @@ -9,7 +9,7 @@ use r2d2_mysql::mysql::prelude::Queryable; use r2d2_mysql::mysql::{params, Opts, OptsBuilder}; use r2d2_mysql::MySqlConnectionManager; use torrust_tracker_primitives::info_hash::InfoHash; -use torrust_tracker_primitives::DatabaseDriver; +use torrust_tracker_primitives::{DatabaseDriver, PersistentTorrents}; use super::{Database, Error}; use crate::core::auth::{self, Key}; @@ -105,7 +105,7 @@ impl Database for Mysql { } /// Refer to [`databases::Database::load_persistent_torrents`](crate::core::databases::Database::load_persistent_torrents). - async fn load_persistent_torrents(&self) -> Result, Error> { + async fn load_persistent_torrents(&self) -> Result { let mut conn = self.pool.get().map_err(|e| (e, DRIVER))?; let torrents = conn.query_map( @@ -116,7 +116,7 @@ impl Database for Mysql { }, )?; - Ok(torrents) + Ok(torrents.iter().copied().collect()) } /// Refer to [`databases::Database::load_keys`](crate::core::databases::Database::load_keys). diff --git a/src/core/databases/sqlite.rs b/src/core/databases/sqlite.rs index 5a3ac144a..53a01f80c 100644 --- a/src/core/databases/sqlite.rs +++ b/src/core/databases/sqlite.rs @@ -6,7 +6,7 @@ use async_trait::async_trait; use r2d2::Pool; use r2d2_sqlite::SqliteConnectionManager; use torrust_tracker_primitives::info_hash::InfoHash; -use torrust_tracker_primitives::{DatabaseDriver, DurationSinceUnixEpoch}; +use torrust_tracker_primitives::{DatabaseDriver, DurationSinceUnixEpoch, PersistentTorrents}; use super::{Database, Error}; use crate::core::auth::{self, Key}; @@ -89,7 +89,7 @@ impl Database for Sqlite { } /// Refer to [`databases::Database::load_persistent_torrents`](crate::core::databases::Database::load_persistent_torrents). - async fn load_persistent_torrents(&self) -> Result, Error> { + async fn load_persistent_torrents(&self) -> Result { let conn = self.pool.get().map_err(|e| (e, DRIVER))?; let mut stmt = conn.prepare("SELECT info_hash, completed FROM torrents")?; @@ -101,12 +101,7 @@ impl Database for Sqlite { Ok((info_hash, completed)) })?; - //torrent_iter?; - //let torrent_iter = torrent_iter.unwrap(); - - let torrents: Vec<(InfoHash, u32)> = torrent_iter.filter_map(std::result::Result::ok).collect(); - - Ok(torrents) + Ok(torrent_iter.filter_map(std::result::Result::ok).collect()) } /// Refer to [`databases::Database::load_keys`](crate::core::databases::Database::load_keys). diff --git a/src/core/mod.rs b/src/core/mod.rs index 21cd1b501..6628426c1 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -684,7 +684,7 @@ impl Tracker { fn get_torrent_peers_for_peer(&self, info_hash: &InfoHash, peer: &peer::Peer) -> Vec> { match self.torrents.get(info_hash) { None => vec![], - Some(entry) => entry.get_peers_for_peer(peer, Some(TORRENT_PEERS_LIMIT)), + Some(entry) => entry.get_peers_for_client(&peer.peer_addr, Some(TORRENT_PEERS_LIMIT)), } } @@ -1115,9 +1115,9 @@ mod tests { assert_eq!( torrents_metrics, TorrentsMetrics { - seeders: 0, - completed: 0, - leechers: 0, + complete: 0, + downloaded: 0, + incomplete: 0, torrents: 0 } ); @@ -1164,9 +1164,9 @@ mod tests { assert_eq!( torrent_metrics, TorrentsMetrics { - seeders: 0, - completed: 0, - leechers: 1, + complete: 0, + downloaded: 0, + incomplete: 1, torrents: 1, } ); @@ -1191,9 +1191,9 @@ mod tests { assert_eq!( (torrent_metrics), (TorrentsMetrics { - seeders: 0, - completed: 0, - leechers: 1_000_000, + complete: 0, + downloaded: 0, + incomplete: 1_000_000, torrents: 1_000_000, }), "{result_a:?} {result_b:?}" diff --git a/src/core/torrent/mod.rs b/src/core/torrent/mod.rs index 2b3f9cbf7..ab78de683 100644 --- a/src/core/torrent/mod.rs +++ b/src/core/torrent/mod.rs @@ -29,6 +29,3 @@ use torrust_tracker_torrent_repository::TorrentsRwLockStdMutexStd; pub type Torrents = TorrentsRwLockStdMutexStd; // Currently Used - -#[cfg(test)] -mod tests {} diff --git a/src/servers/apis/v1/context/stats/resources.rs b/src/servers/apis/v1/context/stats/resources.rs index 48ac660cf..9e8ab6bab 100644 --- a/src/servers/apis/v1/context/stats/resources.rs +++ b/src/servers/apis/v1/context/stats/resources.rs @@ -50,9 +50,9 @@ impl From for Stats { fn from(metrics: TrackerMetrics) -> Self { Self { torrents: metrics.torrents_metrics.torrents, - seeders: metrics.torrents_metrics.seeders, - completed: metrics.torrents_metrics.completed, - leechers: metrics.torrents_metrics.leechers, + seeders: metrics.torrents_metrics.complete, + completed: metrics.torrents_metrics.downloaded, + leechers: metrics.torrents_metrics.incomplete, tcp4_connections_handled: metrics.protocol_metrics.tcp4_connections_handled, tcp4_announces_handled: metrics.protocol_metrics.tcp4_announces_handled, tcp4_scrapes_handled: metrics.protocol_metrics.tcp4_scrapes_handled, @@ -82,9 +82,9 @@ mod tests { assert_eq!( Stats::from(TrackerMetrics { torrents_metrics: TorrentsMetrics { - seeders: 1, - completed: 2, - leechers: 3, + complete: 1, + downloaded: 2, + incomplete: 3, torrents: 4 }, protocol_metrics: Metrics { diff --git a/src/servers/udp/handlers.rs b/src/servers/udp/handlers.rs index 59aec0ff3..2d5038ec3 100644 --- a/src/servers/udp/handlers.rs +++ b/src/servers/udp/handlers.rs @@ -320,7 +320,6 @@ mod tests { use torrust_tracker_clock::clock::Time; use torrust_tracker_configuration::Configuration; - use torrust_tracker_primitives::announce_event::AnnounceEvent; use torrust_tracker_primitives::{peer, NumberOfBytes}; use torrust_tracker_test_helpers::configuration; @@ -368,39 +367,41 @@ mod tests { SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080) } - struct TorrentPeerBuilder { + #[derive(Debug, Default)] + pub struct TorrentPeerBuilder { peer: peer::Peer, } impl TorrentPeerBuilder { - pub fn default() -> TorrentPeerBuilder { - let default_peer = peer::Peer { - peer_id: peer::Id([255u8; 20]), - peer_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(126, 0, 0, 1)), 8080), - updated: CurrentClock::now(), - uploaded: NumberOfBytes(0), - downloaded: NumberOfBytes(0), - left: NumberOfBytes(0), - event: AnnounceEvent::Started, - }; - TorrentPeerBuilder { peer: default_peer } + #[must_use] + pub fn new() -> Self { + Self { + peer: peer::Peer { + updated: CurrentClock::now(), + ..Default::default() + }, + } } - pub fn with_peer_id(mut self, peer_id: peer::Id) -> Self { - self.peer.peer_id = peer_id; + #[must_use] + pub fn with_peer_address(mut self, peer_addr: SocketAddr) -> Self { + self.peer.peer_addr = peer_addr; self } - pub fn with_peer_addr(mut self, peer_addr: SocketAddr) -> Self { - self.peer.peer_addr = peer_addr; + #[must_use] + pub fn with_peer_id(mut self, peer_id: peer::Id) -> Self { + self.peer.peer_id = peer_id; self } - pub fn with_bytes_left(mut self, left: i64) -> Self { + #[must_use] + pub fn with_number_of_bytes_left(mut self, left: i64) -> Self { self.peer.left = NumberOfBytes(left); self } + #[must_use] pub fn into(self) -> peer::Peer { self.peer } @@ -640,9 +641,9 @@ mod tests { let peers = tracker.get_torrent_peers(&info_hash.0.into()); - let expected_peer = TorrentPeerBuilder::default() + let expected_peer = TorrentPeerBuilder::new() .with_peer_id(peer::Id(peer_id.0)) - .with_peer_addr(SocketAddr::new(IpAddr::V4(client_ip), client_port)) + .with_peer_address(SocketAddr::new(IpAddr::V4(client_ip), client_port)) .into(); assert_eq!(peers[0], Arc::new(expected_peer)); @@ -712,9 +713,9 @@ mod tests { let client_port = 8080; let peer_id = AquaticPeerId([255u8; 20]); - let peer_using_ipv6 = TorrentPeerBuilder::default() + let peer_using_ipv6 = TorrentPeerBuilder::new() .with_peer_id(peer::Id(peer_id.0)) - .with_peer_addr(SocketAddr::new(IpAddr::V6(client_ip_v6), client_port)) + .with_peer_address(SocketAddr::new(IpAddr::V6(client_ip_v6), client_port)) .into(); tracker @@ -808,9 +809,9 @@ mod tests { let external_ip_in_tracker_configuration = tracker.get_maybe_external_ip().unwrap(); - let expected_peer = TorrentPeerBuilder::default() + let expected_peer = TorrentPeerBuilder::new() .with_peer_id(peer::Id(peer_id.0)) - .with_peer_addr(SocketAddr::new(external_ip_in_tracker_configuration, client_port)) + .with_peer_address(SocketAddr::new(external_ip_in_tracker_configuration, client_port)) .into(); assert_eq!(peers[0], Arc::new(expected_peer)); @@ -863,9 +864,9 @@ mod tests { let peers = tracker.get_torrent_peers(&info_hash.0.into()); - let expected_peer = TorrentPeerBuilder::default() + let expected_peer = TorrentPeerBuilder::new() .with_peer_id(peer::Id(peer_id.0)) - .with_peer_addr(SocketAddr::new(IpAddr::V6(client_ip_v6), client_port)) + .with_peer_address(SocketAddr::new(IpAddr::V6(client_ip_v6), client_port)) .into(); assert_eq!(peers[0], Arc::new(expected_peer)); @@ -938,9 +939,9 @@ mod tests { let client_port = 8080; let peer_id = AquaticPeerId([255u8; 20]); - let peer_using_ipv4 = TorrentPeerBuilder::default() + let peer_using_ipv4 = TorrentPeerBuilder::new() .with_peer_id(peer::Id(peer_id.0)) - .with_peer_addr(SocketAddr::new(IpAddr::V4(client_ip_v4), client_port)) + .with_peer_address(SocketAddr::new(IpAddr::V4(client_ip_v4), client_port)) .into(); tracker @@ -1112,10 +1113,10 @@ mod tests { async fn add_a_seeder(tracker: Arc, remote_addr: &SocketAddr, info_hash: &InfoHash) { let peer_id = peer::Id([255u8; 20]); - let peer = TorrentPeerBuilder::default() + let peer = TorrentPeerBuilder::new() .with_peer_id(peer::Id(peer_id.0)) - .with_peer_addr(*remote_addr) - .with_bytes_left(0) + .with_peer_address(*remote_addr) + .with_number_of_bytes_left(0) .into(); tracker