Skip to content
Snippets Groups Projects

Handle repository creations, deletions and renames atomically

Merged Sami Hiltunen requested to merge smh-repository-id-rename-repository into master
3 files
+ 80
40
Compare changes
  • Side-by-side
  • Inline
Files
3
  • 09bf43a8
    Praefect currently handles RenameRepository like every other mutator.
    This is problematic as Praefect ends up renaming repositories first on
    the disks of the Gitalys and then in the database. If only the first
    operation succeeds, Praefect has effectively lost track of the repositories.
    With Praefect now generating unique relative paths for each repository,
    there's no need to actually move the repositories on the disks anymore.
    It is sufficient to rename the repository only in the database and leave the
    replicas in their existing locations on disk. This commit intercepts
    RenameRepository in Praefect and renames repositories only in the database.
    
    Changelog: fixed
+ 50
0
package praefect
import (
"errors"
"fmt"
"gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/storage"
"gitlab.com/gitlab-org/gitaly/v14/internal/helper"
"gitlab.com/gitlab-org/gitaly/v14/internal/praefect/commonerr"
"gitlab.com/gitlab-org/gitaly/v14/internal/praefect/datastore"
"gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb"
"google.golang.org/grpc"
)
// RenameRepositoryHandler handles /gitaly.RepositoryService/RenameRepository calls by renaming
// the repository in the lookup table stored in the database.
func RenameRepositoryHandler(rs datastore.RepositoryStore) grpc.StreamHandler {
return func(srv interface{}, stream grpc.ServerStream) error {
var req gitalypb.RenameRepositoryRequest
if err := stream.RecvMsg(&req); err != nil {
return fmt.Errorf("receive request: %w", err)
}
// These checks are not strictly necessary but they exist to keep retain compatibility with
// Gitaly's tested behavior.
if req.GetRepository() == nil {
return helper.ErrInvalidArgumentf("missing repository")
} else if req.GetRelativePath() == "" {
return helper.ErrInvalidArgumentf("missing new relativePath")
} else if _, err := storage.ValidateRelativePath("/root", req.GetRelativePath()); err != nil {
return helper.ErrInvalidArgument(err)
}
if err := rs.RenameRepositoryInPlace(stream.Context(),
req.GetRepository().GetStorageName(),
req.GetRepository().GetRelativePath(),
req.GetRelativePath(),
); err != nil {
if errors.Is(err, commonerr.ErrRepositoryNotFound) {
return helper.ErrNotFound(err)
} else if errors.Is(err, commonerr.ErrRepositoryAlreadyExists) {
return helper.ErrFailedPreconditionf("destination already exists")
}
return helper.ErrInternal(err)
}
return stream.SendMsg(&gitalypb.RenameRepositoryResponse{})
}
}
Loading