Compute the full `RemoteDelta` for a given view. This is the main entry point for sync operations. It: 1. Runs the dichotomy algorithm to find the divergence point 2. Downloads the remote's changelist from the divergence point 3. Compares "ours" (cached) vs "theirs" (actual) after divergence 4. Identifies changes to download, unrecords, and unknowns 5. Updates the local cache # Arguments `view
(
&mut self,
view: &str,
local_hashes: &HashSet<String>,
)
| 633 | /// * `local_hashes` - Set of change hashes present in the local repository. |
| 634 | /// Used to determine which remote changes need to be downloaded. |
| 635 | pub async fn compute_delta( |
| 636 | &mut self, |
| 637 | view: &str, |
| 638 | local_hashes: &HashSet<String>, |
| 639 | ) -> RemoteResult<RemoteDelta> { |
| 640 | // Order-invariant fast path (additive; dormant until the server |
| 641 | // advertises a SetId). If the remote publishes the SetId of its |
| 642 | // effective change set and it equals the SetId folded from our local |
| 643 | // hashes, both sides hold the same SET of changes regardless of order |
| 644 | // — we are in sync and can skip the O(log n) Merkle dichotomy and the |
| 645 | // changelist download entirely. When the field is absent (older |
| 646 | // server) or the local set cannot be folded, we fall through to the |
| 647 | // existing dichotomy with no behavior change. |
| 648 | let remote_state = self.remote.get_state(view).await?; |
| 649 | if let (Some(remote_sid), Some(local_sid)) = |
| 650 | (remote_state.set_id(), Self::local_set_id(local_hashes)) |
| 651 | { |
| 652 | if remote_sid == local_sid { |
| 653 | debug!("sync: SetId match ({}), views already in sync", remote_sid); |
| 654 | return Ok(RemoteDelta::in_sync()); |
| 655 | } |
| 656 | } |
| 657 | |
| 658 | // Handle from-scratch sync (no cache) |
| 659 | if self.cache.is_empty() { |
| 660 | return self.compute_delta_from_scratch(view, local_hashes).await; |
| 661 | } |
| 662 | |
| 663 | // Find divergence point |
| 664 | let divergence = self.dichotomy_changelist(view).await?; |
| 665 | self.stats.divergence_point = divergence; |
| 666 | |
| 667 | // Collect our cached entries at or after divergence |
| 668 | let ours_ge_dichotomy: Vec<(u64, Node)> = self |
| 669 | .cache |
| 670 | .entries_from(divergence) |
| 671 | .into_iter() |
| 672 | .map(|(seq, node)| (seq, node.clone())) |
| 673 | .collect(); |
| 674 | |
| 675 | let ours_ge_dichotomy_set: HashSet<Node> = ours_ge_dichotomy |
| 676 | .iter() |
| 677 | .map(|(_, node)| node.clone()) |
| 678 | .collect(); |
| 679 | |
| 680 | // Download the remote's changelist from the divergence point |
| 681 | let remote_entries = self.remote.get_changelist(view, divergence).await?; |
| 682 | self.stats.changelist_entries_fetched = remote_entries.len(); |
| 683 | |
| 684 | // Build "theirs" sets |
| 685 | let mut theirs_ge_dichotomy = Vec::new(); |
| 686 | let mut theirs_ge_dichotomy_set = HashSet::new(); |
| 687 | let mut to_download = Vec::new(); |
| 688 | |
| 689 | for entry in &remote_entries { |
| 690 | let node = entry.to_node(); |
| 691 | theirs_ge_dichotomy_set.insert(node.clone()); |
| 692 | theirs_ge_dichotomy.push((entry.sequence, node.clone())); |
no test coverage detected