diff options
author | Sami Hiltunen <shiltunen@gitlab.com> | 2021-09-01 12:44:09 +0300 |
---|---|---|
committer | Sami Hiltunen <shiltunen@gitlab.com> | 2021-09-01 13:00:45 +0300 |
commit | b22ff45e1de56ee71c2b99665db5c7201e7edbbc (patch) | |
tree | cef4c68195ea48dd8301f257fa4323ba4473bd86 | |
parent | 6d586d080cbcb8583fb6ec35dc534952a737f540 (diff) |
Derive virtual storage's filesystem id from its name
Gitaly storages contain a UUID filesystem ID that is generated by
the Gitaly for each of its storages. The ID is used to determine
which storages can be accessed by Rails directly when rugged patches
are enabled and to see whether two different storages point to the same
directory when doing repository moves.
When repository moves are performed, the worker first checks whether the
repository's destination and source storage are the same. If they are, the
move is not performed. The check is performed by comparing the filesystem
IDs of the storages'. As Praefect is currently routing the server info RPC
to a random Gitaly node, the filesystem ID can differ between calls as each
of the Gitalys have their own ID. This causes the repository moving worker
to occasionally delete repositories from the virtual storage as it receives
two different IDs on sequential calls.
The filesystem ID can identify cases when two storages refer to the same
directory on a Gitaly node as the id is stored in a file in the storage.
This is not really possible with Praefect. The storage's are only identified
by the virtual storage's name. If the name changes, we can't really correlate
the ID between the different names as Praefect would consider them different
storages. Praefect also supports multiple virtual storages so it's not possible
to generate a single ID and use it for all of the virtual storages. Given this,
the approach taken here is to derive a stable filesystem ID from the virtual
storage's name. This guarantees calls to a given virtual storage always return
the same filesystem ID.
Configuring two storages that point to the same filesystem should be considered
an invalid configuration anyway. Historically, there's been cases when that has
been done for plain Gitalys. This is not done for Praefect and wouldn't work as
Praefect wouldn't find the repositories with an alternative virtual storage name.
With that in mind, we don't have to consider the case where two virtual storages
of different names point to the same backing Gitaly storages.
The use cases for the filesystem ID seem to be limited and we may be able to
remove it in the future once the rugged patches are removed.
Changelog: fixed
-rw-r--r-- | internal/praefect/server_test.go | 10 | ||||
-rw-r--r-- | internal/praefect/service/server/info.go | 13 |
2 files changed, 21 insertions, 2 deletions
diff --git a/internal/praefect/server_test.go b/internal/praefect/server_test.go index 8278946ef..fab69d330 100644 --- a/internal/praefect/server_test.go +++ b/internal/praefect/server_test.go @@ -33,6 +33,7 @@ import ( "gitlab.com/gitlab-org/gitaly/v14/internal/praefect/nodes" "gitlab.com/gitlab-org/gitaly/v14/internal/praefect/nodes/tracker" "gitlab.com/gitlab-org/gitaly/v14/internal/praefect/protoregistry" + serversvc "gitlab.com/gitlab-org/gitaly/v14/internal/praefect/service/server" "gitlab.com/gitlab-org/gitaly/v14/internal/praefect/service/transaction" "gitlab.com/gitlab-org/gitaly/v14/internal/praefect/transactions" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" @@ -158,7 +159,13 @@ func TestGitalyServerInfo(t *testing.T) { ServerVersion: version.GetVersion(), GitVersion: gitVersion.String(), StorageStatuses: []*gitalypb.ServerInfoResponse_StorageStatus{ - {StorageName: conf.VirtualStorages[0].Name, Readable: true, Writeable: true, ReplicationFactor: 2}, + { + StorageName: conf.VirtualStorages[0].Name, + FilesystemId: serversvc.DeriveFilesystemID(conf.VirtualStorages[0].Name).String(), + Readable: true, + Writeable: true, + ReplicationFactor: 2, + }, }, } @@ -167,7 +174,6 @@ func TestGitalyServerInfo(t *testing.T) { require.NoError(t, err) for _, ss := range actual.StorageStatuses { ss.FsType = "" - ss.FilesystemId = "" } require.True(t, proto.Equal(expected, actual), "expected: %v, got: %v", expected, actual) }) diff --git a/internal/praefect/service/server/info.go b/internal/praefect/service/server/info.go index 4650981b6..1094a3ba7 100644 --- a/internal/praefect/service/server/info.go +++ b/internal/praefect/service/server/info.go @@ -4,11 +4,21 @@ import ( "context" "sync" + "github.com/google/uuid" "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus" "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" "google.golang.org/grpc" ) +// filesystemIDNamespace is the UUID that is used as the namespace component when generating the UUIDv5 filesystem +// ID from a virtual storage's name. +var filesystemIDNamespace = uuid.MustParse("1ef1a8c6-cf52-4d0a-92a6-ca643e8bc7c5") + +// DeriveFilesystemID derives the virtual storage's filesystem ID from its name. +func DeriveFilesystemID(virtualStorage string) uuid.UUID { + return uuid.NewSHA1(filesystemIDNamespace, []byte(virtualStorage)) +} + // ServerInfo sends ServerInfoRequest to all of a praefect server's internal gitaly nodes and aggregates the results into // a response func (s *Server) ServerInfo(ctx context.Context, in *gitalypb.ServerInfoRequest) (*gitalypb.ServerInfoResponse, error) { @@ -67,6 +77,9 @@ func (s *Server) ServerInfo(ctx context.Context, in *gitalypb.ServerInfoRequest) for _, storageStatus := range resp.GetStorageStatuses() { if storageStatus.StorageName == storage { storageStatuses[i] = storageStatus + // Each of the Gitaly nodes have a different filesystem ID they've generated. To have a stable filesystem + // ID for a given virtual storage, the filesystem ID is generated from the virtual storage's name. + storageStatuses[i].FilesystemId = DeriveFilesystemID(virtualStorage).String() // the storage name in the response needs to be rewritten to be the virtual storage name // because the praefect client has no concept of internal gitaly nodes that are behind praefect. // From the perspective of the praefect client, the primary internal gitaly node's storage status is equivalent |