feat: download_task with spawn (#123)
Signed-off-by: Gaius <gaius.qi@gmail.com>
This commit is contained in:
parent
6eefe73218
commit
d13c0af919
|
|
@ -292,7 +292,7 @@ impl Dfdaemon for DfdaemonServerHandler {
|
||||||
// Get the piece content from the local storage.
|
// Get the piece content from the local storage.
|
||||||
let mut reader = match task
|
let mut reader = match task
|
||||||
.piece
|
.piece
|
||||||
.download_from_local_peer(&task_id, None, interested_piece_number)
|
.download_from_local_peer(&task_id, interested_piece_number)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(reader) => reader,
|
Ok(reader) => reader,
|
||||||
|
|
@ -347,7 +347,7 @@ impl Dfdaemon for DfdaemonServerHandler {
|
||||||
type DownloadTaskStream = ReceiverStream<Result<DownloadTaskResponse, Status>>;
|
type DownloadTaskStream = ReceiverStream<Result<DownloadTaskResponse, Status>>;
|
||||||
|
|
||||||
// download_task tells the dfdaemon to download the task.
|
// download_task tells the dfdaemon to download the task.
|
||||||
#[instrument(skip_all, fields(task_id, peer_id), ret)]
|
#[instrument(skip_all, fields(task_id, peer_id))]
|
||||||
async fn download_task(
|
async fn download_task(
|
||||||
&self,
|
&self,
|
||||||
request: Request<DownloadTaskRequest>,
|
request: Request<DownloadTaskRequest>,
|
||||||
|
|
@ -431,58 +431,17 @@ impl Dfdaemon for DfdaemonServerHandler {
|
||||||
// Initialize stream channel.
|
// Initialize stream channel.
|
||||||
let (out_stream_tx, out_stream_rx) = mpsc::channel(128);
|
let (out_stream_tx, out_stream_rx) = mpsc::channel(128);
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
match task
|
task.download_into_file(
|
||||||
.download_into_file(
|
|
||||||
task_id.as_str(),
|
task_id.as_str(),
|
||||||
host_id.as_str(),
|
host_id.as_str(),
|
||||||
peer_id.as_str(),
|
peer_id.as_str(),
|
||||||
content_length,
|
content_length,
|
||||||
header.clone(),
|
header.clone(),
|
||||||
download.clone(),
|
download.clone(),
|
||||||
|
out_stream_tx.clone(),
|
||||||
)
|
)
|
||||||
.await
|
.await;
|
||||||
{
|
drop(out_stream_tx);
|
||||||
Ok(mut download_progress_rx) => {
|
|
||||||
while let Some(finished_piece) = download_progress_rx.recv().await {
|
|
||||||
out_stream_tx
|
|
||||||
.send(Ok(DownloadTaskResponse {
|
|
||||||
content_length,
|
|
||||||
piece: Some(Piece {
|
|
||||||
number: finished_piece.number,
|
|
||||||
parent_id: None,
|
|
||||||
offset: finished_piece.offset,
|
|
||||||
length: finished_piece.length,
|
|
||||||
digest: finished_piece.clone().digest,
|
|
||||||
content: None,
|
|
||||||
traffic_type: None,
|
|
||||||
cost: finished_piece.prost_cost(),
|
|
||||||
created_at: Some(prost_wkt_types::Timestamp::from(
|
|
||||||
finished_piece.created_at,
|
|
||||||
)),
|
|
||||||
}),
|
|
||||||
}))
|
|
||||||
.await
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
error!("send to out stream: {}", e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("download task: {}", e);
|
|
||||||
out_stream_tx
|
|
||||||
.send(Err(Status::internal(e.to_string())))
|
|
||||||
.await
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
error!("send to out stream: {}", e);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Download task failed.
|
|
||||||
task.download_task_failed(task_id.as_str())
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
error!("download task failed: {}", e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Response::new(ReceiverStream::new(out_stream_rx)))
|
Ok(Response::new(ReceiverStream::new(out_stream_rx)))
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ impl Content {
|
||||||
}
|
}
|
||||||
|
|
||||||
// read_piece reads the piece from the content.
|
// read_piece reads the piece from the content.
|
||||||
#[instrument(skip(self, offset, length))]
|
#[instrument(skip_all)]
|
||||||
pub async fn read_piece(
|
pub async fn read_piece(
|
||||||
&self,
|
&self,
|
||||||
task_id: &str,
|
task_id: &str,
|
||||||
|
|
@ -67,7 +67,7 @@ impl Content {
|
||||||
}
|
}
|
||||||
|
|
||||||
// write_piece writes the piece to the content.
|
// write_piece writes the piece to the content.
|
||||||
#[instrument(skip(self, offset, reader))]
|
#[instrument(skip_all)]
|
||||||
pub async fn write_piece<R: AsyncRead + Unpin + ?Sized>(
|
pub async fn write_piece<R: AsyncRead + Unpin + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
task_id: &str,
|
task_id: &str,
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_task_started updates the metadata of the task when the task downloads started.
|
// download_task_started updates the metadata of the task when the task downloads started.
|
||||||
#[instrument(skip(self, piece_length))]
|
#[instrument(skip_all)]
|
||||||
pub fn download_task_started(&self, id: &str, piece_length: u64) -> Result<()> {
|
pub fn download_task_started(&self, id: &str, piece_length: u64) -> Result<()> {
|
||||||
let task = match self.get_task(id)? {
|
let task = match self.get_task(id)? {
|
||||||
// If the task exists, update the updated_at.
|
// If the task exists, update the updated_at.
|
||||||
|
|
@ -227,7 +227,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// set_task_content_length sets the content length of the task.
|
// set_task_content_length sets the content length of the task.
|
||||||
#[instrument(skip(self, content_length))]
|
#[instrument(skip_all)]
|
||||||
pub fn set_task_content_length(&self, id: &str, content_length: u64) -> Result<()> {
|
pub fn set_task_content_length(&self, id: &str, content_length: u64) -> Result<()> {
|
||||||
if let Some(mut task) = self.get_task(id)? {
|
if let Some(mut task) = self.get_task(id)? {
|
||||||
task.content_length = Some(content_length);
|
task.content_length = Some(content_length);
|
||||||
|
|
@ -238,7 +238,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// upload_task_finished updates the metadata of the task when task uploads finished.
|
// upload_task_finished updates the metadata of the task when task uploads finished.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn upload_task_finished(&self, id: &str) -> Result<()> {
|
pub fn upload_task_finished(&self, id: &str) -> Result<()> {
|
||||||
match self.get_task(id)? {
|
match self.get_task(id)? {
|
||||||
Some(mut task) => {
|
Some(mut task) => {
|
||||||
|
|
@ -251,7 +251,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_task_failed updates the metadata of the task when the task downloads failed.
|
// download_task_failed updates the metadata of the task when the task downloads failed.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn download_task_failed(&self, id: &str) -> Result<()> {
|
pub fn download_task_failed(&self, id: &str) -> Result<()> {
|
||||||
match self.get_task(id)? {
|
match self.get_task(id)? {
|
||||||
Some(_piece) => self.delete_task(id),
|
Some(_piece) => self.delete_task(id),
|
||||||
|
|
@ -260,7 +260,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get_task gets the task metadata.
|
// get_task gets the task metadata.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn get_task(&self, id: &str) -> Result<Option<Task>> {
|
pub fn get_task(&self, id: &str) -> Result<Option<Task>> {
|
||||||
let handle = self.cf_handle(TASK_CF_NAME)?;
|
let handle = self.cf_handle(TASK_CF_NAME)?;
|
||||||
match self.db.get_cf(handle, id)? {
|
match self.db.get_cf(handle, id)? {
|
||||||
|
|
@ -270,7 +270,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// put_task puts the task metadata.
|
// put_task puts the task metadata.
|
||||||
#[instrument(skip(self, task))]
|
#[instrument(skip_all)]
|
||||||
fn put_task(&self, id: &str, task: &Task) -> Result<()> {
|
fn put_task(&self, id: &str, task: &Task) -> Result<()> {
|
||||||
let handle = self.cf_handle(TASK_CF_NAME)?;
|
let handle = self.cf_handle(TASK_CF_NAME)?;
|
||||||
let json = serde_json::to_string(&task)?;
|
let json = serde_json::to_string(&task)?;
|
||||||
|
|
@ -279,7 +279,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete_task deletes the task metadata.
|
// delete_task deletes the task metadata.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
fn delete_task(&self, id: &str) -> Result<()> {
|
fn delete_task(&self, id: &str) -> Result<()> {
|
||||||
let handle = self.cf_handle(TASK_CF_NAME)?;
|
let handle = self.cf_handle(TASK_CF_NAME)?;
|
||||||
self.db.delete_cf(handle, id.as_bytes())?;
|
self.db.delete_cf(handle, id.as_bytes())?;
|
||||||
|
|
@ -287,7 +287,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_piece_started updates the metadata of the piece when the piece downloads started.
|
// download_piece_started updates the metadata of the piece when the piece downloads started.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn download_piece_started(&self, task_id: &str, number: u32) -> Result<()> {
|
pub fn download_piece_started(&self, task_id: &str, number: u32) -> Result<()> {
|
||||||
self.put_piece(
|
self.put_piece(
|
||||||
task_id,
|
task_id,
|
||||||
|
|
@ -301,7 +301,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_piece_finished updates the metadata of the piece when the piece downloads finished.
|
// download_piece_finished updates the metadata of the piece when the piece downloads finished.
|
||||||
#[instrument(skip(self, offset, length, digest))]
|
#[instrument(skip_all)]
|
||||||
pub fn download_piece_finished(
|
pub fn download_piece_finished(
|
||||||
&self,
|
&self,
|
||||||
task_id: &str,
|
task_id: &str,
|
||||||
|
|
@ -324,7 +324,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_piece_failed updates the metadata of the piece when the piece downloads failed.
|
// download_piece_failed updates the metadata of the piece when the piece downloads failed.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn download_piece_failed(&self, task_id: &str, number: u32) -> Result<()> {
|
pub fn download_piece_failed(&self, task_id: &str, number: u32) -> Result<()> {
|
||||||
match self.get_piece(task_id, number)? {
|
match self.get_piece(task_id, number)? {
|
||||||
Some(_piece) => self.delete_piece(task_id, number),
|
Some(_piece) => self.delete_piece(task_id, number),
|
||||||
|
|
@ -333,7 +333,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// upload_piece_finished updates the metadata of the piece when piece uploads finished.
|
// upload_piece_finished updates the metadata of the piece when piece uploads finished.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn upload_piece_finished(&self, task_id: &str, number: u32) -> Result<()> {
|
pub fn upload_piece_finished(&self, task_id: &str, number: u32) -> Result<()> {
|
||||||
match self.get_piece(task_id, number)? {
|
match self.get_piece(task_id, number)? {
|
||||||
Some(mut piece) => {
|
Some(mut piece) => {
|
||||||
|
|
@ -346,7 +346,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get_piece gets the piece metadata.
|
// get_piece gets the piece metadata.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn get_piece(&self, task_id: &str, number: u32) -> Result<Option<Piece>> {
|
pub fn get_piece(&self, task_id: &str, number: u32) -> Result<Option<Piece>> {
|
||||||
let id = self.piece_id(task_id, number);
|
let id = self.piece_id(task_id, number);
|
||||||
let handle = self.cf_handle(PIECE_CF_NAME)?;
|
let handle = self.cf_handle(PIECE_CF_NAME)?;
|
||||||
|
|
@ -357,7 +357,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get_pieces gets the pieces metadata.
|
// get_pieces gets the pieces metadata.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn get_pieces(&self, task_id: &str) -> Result<Vec<Piece>> {
|
pub fn get_pieces(&self, task_id: &str) -> Result<Vec<Piece>> {
|
||||||
let handle = self.cf_handle(PIECE_CF_NAME)?;
|
let handle = self.cf_handle(PIECE_CF_NAME)?;
|
||||||
let iter = self.db.prefix_iterator_cf(handle, task_id.as_bytes());
|
let iter = self.db.prefix_iterator_cf(handle, task_id.as_bytes());
|
||||||
|
|
@ -374,7 +374,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// put_piece puts the piece metadata.
|
// put_piece puts the piece metadata.
|
||||||
#[instrument(skip(self, piece))]
|
#[instrument(skip_all)]
|
||||||
fn put_piece(&self, task_id: &str, piece: &Piece) -> Result<()> {
|
fn put_piece(&self, task_id: &str, piece: &Piece) -> Result<()> {
|
||||||
let id = self.piece_id(task_id, piece.number);
|
let id = self.piece_id(task_id, piece.number);
|
||||||
let handle = self.cf_handle(PIECE_CF_NAME)?;
|
let handle = self.cf_handle(PIECE_CF_NAME)?;
|
||||||
|
|
@ -384,7 +384,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete_piece deletes the piece metadata.
|
// delete_piece deletes the piece metadata.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
fn delete_piece(&self, task_id: &str, number: u32) -> Result<()> {
|
fn delete_piece(&self, task_id: &str, number: u32) -> Result<()> {
|
||||||
let id = self.piece_id(task_id, number);
|
let id = self.piece_id(task_id, number);
|
||||||
let handle = self.cf_handle(PIECE_CF_NAME)?;
|
let handle = self.cf_handle(PIECE_CF_NAME)?;
|
||||||
|
|
@ -393,7 +393,7 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
// piece_id returns the piece id.
|
// piece_id returns the piece id.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn piece_id(&self, task_id: &str, number: u32) -> String {
|
pub fn piece_id(&self, task_id: &str, number: u32) -> String {
|
||||||
format!("{}-{}", task_id, number)
|
format!("{}-{}", task_id, number)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,31 +42,31 @@ impl Storage {
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_task_started updates the metadata of the task when the task downloads started.
|
// download_task_started updates the metadata of the task when the task downloads started.
|
||||||
#[instrument(skip(self, piece_length))]
|
#[instrument(skip_all)]
|
||||||
pub fn download_task_started(&self, id: &str, piece_length: u64) -> Result<()> {
|
pub fn download_task_started(&self, id: &str, piece_length: u64) -> Result<()> {
|
||||||
self.metadata.download_task_started(id, piece_length)
|
self.metadata.download_task_started(id, piece_length)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set_task_content_length sets the content length of the task.
|
// set_task_content_length sets the content length of the task.
|
||||||
#[instrument(skip(self, content_length))]
|
#[instrument(skip_all)]
|
||||||
pub fn set_task_content_length(&self, id: &str, content_length: u64) -> Result<()> {
|
pub fn set_task_content_length(&self, id: &str, content_length: u64) -> Result<()> {
|
||||||
self.metadata.set_task_content_length(id, content_length)
|
self.metadata.set_task_content_length(id, content_length)
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_task_failed updates the metadata of the task when the task downloads failed.
|
// download_task_failed updates the metadata of the task when the task downloads failed.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn download_task_failed(&self, id: &str) -> Result<()> {
|
pub fn download_task_failed(&self, id: &str) -> Result<()> {
|
||||||
self.metadata.download_task_failed(id)
|
self.metadata.download_task_failed(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// upload_task_finished updates the metadata of the task when task uploads finished.
|
// upload_task_finished updates the metadata of the task when task uploads finished.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn upload_task_finished(&self, id: &str) -> Result<()> {
|
pub fn upload_task_finished(&self, id: &str) -> Result<()> {
|
||||||
self.metadata.upload_task_finished(id)
|
self.metadata.upload_task_finished(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get_task returns the task metadata.
|
// get_task returns the task metadata.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn get_task(&self, id: &str) -> Result<Option<metadata::Task>> {
|
pub fn get_task(&self, id: &str) -> Result<Option<metadata::Task>> {
|
||||||
let task = self.metadata.get_task(id)?;
|
let task = self.metadata.get_task(id)?;
|
||||||
Ok(task)
|
Ok(task)
|
||||||
|
|
@ -74,13 +74,13 @@ impl Storage {
|
||||||
|
|
||||||
// download_piece_started updates the metadata of the piece and writes
|
// download_piece_started updates the metadata of the piece and writes
|
||||||
// the data of piece to file when the piece downloads started.
|
// the data of piece to file when the piece downloads started.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn download_piece_started(&self, task_id: &str, number: u32) -> Result<()> {
|
pub fn download_piece_started(&self, task_id: &str, number: u32) -> Result<()> {
|
||||||
self.metadata.download_piece_started(task_id, number)
|
self.metadata.download_piece_started(task_id, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_piece_from_source_finished is used for downloading piece from source.
|
// download_piece_from_source_finished is used for downloading piece from source.
|
||||||
#[instrument(skip(self, offset, length, reader))]
|
#[instrument(skip_all)]
|
||||||
pub async fn download_piece_from_source_finished<R: AsyncRead + Unpin + ?Sized>(
|
pub async fn download_piece_from_source_finished<R: AsyncRead + Unpin + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
task_id: &str,
|
task_id: &str,
|
||||||
|
|
@ -103,7 +103,7 @@ impl Storage {
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_piece_from_remote_peer_finished is used for downloading piece from remote peer.
|
// download_piece_from_remote_peer_finished is used for downloading piece from remote peer.
|
||||||
#[instrument(skip(self, offset, expected_digest, reader))]
|
#[instrument(skip_all)]
|
||||||
pub async fn download_piece_from_remote_peer_finished<R: AsyncRead + Unpin + ?Sized>(
|
pub async fn download_piece_from_remote_peer_finished<R: AsyncRead + Unpin + ?Sized>(
|
||||||
&self,
|
&self,
|
||||||
task_id: &str,
|
task_id: &str,
|
||||||
|
|
@ -132,14 +132,14 @@ impl Storage {
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_piece_failed updates the metadata of the piece when the piece downloads failed.
|
// download_piece_failed updates the metadata of the piece when the piece downloads failed.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn download_piece_failed(&self, task_id: &str, number: u32) -> Result<()> {
|
pub fn download_piece_failed(&self, task_id: &str, number: u32) -> Result<()> {
|
||||||
self.metadata.download_piece_failed(task_id, number)
|
self.metadata.download_piece_failed(task_id, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
// upload_piece updates the metadata of the piece and
|
// upload_piece updates the metadata of the piece and
|
||||||
// returns the data of the piece.
|
// returns the data of the piece.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub async fn upload_piece(&self, task_id: &str, number: u32) -> Result<impl AsyncRead> {
|
pub async fn upload_piece(&self, task_id: &str, number: u32) -> Result<impl AsyncRead> {
|
||||||
match self.metadata.get_piece(task_id, number)? {
|
match self.metadata.get_piece(task_id, number)? {
|
||||||
Some(piece) => {
|
Some(piece) => {
|
||||||
|
|
@ -155,20 +155,20 @@ impl Storage {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get_piece returns the piece metadata.
|
// get_piece returns the piece metadata.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn get_piece(&self, task_id: &str, number: u32) -> Result<Option<metadata::Piece>> {
|
pub fn get_piece(&self, task_id: &str, number: u32) -> Result<Option<metadata::Piece>> {
|
||||||
let piece = self.metadata.get_piece(task_id, number)?;
|
let piece = self.metadata.get_piece(task_id, number)?;
|
||||||
Ok(piece)
|
Ok(piece)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get_pieces returns the pieces metadata.
|
// get_pieces returns the pieces metadata.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn get_pieces(&self, task_id: &str) -> Result<Vec<metadata::Piece>> {
|
pub fn get_pieces(&self, task_id: &str) -> Result<Vec<metadata::Piece>> {
|
||||||
self.metadata.get_pieces(task_id)
|
self.metadata.get_pieces(task_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// piece_id returns the piece id.
|
// piece_id returns the piece id.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all)]
|
||||||
pub fn piece_id(&self, task_id: &str, number: u32) -> String {
|
pub fn piece_id(&self, task_id: &str, number: u32) -> String {
|
||||||
self.metadata.piece_id(task_id, number)
|
self.metadata.piece_id(task_id, number)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
380
src/task/mod.rs
380
src/task/mod.rs
|
|
@ -19,14 +19,15 @@ use crate::grpc::scheduler::SchedulerClient;
|
||||||
use crate::storage::{metadata, Storage};
|
use crate::storage::{metadata, Storage};
|
||||||
use crate::utils::http::headermap_to_hashmap;
|
use crate::utils::http::headermap_to_hashmap;
|
||||||
use crate::utils::id_generator::IDGenerator;
|
use crate::utils::id_generator::IDGenerator;
|
||||||
use crate::{Error, Result};
|
use crate::{Error, Result as ClientResult};
|
||||||
use dragonfly_api::common::v2::{Download, Piece, TrafficType};
|
use dragonfly_api::common::v2::{Download, Piece, TrafficType};
|
||||||
|
use dragonfly_api::dfdaemon::v2::DownloadTaskResponse;
|
||||||
use dragonfly_api::scheduler::v2::{
|
use dragonfly_api::scheduler::v2::{
|
||||||
announce_peer_request, announce_peer_response, download_piece_back_to_source_failed_request,
|
announce_peer_request, announce_peer_response, download_piece_back_to_source_failed_request,
|
||||||
AnnouncePeerRequest, DownloadPeerStartedRequest, DownloadPieceBackToSourceFailedRequest,
|
AnnouncePeerRequest, DownloadPeerStartedRequest, DownloadPieceBackToSourceFailedRequest,
|
||||||
DownloadPieceFailedRequest, DownloadPieceFinishedRequest, HttpResponse, RegisterPeerRequest,
|
DownloadPieceFailedRequest, DownloadPieceFinishedRequest, HttpResponse, RegisterPeerRequest,
|
||||||
};
|
};
|
||||||
use mpsc::Receiver;
|
use mpsc::Sender;
|
||||||
use reqwest::header::{self, HeaderMap};
|
use reqwest::header::{self, HeaderMap};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
@ -37,6 +38,7 @@ use tokio::{
|
||||||
};
|
};
|
||||||
use tokio_stream::wrappers::ReceiverStream;
|
use tokio_stream::wrappers::ReceiverStream;
|
||||||
use tonic::Request;
|
use tonic::Request;
|
||||||
|
use tonic::Status;
|
||||||
use tracing::{error, info, instrument};
|
use tracing::{error, info, instrument};
|
||||||
|
|
||||||
pub mod piece;
|
pub mod piece;
|
||||||
|
|
@ -86,24 +88,25 @@ impl Task {
|
||||||
|
|
||||||
// get gets a task metadata.
|
// get gets a task metadata.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub fn get(&self, task_id: &str) -> Result<Option<metadata::Task>> {
|
pub fn get(&self, task_id: &str) -> ClientResult<Option<metadata::Task>> {
|
||||||
self.storage.get_task(task_id)
|
self.storage.get_task(task_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_task_started updates the metadata of the task when the task downloads started.
|
// download_task_started updates the metadata of the task when the task downloads started.
|
||||||
#[instrument(skip(self, piece_length))]
|
#[instrument(skip(self, piece_length))]
|
||||||
pub fn download_task_started(&self, id: &str, piece_length: u64) -> Result<()> {
|
pub fn download_task_started(&self, id: &str, piece_length: u64) -> ClientResult<()> {
|
||||||
self.storage.download_task_started(id, piece_length)
|
self.storage.download_task_started(id, piece_length)
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_task_failed updates the metadata of the task when the task downloads failed.
|
// download_task_failed updates the metadata of the task when the task downloads failed.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub fn download_task_failed(&self, id: &str) -> Result<()> {
|
pub fn download_task_failed(&self, id: &str) -> ClientResult<()> {
|
||||||
self.storage.download_task_failed(id)
|
self.storage.download_task_failed(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_into_file downloads a task into a file.
|
// download_into_file downloads a task into a file.
|
||||||
#[instrument(skip(self, content_length, header, download))]
|
#[instrument(skip(self, content_length, header, download, download_progress_tx))]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn download_into_file(
|
pub async fn download_into_file(
|
||||||
&self,
|
&self,
|
||||||
task_id: &str,
|
task_id: &str,
|
||||||
|
|
@ -112,79 +115,155 @@ impl Task {
|
||||||
content_length: u64,
|
content_length: u64,
|
||||||
header: HeaderMap,
|
header: HeaderMap,
|
||||||
download: Download,
|
download: Download,
|
||||||
) -> Result<Receiver<metadata::Piece>> {
|
download_progress_tx: Sender<Result<DownloadTaskResponse, Status>>,
|
||||||
// Initialize the download progress channel.
|
) {
|
||||||
let (download_progress_tx, download_progress_rx) = mpsc::channel(128);
|
|
||||||
|
|
||||||
// Convert the timeout.
|
// Convert the timeout.
|
||||||
let timeout: Option<Duration> = match download.timeout.clone() {
|
let timeout: Option<Duration> = match download.timeout.clone() {
|
||||||
Some(timeout) => {
|
Some(timeout) => match Duration::try_from(timeout) {
|
||||||
Some(Duration::try_from(timeout).map_err(|_| Error::InvalidParameter())?)
|
Ok(timeout) => Some(timeout),
|
||||||
|
Err(err) => {
|
||||||
|
error!("convert timeout error: {:?}", err);
|
||||||
|
if let Err(err) = download_progress_tx
|
||||||
|
.send(Err(Status::invalid_argument("invalid timeout")))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("send download progress error: {:?}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Open the file.
|
// Open the file.
|
||||||
let mut f = OpenOptions::new()
|
let mut f = match OpenOptions::new()
|
||||||
.create(true)
|
.create(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.open(download.output_path.as_str())
|
.open(download.output_path.as_str())
|
||||||
.await?;
|
.await
|
||||||
|
{
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(err) => {
|
||||||
|
error!("open file error: {:?}", err);
|
||||||
|
if let Err(err) = download_progress_tx
|
||||||
|
.send(Err(Status::internal("open file error")))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("send download progress error: {:?}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Calculate the interested pieces to download.
|
// Calculate the interested pieces to download.
|
||||||
let interested_pieces = self.piece.calculate_interested(
|
let interested_pieces = match self.piece.calculate_interested(
|
||||||
task_id,
|
|
||||||
peer_id,
|
|
||||||
download.piece_length,
|
download.piece_length,
|
||||||
content_length,
|
content_length,
|
||||||
download.range.clone(),
|
download.range.clone(),
|
||||||
)?;
|
) {
|
||||||
|
Ok(interested_pieces) => interested_pieces,
|
||||||
|
Err(err) => {
|
||||||
|
error!("calculate interested pieces error: {:?}", err);
|
||||||
|
if let Err(err) = download_progress_tx
|
||||||
|
.send(Err(Status::invalid_argument(
|
||||||
|
"calculate interested pieces error",
|
||||||
|
)))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("send download progress error: {:?}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
info!("interested pieces: {:?}", interested_pieces);
|
info!("interested pieces: {:?}", interested_pieces);
|
||||||
|
|
||||||
// Get the task from the local storage.
|
// Get the task from the local storage.
|
||||||
let task = self
|
let task = match self.get(task_id) {
|
||||||
.get(task_id)?
|
Ok(Some(task)) => task,
|
||||||
.ok_or(Error::TaskNotFound(task_id.to_string()))?;
|
Ok(None) => {
|
||||||
|
error!("task not found");
|
||||||
|
if let Err(err) = download_progress_tx
|
||||||
|
.send(Err(Status::not_found("task not found")))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("send download progress error: {:?}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("get task error: {:?}", err);
|
||||||
|
if let Err(err) = download_progress_tx
|
||||||
|
.send(Err(Status::internal("get task error")))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("send download progress error: {:?}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// If the task is finished, return the file.
|
// If the task is finished, return the file.
|
||||||
if task.is_finished() {
|
if task.is_finished() {
|
||||||
info!("task is finished, download the pieces from the local peer");
|
info!("task is finished, download the pieces from the local peer");
|
||||||
|
|
||||||
// Download the pieces from the local peer.
|
// Download the pieces from the local peer.
|
||||||
return self
|
match self
|
||||||
.download_partial_from_local_peer_into_file(
|
.download_partial_from_local_peer_into_file(
|
||||||
&mut f,
|
&mut f,
|
||||||
task_id,
|
task_id,
|
||||||
peer_id,
|
interested_pieces.clone(),
|
||||||
interested_pieces,
|
content_length,
|
||||||
|
download_progress_tx.clone(),
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => {
|
||||||
|
error!("download from local peer error: {:?}", err);
|
||||||
|
if let Err(err) = download_progress_tx
|
||||||
|
.send(Err(Status::internal("download from local peer error")))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("send download progress error: {:?}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("download the pieces from local peer");
|
info!("download the pieces from local peer");
|
||||||
|
|
||||||
// Download the pieces from the local peer.
|
// Download the pieces from the local peer.
|
||||||
let mut finished_pieces: Vec<metadata::Piece> = Vec::new();
|
let finished_pieces = match self
|
||||||
while let Some(finished_piece) = self
|
|
||||||
.download_partial_from_local_peer_into_file(
|
.download_partial_from_local_peer_into_file(
|
||||||
&mut f,
|
&mut f,
|
||||||
task_id,
|
task_id,
|
||||||
peer_id,
|
|
||||||
interested_pieces.clone(),
|
interested_pieces.clone(),
|
||||||
|
content_length,
|
||||||
|
download_progress_tx.clone(),
|
||||||
)
|
)
|
||||||
.await?
|
|
||||||
.recv()
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
info!("finished piece from local peer: {:?}", finished_piece);
|
Ok(finished_pieces) => finished_pieces,
|
||||||
|
Err(err) => {
|
||||||
// Send the download progress.
|
error!("download from local peer error: {:?}", err);
|
||||||
download_progress_tx.send(finished_piece.clone()).await?;
|
if let Err(err) = download_progress_tx
|
||||||
|
.send(Err(Status::internal("download from local peer error")))
|
||||||
// Store the finished piece.
|
.await
|
||||||
finished_pieces.push(finished_piece);
|
{
|
||||||
|
error!("send download progress error: {:?}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Remove the finished pieces from the pieces.
|
// Remove the finished pieces from the pieces.
|
||||||
let interested_pieces = self
|
let interested_pieces = self
|
||||||
.piece
|
.piece
|
||||||
|
|
@ -197,7 +276,7 @@ impl Task {
|
||||||
// Check if all pieces are downloaded.
|
// Check if all pieces are downloaded.
|
||||||
if interested_pieces.is_empty() {
|
if interested_pieces.is_empty() {
|
||||||
info!("all pieces are downloaded from local peer");
|
info!("all pieces are downloaded from local peer");
|
||||||
return Ok(download_progress_rx);
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
info!("download the pieces with scheduler");
|
info!("download the pieces with scheduler");
|
||||||
|
|
@ -210,31 +289,50 @@ impl Task {
|
||||||
host_id,
|
host_id,
|
||||||
peer_id,
|
peer_id,
|
||||||
interested_pieces.clone(),
|
interested_pieces.clone(),
|
||||||
|
content_length,
|
||||||
download.clone(),
|
download.clone(),
|
||||||
|
download_progress_tx.clone(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(download_progress_rx) => Ok(download_progress_rx),
|
Ok(_) => {}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("download partial with scheduler into file error: {:?}", err);
|
error!("download with scheduler error: {:?}", err);
|
||||||
|
|
||||||
// Download the pieces from the source.
|
// Download the pieces from the source.
|
||||||
self.download_partial_from_source_into_file(
|
match self
|
||||||
|
.download_partial_from_source_into_file(
|
||||||
&mut f,
|
&mut f,
|
||||||
interested_pieces,
|
|
||||||
task_id,
|
task_id,
|
||||||
peer_id,
|
interested_pieces.clone(),
|
||||||
download.url.clone(),
|
download.url.clone(),
|
||||||
header.clone(),
|
header.clone(),
|
||||||
|
content_length,
|
||||||
timeout,
|
timeout,
|
||||||
|
download_progress_tx.clone(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => {
|
||||||
|
error!("download from source error: {:?}", err);
|
||||||
|
if let Err(err) = download_progress_tx
|
||||||
|
.send(Err(Status::internal("download from source error")))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("send download progress error: {:?}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_partial_with_scheduler_into_file downloads a partial task with scheduler into a file.
|
// download_partial_with_scheduler_into_file downloads a partial task with scheduler into a file.
|
||||||
#[instrument(skip(self, f, interested_pieces, download))]
|
#[instrument(skip_all)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn download_partial_with_scheduler_into_file(
|
async fn download_partial_with_scheduler_into_file(
|
||||||
&self,
|
&self,
|
||||||
f: &mut fs::File,
|
f: &mut fs::File,
|
||||||
|
|
@ -242,11 +340,10 @@ impl Task {
|
||||||
host_id: &str,
|
host_id: &str,
|
||||||
peer_id: &str,
|
peer_id: &str,
|
||||||
interested_pieces: Vec<metadata::Piece>,
|
interested_pieces: Vec<metadata::Piece>,
|
||||||
|
content_length: u64,
|
||||||
download: Download,
|
download: Download,
|
||||||
) -> Result<Receiver<metadata::Piece>> {
|
download_progress_tx: Sender<Result<DownloadTaskResponse, Status>>,
|
||||||
// Initialize the download progress channel.
|
) -> ClientResult<Vec<metadata::Piece>> {
|
||||||
let (download_progress_tx, download_progress_rx) = mpsc::channel(128);
|
|
||||||
|
|
||||||
// Convert the header.
|
// Convert the header.
|
||||||
let header: HeaderMap = (&download.header).try_into()?;
|
let header: HeaderMap = (&download.header).try_into()?;
|
||||||
|
|
||||||
|
|
@ -296,7 +393,8 @@ impl Task {
|
||||||
announce_peer_response::Response::EmptyTaskResponse(response) => {
|
announce_peer_response::Response::EmptyTaskResponse(response) => {
|
||||||
// If the task is empty, return an empty vector.
|
// If the task is empty, return an empty vector.
|
||||||
info!("empty task response: {:?}", response);
|
info!("empty task response: {:?}", response);
|
||||||
return Ok(download_progress_rx);
|
drop(in_stream_tx);
|
||||||
|
return Ok(Vec::new());
|
||||||
}
|
}
|
||||||
announce_peer_response::Response::NormalTaskResponse(response) => {
|
announce_peer_response::Response::NormalTaskResponse(response) => {
|
||||||
// If the task is normal, download the pieces from the remote peer.
|
// If the task is normal, download the pieces from the remote peer.
|
||||||
|
|
@ -307,7 +405,6 @@ impl Task {
|
||||||
.piece
|
.piece
|
||||||
.collect_interested_from_remote_peer(
|
.collect_interested_from_remote_peer(
|
||||||
task_id,
|
task_id,
|
||||||
peer_id,
|
|
||||||
interested_pieces.clone(),
|
interested_pieces.clone(),
|
||||||
candidate_parents,
|
candidate_parents,
|
||||||
)
|
)
|
||||||
|
|
@ -318,7 +415,6 @@ impl Task {
|
||||||
.piece
|
.piece
|
||||||
.download_from_remote_peer(
|
.download_from_remote_peer(
|
||||||
task_id,
|
task_id,
|
||||||
peer_id,
|
|
||||||
collect_interested_piece.number,
|
collect_interested_piece.number,
|
||||||
collect_interested_piece.parent.clone(),
|
collect_interested_piece.parent.clone(),
|
||||||
)
|
)
|
||||||
|
|
@ -365,6 +461,24 @@ impl Task {
|
||||||
.write_into_file_and_verify(&mut reader, f, metadata.digest.as_str())
|
.write_into_file_and_verify(&mut reader, f, metadata.digest.as_str())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"finished piece {} from remote peer {}",
|
||||||
|
collect_interested_piece.parent.id, metadata.number
|
||||||
|
);
|
||||||
|
|
||||||
|
// Construct the piece.
|
||||||
|
let piece = Piece {
|
||||||
|
number: metadata.number,
|
||||||
|
parent_id: Some(collect_interested_piece.parent.id.clone()),
|
||||||
|
offset: metadata.offset,
|
||||||
|
length: metadata.length,
|
||||||
|
digest: metadata.digest.clone(),
|
||||||
|
content: None,
|
||||||
|
traffic_type: Some(TrafficType::RemotePeer as i32),
|
||||||
|
cost: metadata.prost_cost(),
|
||||||
|
created_at: Some(prost_wkt_types::Timestamp::from(metadata.created_at)),
|
||||||
|
};
|
||||||
|
|
||||||
// Send the download piece finished request.
|
// Send the download piece finished request.
|
||||||
in_stream_tx
|
in_stream_tx
|
||||||
.send(AnnouncePeerRequest {
|
.send(AnnouncePeerRequest {
|
||||||
|
|
@ -374,21 +488,7 @@ impl Task {
|
||||||
request: Some(
|
request: Some(
|
||||||
announce_peer_request::Request::DownloadPieceFinishedRequest(
|
announce_peer_request::Request::DownloadPieceFinishedRequest(
|
||||||
DownloadPieceFinishedRequest {
|
DownloadPieceFinishedRequest {
|
||||||
piece: Some(Piece {
|
piece: Some(piece.clone()),
|
||||||
number: metadata.number,
|
|
||||||
parent_id: Some(
|
|
||||||
collect_interested_piece.parent.id.clone(),
|
|
||||||
),
|
|
||||||
offset: metadata.offset,
|
|
||||||
length: metadata.length,
|
|
||||||
digest: metadata.digest.clone(),
|
|
||||||
content: None,
|
|
||||||
traffic_type: Some(TrafficType::RemotePeer as i32),
|
|
||||||
cost: metadata.prost_cost(),
|
|
||||||
created_at: Some(prost_wkt_types::Timestamp::from(
|
|
||||||
metadata.created_at,
|
|
||||||
)),
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -396,11 +496,23 @@ impl Task {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Send the download progress.
|
// Send the download progress.
|
||||||
download_progress_tx.send(metadata.clone()).await?;
|
download_progress_tx
|
||||||
|
.send(Ok(DownloadTaskResponse {
|
||||||
|
content_length,
|
||||||
|
piece: Some(piece.clone()),
|
||||||
|
}))
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Store the finished piece.
|
// Store the finished piece.
|
||||||
finished_pieces.push(metadata.clone());
|
finished_pieces.push(metadata.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if all pieces are downloaded.
|
||||||
|
if finished_pieces.len() == interested_pieces.len() {
|
||||||
|
info!("all pieces are downloaded with scheduler");
|
||||||
|
drop(in_stream_tx);
|
||||||
|
return Ok(finished_pieces);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
announce_peer_response::Response::NeedBackToSourceResponse(response) => {
|
announce_peer_response::Response::NeedBackToSourceResponse(response) => {
|
||||||
// If the task need back to source, download the pieces from the source.
|
// If the task need back to source, download the pieces from the source.
|
||||||
|
|
@ -410,7 +522,7 @@ impl Task {
|
||||||
interested_pieces.clone(),
|
interested_pieces.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
for interested_piece in interested_pieces {
|
for interested_piece in interested_pieces.clone() {
|
||||||
// Seek to the offset of the piece.
|
// Seek to the offset of the piece.
|
||||||
if let Err(err) = f.seek(SeekFrom::Start(interested_piece.offset)).await {
|
if let Err(err) = f.seek(SeekFrom::Start(interested_piece.offset)).await {
|
||||||
error!("seek error: {:?}", err);
|
error!("seek error: {:?}", err);
|
||||||
|
|
@ -422,7 +534,6 @@ impl Task {
|
||||||
.piece
|
.piece
|
||||||
.download_from_source(
|
.download_from_source(
|
||||||
task_id,
|
task_id,
|
||||||
peer_id,
|
|
||||||
interested_piece.number,
|
interested_piece.number,
|
||||||
download.url.clone().as_str(),
|
download.url.clone().as_str(),
|
||||||
interested_piece.offset,
|
interested_piece.offset,
|
||||||
|
|
@ -496,19 +607,56 @@ impl Task {
|
||||||
.write_into_file_and_verify(&mut reader, f, metadata.digest.as_str())
|
.write_into_file_and_verify(&mut reader, f, metadata.digest.as_str())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
info!("finished piece {} from source", metadata.number);
|
||||||
|
|
||||||
|
// Construct the piece.
|
||||||
|
let piece = Piece {
|
||||||
|
number: metadata.number,
|
||||||
|
parent_id: None,
|
||||||
|
offset: metadata.offset,
|
||||||
|
length: metadata.length,
|
||||||
|
digest: metadata.digest.clone(),
|
||||||
|
content: None,
|
||||||
|
traffic_type: Some(TrafficType::BackToSource as i32),
|
||||||
|
cost: metadata.prost_cost(),
|
||||||
|
created_at: Some(prost_wkt_types::Timestamp::from(metadata.created_at)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send the download piece finished request.
|
||||||
|
in_stream_tx
|
||||||
|
.send(AnnouncePeerRequest {
|
||||||
|
host_id: host_id.to_string(),
|
||||||
|
task_id: task_id.to_string(),
|
||||||
|
peer_id: peer_id.to_string(),
|
||||||
|
request: Some(
|
||||||
|
announce_peer_request::Request::DownloadPieceFinishedRequest(
|
||||||
|
DownloadPieceFinishedRequest {
|
||||||
|
piece: Some(piece.clone()),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Send the download progress.
|
// Send the download progress.
|
||||||
download_progress_tx.send(metadata).await?;
|
download_progress_tx
|
||||||
|
.send(Ok(DownloadTaskResponse {
|
||||||
|
content_length,
|
||||||
|
piece: Some(piece.clone()),
|
||||||
|
}))
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Store the finished piece.
|
// Store the finished piece.
|
||||||
finished_pieces.push(interested_piece.clone());
|
finished_pieces.push(metadata.clone());
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if all pieces are downloaded.
|
|
||||||
if finished_pieces.len() == interested_pieces.len() {
|
if finished_pieces.len() == interested_pieces.len() {
|
||||||
return Ok(download_progress_rx);
|
info!("all pieces are downloaded from source");
|
||||||
|
drop(in_stream_tx);
|
||||||
|
return Ok(finished_pieces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not all pieces are downloaded, return an error.
|
// If not all pieces are downloaded, return an error.
|
||||||
|
|
@ -518,17 +666,15 @@ impl Task {
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_partial_from_local_peer_into_file downloads a partial task from a local peer into a file.
|
// download_partial_from_local_peer_into_file downloads a partial task from a local peer into a file.
|
||||||
#[instrument(skip(self, f, interested_pieces))]
|
#[instrument(skip_all)]
|
||||||
async fn download_partial_from_local_peer_into_file(
|
async fn download_partial_from_local_peer_into_file(
|
||||||
&self,
|
&self,
|
||||||
f: &mut fs::File,
|
f: &mut fs::File,
|
||||||
task_id: &str,
|
task_id: &str,
|
||||||
peer_id: &str,
|
|
||||||
interested_pieces: Vec<metadata::Piece>,
|
interested_pieces: Vec<metadata::Piece>,
|
||||||
) -> Result<Receiver<metadata::Piece>> {
|
content_length: u64,
|
||||||
// Initialize the download progress channel.
|
download_progress_tx: Sender<Result<DownloadTaskResponse, Status>>,
|
||||||
let (download_progress_tx, download_progress_rx) = mpsc::channel(128);
|
) -> ClientResult<Vec<metadata::Piece>> {
|
||||||
|
|
||||||
// Initialize the finished pieces.
|
// Initialize the finished pieces.
|
||||||
let mut finished_pieces: Vec<metadata::Piece> = Vec::new();
|
let mut finished_pieces: Vec<metadata::Piece> = Vec::new();
|
||||||
|
|
||||||
|
|
@ -542,7 +688,7 @@ impl Task {
|
||||||
// Download the piece from the local peer.
|
// Download the piece from the local peer.
|
||||||
let mut reader = match self
|
let mut reader = match self
|
||||||
.piece
|
.piece
|
||||||
.download_from_local_peer(task_id, Some(peer_id), interested_piece.number)
|
.download_from_local_peer(task_id, interested_piece.number)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(reader) => reader,
|
Ok(reader) => reader,
|
||||||
|
|
@ -563,32 +709,50 @@ impl Task {
|
||||||
.write_into_file_and_verify(&mut reader, f, metadata.digest.as_str())
|
.write_into_file_and_verify(&mut reader, f, metadata.digest.as_str())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
info!("finished piece {} from local peer", metadata.number);
|
||||||
|
|
||||||
|
// Construct the piece.
|
||||||
|
let piece = Piece {
|
||||||
|
number: metadata.number,
|
||||||
|
parent_id: None,
|
||||||
|
offset: metadata.offset,
|
||||||
|
length: metadata.length,
|
||||||
|
digest: metadata.digest.clone(),
|
||||||
|
content: None,
|
||||||
|
traffic_type: Some(TrafficType::LocalPeer as i32),
|
||||||
|
cost: metadata.prost_cost(),
|
||||||
|
created_at: Some(prost_wkt_types::Timestamp::from(metadata.created_at)),
|
||||||
|
};
|
||||||
|
|
||||||
// Send the download progress.
|
// Send the download progress.
|
||||||
download_progress_tx.send(metadata).await?;
|
download_progress_tx
|
||||||
|
.send(Ok(DownloadTaskResponse {
|
||||||
|
content_length,
|
||||||
|
piece: Some(piece.clone()),
|
||||||
|
}))
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Store the finished piece.
|
// Store the finished piece.
|
||||||
finished_pieces.push(interested_piece.clone());
|
finished_pieces.push(interested_piece.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(download_progress_rx)
|
Ok(finished_pieces)
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_partial_from_source_into_file downloads a partial task from the source into a file.
|
// download_partial_from_source_into_file downloads a partial task from the source into a file.
|
||||||
#[instrument(skip(self, f, interested_pieces, url, header, timeout))]
|
#[instrument(skip_all)]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn download_partial_from_source_into_file(
|
async fn download_partial_from_source_into_file(
|
||||||
&self,
|
&self,
|
||||||
f: &mut fs::File,
|
f: &mut fs::File,
|
||||||
interested_pieces: Vec<metadata::Piece>,
|
|
||||||
task_id: &str,
|
task_id: &str,
|
||||||
peer_id: &str,
|
interested_pieces: Vec<metadata::Piece>,
|
||||||
url: String,
|
url: String,
|
||||||
header: HeaderMap,
|
header: HeaderMap,
|
||||||
|
content_length: u64,
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
) -> Result<Receiver<metadata::Piece>> {
|
download_progress_tx: Sender<Result<DownloadTaskResponse, Status>>,
|
||||||
// Initialize the download progress channel.
|
) -> ClientResult<Vec<metadata::Piece>> {
|
||||||
let (download_progress_tx, download_progress_rx) = mpsc::channel(128);
|
|
||||||
|
|
||||||
// Initialize the finished pieces.
|
// Initialize the finished pieces.
|
||||||
let mut finished_pieces: Vec<metadata::Piece> = Vec::new();
|
let mut finished_pieces: Vec<metadata::Piece> = Vec::new();
|
||||||
|
|
||||||
|
|
@ -599,7 +763,6 @@ impl Task {
|
||||||
.piece
|
.piece
|
||||||
.download_from_source(
|
.download_from_source(
|
||||||
task_id,
|
task_id,
|
||||||
peer_id,
|
|
||||||
interested_piece.number,
|
interested_piece.number,
|
||||||
url.as_str(),
|
url.as_str(),
|
||||||
interested_piece.offset,
|
interested_piece.offset,
|
||||||
|
|
@ -620,16 +783,37 @@ impl Task {
|
||||||
.write_into_file_and_verify(&mut reader, f, metadata.digest.as_str())
|
.write_into_file_and_verify(&mut reader, f, metadata.digest.as_str())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
info!("finished piece {} from source", metadata.number);
|
||||||
|
|
||||||
|
// Construct the piece.
|
||||||
|
let piece = Piece {
|
||||||
|
number: metadata.number,
|
||||||
|
parent_id: None,
|
||||||
|
offset: metadata.offset,
|
||||||
|
length: metadata.length,
|
||||||
|
digest: metadata.digest.clone(),
|
||||||
|
content: None,
|
||||||
|
traffic_type: Some(TrafficType::LocalPeer as i32),
|
||||||
|
cost: metadata.prost_cost(),
|
||||||
|
created_at: Some(prost_wkt_types::Timestamp::from(metadata.created_at)),
|
||||||
|
};
|
||||||
|
|
||||||
// Send the download progress.
|
// Send the download progress.
|
||||||
download_progress_tx.send(metadata).await?;
|
download_progress_tx
|
||||||
|
.send(Ok(DownloadTaskResponse {
|
||||||
|
content_length,
|
||||||
|
piece: Some(piece.clone()),
|
||||||
|
}))
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Store the finished piece.
|
// Store the finished piece.
|
||||||
finished_pieces.push(interested_piece.clone());
|
finished_pieces.push(metadata.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if all pieces are downloaded.
|
// Check if all pieces are downloaded.
|
||||||
if finished_pieces.len() == interested_pieces.len() {
|
if finished_pieces.len() == interested_pieces.len() {
|
||||||
return Ok(download_progress_rx);
|
info!("all pieces are downloaded from source");
|
||||||
|
return Ok(finished_pieces);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not all pieces are downloaded, return an error.
|
// If not all pieces are downloaded, return an error.
|
||||||
|
|
@ -639,14 +823,14 @@ impl Task {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get_content_length gets the content length of the task.
|
// get_content_length gets the content length of the task.
|
||||||
#[instrument(skip(self, url, header, timeout))]
|
#[instrument(skip_all)]
|
||||||
pub async fn get_content_length(
|
pub async fn get_content_length(
|
||||||
&self,
|
&self,
|
||||||
task_id: &str,
|
task_id: &str,
|
||||||
url: &str,
|
url: &str,
|
||||||
header: HeaderMap,
|
header: HeaderMap,
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
) -> Result<u64> {
|
) -> ClientResult<u64> {
|
||||||
let task = self
|
let task = self
|
||||||
.storage
|
.storage
|
||||||
.get_task(task_id)?
|
.get_task(task_id)?
|
||||||
|
|
|
||||||
|
|
@ -117,11 +117,9 @@ impl Piece {
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate_interested calculates the interested pieces by content_length and range.
|
// calculate_interested calculates the interested pieces by content_length and range.
|
||||||
#[instrument(skip(self, piece_length, content_length, range))]
|
#[instrument(skip(self))]
|
||||||
pub fn calculate_interested(
|
pub fn calculate_interested(
|
||||||
&self,
|
&self,
|
||||||
task_id: &str,
|
|
||||||
peer_id: &str,
|
|
||||||
piece_length: u64,
|
piece_length: u64,
|
||||||
content_length: u64,
|
content_length: u64,
|
||||||
range: Option<Range>,
|
range: Option<Range>,
|
||||||
|
|
@ -146,7 +144,7 @@ impl Piece {
|
||||||
// If offset is greater than content_length, break the loop.
|
// If offset is greater than content_length, break the loop.
|
||||||
if offset >= content_length {
|
if offset >= content_length {
|
||||||
let mut piece = pieces.pop().ok_or(Error::InvalidParameter())?;
|
let mut piece = pieces.pop().ok_or(Error::InvalidParameter())?;
|
||||||
piece.length = piece_length + content_length - piece.offset;
|
piece.length = piece_length + content_length - offset;
|
||||||
pieces.push(piece);
|
pieces.push(piece);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -156,7 +154,6 @@ impl Piece {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = (number + 1) * piece_length;
|
|
||||||
if offset > range.start {
|
if offset > range.start {
|
||||||
pieces.push(metadata::Piece {
|
pieces.push(metadata::Piece {
|
||||||
number: number as u32,
|
number: number as u32,
|
||||||
|
|
@ -170,6 +167,7 @@ impl Piece {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offset = (number + 1) * piece_length;
|
||||||
number += 1;
|
number += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,12 +186,11 @@ impl Piece {
|
||||||
// If offset is greater than content_length, break the loop.
|
// If offset is greater than content_length, break the loop.
|
||||||
if offset >= content_length {
|
if offset >= content_length {
|
||||||
let mut piece = pieces.pop().ok_or(Error::InvalidParameter())?;
|
let mut piece = pieces.pop().ok_or(Error::InvalidParameter())?;
|
||||||
piece.length = piece_length + content_length - piece.offset;
|
piece.length = piece_length + content_length - offset;
|
||||||
pieces.push(piece);
|
pieces.push(piece);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = (number + 1) * piece_length;
|
|
||||||
pieces.push(metadata::Piece {
|
pieces.push(metadata::Piece {
|
||||||
number: number as u32,
|
number: number as u32,
|
||||||
offset,
|
offset,
|
||||||
|
|
@ -205,6 +202,7 @@ impl Piece {
|
||||||
finished_at: None,
|
finished_at: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
offset = (number + 1) * piece_length;
|
||||||
number += 1;
|
number += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,11 +232,10 @@ impl Piece {
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect_interested_from_remote_peer collects the interested pieces from remote peers.
|
// collect_interested_from_remote_peer collects the interested pieces from remote peers.
|
||||||
#[instrument(skip(self, interested_pieces, candidate_parents))]
|
#[instrument(skip_all)]
|
||||||
pub async fn collect_interested_from_remote_peer(
|
pub async fn collect_interested_from_remote_peer(
|
||||||
&self,
|
&self,
|
||||||
task_id: &str,
|
task_id: &str,
|
||||||
peer_id: &str,
|
|
||||||
interested_pieces: Vec<metadata::Piece>,
|
interested_pieces: Vec<metadata::Piece>,
|
||||||
candidate_parents: Vec<Peer>,
|
candidate_parents: Vec<Peer>,
|
||||||
) -> Vec<CollectPiece> {
|
) -> Vec<CollectPiece> {
|
||||||
|
|
@ -309,22 +306,20 @@ impl Piece {
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_from_local_peer downloads a single piece from a local peer.
|
// download_from_local_peer downloads a single piece from a local peer.
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip_all, fields(number))]
|
||||||
pub async fn download_from_local_peer(
|
pub async fn download_from_local_peer(
|
||||||
&self,
|
&self,
|
||||||
task_id: &str,
|
task_id: &str,
|
||||||
peer_id: Option<&str>,
|
|
||||||
number: u32,
|
number: u32,
|
||||||
) -> Result<impl AsyncRead> {
|
) -> Result<impl AsyncRead> {
|
||||||
self.storage.upload_piece(task_id, number).await
|
self.storage.upload_piece(task_id, number).await
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_from_remote_peer downloads a single piece from a remote peer.
|
// download_from_remote_peer downloads a single piece from a remote peer.
|
||||||
#[instrument(skip(self, remote_peer))]
|
#[instrument(skip_all, fields(number))]
|
||||||
pub async fn download_from_remote_peer(
|
pub async fn download_from_remote_peer(
|
||||||
&self,
|
&self,
|
||||||
task_id: &str,
|
task_id: &str,
|
||||||
peer_id: &str,
|
|
||||||
number: u32,
|
number: u32,
|
||||||
remote_peer: Peer,
|
remote_peer: Peer,
|
||||||
) -> Result<impl AsyncRead> {
|
) -> Result<impl AsyncRead> {
|
||||||
|
|
@ -408,12 +403,11 @@ impl Piece {
|
||||||
}
|
}
|
||||||
|
|
||||||
// download_from_source downloads a single piece from the source.
|
// download_from_source downloads a single piece from the source.
|
||||||
#[instrument(skip(self, url, offset, length, header, timeout))]
|
#[instrument(skip_all, fields(number))]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn download_from_source(
|
pub async fn download_from_source(
|
||||||
&self,
|
&self,
|
||||||
task_id: &str,
|
task_id: &str,
|
||||||
peer_id: &str,
|
|
||||||
number: u32,
|
number: u32,
|
||||||
url: &str,
|
url: &str,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue