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);
|
||||
|
||||
@@ -100,6 +100,7 @@ a live account since.
|
||||
|
||||
| Entry | Title |
|
||||
|---|---|
|
||||
| [B254](#b254--an-edit-in-the-same-second-as-a-new-upload-never-arrives) | An edit in the same second as a new upload never arrives |
|
||||
| [B253](#b253--the-app-shows-the-sign-in-page-when-the-keyring-is-locked) | The app shows the sign-in page when the keyring is locked |
|
||||
| [B252](#b252--computers-sharing-a-device-overwrite-each-others-profile) | Computers sharing a device overwrite each other's profile |
|
||||
| [B251](#b251--two-computers-can-make-the-same-conflict-copy-name) | Two computers can make the same conflict copy name |
|
||||
@@ -356,6 +357,33 @@ a live account since.
|
||||
|
||||
Newest first.
|
||||
|
||||
### B254 — An edit in the same second as a new upload never arrives
|
||||
|
||||
**Status:** Fixed, not verified live
|
||||
**Found:** 2026-10-16, the B250 simulation failing about one run in five on CI.
|
||||
**Where:** `unchanged_remote_size` in `crates/pdfs-fuse/src/sync/planner.rs`; the
|
||||
`FilePlan::Unchanged` arm of `do_reconcile` in `crates/pdfs-fuse/src/sync.rs`
|
||||
|
||||
**Repro:** A mirror pass uploads a new file, and Drive does not list it yet when the pass
|
||||
reads it back. Another computer saves a new revision with the same modification time, to the
|
||||
second. The mirror keeps the old content, and no pass ever downloads the new one.
|
||||
|
||||
**Cause.** The read-back found no node, so the baseline row got the local modification time
|
||||
and size but no revision id. The next walk reused the recorded size whenever the modification
|
||||
time matched and the revision id had not moved. A row without an id never counts as moved, so
|
||||
the walk never read the new size, and the file looked unchanged.
|
||||
|
||||
**Fix.** A row without a revision id reuses its size only while Drive gives no id either.
|
||||
Otherwise the walk reads the size for real. If the file is still unchanged, the pass writes
|
||||
Drive's revision id into the row, so the cost is one extra read per row. Rows from before
|
||||
schema 44 have no id either, so the first pass after upgrading reads every file's size once.
|
||||
|
||||
**Test:** `a_row_without_a_revision_makes_the_walk_read_the_size_again` in `pdfs-fuse`. The
|
||||
simulation `a_revision_made_during_a_mirror_download_still_arrives` failed 3 of about 15 runs
|
||||
before the fix and passed 25 of 25 after it.
|
||||
|
||||
---
|
||||
|
||||
### B253 — The app shows the sign-in page when the keyring is locked
|
||||
|
||||
**Status:** Fixed, not verified live
|
||||
|
||||
@@ -31,6 +31,9 @@ version on Proton Drive it was synced at. Nothing is rewritten, and the upgrade
|
||||
- **The app showed the sign-in page when the system keyring was locked or not running yet**, as
|
||||
if the sign-in had been lost. It now says that the keyring could not be read, and opens on its
|
||||
own once the keyring answers (B253).
|
||||
- **A new version saved on another computer in the same second as an upload from here could
|
||||
be missed for good** when Drive had not listed the uploaded file yet. The first sync pass
|
||||
after upgrading reads each file's size from Drive once more to close this (B254).
|
||||
- **An edit in a synced folder could replace one another computer had just saved.** The newer
|
||||
version from the other computer was left only in the version history. The edit is now kept as
|
||||
a conflict copy (B249).
|
||||
|
||||
Reference in new issue
Block a user