diff --git a/src/app/materialize.rs b/src/app/materialize.rs index ccb0708..cbf39bd 100644 --- a/src/app/materialize.rs +++ b/src/app/materialize.rs @@ -17,12 +17,17 @@ use crate::{ storage::FolderConfig, }; +pub(super) struct RemoteManifestResult { + pub(super) manifest: Manifest, + pub(super) synchronized_file_count: usize, +} + pub(super) async fn apply_remote_manifest( context: &SyncContext<'_>, local: &Manifest, remote: &Manifest, peer: iroh::EndpointAddr, -) -> anyhow::Result<(Manifest, usize)> { +) -> anyhow::Result { let mut merged = local.clone(); let mut synchronized_count = 0; let ignore_rules = IgnoreRules::load(&context.folder.path)?; @@ -45,7 +50,10 @@ pub(super) async fn apply_remote_manifest( downloads, ) .await?; - Ok((merged, synchronized_count)) + Ok(RemoteManifestResult { + manifest: merged, + synchronized_file_count: synchronized_count, + }) } pub(super) async fn restore_manifest( diff --git a/src/app/sync.rs b/src/app/sync.rs index 838cbf4..9ad0adf 100644 --- a/src/app/sync.rs +++ b/src/app/sync.rs @@ -2,7 +2,9 @@ use crate::{ app::{AppResult, AppaService, MAX_CONCURRENT_PEER_REQUESTS, SyncContext}, app::{ manifest::{ManifestBuild, build_manifest_with_stages, report_local_changes}, - materialize::{apply_remote_manifest, recover_pending_materialization}, + materialize::{ + RemoteManifestResult, apply_remote_manifest, recover_pending_materialization, + }, }, domain::{FolderRoster, manifest_root_hash}, iroh::{FolderSession, NodeHost, PeerSummaryResponse}, @@ -97,14 +99,14 @@ impl AppaService { .await; let mut synchronized_count = 0; for (peer, response) in peer_summaries { - let Some((merged_manifest, synchronized_files)) = self + let Some(remote_result) = self .synchronize_peer(&context, &local_manifest, peer, response) .await? else { continue; }; - local_manifest = merged_manifest; - synchronized_count += synchronized_files; + local_manifest = remote_result.manifest; + synchronized_count += remote_result.synchronized_file_count; self.state_store.save_manifest(&local_manifest)?; node.publish_manifest(local_manifest.clone()).await?; } @@ -220,7 +222,7 @@ impl AppaService { local_manifest: &crate::domain::Manifest, peer: iroh::EndpointAddr, response: anyhow::Result, - ) -> AppResult> { + ) -> AppResult> { let folder = context.folder; let PeerSummaryResponse { summary, diff --git a/src/storage/manifests.rs b/src/storage/manifests.rs index 3bc8b2a..9c9defa 100644 --- a/src/storage/manifests.rs +++ b/src/storage/manifests.rs @@ -51,8 +51,8 @@ impl StateStore { let transaction = self.connection.unchecked_transaction()?; transaction.execute("INSERT INTO manifests (folder_id, manifest) VALUES (?1, ?2) ON CONFLICT(folder_id) DO UPDATE SET manifest = excluded.manifest", params![manifest.folder_id.to_string(), serialized])?; let revision_count = history_count(&transaction, manifest.folder_id)?; - let (kind, payload) = history_payload(existing.as_deref(), manifest, revision_count)?; - transaction.execute("INSERT INTO manifest_history_compact (folder_id, saved_at, kind, payload) VALUES (?1, ?2, ?3, ?4)", params![manifest.folder_id.to_string(), OffsetDateTime::now_utc().format(&Rfc3339)?, kind, payload])?; + let payload = history_payload(existing.as_deref(), manifest, revision_count)?; + transaction.execute("INSERT INTO manifest_history_compact (folder_id, saved_at, kind, payload) VALUES (?1, ?2, ?3, ?4)", params![manifest.folder_id.to_string(), OffsetDateTime::now_utc().format(&Rfc3339)?, payload.kind, payload.bytes])?; checkpoint_retention_boundary(&transaction, manifest.folder_id, self.history_limit)?; prune_manifest_history(&transaction, manifest.folder_id, self.history_limit)?; transaction.commit()?; @@ -108,6 +108,11 @@ struct HistoryRecord { payload: Vec, } +struct HistoryPayload { + kind: &'static str, + bytes: Vec, +} + fn history_records( connection: &rusqlite::Connection, folder_id: FolderId, @@ -141,17 +146,20 @@ fn history_payload( previous: Option<&str>, manifest: &Manifest, revision_count: u64, -) -> anyhow::Result<(&'static str, Vec)> { +) -> anyhow::Result { if previous.is_none() || revision_count.is_multiple_of(HISTORY_CHECKPOINT_INTERVAL) { - return Ok((CHECKPOINT_RECORD_KIND, compress(manifest)?)); + return Ok(HistoryPayload { + kind: CHECKPOINT_RECORD_KIND, + bytes: compress(manifest)?, + }); } let previous = previous.ok_or_else(|| anyhow::anyhow!("manifest history is missing its predecessor"))?; let previous = serde_json::from_str(previous)?; - Ok(( - DELTA_RECORD_KIND, - compress(&manifest_delta(&previous, manifest))?, - )) + Ok(HistoryPayload { + kind: DELTA_RECORD_KIND, + bytes: compress(&manifest_delta(&previous, manifest))?, + }) } fn checkpoint_retention_boundary( diff --git a/src/storage/materializations.rs b/src/storage/materializations.rs index fcb78bc..4fcd336 100644 --- a/src/storage/materializations.rs +++ b/src/storage/materializations.rs @@ -13,10 +13,21 @@ enum StoredMaterializationEntries { Batch(Vec), } -fn parse_pending_entries(entries: &str) -> anyhow::Result<(Vec, bool)> { +struct ParsedPendingEntries { + entries: Vec, + is_legacy_single_entry: bool, +} + +fn parse_pending_entries(entries: &str) -> anyhow::Result { match serde_json::from_str(entries)? { - StoredMaterializationEntries::Single(entry) => Ok((vec![entry], true)), - StoredMaterializationEntries::Batch(entries) => Ok((entries, false)), + StoredMaterializationEntries::Single(entry) => Ok(ParsedPendingEntries { + entries: vec![entry], + is_legacy_single_entry: true, + }), + StoredMaterializationEntries::Batch(entries) => Ok(ParsedPendingEntries { + entries, + is_legacy_single_entry: false, + }), } } @@ -45,11 +56,11 @@ impl StateStore { ) .optional()? .map(|(entries, manifest)| { - let (entries, is_legacy_single_entry) = parse_pending_entries(&entries)?; + let parsed_entries = parse_pending_entries(&entries)?; Ok(PendingMaterialization { - entries, + entries: parsed_entries.entries, resulting_manifest: serde_json::from_str(&manifest)?, - is_legacy_single_entry, + is_legacy_single_entry: parsed_entries.is_legacy_single_entry, }) }) .transpose()