Skip to content

Commit b14cf3a

Browse files
committed
Trying to improve responsiveness by batch sending data
1 parent 9f15c59 commit b14cf3a

File tree

6 files changed

+91
-16
lines changed

6 files changed

+91
-16
lines changed

src/udp/impls.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ pub mod request;
33
pub mod response;
44
pub mod ipv4_addr;
55
pub mod ipv6_addr;
6-
pub mod udp_server;
6+
pub mod udp_server;
7+
pub mod response_batch_manager;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use std::net::SocketAddr;
2+
use std::sync::{Arc, OnceLock};
3+
use std::time::Duration;
4+
use std::collections::{HashMap, VecDeque};
5+
use tokio::net::UdpSocket;
6+
use tokio::sync::{mpsc, RwLock};
7+
use tokio::time::interval;
8+
use crate::udp::structs::queued_response::QueuedResponse;
9+
use crate::udp::structs::response_batch_manager::ResponseBatchManager;
10+
11+
static BATCH_MANAGERS: OnceLock<RwLock<HashMap<String, Arc<ResponseBatchManager>>>> = OnceLock::new();
12+
13+
impl ResponseBatchManager {
14+
fn new(socket: Arc<UdpSocket>) -> Self {
15+
let (sender, mut receiver) = mpsc::unbounded_channel::<QueuedResponse>();
16+
17+
tokio::spawn(async move {
18+
let mut buffer = VecDeque::with_capacity(1000);
19+
let mut timer = interval(Duration::from_millis(5));
20+
21+
loop {
22+
tokio::select! {
23+
Some(response) = receiver.recv() => {
24+
buffer.push_back(response);
25+
26+
if buffer.len() >= 500 {
27+
Self::flush_buffer(&socket, &mut buffer).await;
28+
}
29+
}
30+
31+
_ = timer.tick() => {
32+
if !buffer.is_empty() {
33+
Self::flush_buffer(&socket, &mut buffer).await;
34+
}
35+
}
36+
}
37+
}
38+
});
39+
40+
Self { sender }
41+
}
42+
43+
pub(crate) fn queue_response(&self, remote_addr: SocketAddr, payload: Vec<u8>) {
44+
let _ = self.sender.send(QueuedResponse { remote_addr, payload });
45+
}
46+
47+
async fn flush_buffer(socket: &UdpSocket, buffer: &mut VecDeque<QueuedResponse>) {
48+
while let Some(response) = buffer.pop_front() {
49+
let _ = socket.send_to(&response.payload, &response.remote_addr).await;
50+
}
51+
}
52+
53+
pub(crate) async fn get_for_socket(socket: Arc<UdpSocket>) -> Arc<ResponseBatchManager> {
54+
let socket_key = format!("{:?}", socket.local_addr().unwrap_or_else(|_| "0.0.0.0:0".parse().unwrap()));
55+
56+
let registry = BATCH_MANAGERS.get_or_init(|| RwLock::new(HashMap::new()));
57+
58+
if let Some(manager) = registry.read().await.get(&socket_key) {
59+
return manager.clone();
60+
}
61+
62+
let manager = Arc::new(ResponseBatchManager::new(socket));
63+
registry.write().await.insert(socket_key, manager.clone());
64+
65+
manager
66+
}
67+
}

