[#316] endpoint / replication protocol: more robust step-holds and replication cursor management
- drop HintMostRecentCommonAncestor rpc call
- it is wrong to put faith into the active side of the replication to always make that call
(we might not trust it, ref pull setup)
- clean up step holds + step bookmarks + replication cursor bookmarks on
send RPC instead
- this makes it symmetric with Receive RPC
- use a cache (endpoint.sendAbstractionsCache) to avoid the cost of
listing the on-disk endpoint abstractions state on every step
The "create" methods for endpoint abstractions (CreateReplicationCursor, HoldStep) are now fully
idempotent and return an Abstraction.
Notes about endpoint.sendAbstractionsCache:
- fills lazily from disk state on first `Get` operation
- fill from disk is generally only attempted once
- unless the `ListAbstractions` fails, in which case the fill from
disk is retried on next `Get` (the current `Get` will observe a
subset of the actual on-disk abstractions)
- the `Invalidate` method is called
- it is a global (zrepl process-wide) cache
fixes #316
This commit is contained in:
@@ -116,6 +116,13 @@ func (v FilesystemVersion) RelName() string {
|
||||
}
|
||||
func (v FilesystemVersion) String() string { return v.RelName() }
|
||||
|
||||
// Only takes into account those attributes of FilesystemVersion that
|
||||
// are immutable over time in ZFS.
|
||||
func FilesystemVersionEqualIdentity(a, b FilesystemVersion) bool {
|
||||
// .Name is mutable
|
||||
return a.Guid == b.Guid && a.CreateTXG == b.CreateTXG && a.Creation == b.Creation
|
||||
}
|
||||
|
||||
func (v FilesystemVersion) ToAbsPath(p *DatasetPath) string {
|
||||
var b bytes.Buffer
|
||||
b.WriteString(p.ToString())
|
||||
|
||||
+23
-14
@@ -1595,22 +1595,32 @@ var ErrBookmarkCloningNotSupported = fmt.Errorf("bookmark cloning feature is not
|
||||
//
|
||||
// does not destroy an existing bookmark, returns
|
||||
//
|
||||
func ZFSBookmark(ctx context.Context, fs string, v ZFSSendArgVersion, bookmark string) (err error) {
|
||||
func ZFSBookmark(ctx context.Context, fs string, v FilesystemVersion, bookmark string) (bm FilesystemVersion, err error) {
|
||||
|
||||
bm = FilesystemVersion{
|
||||
Type: Bookmark,
|
||||
Name: bookmark,
|
||||
UserRefs: OptionUint64{Valid: false},
|
||||
// bookmarks have the same createtxg, guid and creation as their origin
|
||||
CreateTXG: v.CreateTXG,
|
||||
Guid: v.Guid,
|
||||
Creation: v.Creation,
|
||||
}
|
||||
|
||||
promTimer := prometheus.NewTimer(prom.ZFSBookmarkDuration.WithLabelValues(fs))
|
||||
defer promTimer.ObserveDuration()
|
||||
|
||||
if !v.IsSnapshot() {
|
||||
return ErrBookmarkCloningNotSupported // TODO This is work in progress: https://github.com/zfsonlinux/zfs/pull/9571
|
||||
return bm, ErrBookmarkCloningNotSupported // TODO This is work in progress: https://github.com/zfsonlinux/zfs/pull/9571
|
||||
}
|
||||
|
||||
snapname := v.FullPath(fs)
|
||||
if err := EntityNamecheck(snapname, EntityTypeSnapshot); err != nil {
|
||||
return err
|
||||
return bm, err
|
||||
}
|
||||
bookmarkname := fmt.Sprintf("%s#%s", fs, bookmark)
|
||||
if err := EntityNamecheck(bookmarkname, EntityTypeBookmark); err != nil {
|
||||
return err
|
||||
return bm, err
|
||||
}
|
||||
|
||||
debug("bookmark: %q %q", snapname, bookmarkname)
|
||||
@@ -1619,27 +1629,27 @@ func ZFSBookmark(ctx context.Context, fs string, v ZFSSendArgVersion, bookmark s
|
||||
stdio, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if ddne := tryDatasetDoesNotExist(snapname, stdio); ddne != nil {
|
||||
return ddne
|
||||
return bm, ddne
|
||||
} else if zfsBookmarkExistsRegex.Match(stdio) {
|
||||
|
||||
// check if this was idempotent
|
||||
bookGuid, err := ZFSGetGUID(ctx, fs, "#"+bookmark)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "bookmark idempotency check") // guid error expressive enough
|
||||
return bm, errors.Wrap(err, "bookmark idempotency check") // guid error expressive enough
|
||||
}
|
||||
|
||||
if v.GUID == bookGuid {
|
||||
debug("bookmark: %q %q was idempotent: {snap,book}guid %d == %d", snapname, bookmarkname, v.GUID, bookGuid)
|
||||
return nil
|
||||
if v.Guid == bookGuid {
|
||||
debug("bookmark: %q %q was idempotent: {snap,book}guid %d == %d", snapname, bookmarkname, v.Guid, bookGuid)
|
||||
return bm, nil
|
||||
}
|
||||
return &BookmarkExists{
|
||||
fs: fs, bookmarkOrigin: v, bookmark: bookmark,
|
||||
return bm, &BookmarkExists{
|
||||
fs: fs, bookmarkOrigin: v.ToSendArgVersion(), bookmark: bookmark,
|
||||
zfsMsg: string(stdio),
|
||||
bookGuid: bookGuid,
|
||||
}
|
||||
|
||||
} else {
|
||||
return &ZFSError{
|
||||
return bm, &ZFSError{
|
||||
Stderr: stdio,
|
||||
WaitErr: err,
|
||||
}
|
||||
@@ -1647,8 +1657,7 @@ func ZFSBookmark(ctx context.Context, fs string, v ZFSSendArgVersion, bookmark s
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
return bm, nil
|
||||
}
|
||||
|
||||
func ZFSRollback(ctx context.Context, fs *DatasetPath, snapshot FilesystemVersion, rollbackArgs ...string) (err error) {
|
||||
|
||||
Reference in New Issue
Block a user