mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-03 16:41:26 +01:00
fix(fuse): read the size again for a baseline row without a revision id
A mirror upload that Drive did not list yet when it was read back left a baseline row with no revision id. The next walk reused the recorded size whenever the modification time matched, and a row without an id never counts as moved. So a revision another computer saved in the same second looked unchanged and never arrived (B254). The B250 simulation hit this about one run in five on CI. A row without an id now reuses its size only while Drive gives no id either. Otherwise the walk reads the size, and an unchanged file gets Drive's revision id written into its row, so each row costs one extra read. Rows from before schema 44 are read once after upgrading.
This commit is contained in:
1 parent
c1911ea1de
commit
8f771fdcc4
4 files changed
+67
-2
No files matched your search
@@ -776,7 +776,23 @@ impl Core {
|
||||
}
|
||||
|
||||
match plan_file(l, r, baseline.get(rel)) {
|
||||
FilePlan::Unchanged => {}
|
||||
// The size was read for real for a row without a revision id
|
||||
// (B254), and still matches: give the row the id, so later
|
||||
// walks trust the recorded size again.
|
||||
FilePlan::Unchanged => {
|
||||
if let (Some(base), Some(revision)) =
|
||||
(baseline.get(rel), r.and_then(|r| r.revision.as_ref()))
|
||||
&& base.remote_revision_id.is_none()
|
||||
{
|
||||
let row = StoredSyncEntry {
|
||||
remote_revision_id: Some(revision.clone()),
|
||||
..base.clone()
|
||||
};
|
||||
if let Err(e) = self.db.sync_entry_upsert(folder_id, &row) {
|
||||
debug!(rel, error = ?e, "sync: cannot record the revision id");
|
||||
}
|
||||
}
|
||||
}
|
||||
FilePlan::AdoptBaseline => {
|
||||
// First sync of a file the same size on both sides: the apply
|
||||
// step hashes the local copy and records the baseline only if
|
||||
|
||||
@@ -333,6 +333,13 @@ pub(super) fn remote_subtree_changed(
|
||||
|
||||
/// The size the baseline recorded for `rel`, if its remote signature's mtime
|
||||
/// still matches `mtime` and its revision is still `revision`.
|
||||
///
|
||||
/// A row without a revision id, while Drive reports one, cannot vouch for the
|
||||
/// revision, so the size is read for real. Such a row comes from before
|
||||
/// schema 44, or from an upload Drive did not list yet when it was read back.
|
||||
/// Another computer's revision in the same second as that upload kept the
|
||||
/// modification time, and only the size could show it (B254). The pass then
|
||||
/// records the id on the row, so this costs one read per row.
|
||||
pub(super) fn unchanged_remote_size(
|
||||
baseline: &HashMap<String, StoredSyncEntry>,
|
||||
rel: &str,
|
||||
@@ -340,7 +347,9 @@ pub(super) fn unchanged_remote_size(
|
||||
revision: Option<&str>,
|
||||
) -> Option<i64> {
|
||||
let base = baseline.get(rel)?;
|
||||
if revision_moved(Some(base), revision) {
|
||||
if revision_moved(Some(base), revision)
|
||||
|| (base.remote_revision_id.is_none() && revision.is_some())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
match remote_sig(base) {
|
||||
@@ -756,6 +765,15 @@ mod file_plan_tests {
|
||||
assert_eq!(unchanged_remote_size(&rows, "file", 31, Some("r1")), None);
|
||||
}
|
||||
|
||||
/// A row without a revision id reuses its size only while Drive gives no
|
||||
/// id either (B254).
|
||||
#[test]
|
||||
fn a_row_without_a_revision_makes_the_walk_read_the_size_again() {
|
||||
let rows = HashMap::from([("file".to_string(), baseline(10, 20, 30, 40))]);
|
||||
assert_eq!(unchanged_remote_size(&rows, "file", 30, Some("r1")), None);
|
||||
assert_eq!(unchanged_remote_size(&rows, "file", 30, None), Some(40));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_missing_remote_signature_is_treated_as_changed() {
|
||||
let local = local(10, 20);
|
||||
|
||||
Reference in new issue
Block a user