src/udp/impls/udp_server.rs

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use crate::udp::structs::error_response::ErrorResponse;
2525
use crate::udp::structs::number_of_downloads::NumberOfDownloads;
2626
use crate::udp::structs::number_of_peers::NumberOfPeers;
2727
use crate::udp::structs::port::Port;
28+
use crate::udp::structs::response_batch_manager::ResponseBatchManager;
2829
use crate::udp::structs::response_peer::ResponsePeer;
2930
use crate::udp::structs::scrape_request::ScrapeRequest;
3031
use crate::udp::structs::scrape_response::ScrapeResponse;
@@ -46,7 +47,6 @@ impl UdpServer {
4647
socket.bind(&bind_address.into()).map_err(tokio::io::Error::other)?;
4748
socket.set_nonblocking(true).map_err(tokio::io::Error::other)?;
4849

49-
// Convert to std::net::UdpSocket, then to tokio::net::UdpSocket
5050
let std_socket: std::net::UdpSocket = socket.into();
5151
let tokio_socket = UdpSocket::from_std(std_socket)?;
5252

@@ -82,7 +82,6 @@ impl UdpServer {
8282

8383
let tracker_cloned = tracker.clone();
8484
let socket_cloned = socket_clone.clone();
85-
// Use payload slice instead of cloning the entire Vec
8685
let payload_vec = payload.to_vec();
8786
tokio::spawn(async move {
8887
let response = UdpServer::handle_packet(remote_addr, payload_vec, tracker_cloned.clone()).await;
@@ -102,15 +101,16 @@ impl UdpServer {
102101
let sentry = sentry::TransactionContext::new("udp server", "send response");
103102
let transaction = sentry::start_transaction(sentry);
104103

105-
// Pre-allocate buffer with exact capacity instead of MAX_PACKET_SIZE
106-
let mut buffer = Vec::with_capacity(512); // Most responses are much smaller than MAX_PACKET_SIZE
104+
let mut buffer = Vec::with_capacity(512);
107105
let mut cursor = Cursor::new(&mut buffer);
108106

109107
match response.write(&mut cursor) {
110108
Ok(_) => {
111109
let position = cursor.position() as usize;
112110
debug!("Response bytes: {:?}", &buffer[..position]);
113-
UdpServer::send_packet(socket, &remote_addr, &buffer[..position]).await;
111+
112+
let batch_manager = ResponseBatchManager::get_for_socket(socket).await;
113+
batch_manager.queue_response(remote_addr, buffer[..position].to_vec());
114114
}
115115
Err(error) => {
116116
sentry::capture_error(&error);
@@ -214,7 +214,6 @@ impl UdpServer {
214214
pub async fn handle_udp_announce(remote_addr: SocketAddr, request: &AnnounceRequest, tracker: Arc<TorrentTracker>) -> Result<Response, ServerError> {
215215
let config = tracker.config.tracker_config.clone();
216216

217-
// Whitelist/Blacklist checks
218217
if config.whitelist_enabled && !tracker.check_whitelist(InfoHash(request.info_hash.0)) {
219218
debug!("[UDP ERROR] Torrent Not Whitelisted");
220219
return Err(ServerError::TorrentNotWhitelisted);
@@ -224,7 +223,6 @@ impl UdpServer {
224223
return Err(ServerError::TorrentBlacklisted);
225224
}
226225

227-
// Key validation
228226
if config.keys_enabled {
229227
if request.path.len() < 50 {
230228
debug!("[UDP ERROR] Unknown Key");
@@ -248,7 +246,6 @@ impl UdpServer {
248246
}
249247
}
250248

251-
// User key validation
252249
let user_key = if config.users_enabled {
253250
let user_key_path_extract = if request.path.len() >= 91 {
254251
Some(&request.path[51..=91])
@@ -281,7 +278,6 @@ impl UdpServer {
281278
return Err(ServerError::PeerKeyNotValid);
282279
}
283280

284-
// Handle announce
285281
let torrent = match tracker.handle_announce(tracker.clone(), AnnounceQueryRequest {
286282
info_hash: InfoHash(request.info_hash.0),
287283
peer_id: PeerId(request.peer_id.0),
@@ -302,15 +298,13 @@ impl UdpServer {
302298
}
303299
};
304300

305-
// Get peers efficiently
306301
let torrent_peers = tracker.get_torrent_peers(request.info_hash, 72, TorrentPeersType::All, Some(remote_addr.ip()));
307302

308303
let (peers, peers6) = if let Some(torrent_peers_unwrapped) = torrent_peers {
309304
let mut peers = Vec::with_capacity(72);
310305
let mut peers6 = Vec::with_capacity(72);
311306
let mut count = 0;
312307

313-
// Only collect peers if not completed download
314308
if request.bytes_left.0 != 0 {
315309
if remote_addr.is_ipv4() {
316310
for torrent_peer in torrent_peers_unwrapped.seeds_ipv4.values().take(72) {
@@ -331,7 +325,6 @@ impl UdpServer {
331325
}
332326
}
333327

334-
// Collect regular peers
335328
if remote_addr.is_ipv4() {
336329
for torrent_peer in torrent_peers_unwrapped.peers_ipv4.values().take(72 - count) {
337330
if let Ok(ip) = torrent_peer.peer_addr.ip().to_string().parse::<Ipv4Addr>() {
@@ -351,7 +344,6 @@ impl UdpServer {
351344
(Vec::new(), Vec::new())
352345
};
353346

354-
// Create response
355347
let response = if remote_addr.is_ipv6() {
356348
Response::from(AnnounceResponse {
357349
transaction_id: request.transaction_id,
@@ -370,7 +362,6 @@ impl UdpServer {
370362
})
371363
};
372364

373-
// Update stats
374365
let stats_event = if remote_addr.is_ipv4() {
375366
StatsEvent::Udp4AnnouncesHandled
376367
} else {

src/udp/structs.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ pub mod number_of_downloads;
1414
pub mod port;
1515
pub mod peer_key;
1616
pub mod response_peer;
17-
pub mod udp_server;
17+
pub mod udp_server;
18+
pub mod queued_response;
19+
pub mod response_batch_manager;

src/udp/structs/queued_response.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use std::net::SocketAddr;
2+
3+
#[derive(Debug, Clone)]
4+
pub struct QueuedResponse {
5+
pub(crate) remote_addr: SocketAddr,
6+
pub(crate) payload: Vec<u8>,
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use crate::udp::structs::queued_response::QueuedResponse;
2+
use mpsc::UnboundedSender;
3+
use tokio::sync::mpsc;
4+
5+
pub struct ResponseBatchManager {
6+
pub(crate) sender: UnboundedSender<QueuedResponse>,
7+
}

0 commit comments

Comments
 (0)