Now saving torrent updates properly to the new db

This commit is contained in:
Igor Katson 2024-08-15 11:20:20 +01:00
parent f29dccf8bd
commit d77d96bd48
No known key found for this signature in database
GPG key ID: B4EC22B66D61A3F5
6 changed files with 92 additions and 37 deletions

View file

@ -96,19 +96,21 @@ impl Api {
.per_peer_stats_snapshot(filter))
}
pub fn api_torrent_action_pause(&self, idx: TorrentId) -> Result<EmptyJsonResponse> {
pub async fn api_torrent_action_pause(&self, idx: TorrentId) -> Result<EmptyJsonResponse> {
let handle = self.mgr_handle(idx)?;
handle
.pause()
self.session()
.pause(&handle)
.await
.context("error pausing torrent")
.with_error_status_code(StatusCode::BAD_REQUEST)?;
Ok(Default::default())
}
pub fn api_torrent_action_start(&self, idx: TorrentId) -> Result<EmptyJsonResponse> {
pub async fn api_torrent_action_start(&self, idx: TorrentId) -> Result<EmptyJsonResponse> {
let handle = self.mgr_handle(idx)?;
self.session
.unpause(&handle)
.await
.context("error unpausing torrent")
.with_error_status_code(StatusCode::BAD_REQUEST)?;
Ok(Default::default())
@ -130,7 +132,7 @@ impl Api {
Ok(Default::default())
}
pub fn api_torrent_action_update_only_files(
pub async fn api_torrent_action_update_only_files(
&self,
idx: TorrentId,
only_files: &HashSet<usize>,
@ -138,6 +140,7 @@ impl Api {
let handle = self.mgr_handle(idx)?;
self.session
.update_only_files(&handle, only_files)
.await
.context("error updating only_files")?;
Ok(Default::default())
}

View file

@ -368,14 +368,14 @@ impl HttpApi {
State(state): State<ApiState>,
Path(idx): Path<usize>,
) -> Result<impl IntoResponse> {
state.api_torrent_action_pause(idx).map(axum::Json)
state.api_torrent_action_pause(idx).await.map(axum::Json)
}
async fn torrent_action_start(
State(state): State<ApiState>,
Path(idx): Path<usize>,
) -> Result<impl IntoResponse> {
state.api_torrent_action_start(idx).map(axum::Json)
state.api_torrent_action_start(idx).await.map(axum::Json)
}
async fn torrent_action_forget(
@ -404,6 +404,7 @@ impl HttpApi {
) -> Result<impl IntoResponse> {
state
.api_torrent_action_update_only_files(idx, &req.only_files.into_iter().collect())
.await
.map(axum::Json)
}

View file

@ -993,7 +993,17 @@ impl Session {
}));
}
let id = if let Some(id) = opts.preferred_id {
id
} else if let Some(p) = self.persistence.as_ref() {
p.next_id().await?
} else {
self.next_id
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
};
let mut builder = ManagedTorrentBuilder::new(
id,
info,
info_hash,
torrent_bytes,
@ -1029,15 +1039,6 @@ impl Session {
builder.peer_read_write_timeout(t);
}
let id = if let Some(id) = opts.preferred_id {
id
} else if let Some(p) = self.persistence.as_ref() {
p.next_id().await?
} else {
self.next_id
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
};
let managed_torrent = {
let mut g = self.db.write();
if let Some((id, handle)) = g.torrents.iter().find_map(|(eid, t)| {
@ -1175,7 +1176,21 @@ impl Session {
Ok(merge_two_optional_streams(dht_rx, peer_rx))
}
pub fn unpause(self: &Arc<Self>, handle: &ManagedTorrentHandle) -> anyhow::Result<()> {
async fn try_update_persistence_metadata(&self, handle: &ManagedTorrentHandle) {
if let Some(p) = self.persistence.as_ref() {
if let Err(e) = p.update_metadata(handle.id(), handle).await {
warn!(storage=?p, error=?e, "error updating metadata")
}
}
}
pub async fn pause(&self, handle: &ManagedTorrentHandle) -> anyhow::Result<()> {
handle.pause()?;
self.try_update_persistence_metadata(handle).await;
Ok(())
}
pub async fn unpause(self: &Arc<Self>, handle: &ManagedTorrentHandle) -> anyhow::Result<()> {
let peer_rx = self.make_peer_rx(
handle.info_hash(),
handle.info().trackers.clone().into_iter().collect(),
@ -1183,15 +1198,17 @@ impl Session {
handle.info().options.force_tracker_interval,
)?;
handle.start(peer_rx, false, self.cancellation_token.child_token())?;
self.try_update_persistence_metadata(handle).await;
Ok(())
}
pub fn update_only_files(
pub async fn update_only_files(
self: &Arc<Self>,
handle: &ManagedTorrentHandle,
only_files: &HashSet<usize>,
) -> anyhow::Result<()> {
handle.update_only_files(only_files)?;
self.try_update_persistence_metadata(handle).await;
Ok(())
}

View file

@ -92,24 +92,13 @@ impl JsonSessionPersistenceStore {
fn torrent_bytes_filename(&self, info_hash: &Id20) -> PathBuf {
self.output_folder.join(format!("{:?}.torrent", info_hash))
}
}
#[async_trait]
impl SessionPersistenceStore for JsonSessionPersistenceStore {
async fn next_id(&self) -> anyhow::Result<TorrentId> {
Ok(self
.db_content
.read()
.await
.torrents
.keys()
.copied()
.max()
.map(|max| max + 1)
.unwrap_or(0))
}
async fn store(&self, id: TorrentId, torrent: &ManagedTorrentHandle) -> anyhow::Result<()> {
async fn update_db(
&self,
id: TorrentId,
torrent: &ManagedTorrentHandle,
write_torrent_file: bool,
) -> anyhow::Result<()> {
if !torrent
.storage_factory
.is_type_id(TypeId::of::<FilesystemStorageFactory>())
@ -132,7 +121,7 @@ impl SessionPersistenceStore for JsonSessionPersistenceStore {
output_folder: torrent.info().options.output_folder.clone(),
};
if !torrent.info().torrent_bytes.is_empty() {
if write_torrent_file && !torrent.info().torrent_bytes.is_empty() {
let torrent_bytes_file = self.torrent_bytes_filename(&torrent.info_hash());
match tokio::fs::OpenOptions::new()
.create(true)
@ -157,6 +146,22 @@ impl SessionPersistenceStore for JsonSessionPersistenceStore {
Ok(())
}
}
#[async_trait]
impl SessionPersistenceStore for JsonSessionPersistenceStore {
async fn next_id(&self) -> anyhow::Result<TorrentId> {
Ok(self
.db_content
.read()
.await
.torrents
.keys()
.copied()
.max()
.map(|max| max + 1)
.unwrap_or(0))
}
async fn delete(&self, id: TorrentId) -> anyhow::Result<()> {
if let Some(t) = self.db_content.write().await.torrents.remove(&id) {
@ -211,4 +216,16 @@ impl SessionPersistenceStore for JsonSessionPersistenceStore {
.then(move |id| async move { self.get(id).await.map(move |st| (id, st)) })
.boxed())
}
async fn store(&self, id: TorrentId, torrent: &ManagedTorrentHandle) -> anyhow::Result<()> {
self.update_db(id, torrent, true).await
}
async fn update_metadata(
&self,
id: TorrentId,
torrent: &ManagedTorrentHandle,
) -> anyhow::Result<()> {
self.update_db(id, torrent, false).await
}
}

View file

@ -59,12 +59,18 @@ impl SerializedTorrent {
}
}
// TODO: make this info_hash first, ID-second.
#[async_trait]
pub trait SessionPersistenceStore: core::fmt::Debug + Send + Sync {
async fn next_id(&self) -> anyhow::Result<TorrentId>;
async fn store(&self, id: TorrentId, torrent: &ManagedTorrentHandle) -> anyhow::Result<()>;
async fn delete(&self, id: TorrentId) -> anyhow::Result<()>;
async fn get(&self, id: TorrentId) -> anyhow::Result<SerializedTorrent>;
async fn update_metadata(
&self,
id: TorrentId,
torrent: &ManagedTorrentHandle,
) -> anyhow::Result<()>;
async fn stream_all(
&self,
) -> anyhow::Result<BoxStream<'_, anyhow::Result<(TorrentId, SerializedTorrent)>>>;

View file

@ -36,6 +36,7 @@ use tracing::warn;
use crate::chunk_tracker::ChunkTracker;
use crate::file_info::FileInfo;
use crate::session::TorrentId;
use crate::spawn_utils::BlockingSpawner;
use crate::storage::BoxStorageFactory;
use crate::stream_connect::StreamConnector;
@ -114,6 +115,8 @@ pub struct ManagedTorrentInfo {
}
pub struct ManagedTorrent {
pub id: TorrentId,
// TODO: merge ManagedTorrent and ManagedTorrentInfo
pub info: Arc<ManagedTorrentInfo>,
pub(crate) storage_factory: BoxStorageFactory,
@ -122,6 +125,10 @@ pub struct ManagedTorrent {
}
impl ManagedTorrent {
pub fn id(&self) -> TorrentId {
self.id
}
pub fn info(&self) -> &ManagedTorrentInfo {
&self.info
}
@ -344,7 +351,7 @@ impl ManagedTorrent {
}
/// Pause the torrent if it's live.
pub fn pause(&self) -> anyhow::Result<()> {
pub(crate) fn pause(&self) -> anyhow::Result<()> {
let mut g = self.locked.write();
match &g.state {
ManagedTorrentState::Live(live) => {
@ -501,6 +508,7 @@ impl ManagedTorrent {
}
pub(crate) struct ManagedTorrentBuilder {
id: TorrentId,
info: TorrentMetaV1Info<ByteBufOwned>,
output_folder: PathBuf,
info_hash: Id20,
@ -521,6 +529,7 @@ pub(crate) struct ManagedTorrentBuilder {
impl ManagedTorrentBuilder {
pub fn new(
id: usize,
info: TorrentMetaV1Info<ByteBufOwned>,
info_hash: Id20,
torrent_bytes: Bytes,
@ -529,6 +538,7 @@ impl ManagedTorrentBuilder {
storage_factory: BoxStorageFactory,
) -> Self {
Self {
id,
info,
info_hash,
torrent_bytes,
@ -641,6 +651,7 @@ impl ManagedTorrentBuilder {
self.storage_factory.create_and_init(&info)?,
));
Ok(Arc::new(ManagedTorrent {
id: self.id,
locked: RwLock::new(ManagedTorrentLocked {
state: ManagedTorrentState::Initializing(initializing),
only_files: self.only_files,