fix(lib): advertise real slot, speed and queue figures in search replies
Every reply to a peer's search claimed a free upload slot, no speed and an empty queue, whatever the sharer was doing, and searchers rank sources on exactly those fields. A reply now says whether a slot is free, the bytes per second of the last completed upload, and how many uploads are waiting.
This commit is contained in:
@@ -150,6 +150,9 @@ fn build_search_response(
|
||||
own_username: &str,
|
||||
token: u32,
|
||||
query: &str,
|
||||
free_slot: bool,
|
||||
speed: u32,
|
||||
queue_length: u32,
|
||||
) -> Option<crate::message::Message> {
|
||||
let matches = shares.search(query);
|
||||
if matches.is_empty() {
|
||||
@@ -168,8 +171,9 @@ fn build_search_response(
|
||||
own_username,
|
||||
token,
|
||||
&entries,
|
||||
1,
|
||||
0,
|
||||
u8::from(free_slot),
|
||||
speed,
|
||||
queue_length,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -376,6 +380,9 @@ pub struct ClientContext {
|
||||
upload_seq: u64,
|
||||
/// How many uploads may be in flight at once.
|
||||
upload_slots: usize,
|
||||
/// Bytes per second of the last completed upload, advertised in search
|
||||
/// replies; zero until one has finished.
|
||||
last_upload_speed: u32,
|
||||
/// Queued-upload states that came and went between two polls of
|
||||
/// [`Client::uploads`]. A caller sampling that snapshot would otherwise
|
||||
/// never see a peer that queued and was served inside one poll interval,
|
||||
@@ -458,6 +465,7 @@ impl ClientContext {
|
||||
upload_queue: Vec::new(),
|
||||
upload_seq: 0,
|
||||
upload_slots: DEFAULT_UPLOAD_SLOTS,
|
||||
last_upload_speed: 0,
|
||||
upload_events: Vec::new(),
|
||||
downloads: DownloadStore::new(),
|
||||
actor_system,
|
||||
|
||||
@@ -593,6 +593,9 @@ impl Client {
|
||||
&own_username,
|
||||
token,
|
||||
&query,
|
||||
ctx.has_free_upload_slot(),
|
||||
ctx.last_upload_speed,
|
||||
ctx.upload_queue.len() as u32,
|
||||
),
|
||||
Err(e) => {
|
||||
error!("[client] IncomingSearch read: {}", e);
|
||||
|
||||
@@ -160,8 +160,9 @@ fn build_search_response_matches_shares_and_echoes_token() {
|
||||
std::fs::write(dir.join("probe_xyzzy.bin"), b"data").unwrap();
|
||||
let shares = Shares::scan(&dir).unwrap();
|
||||
|
||||
let response = build_search_response(&shares, "me", 99, "xyzzy")
|
||||
.expect("a matching share yields a response");
|
||||
let response =
|
||||
build_search_response(&shares, "me", 99, "xyzzy", true, 0, 0)
|
||||
.expect("a matching share yields a response");
|
||||
let mut decoded =
|
||||
crate::message::Message::new_with_data(response.get_buffer());
|
||||
decoded.set_pointer(8);
|
||||
@@ -170,7 +171,10 @@ fn build_search_response_matches_shares_and_echoes_token() {
|
||||
assert_eq!(result.token, 99);
|
||||
assert!(result.files.iter().any(|f| f.name.contains("probe_xyzzy")));
|
||||
|
||||
assert!(build_search_response(&shares, "me", 1, "nomatch").is_none());
|
||||
assert!(
|
||||
build_search_response(&shares, "me", 1, "nomatch", true, 0, 0)
|
||||
.is_none()
|
||||
);
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ impl ClientContext {
|
||||
mut next_token: impl FnMut() -> u32,
|
||||
) -> (Option<PeerRegistry>, Vec<UploadOffer>) {
|
||||
let mut offers = Vec::new();
|
||||
while self.uploads_in_flight() < self.upload_slots {
|
||||
while self.has_free_upload_slot() {
|
||||
let Some(index) = next_to_serve(&self.upload_queue) else {
|
||||
break;
|
||||
};
|
||||
@@ -190,6 +190,10 @@ impl ClientContext {
|
||||
.count()
|
||||
}
|
||||
|
||||
pub(super) fn has_free_upload_slot(&self) -> bool {
|
||||
self.uploads_in_flight() < self.upload_slots
|
||||
}
|
||||
|
||||
/// The peers still waiting, in the order they will be served, each carrying
|
||||
/// its own 1-based place.
|
||||
pub(super) fn queued_uploads(&self) -> Vec<UploadInfo> {
|
||||
|
||||
@@ -145,8 +145,9 @@ impl Client {
|
||||
&bytes_sent,
|
||||
&cancel,
|
||||
);
|
||||
let streamed = result.as_ref().ok().copied();
|
||||
let status = match &result {
|
||||
Ok(()) => UploadStatus::Completed,
|
||||
Ok(_) => UploadStatus::Completed,
|
||||
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {
|
||||
UploadStatus::Cancelled
|
||||
}
|
||||
@@ -158,7 +159,13 @@ impl Client {
|
||||
if let Ok(mut ctx) = context.write_safe()
|
||||
&& let Some(upload) = ctx.active_uploads.get_mut(&token)
|
||||
{
|
||||
let secs = upload.started.elapsed().as_secs_f64();
|
||||
upload.status = status;
|
||||
if let Some(bytes) = streamed
|
||||
&& secs > 0.0
|
||||
{
|
||||
ctx.last_upload_speed = (bytes as f64 / secs) as u32;
|
||||
}
|
||||
}
|
||||
// The slot this transfer held is free now, so whoever is next in
|
||||
// line gets it without waiting for another request to arrive.
|
||||
|
||||
Reference in New Issue
Block a user