bisync: make max-delete aware of tracked renames - #9812
Conversation
|
I think this needs your expert eyes @nielash :-) |
I'm on it. 👍 Apologies in advance if it takes me a few days to get to it! |
nielash
left a comment
There was a problem hiding this comment.
This is off to a good start, thank you! Some thoughts on the design:
-
The new
b.march.objects1andb.march.objects2retain anfs.Objectfor every file on both paths for the whole run. That's a significant memory cost for large runs, and I'm not sure it's necessary, given that we have a lighter option in plain sight:b.march.ls1andls2(*fileList) already store the file's size and modtime, and have a hash field we simply aren't populating by default -- all the components needed for the renames strategy analysis.objects1andobjects2exist only because the preflight needs hashes the listing doesn't carry -- the easier fix is to make the listings carry them. I'd suggest deleting the changes inmarch.goand instead, forcing a common hash into both listings incompare.go.setHashType's early-return branch already forces a common hash -- it's just a matter of making sure we end up there when--max-delete-renames-awareis in use with--track-renames-strategy hash, and reconciling conflicting flags (--ignore-listing-checksum) with appropriate ignore / error logic (seesetCompareDefaultsfor examples). Note that the preflight's strategy must be equal to or stronger than what the sync will actually use -- so when in doubt, it's safer to error or disable, not fall back to a looser strategy.Since
CountGuaranteedMatchesis not currently used bysync, we could change the function signature without changing anything insync. Currently you have:
func (m *Matcher) CountGuaranteedMatches(sources []fs.Object) int
Consider instead something like:
func CountGuaranteedMatches(strategy Strategy, modifyWindow time.Duration, srcs, dsts []Candidate) int
where Candidate is a struct like:
type Candidate struct {
Remote string
Size int64
ModTime time.Time
Hash string
}
-
An invalid
--track-renames-strategy(say,--track-renames-strategy banana) fails late, if at all. That's becausetrackrenames.ParseStrategyis called only in the event that someone exceeds the delete threshold. I'd move that check up to where your existingif opt.MaxDeleteRenamesAware && !ci.TrackRenamescheck is inBisync(). -
I like that it's gated behind
if !opt.Force, but a consequence of this is that with--force --max-delete-renames-aware, it still does all of the setup but never reads it. Under the change I'm proposing in point 1, this still matters, as we'd be storing (perhaps even calculating) lots of hashes we might never need. I'd suggest checking for--forceearly and skipping most of the--max-delete-renames-awarelogic whentrue. -
trackedRenameExemptionsForPathduplicates many of the checks from fs/sync/sync.go:240-260 (CanServerSideMove,strategy.UsesHash(), etc.), which risks the requirements diverging over time. I'd suggest refactoring these checks to one function in thefs/sync/trackrenameslibrary, which bothsyncandbisyncwill call. -
Rather than threading an
exemptionsintparameter througheffectiveDeletes/exceedsDeletes/excessDeletes, I'd suggest making it a field ondeltaSetalongsidedeletedandoldCount. That also letsexcessDeletes()keep its original zero-argument signature and shrinks thedeltas.godiff.
Housekeeping: -
cmd/bisync/rc.mdis autogenerated and should not be edited (your edits would eventually get overwritten anyway) -
The refactoring from
fs/sync/sync.gotofs/sync/trackrenames/trackrenames.goshould be its own self-contained, behavior-preserving commit with no bisync changes. The bisync changes should then be a separate commit on top of that. -
Both commits should have a commit message in the house style (see CONTRIBUTING.md)
Overall design note: as much as possible, try to keep the --track-renames logic in fs/sync/trackrenames instead of duplicating it in sync and bisync, and try to keep the bisync-specific stuff in cmd/bisync/trackrenames.go when possible -- there's a maintenance cost to having to reason with this code in lots of key parts of bisync in the future, rather than reasoning with it once now in a separate file we shouldn't have to revisit much.
Thank you!
74a3ea5 to
7548648
Compare
|
Thanks, @nielash — I reworked this around the design you suggested.
I also expanded the bisync scenario to cover both directions, invalid/conflicting options, the force path, and a partial-match case that must still abort. The focused bisync scenario, |
nielash
left a comment
There was a problem hiding this comment.
Very nice job with the revisions! It's very close now. See my comments inline.
nielash
left a comment
There was a problem hiding this comment.
Great work, thanks! I have just one inline comment here, and it's an easy fix 🙂
nielash
left a comment
There was a problem hiding this comment.
Looks good to me. Thanks again for your work on this 🙂
(The lint CI failure is an unrelated issue already fixed on master)
Move strategy parsing, capability checks, and matcher behavior into a reusable package without changing sync behavior. Add a lightweight candidate API so callers can count guaranteed matches without retaining filesystem objects.
Add an opt-in preflight that excludes only guaranteed tracked renames from the max-delete count. Reuse normal bisync listing metadata, validate strategy and flag conflicts early, and skip preflight work when --force bypasses the safety check. The max_delete_track_renames scenario uses the default hash track-renames strategy, which requires a hash common to both paths. Skip it on remote combinations without one, such as crypt, instead of failing the integration tests. Fixes rclone#8685
772eceb to
62b691d
Compare
|
I rebased this and squashed into 2 commits - will merge when CI goes green. |
What does this change do?
Adds an opt-in
--max-delete-renames-awarepreflight for Bisync. When the raw deletion count would exceed--max-delete, Bisync can now use the configured track-renames strategy to subtract only guaranteed one-sided rename matches before deciding whether to abort.The change also:
fs/sync/trackrenamesso Bisync and sync use the same size/hash/leaf/modtime semantics;--track-renamesand--max-delete-renames-awareare set;Linked issue
Fixes #8685
For new or changed backends
Not applicable; this does not change a backend.
Validation
go buildRCLONE_CONFIG=/notfound go test ./fs/sync/... ./cmd/bisync -count=1go test -race ./fs/sync/trackrenamesgo test -race ./fs/sync -run '^TestSyncWithTrackRenames'RCLONE_CONFIG=/notfound go test -race ./cmd/bisync -run '^TestBisyncRemoteRemote$' -remote local -case max_delete_track_renamescmd/mount,cmd/mount2, andcmd/serve/nfs; the local container has no/dev/fuse, and its overlay filesystem does not support the NFS name-to-handle operation. Those three packages were compile-checked withgo test -run '^$'.golangci-lint run --new-from-rev=upstream/master ./...git diff --checkChecklist