mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-03 16:41:26 +01:00
fix(core): a file deleted after its failed create was written again leaves Drive (B191)
This commit is contained in:
1 parent
1436c5f37e
commit
57a825887c
4 files changed
+71
-3
No files matched your search
@@ -107,6 +107,10 @@ pub fn op_supersedes(kind: &str) -> bool {
|
||||
/// name and bytes (`docs/BUGS.md` B151). Such a trash keeps the create's
|
||||
/// parent and blob, which the drain looks for the node by, and which it
|
||||
/// discards once the trash is done ([`Db::complete_trash_op`]).
|
||||
///
|
||||
/// Sent is where it was sent to ([`Db::note_create_target`]), not only its
|
||||
/// attempts: a write after the failure clears those for a prompt retry, and
|
||||
/// the delete then dropped the create whole (`docs/BUGS.md` B191).
|
||||
fn drop_doomed_ops(tx: &rusqlite::Transaction<'_>, uid: &str) -> Result<Vec<String>> {
|
||||
const DOOMED: &str = "
|
||||
WITH RECURSIVE
|
||||
@@ -134,7 +138,8 @@ fn drop_doomed_ops(tx: &rusqlite::Transaction<'_>, uid: &str) -> Result<Vec<Stri
|
||||
OR (kind = 'revision'
|
||||
AND (uid IN (SELECT uid FROM below) OR lid IN (SELECT lid FROM below)))
|
||||
)";
|
||||
const SENT: &str = "kind IN ('create', 'mkdir') AND (claimed_at <> 0 OR attempts > 0)";
|
||||
const SENT: &str = "kind IN ('create', 'mkdir')
|
||||
AND (claimed_at <> 0 OR attempts > 0 OR sent_to IS NOT NULL)";
|
||||
let lid = node_lid(tx, uid)?;
|
||||
let blobs: Vec<String> = {
|
||||
let mut stmt = tx.prepare(&format!(
|
||||
|
||||
@@ -3951,6 +3951,48 @@ fn what_a_create_sent_outlives_a_newer_blob_its_trash_and_a_restart() {
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
|
||||
/// A write after a create failed clears its attempts, so the next try goes
|
||||
/// out at once. The create was still sent: deleting the file then left no
|
||||
/// trash, and the file a lost answer made stayed on Drive under the name it
|
||||
/// was sent with (`docs/BUGS.md` B191).
|
||||
#[test]
|
||||
fn a_create_rewritten_after_it_failed_still_leaves_a_trash_when_deleted() {
|
||||
let db = Db::open_in_memory().unwrap();
|
||||
let parent = uid("parent").to_string();
|
||||
let (id, _) = db
|
||||
.enqueue_op(&PendingOp {
|
||||
id: 0,
|
||||
kind: OP_CREATE.to_string(),
|
||||
uid: "local~sent".to_string(),
|
||||
parent_uid: Some(parent.clone()),
|
||||
name: Some("a.txt".to_string()),
|
||||
blob_path: Some("/staging/first".to_string()),
|
||||
meta_json: Some("{}".to_string()),
|
||||
created_at: 1,
|
||||
attempts: 0,
|
||||
last_error: None,
|
||||
next_attempt_at: 0,
|
||||
})
|
||||
.unwrap();
|
||||
db.note_create_target(id, &parent, "a.txt").unwrap();
|
||||
db.note_create_sent(id, "aaaa").unwrap();
|
||||
db.record_op_failure(id, "no answer", 0).unwrap();
|
||||
db.attach_blob_to_create("local~sent", "/staging/second", "{}")
|
||||
.unwrap()
|
||||
.expect("the create is still queued");
|
||||
|
||||
assert!(db.delete_ops_for_uid("local~sent").unwrap().is_empty());
|
||||
|
||||
let ops = db.pending_ops().unwrap();
|
||||
assert_eq!(ops.len(), 1);
|
||||
assert_eq!((ops[0].id, ops[0].kind.as_str()), (id, OP_TRASH));
|
||||
assert_eq!(ops[0].blob_path.as_deref(), Some("/staging/second"));
|
||||
assert_eq!(
|
||||
db.create_targets(id).unwrap(),
|
||||
vec![(parent.clone(), "a.txt".to_string())]
|
||||
);
|
||||
}
|
||||
|
||||
/// A create deleted after it was sent becomes a trash in its place. The
|
||||
/// create's backoff, and the failure of an attempt still on the wire, are the
|
||||
/// create's: the trash is due at once (`docs/BUGS.md` B171).
|
||||
|
||||
@@ -12,6 +12,25 @@ Conventions:
|
||||
|
||||
---
|
||||
|
||||
## B191 — A file deleted after its create failed and was written again can stay on Drive
|
||||
|
||||
**Status:** Fixed (unverified).
|
||||
**Found:** 2026-10-05, in the confirmation run on 1436c5f (`one_client_on_a_flaky_link` seed 11,
|
||||
one replay in six). A create of `a.txt` got no answer, though Drive had made the file. The file
|
||||
was written again, renamed twice and deleted. Drive kept `a.txt`, and nothing was queued.
|
||||
|
||||
**Where:** `drop_doomed_ops` in `crates/pdfs-core/src/db/ops.rs`.
|
||||
|
||||
**Cause.** A delete turns a create that was sent into a trash of what it may have made (B151).
|
||||
Whether it was sent was read from its attempts and its claim. A write after the failure attaches
|
||||
its bytes to the create and clears the attempts, so that the retry goes out at once
|
||||
(`Db::attach_blob_to_create`). The delete then took the create for one never sent, and dropped
|
||||
it whole.
|
||||
|
||||
**Fix.** A create with a target recorded (`sent_to`, B184) counts as sent as well. Test
|
||||
`a_create_rewritten_after_it_failed_still_leaves_a_trash_when_deleted` in
|
||||
`crates/pdfs-core/src/db/tests.rs`.
|
||||
|
||||
## B190 — A file saved just before the daemon stops can keep its old content on Drive
|
||||
|
||||
**Status:** Fixed (unverified).
|
||||
|
||||
@@ -9,7 +9,7 @@ Tracks `docs/MILESTONE-3.0.0.md`. Update it with every commit that moves a phase
|
||||
- **Phase 2**: local ids, inode = local id, one tree for every mount (2.10.0).
|
||||
- **Phase 3, first part**: mkdir, create, rename, unlink and rmdir on the mount are recorded
|
||||
locally and sent from the queue; `"local_first": false` brings back the old path (5e0f30c).
|
||||
- Fixes from the simulation and account runs since then: B125, B127, B130 and B141 to B190.
|
||||
- Fixes from the simulation and account runs since then: B125, B127, B130 and B141 to B191.
|
||||
- **Sync issues, first cut** (§6.2): refusals sorted into issues, shown at once in the app,
|
||||
tray and CLI; Export button and `pdfs sync export`; `pdfs sync issues`; Discard button and
|
||||
`pdfs sync discard`; a warning badge on the node in the file browser.
|
||||
@@ -116,7 +116,9 @@ Tracks `docs/MILESTONE-3.0.0.md`. Update it with every commit that moves a phase
|
||||
The run on 2baad67 found that a restart before such a file's trash went out queued
|
||||
its kept bytes again, for good (B188, fixed), and changes that sat out their retry
|
||||
after an outage only the drain had met (B189, fixed; older than 3.0.0), and a file
|
||||
saved just before a stop whose release the stop dropped (B190, fixed; older too).
|
||||
saved just before a stop whose release the stop dropped (B190, fixed; older too). The run
|
||||
on 1436c5f found a file deleted after its failed create was written again left on Drive
|
||||
(B191, fixed).
|
||||
- [ ] A clean account run. The one on a6f36d5 (2026-10-04, LAN: 427 passed, 12 failed)
|
||||
failed B113 and B121 on every on-demand mount, two mirror-to-mirror move cases, and
|
||||
the journal check. B121 was a bug (B149, fixed); B113's case still expected creates
|
||||
|
||||
Reference in new issue
Block a user