syncweb_core/sync/
checkpoint.rs1use std::collections::HashSet;
2
3use iroh_blobs::Hash;
4use iroh_docs::NamespaceId;
5
6use crate::{Result, storage::node_db::NodeDatabase};
7
8#[derive(Clone, Debug)]
14pub struct SyncCheckpoint {
15 namespace_id: NamespaceId,
16 session_id: String,
17 database: NodeDatabase,
18}
19
20#[derive(Clone, Debug)]
22#[non_exhaustive]
23pub struct EntryProgress {
24 pub entry_key: Vec<u8>,
25 pub hash: Hash,
26 pub size: u64,
27 pub status: String,
28 pub retries: u32,
29 pub error_message: Option<String>,
30}
31
32#[derive(Clone, Copy, Debug)]
34#[non_exhaustive]
35pub struct CheckpointProgress {
36 pub total: usize,
37 pub completed: usize,
38 pub failed: usize,
39 pub skipped: usize,
40 pub pending: usize,
41 pub bytes_transferred: u64,
42 pub bytes_total: Option<u64>,
43 pub percentage: f64,
44}
45
46impl SyncCheckpoint {
47 #[must_use]
49 pub fn new(database: &NodeDatabase, namespace_id: NamespaceId) -> Self {
50 let session_id = uuid::Uuid::new_v4().to_string();
51 Self {
52 namespace_id,
53 session_id,
54 database: database.clone(),
55 }
56 }
57
58 #[must_use]
60 pub fn session_id(&self) -> &str {
61 &self.session_id
62 }
63
64 #[must_use]
66 pub const fn namespace_id(&self) -> NamespaceId {
67 self.namespace_id
68 }
69
70 pub fn create_session(database: &NodeDatabase, namespace_id: NamespaceId) -> Result<Self> {
76 let cp = Self::new(database, namespace_id);
77 database.create_sync_checkpoint(&cp.namespace_id.to_string(), &cp.session_id)?;
78 Ok(cp)
79 }
80
81 pub fn mark_completed(&self, entry_key: &[u8], hash: Hash, size: u64) -> Result<()> {
87 self.database
88 .upsert_sync_entry(&crate::storage::node_db::SyncEntryParams {
89 namespace_id: &self.namespace_id.to_string(),
90 session_id: &self.session_id,
91 entry_key,
92 hash: &hash.to_string().into_bytes(),
93 size,
94 status: "completed",
95 retries: 0,
96 error_message: None,
97 })
98 }
99
100 pub fn mark_failed(&self, entry_key: &[u8], error: &str) -> Result<()> {
106 self.database
107 .upsert_sync_entry(&crate::storage::node_db::SyncEntryParams {
108 namespace_id: &self.namespace_id.to_string(),
109 session_id: &self.session_id,
110 entry_key,
111 hash: &[],
112 size: 0,
113 status: "failed",
114 retries: 0,
115 error_message: Some(error),
116 })
117 }
118
119 pub fn mark_skipped(&self, entry_key: &[u8], hash: Hash, size: u64) -> Result<()> {
125 self.database
126 .upsert_sync_entry(&crate::storage::node_db::SyncEntryParams {
127 namespace_id: &self.namespace_id.to_string(),
128 session_id: &self.session_id,
129 entry_key,
130 hash: &hash.to_string().into_bytes(),
131 size,
132 status: "skipped",
133 retries: 0,
134 error_message: None,
135 })
136 }
137
138 pub fn completed_entries(&self) -> Result<Vec<EntryProgress>> {
144 self.database
145 .list_sync_entries(&self.namespace_id.to_string(), &self.session_id, "completed")
146 }
147
148 pub fn failed_entries(&self) -> Result<Vec<EntryProgress>> {
154 self.database
155 .list_sync_entries(&self.namespace_id.to_string(), &self.session_id, "failed")
156 }
157
158 pub fn completed_keys(&self) -> Result<HashSet<Vec<u8>>> {
164 let entries = self.completed_entries()?;
165 Ok(entries.into_iter().map(|e| e.entry_key).collect())
166 }
167
168 pub fn progress(&self) -> Result<CheckpointProgress> {
174 self.database
175 .get_checkpoint_progress(&self.namespace_id.to_string(), &self.session_id)
176 }
177
178 pub fn complete(&self) -> Result<()> {
184 self.database
185 .update_checkpoint_status(&self.namespace_id.to_string(), &self.session_id, "completed")
186 }
187
188 pub const fn mark_incomplete(&self) -> Result<()> {
194 Ok(())
196 }
197
198 pub fn delete(&self) -> Result<()> {
204 self.database
205 .delete_sync_checkpoint(&self.namespace_id.to_string(), &self.session_id)
206 }
207
208 pub fn resume(database: &NodeDatabase, namespace_id: NamespaceId) -> Result<Option<Self>> {
216 let ns = namespace_id.to_string();
217 Ok(database.find_unfinished_checkpoint(&ns)?.map(|sid| Self {
218 namespace_id,
219 session_id: sid,
220 database: database.clone(),
221 }))
222 }
223}