Skip to content

Commit 4a17fa1

Browse files
committed
refactor: performance optimization in tracker announce
The tracker announce method uses two locks: 1. To write the new peer in the torrent entry peer list (write lock). 2. To read the peers list from the torrent entry (read lock). I was wondering if the repo would be faster removing the second lock since we can get the peer list in the first lock. This are the benchmarking results: BEFORE: tokio::sync::RwLock<std::collections::BTreeMap<InfoHash, Entry>> add_one_torrent: Avg/AdjAvg: (57ns, 59ns) update_one_torrent_in_parallel: Avg/AdjAvg: (15.769541ms, 16.69948ms) add_multiple_torrents_in_parallel: Avg/AdjAvg: (20.6541ms, 20.010245ms) update_multiple_torrents_in_parallel: Avg/AdjAvg: (18.326633ms, 18.326633ms) std::sync::RwLock<std::collections::BTreeMap<InfoHash, Entry>> add_one_torrent: Avg/AdjAvg: (38ns, 39ns) update_one_torrent_in_parallel: Avg/AdjAvg: (7.154301ms, 7.296907ms) add_multiple_torrents_in_parallel: Avg/AdjAvg: (8.370377ms, 8.370377ms) update_multiple_torrents_in_parallel: Avg/AdjAvg: (6.902327ms, 6.902327ms) std::sync::RwLock<std::collections::BTreeMap<InfoHash, Arc<std::sync::Mutex<Entry>>>> add_one_torrent: Avg/AdjAvg: (44ns, 40ns) update_one_torrent_in_parallel: Avg/AdjAvg: (6.023789ms, 6.023789ms) add_multiple_torrents_in_parallel: Avg/AdjAvg: (12.306719ms, 12.306719ms) update_multiple_torrents_in_parallel: Avg/AdjAvg: (8.429203ms, 8.429203ms) tokio::sync::RwLock<std::collections::BTreeMap<InfoHash, Arc<std::sync::Mutex<Entry>>>> add_one_torrent: Avg/AdjAvg: (71ns, 69ns) update_one_torrent_in_parallel: Avg/AdjAvg: (7.022376ms, 7.022376ms) add_multiple_torrents_in_parallel: Avg/AdjAvg: (28.125712ms, 27.811162ms) update_multiple_torrents_in_parallel: Avg/AdjAvg: (8.908545ms, 9.036288ms) tokio::sync::RwLock<std::collections::BTreeMap<InfoHash, Arc<tokio::sync::Mutex<Entry>>>> add_one_torrent: Avg/AdjAvg: (85ns, 81ns) update_one_torrent_in_parallel: Avg/AdjAvg: (15.285006ms, 15.555171ms) add_multiple_torrents_in_parallel: Avg/AdjAvg: (29.794849ms, 30.254531ms) update_multiple_torrents_in_parallel: Avg/AdjAvg: (9.302051ms, 9.302051ms) AFTER: tokio::sync::RwLock<std::collections::BTreeMap<InfoHash, Entry>> add_one_torrent: Avg/AdjAvg: (64ns, 64ns) update_one_torrent_in_parallel: Avg/AdjAvg: (15.652934ms, 15.54617ms) add_multiple_torrents_in_parallel: Avg/AdjAvg: (23.834278ms, 22.935421ms) update_multiple_torrents_in_parallel: Avg/AdjAvg: (17.973855ms, 18.28944ms) std::sync::RwLock<std::collections::BTreeMap<InfoHash, Entry>> add_one_torrent: Avg/AdjAvg: (43ns, 39ns) update_one_torrent_in_parallel: Avg/AdjAvg: (6.362147ms, 6.362147ms) add_multiple_torrents_in_parallel: Avg/AdjAvg: (8.869102ms, 8.869102ms) update_multiple_torrents_in_parallel: Avg/AdjAvg: (6.589264ms, 6.510441ms) std::sync::RwLock<std::collections::BTreeMap<InfoHash, Arc<std::sync::Mutex<Entry>>>> add_one_torrent: Avg/AdjAvg: (48ns, 49ns) update_one_torrent_in_parallel: Avg/AdjAvg: (6.194211ms, 6.194211ms) add_multiple_torrents_in_parallel: Avg/AdjAvg: (12.50352ms, 12.50352ms) update_multiple_torrents_in_parallel: Avg/AdjAvg: (8.411279ms, 8.411279ms) tokio::sync::RwLock<std::collections::BTreeMap<InfoHash, Arc<std::sync::Mutex<Entry>>>> add_one_torrent: Avg/AdjAvg: (79ns, 79ns) update_one_torrent_in_parallel: Avg/AdjAvg: (6.966462ms, 6.966462ms) add_multiple_torrents_in_parallel: Avg/AdjAvg: (27.584361ms, 27.411202ms) update_multiple_torrents_in_parallel: Avg/AdjAvg: (6.965519ms, 7.474346ms) tokio::sync::RwLock<std::collections::BTreeMap<InfoHash, Arc<tokio::sync::Mutex<Entry>>>> add_one_torrent: Avg/AdjAvg: (97ns, 97ns) update_one_torrent_in_parallel: Avg/AdjAvg: (16.046499ms, 16.430313ms) add_multiple_torrents_in_parallel: Avg/AdjAvg: (28.42327ms, 27.876115ms) update_multiple_torrents_in_parallel: Avg/AdjAvg: (6.576256ms, 6.326548ms) Only in a couple of cases is a litlte bit faster. But I think the code is simpler.
1 parent 3bd2a9c commit 4a17fa1

