Skip to content

Timeout `get-file` faster when we already know we won't have enough blocks to make the file

Description

Currently the get-file function times out after 10s which is long, the goal is to reduce this.

Problem

The reason for which we can't exit faster has to do with mpsc channel Receiver not being dropped properly when there is one Sender left. If we take a look at the function which downloads the first k blocks (k minimum number of blocks required to make a file) we have a tokio::select in a loop. A simplified version looks like this:

let (block_sender, mut block_receiver) = mpsc::unbounded_channel();

'download_first_k_blocks: loop {
    tokio::select! {
            biased;
            Some(response) = info_receiver.recv() => {
                // response contains information about all the blocks a peer has for the file we are trying to get
                // clone the `block_sender` as many times as there are blocks in the response
            },
            Some(response) = block_receiver.recv() => {
                // check that the block that was received is correct, if we have k blocks, break out of the loop
            }
    }
}

We create the Sender out of the loop and clone it each time we need to inside of the loop. The original Sender (the one being cloned over and over) is never dropped. We don't know before starting how many times we will need to clone it, so we can't just drop it like that, what if we need to clone it again after ?

Thus the block_receiver.recv() is always awaiting because there is always at least one Sender in scope. This is not a problem if we receive k blocks and break out of the loop, but if we don't receive k blocks then we are stuck waiting for more answers, which might never come.

Proposed solution

Even though we don't know how many times we will need to clone the Sender when we start, at one point we can know when to stop cloning it as we loop. In the case in which all peers answer to the request for information about the blocks they provide, it is possible to then drop the original Sender.

We know how many people we sent a request to (this is done earlier in get-file), it's the number of peer in the result of the get-providers command. Let's say 4 people provide the file. Each time we receive information, we add 1 to a counter. When we get to 4, everyone sent us the information about the blocks they have. At that point, it's possible to drop the original Sender as we know we won't need to clone it anymore.

A problem which might arose has to do with scope, will Rust allow dropping a Sender from an outer scope ? If not, maybe a move will solve the issue.

Edited by DISSOUBRAY Nathan