[#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:
Christian Schwarz
2020-05-10 15:06:44 +02:00
parent dce98d50da
commit 292b85b5ef
19 changed files with 513 additions and 494 deletions
+7
View File
@@ -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
View File
@@ -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) {