diff options
author | Patrick Steinhardt <psteinhardt@gitlab.com> | 2021-01-06 18:04:44 +0300 |
---|---|---|
committer | Patrick Steinhardt <psteinhardt@gitlab.com> | 2021-01-07 09:53:39 +0300 |
commit | 05d856ae3190be03391e4d15e6b390ecd9321c65 (patch) | |
tree | 549daa75ad6a20cb1d5d0a6be8fa16fe36eadc49 | |
parent | f8fc985805eaf7cf53a910c47d18f1573d2f439e (diff) |
repository: Skip fetch if the object exists already
When executing `FetchSourceBranch()`, then we can quite easily optimize
away the fetch in case we know that the desired object exists in the
target repository already. There's basically two cases:
- When source and target repository are the same, then we can always
optimize away the fetch.
- When source and target repository are different repositories, we can
still optimize away the fetch in some cases as we resolve the object's
ID beforehand anyway.
While the first optimization exists already, the second doesn't. So
let's add it.
-rw-r--r-- | changelogs/unreleased/pks-fetch-source-branch-short-circuits.yml | 5 | ||||
-rw-r--r-- | internal/gitaly/service/repository/fetch.go | 47 |
2 files changed, 44 insertions, 8 deletions
diff --git a/changelogs/unreleased/pks-fetch-source-branch-short-circuits.yml b/changelogs/unreleased/pks-fetch-source-branch-short-circuits.yml new file mode 100644 index 000000000..af1d64246 --- /dev/null +++ b/changelogs/unreleased/pks-fetch-source-branch-short-circuits.yml @@ -0,0 +1,5 @@ +--- +title: 'repository: Short-circuit fetches when objects exist already' +merge_request: 2980 +author: +type: performance diff --git a/internal/gitaly/service/repository/fetch.go b/internal/gitaly/service/repository/fetch.go index 58d58a310..62aa94e02 100644 --- a/internal/gitaly/service/repository/fetch.go +++ b/internal/gitaly/service/repository/fetch.go @@ -34,17 +34,48 @@ func (s *server) FetchSourceBranch(ctx context.Context, req *gitalypb.FetchSourc return nil, helper.ErrInternal(err) } - sourceOid, err := sourceRepo.ResolveRefish(ctx, string(req.GetSourceBranch())) - if err != nil { - if errors.Is(err, git.ErrReferenceNotFound) { - return &gitalypb.FetchSourceBranchResponse{Result: false}, nil + var sourceOid string + var containsObject bool + + // If source and target repository are the same, then we know that both + // are local. We can thus optimize and locally resolve the reference + // instead of using an RPC call. We also know that we can always skip + // the fetch as the object will be available. + if helper.RepoPathEqual(req.GetRepository(), req.GetSourceRepository()) { + var err error + + sourceOid, err = targetRepo.ResolveRefish(ctx, string(req.GetSourceBranch())) + if err != nil { + if errors.Is(err, git.ErrReferenceNotFound) { + return &gitalypb.FetchSourceBranchResponse{Result: false}, nil + } + return nil, helper.ErrInternal(err) + } + + containsObject = true + } else { + var err error + + sourceOid, err = sourceRepo.ResolveRefish(ctx, string(req.GetSourceBranch())) + if err != nil { + if errors.Is(err, git.ErrReferenceNotFound) { + return &gitalypb.FetchSourceBranchResponse{Result: false}, nil + } + return nil, helper.ErrInternal(err) + } + + // Otherwise, if the source is a remote repository, we check + // whether the target repo already contains the desired object. + // If so, we can skip the fetch. + containsObject, err = targetRepo.ContainsRef(ctx, sourceOid+"^{commit}") + if err != nil { + return nil, helper.ErrInternal(err) } - return nil, helper.ErrInternal(err) } - // In case source and target repository refer to the same repository, - // then we can simply skip the fetch. - if !helper.RepoPathEqual(req.GetRepository(), req.GetSourceRepository()) { + // There's no need to perform the fetch if we already have the object + // available. + if !containsObject { env, err := gitalyssh.UploadPackEnv(ctx, &gitalypb.SSHUploadPackRequest{Repository: req.SourceRepository}) if err != nil { return nil, err |