fix handling of tenative cursor presence if protection strategy doesn't use it (#714)
Before this PR, we would panic in the `check` phase of `endpoint.Send()`'s `TryBatchDestroy` call in the following cases: the current protection strategy does NOT produce a tentative replication cursor AND * `FromVersion` is a tentative cursor bookmark * `FromVersion` is a snapshot, and there exists a tentative cursor bookmark for that snapshot * `FromVersion` is a bookmark != tentative cursor bookmark, but there exists a tentative cursor bookmark for the same snapshot as the `FromVersion` bookmark In those cases, the `check` concluded that we would delete `FromVersion`. It came to that conclusion because the tentative cursor isn't part of `obsoleteAbs` if the protection strategy doesn't produce a tentative replication cursor. The scenarios above can happen if the user changes the protection strategy from "with tentative cursor" to one "without tentative replication cursor", while there is a tentative replication cursor on disk. The workaround was to rename the tentative cursor. In all cases above, `TryBatchDestroy` would have destroyed the tentative cursor. In case 1, that would fail the `Send` step and potentially break replication if the cursor is the last common bookmark. The `check` conclusion was correct. In cases 2 and 3, deleting the tentative cursor would have been fine because `FromVersion` was a different entity than the tentative cursor. So, destroying the tentative cursor would be the right call. The solution in this PR is as follows: * add the `FromVersion` to the `liveAbs` set of live abstractions * rewrite the `check` closure to use the full dataset path (`fullpath`) to identify the concrete ZFS object instead of the `zfs.FilesystemVersionEqualIdentity`, which is only identified by matching GUID. * Holds have no dataset path and are not the `FromVersion` in any case, so disregard them. fixes #666
This commit is contained in:
committed by
GitHub
parent
bc5e1ede04
commit
bbdc6f5465
@@ -31,7 +31,7 @@ const (
|
||||
AbstractionReplicationCursorBookmarkV2 AbstractionType = "replication-cursor-bookmark-v2"
|
||||
)
|
||||
|
||||
var AbstractionTypesAll = map[AbstractionType]bool{
|
||||
var AbstractionTypesAll = AbstractionTypeSet{
|
||||
AbstractionStepHold: true,
|
||||
AbstractionLastReceivedHold: true,
|
||||
AbstractionTentativeReplicationCursorBookmark: true,
|
||||
@@ -181,6 +181,38 @@ func (s AbstractionTypeSet) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Use the `BookmarkExtractor()` method of each abstraction type in this set
|
||||
// to try extract an abstraction from the given FilesystemVersion.
|
||||
//
|
||||
// Abstraction types in this set that don't have a bookmark extractor are skipped.
|
||||
//
|
||||
// Panics if more than one abstraction type matches.
|
||||
func (s AbstractionTypeSet) ExtractBookmark(dp *zfs.DatasetPath, v *zfs.FilesystemVersion) Abstraction {
|
||||
matched := make(AbstractionTypeSet, 1)
|
||||
var matchedAbs Abstraction
|
||||
for absType := range s {
|
||||
extractor := absType.BookmarkExtractor()
|
||||
if extractor == nil {
|
||||
continue
|
||||
}
|
||||
abstraction := extractor(dp, *v)
|
||||
if abstraction != nil {
|
||||
matched[absType] = true
|
||||
matchedAbs = abstraction
|
||||
}
|
||||
}
|
||||
if len(matched) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(matched) == 1 {
|
||||
if matchedAbs == nil {
|
||||
panic("loop above should always set matchedAbs if there is a match")
|
||||
}
|
||||
return matchedAbs
|
||||
}
|
||||
panic(fmt.Sprintf("abstraction types extractors should not overlap: %s", matched))
|
||||
}
|
||||
|
||||
type BookmarkExtractor func(fs *zfs.DatasetPath, v zfs.FilesystemVersion) Abstraction
|
||||
|
||||
// returns nil if the abstraction type is not bookmark-based
|
||||
@@ -238,6 +270,23 @@ func (t AbstractionType) BookmarkNamer() func(fs string, guid uint64, jobId JobI
|
||||
}
|
||||
}
|
||||
|
||||
func (t AbstractionType) IsSnapshotOrBookmark() bool {
|
||||
switch t {
|
||||
case AbstractionTentativeReplicationCursorBookmark:
|
||||
return true
|
||||
case AbstractionReplicationCursorBookmarkV1:
|
||||
return true
|
||||
case AbstractionReplicationCursorBookmarkV2:
|
||||
return true
|
||||
case AbstractionStepHold:
|
||||
return false
|
||||
case AbstractionLastReceivedHold:
|
||||
return false
|
||||
default:
|
||||
panic(fmt.Sprintf("unimpl: %q", t))
|
||||
}
|
||||
}
|
||||
|
||||
type ListZFSHoldsAndBookmarksQuery struct {
|
||||
FS ListZFSHoldsAndBookmarksQueryFilesystemFilter
|
||||
// What abstraction types should match (any contained in the set)
|
||||
|
||||
Reference in New Issue
Block a user