File tree

8 files changed

+91
-115
lines changed

8 files changed

+91
-115
lines changed

packages/torrent-repository-benchmarks/src/benches/asyn.rs

+6-18
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ pub async fn async_add_one_torrent<T: TRepositoryAsync + Send + Sync + 'static>(
1919

2020
let start_time = std::time::Instant::now();
2121

22-
torrent_repository
23-
.update_torrent_with_peer_and_get_stats(&info_hash, &DEFAULT_PEER)
24-
.await;
22+
torrent_repository.announce(&info_hash, &DEFAULT_PEER).await;
2523

2624
let result = start_time.elapsed();
2725

@@ -45,19 +43,15 @@ pub async fn async_update_one_torrent_in_parallel<T: TRepositoryAsync + Send + S
4543
let handles = FuturesUnordered::new();
4644

4745
// Add the torrent/peer to the torrent repository
48-
torrent_repository
49-
.update_torrent_with_peer_and_get_stats(info_hash, &DEFAULT_PEER)
50-
.await;
46+
torrent_repository.announce(info_hash, &DEFAULT_PEER).await;
5147

5248
let start_time = std::time::Instant::now();
5349

5450
for _ in 0..10_000 {
5551
let torrent_repository_clone = torrent_repository.clone();
5652

5753
let handle = runtime.spawn(async move {
58-
torrent_repository_clone
59-
.update_torrent_with_peer_and_get_stats(info_hash, &DEFAULT_PEER)
60-
.await;
54+
torrent_repository_clone.announce(info_hash, &DEFAULT_PEER).await;
6155

6256
if let Some(sleep_time) = args.sleep {
6357
let start_time = std::time::Instant::now();
@@ -99,9 +93,7 @@ pub async fn async_add_multiple_torrents_in_parallel<T: TRepositoryAsync + Send
9993
let torrent_repository_clone = torrent_repository.clone();
10094

10195
let handle = runtime.spawn(async move {
102-
torrent_repository_clone
103-
.update_torrent_with_peer_and_get_stats(&info_hash, &DEFAULT_PEER)
104-
.await;
96+
torrent_repository_clone.announce(&info_hash, &DEFAULT_PEER).await;
10597

10698
if let Some(sleep_time) = args.sleep {
10799
let start_time = std::time::Instant::now();
@@ -139,9 +131,7 @@ pub async fn async_update_multiple_torrents_in_parallel<T: TRepositoryAsync + Se
139131

140132
// Add the torrents/peers to the torrent repository
141133
for info_hash in &info_hashes {
142-
torrent_repository
143-
.update_torrent_with_peer_and_get_stats(info_hash, &DEFAULT_PEER)
144-
.await;
134+
torrent_repository.announce(info_hash, &DEFAULT_PEER).await;
145135
}
146136

147137
let start_time = std::time::Instant::now();
@@ -150,9 +140,7 @@ pub async fn async_update_multiple_torrents_in_parallel<T: TRepositoryAsync + Se
150140
let torrent_repository_clone = torrent_repository.clone();
151141

152142
let handle = runtime.spawn(async move {
153-
torrent_repository_clone
154-
.update_torrent_with_peer_and_get_stats(&info_hash, &DEFAULT_PEER)
155-
.await;
143+
torrent_repository_clone.announce(&info_hash, &DEFAULT_PEER).await;
156144

157145
if let Some(sleep_time) = args.sleep {
158146
let start_time = std::time::Instant::now();

src/core/mod.rs

+9-32
Original file line numberDiff line numberDiff line change
@@ -651,10 +651,7 @@ impl Tracker {
651651
peer.change_ip(&assign_ip_address_to_peer(remote_client_ip, self.external_ip));
652652
debug!("After: {peer:?}");
653653

654-
// we should update the torrent and get the stats before we get the peer list.
655-
let stats = self.update_torrent_with_peer_and_get_stats(info_hash, peer).await;
656-
657-
let peers = self.get_torrent_peers_for_peer(info_hash, peer).await;
654+
let (stats, peers) = self.inner_announce(info_hash, peer).await;
658655

659656
AnnounceData {
660657
peers,
@@ -722,19 +719,6 @@ impl Tracker {
722719
Ok(())
723720
}
724721

725-
async fn get_torrent_peers_for_peer(&self, info_hash: &InfoHash, peer: &Peer) -> Vec<peer::Peer> {
726-
let read_lock = self.torrents.get_torrents().await;
727-
728-
match read_lock.get(info_hash) {
729-
None => vec![],
730-
Some(entry) => entry
731-
.get_peers_for_peer(peer, TORRENT_PEERS_LIMIT)
732-
.into_iter()
733-
.copied()
734-
.collect(),
735-
}
736-
}
737-
738722
/// # Context: Tracker
739723
///
740724
/// Get all torrent peers for a given torrent
@@ -752,11 +736,8 @@ impl Tracker {
752736
/// needed for a `announce` request response.
753737
///
754738
/// # Context: Tracker
755-
pub async fn update_torrent_with_peer_and_get_stats(&self, info_hash: &InfoHash, peer: &peer::Peer) -> torrent::SwarmStats {
756-
// code-review: consider splitting the function in two (command and query segregation).
757-
// `update_torrent_with_peer` and `get_stats`
758-
759-
let (stats, stats_updated) = self.torrents.update_torrent_with_peer_and_get_stats(info_hash, peer).await;
739+
pub async fn inner_announce(&self, info_hash: &InfoHash, peer: &peer::Peer) -> (torrent::SwarmStats, Vec<Peer>) {
740+
let (stats, stats_updated, peers) = self.torrents.announce(info_hash, peer).await;
760741

761742
if self.policy.persistent_torrent_completed_stat && stats_updated {
762743
let completed = stats.downloaded;
@@ -765,7 +746,7 @@ impl Tracker {
765746
drop(self.database.save_persistent_torrent(&info_hash, completed).await);
766747
}
767748

768-
stats
749+
(stats, peers)
769750
}
770751

771752
/// It calculates and returns the general `Tracker`
@@ -1228,7 +1209,7 @@ mod tests {
12281209
let info_hash = sample_info_hash();
12291210
let peer = sample_peer();
12301211

1231-
tracker.update_torrent_with_peer_and_get_stats(&info_hash, &peer).await;
1212+
tracker.inner_announce(&info_hash, &peer).await;
12321213

12331214
let peers = tracker.get_torrent_peers(&info_hash).await;
12341215

@@ -1242,9 +1223,7 @@ mod tests {
12421223
let info_hash = sample_info_hash();
12431224
let peer = sample_peer();
12441225

1245-
tracker.update_torrent_with_peer_and_get_stats(&info_hash, &peer).await;
1246-
1247-
let peers = tracker.get_torrent_peers_for_peer(&info_hash, &peer).await;
1226+
let (_stats, peers) = tracker.inner_announce(&info_hash, &peer).await;
12481227

12491228
assert_eq!(peers, vec![]);
12501229
}
@@ -1253,9 +1232,7 @@ mod tests {
12531232
async fn it_should_return_the_torrent_metrics() {
12541233
let tracker = public_tracker();
12551234

1256-
tracker
1257-
.update_torrent_with_peer_and_get_stats(&sample_info_hash(), &leecher())
1258-
.await;
1235+
tracker.inner_announce(&sample_info_hash(), &leecher()).await;
12591236

12601237
let torrent_metrics = tracker.get_torrents_metrics().await;
12611238

@@ -1764,11 +1741,11 @@ mod tests {
17641741
let mut peer = sample_peer();
17651742

17661743
peer.event = AnnounceEvent::Started;
1767-
let swarm_stats = tracker.update_torrent_with_peer_and_get_stats(&info_hash, &peer).await;
1744+
let (swarm_stats, _peers) = tracker.inner_announce(&info_hash, &peer).await;
17681745
assert_eq!(swarm_stats.downloaded, 0);
17691746

17701747
peer.event = AnnounceEvent::Completed;
1771-
let swarm_stats = tracker.update_torrent_with_peer_and_get_stats(&info_hash, &peer).await;
1748+
let (swarm_stats, _peers) = tracker.inner_announce(&info_hash, &peer).await;
17721749
assert_eq!(swarm_stats.downloaded, 1);
17731750

17741751
// Remove the newly updated torrent from memory

src/core/services/torrent.rs

+8-24
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,7 @@ mod tests {
213213

214214
let hash = "9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d".to_owned();
215215
let info_hash = InfoHash::from_str(&hash).unwrap();
216-
tracker
217-
.update_torrent_with_peer_and_get_stats(&info_hash, &sample_peer())
218-
.await;
216+
tracker.inner_announce(&info_hash, &sample_peer()).await;
219217

220218
let torrent_info = get_torrent_info(tracker.clone(), &info_hash).await.unwrap();
221219

@@ -265,9 +263,7 @@ mod tests {
265263
let hash = "9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d".to_owned();
266264
let info_hash = InfoHash::from_str(&hash).unwrap();
267265

268-
tracker
269-
.update_torrent_with_peer_and_get_stats(&info_hash, &sample_peer())
270-
.await;
266+
tracker.inner_announce(&info_hash, &sample_peer()).await;
271267

272268
let torrents = get_torrents_page(tracker.clone(), &Pagination::default()).await;
273269

@@ -291,12 +287,8 @@ mod tests {
291287
let hash2 = "03840548643af2a7b63a9f5cbca348bc7150ca3a".to_owned();
292288
let info_hash2 = InfoHash::from_str(&hash2).unwrap();
293289

294-
tracker
295-
.update_torrent_with_peer_and_get_stats(&info_hash1, &sample_peer())
296-
.await;
297-
tracker
298-
.update_torrent_with_peer_and_get_stats(&info_hash2, &sample_peer())
299-
.await;
290+
tracker.inner_announce(&info_hash1, &sample_peer()).await;
291+
tracker.inner_announce(&info_hash2, &sample_peer()).await;
300292

301293
let offset = 0;
302294
let limit = 1;
@@ -315,12 +307,8 @@ mod tests {
315307
let hash2 = "03840548643af2a7b63a9f5cbca348bc7150ca3a".to_owned();
316308
let info_hash2 = InfoHash::from_str(&hash2).unwrap();
317309

318-
tracker
319-
.update_torrent_with_peer_and_get_stats(&info_hash1, &sample_peer())
320-
.await;
321-
tracker
322-
.update_torrent_with_peer_and_get_stats(&info_hash2, &sample_peer())
323-
.await;
310+
tracker.inner_announce(&info_hash1, &sample_peer()).await;
311+
tracker.inner_announce(&info_hash2, &sample_peer()).await;
324312

325313
let offset = 1;
326314
let limit = 4000;
@@ -345,15 +333,11 @@ mod tests {
345333

346334
let hash1 = "9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d".to_owned();
347335
let info_hash1 = InfoHash::from_str(&hash1).unwrap();
348-
tracker
349-
.update_torrent_with_peer_and_get_stats(&info_hash1, &sample_peer())
350-
.await;
336+
tracker.inner_announce(&info_hash1, &sample_peer()).await;
351337

352338
let hash2 = "03840548643af2a7b63a9f5cbca348bc7150ca3a".to_owned();
353339
let info_hash2 = InfoHash::from_str(&hash2).unwrap();
354-
tracker
355-
.update_torrent_with_peer_and_get_stats(&info_hash2, &sample_peer())
356-
.await;
340+
tracker.inner_announce(&info_hash2, &sample_peer()).await;
357341

358342
let torrents = get_torrents_page(tracker.clone(), &Pagination::default()).await;
359343

0 commit comments

Comments
 (0)