From 4b373fbd9509c9cb168aa5b18973cbdd32f96578 Mon Sep 17 00:00:00 2001 From: Christian Schwarz Date: Sat, 8 Jul 2017 13:13:16 +0200 Subject: [PATCH] zfs & replication: explicit conflict types for FilesystemDiff + handling in repl --- cmd/replication.go | 35 ++++++++++++++++++++++++---- zfs/diff.go | 58 ++++++++++++++++++++++++++++------------------ 2 files changed, 65 insertions(+), 28 deletions(-) diff --git a/cmd/replication.go b/cmd/replication.go index 37dcd04..39e2d72 100644 --- a/cmd/replication.go +++ b/cmd/replication.go @@ -321,7 +321,9 @@ func doPull(pull PullContext) (err error) { diff := zfs.MakeFilesystemDiff(versions, theirVersions) log("diff: %#v\n", diff) - if diff.IncrementalPath == nil { + switch diff.Conflict { + case zfs.ConflictAllRight: + log("performing initial sync, following policy: %#v", pull.InitialReplPolicy) if pull.InitialReplPolicy != rpc.InitialReplPolicyMostRecent { @@ -367,10 +369,14 @@ func doPull(pull PullContext) (err error) { } log("finished initial transfer") + return true - } else if len(diff.IncrementalPath) < 2 { - log("remote and local are in sync") - } else { + case zfs.ConflictIncremental: + + if len(diff.IncrementalPath) < 2 { + log("remote and local are in sync") + return true + } log("incremental transfers using path: %#v", diff.IncrementalPath) @@ -407,10 +413,29 @@ func doPull(pull PullContext) (err error) { } log("finished incremental transfer path") + return true + + case zfs.ConflictNoCommonAncestor: + + log("sender and receiver filesystem have snapshots, but no common one") + log("perform manual replication to establish a common snapshot history") + log("sender snapshot list: %#v", diff.MRCAPathRight) + log("receiver snapshot list: %#v", diff.MRCAPathLeft) + return false + + case zfs.ConflictDiverged: + + log("sender and receiver filesystem share a history but have diverged") + log("perform manual replication or delete snapshots on the receiving" + + "side to establish an incremental replication parse") + log("sender-only snapshots: %#v", diff.MRCAPathRight) + log("receiver-only snapshots: %#v", diff.MRCAPathLeft) + return false } - return true + panic("implementation error: this should not be reached") + return false }) diff --git a/zfs/diff.go b/zfs/diff.go index e0387ce..093ab6f 100644 --- a/zfs/diff.go +++ b/zfs/diff.go @@ -12,7 +12,16 @@ func (l fsbyCreateTXG) Less(i, j int) bool { return l[i].CreateTXG < l[j].CreateTXG } -/* The sender (left) wants to know if the receiver (right) has more recent versions +type Conflict int + +const ( + ConflictIncremental = 0 // no conflict, incremental repl possible + ConflictAllRight = 1 // no conflict, initial repl possible + ConflictNoCommonAncestor = 2 + ConflictDiverged = 3 +) + +/* The receiver (left) wants to know if the sender (right) has more recent versions Left : | C | Right: | A | B | C | D | E | @@ -35,27 +44,27 @@ IMPORTANT: since ZFS currently does not export dataset UUIDs, the best heuristic */ type FilesystemDiff struct { - // The increments required to get left up to right's most recent version - // 0th element is the common ancestor, ordered by birthtime, oldest first - // If len() < 2, left and right are at same most recent version - // If nil, there is no incremental path for left to get to right's most recent version - // This means either (check Diverged field to determine which case we are in) - // a) no common ancestor (left deleted all the snapshots it previously transferred to right) - // => consult MRCAPathRight and request initial retransfer after prep on left side - // b) divergence bewteen left and right (left made snapshots that right doesn't have) - // => check MRCAPathLeft and MRCAPathRight and decide what to do based on that + // Which kind of conflict / "way forward" is possible. + // Check this first to determine the semantics of this struct's remaining members + Conflict Conflict + + // Conflict = Incremental | AllRight + // The incremental steps required to get left up to right's most recent version + // 0th element is the common ancestor, ordered by birthtime, oldest first + // If len() < 2, left and right are at same most recent version + // Conflict = otherwise + // nil; there is no incremental path for left to get to right's most recent version IncrementalPath []FilesystemVersion - // true if left and right diverged, false otherwise - Diverged bool - // If Diverged, contains path from left most recent common ancestor (mrca) - // to most recent version on left - // Otherwise: nil + // Conflict = Incremental | AllRight: nil + // Conflict = NoCommonAncestor: left as passed as input + // Conflict = Diverged: contains path from left most recent common ancestor (mrca) to most + // recent version on left MRCAPathLeft []FilesystemVersion - // If Diverged, contains path from right most recent common ancestor (mrca) - // to most recent version on right - // If there is no common ancestor (i.e. not diverged), contains entire list of - // versions on right + // Conflict = Incremental | AllRight: nil + // Conflict = NoCommonAncestor: right as passed as input + // Conflict = Diverged: contains path from right most recent common ancestor (mrca) + // to most recent version on right MRCAPathRight []FilesystemVersion } @@ -66,12 +75,14 @@ func MakeFilesystemDiff(left, right []FilesystemVersion) (diff FilesystemDiff) { if right == nil { panic("right must not be nil") } - if left == nil { // treat like no common ancestor + if left == nil { diff = FilesystemDiff{ IncrementalPath: nil, - Diverged: false, + Conflict: ConflictAllRight, + MRCAPathLeft: left, MRCAPathRight: right, } + return } // Assert both left and right are sorted by createtxg @@ -108,7 +119,8 @@ outer: if mrcaLeft == -1 { diff = FilesystemDiff{ IncrementalPath: nil, - Diverged: false, + Conflict: ConflictNoCommonAncestor, + MRCAPathLeft: left, MRCAPathRight: right, } return @@ -118,7 +130,7 @@ outer: if mrcaLeft != len(left)-1 { diff = FilesystemDiff{ IncrementalPath: nil, - Diverged: true, + Conflict: ConflictDiverged, MRCAPathLeft: left[mrcaLeft:], MRCAPathRight: right[mrcaRight:], }