Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZeger-Jan van de Weg <git@zjvandeweg.nl>2021-07-14 12:08:04 +0300
committerZeger-Jan van de Weg <git@zjvandeweg.nl>2021-07-14 12:08:04 +0300
commit47164700a1ea086c5e8ca0d02feefe4e68bf4f81 (patch)
tree3308274c2c475ca642524bc8f16a653b478dda46
parent40511f7a14ded77c826809d054d740a66e1c106f (diff)
parente900df0947d188194f319b8afb9aa4fe857d357d (diff)
Merge branch 'smh-dataloss-lazy-failovers' into 'master'
Support lazy failovers in `praefect dataloss` See merge request gitlab-org/gitaly!3549
-rw-r--r--cmd/praefect/subcmd_dataloss.go56
-rw-r--r--cmd/praefect/subcmd_dataloss_test.go253
-rw-r--r--internal/praefect/datastore/repository_store.go75
-rw-r--r--internal/praefect/datastore/repository_store_mock.go31
-rw-r--r--internal/praefect/datastore/repository_store_test.go401
-rw-r--r--internal/praefect/service/info/dataloss.go26
-rw-r--r--proto/go/gitalypb/praefect.pb.go232
-rw-r--r--proto/go/gitalypb/praefect_grpc.pb.go4
-rw-r--r--proto/praefect.proto16
-rw-r--r--ruby/proto/gitaly/praefect_pb.rb4
-rw-r--r--ruby/proto/gitaly/praefect_services_pb.rb2
11 files changed, 586 insertions, 514 deletions
diff --git a/cmd/praefect/subcmd_dataloss.go b/cmd/praefect/subcmd_dataloss.go
index 037edc886..06b493d71 100644
--- a/cmd/praefect/subcmd_dataloss.go
+++ b/cmd/praefect/subcmd_dataloss.go
@@ -21,9 +21,9 @@ func (err unexpectedPositionalArgsError) Error() string {
}
type datalossSubcommand struct {
- output io.Writer
- virtualStorage string
- includePartiallyReplicated bool
+ output io.Writer
+ virtualStorage string
+ includePartiallyAvailable bool
}
func newDatalossSubcommand() *datalossSubcommand {
@@ -33,12 +33,10 @@ func newDatalossSubcommand() *datalossSubcommand {
func (cmd *datalossSubcommand) FlagSet() *flag.FlagSet {
fs := flag.NewFlagSet("dataloss", flag.ContinueOnError)
fs.StringVar(&cmd.virtualStorage, "virtual-storage", "", "virtual storage to check for data loss")
- fs.BoolVar(&cmd.includePartiallyReplicated, "partially-replicated", false, strings.TrimSpace(`
-Additionally include repositories which are fully up to date on the
-primary but outdated on some secondaries. Such repositories are writable
-and do not suffer from data loss. The data on the primary is not fully
-replicated to all secondaries which leads to increased risk of data loss
-following a failover.`))
+ fs.BoolVar(&cmd.includePartiallyAvailable, "partially-unavailable", false, strings.TrimSpace(`
+Additionally include repositories which are available but some assigned replicas
+are unavailable. Such repositories are available but are not fully replicated. This
+increases the change of data loss on primary failure`))
return fs
}
@@ -82,7 +80,7 @@ func (cmd *datalossSubcommand) Exec(flags *flag.FlagSet, cfg config.Config) erro
for _, vs := range virtualStorages {
resp, err := client.DatalossCheck(context.Background(), &gitalypb.DatalossCheckRequest{
VirtualStorage: vs,
- IncludePartiallyReplicated: cmd.includePartiallyReplicated,
+ IncludePartiallyReplicated: cmd.includePartiallyAvailable,
})
if err != nil {
return fmt.Errorf("error checking: %v", err)
@@ -90,23 +88,23 @@ func (cmd *datalossSubcommand) Exec(flags *flag.FlagSet, cfg config.Config) erro
cmd.println(0, "Virtual storage: %s", vs)
if len(resp.Repositories) == 0 {
- msg := "All repositories are writable!"
- if cmd.includePartiallyReplicated {
- msg = "All repositories are up to date!"
+ msg := "All repositories are available!"
+ if cmd.includePartiallyAvailable {
+ msg = "All repositories are fully available on all assigned storages!"
}
cmd.println(1, msg)
continue
}
- cmd.println(1, "Outdated repositories:")
+ cmd.println(1, "Repositories:")
for _, repo := range resp.Repositories {
- mode := "writable"
- if repo.ReadOnly {
- mode = "read-only"
+ unavailable := ""
+ if repo.Unavailable {
+ unavailable = " (unavailable)"
}
- cmd.println(2, "%s (%s):", repo.RelativePath, mode)
+ cmd.println(2, "%s%s:", repo.RelativePath, unavailable)
primary := repo.Primary
if primary == "" {
@@ -120,7 +118,11 @@ func (cmd *datalossSubcommand) Exec(flags *flag.FlagSet, cfg config.Config) erro
continue
}
- cmd.println(4, "%s%s", storage.Name, assignedMessage(storage.Assigned))
+ cmd.println(4, "%s%s%s",
+ storage.Name,
+ assignedMessage(storage.Assigned),
+ unhealthyMessage(storage.Healthy),
+ )
}
cmd.println(3, "Outdated Storages:")
@@ -134,7 +136,13 @@ func (cmd *datalossSubcommand) Exec(flags *flag.FlagSet, cfg config.Config) erro
plural = "s"
}
- cmd.println(4, "%s is behind by %d change%s or less%s", storage.Name, storage.BehindBy, plural, assignedMessage(storage.Assigned))
+ cmd.println(4, "%s is behind by %d change%s or less%s%s",
+ storage.Name,
+ storage.BehindBy,
+ plural,
+ assignedMessage(storage.Assigned),
+ unhealthyMessage(storage.Healthy),
+ )
}
}
}
@@ -142,6 +150,14 @@ func (cmd *datalossSubcommand) Exec(flags *flag.FlagSet, cfg config.Config) erro
return nil
}
+func unhealthyMessage(healthy bool) string {
+ if healthy {
+ return ""
+ }
+
+ return ", unhealthy"
+}
+
func assignedMessage(assigned bool) string {
assignedMsg := ""
if assigned {
diff --git a/cmd/praefect/subcmd_dataloss_test.go b/cmd/praefect/subcmd_dataloss_test.go
index e52cf0f42..bfe7c03b3 100644
--- a/cmd/praefect/subcmd_dataloss_test.go
+++ b/cmd/praefect/subcmd_dataloss_test.go
@@ -4,7 +4,6 @@ package main
import (
"bytes"
- "fmt"
"testing"
"github.com/stretchr/testify/require"
@@ -28,63 +27,45 @@ func registerPraefectInfoServer(impl gitalypb.PraefectInfoServiceServer) svcRegi
}
func TestDatalossSubcommand(t *testing.T) {
- for _, scope := range []struct {
- desc string
- electionStrategy config.ElectionStrategy
- primaries map[string]string
- }{
- {
- desc: "sql elector",
- electionStrategy: config.ElectionStrategySQL,
- primaries: map[string]string{
- "repository-1": "gitaly-1",
- "repository-2": "gitaly-1",
+ cfg := config.Config{
+ VirtualStorages: []*config.VirtualStorage{
+ {
+ Name: "virtual-storage-1",
+ Nodes: []*config.Node{
+ {Storage: "gitaly-1"},
+ {Storage: "gitaly-2"},
+ {Storage: "gitaly-3"},
+ },
},
- },
- {
- desc: "per_repository elector",
- electionStrategy: config.ElectionStrategyPerRepository,
- primaries: map[string]string{
- "repository-1": "gitaly-1",
- "repository-2": "gitaly-3",
+ {
+ Name: "virtual-storage-2",
+ Nodes: []*config.Node{
+ {Storage: "gitaly-4"},
+ },
},
},
- } {
- t.Run(scope.desc, func(t *testing.T) {
- cfg := config.Config{
- Failover: config.Failover{ElectionStrategy: scope.electionStrategy},
- VirtualStorages: []*config.VirtualStorage{
- {
- Name: "virtual-storage-1",
- Nodes: []*config.Node{
- {Storage: "gitaly-1"},
- {Storage: "gitaly-2"},
- {Storage: "gitaly-3"},
- },
- },
- {
- Name: "virtual-storage-2",
- Nodes: []*config.Node{
- {Storage: "gitaly-4"},
- },
- },
- },
- }
+ }
+
+ tx, err := getDB(t).Begin()
+ require.NoError(t, err)
+ defer tx.Rollback()
- db := getDB(t)
- gs := datastore.NewPostgresRepositoryStore(db, cfg.StorageNames())
+ ctx, cancel := testhelper.Context()
+ defer cancel()
- ctx, cancel := testhelper.Context()
- defer cancel()
+ testhelper.SetHealthyNodes(t, ctx, tx, map[string]map[string][]string{"praefect-0": {
+ "virtual-storage-1": {"gitaly-1", "gitaly-3"},
+ }})
+ gs := datastore.NewPostgresRepositoryStore(tx, cfg.StorageNames())
- for _, q := range []string{
- `
+ for _, q := range []string{
+ `
INSERT INTO repositories (virtual_storage, relative_path, "primary")
VALUES
('virtual-storage-1', 'repository-1', 'gitaly-1'),
('virtual-storage-1', 'repository-2', 'gitaly-3')
`,
- `
+ `
INSERT INTO repository_assignments (virtual_storage, relative_path, storage)
VALUES
('virtual-storage-1', 'repository-1', 'gitaly-1'),
@@ -92,128 +73,122 @@ func TestDatalossSubcommand(t *testing.T) {
('virtual-storage-1', 'repository-2', 'gitaly-1'),
('virtual-storage-1', 'repository-2', 'gitaly-3')
`,
- `
- INSERT INTO shard_primaries (shard_name, node_name, elected_by_praefect, elected_at)
- VALUES ('virtual-storage-1', 'gitaly-1', 'ignored', now())
- `,
- } {
- _, err := db.ExecContext(ctx, q)
- require.NoError(t, err)
- }
+ } {
+ _, err := tx.ExecContext(ctx, q)
+ require.NoError(t, err)
+ }
- require.NoError(t, gs.SetGeneration(ctx, "virtual-storage-1", "repository-1", "gitaly-1", 1))
- require.NoError(t, gs.SetGeneration(ctx, "virtual-storage-1", "repository-1", "gitaly-2", 0))
- require.NoError(t, gs.SetGeneration(ctx, "virtual-storage-1", "repository-1", "gitaly-3", 0))
+ require.NoError(t, gs.SetGeneration(ctx, "virtual-storage-1", "repository-1", "gitaly-1", 1))
+ require.NoError(t, gs.SetGeneration(ctx, "virtual-storage-1", "repository-1", "gitaly-2", 0))
+ require.NoError(t, gs.SetGeneration(ctx, "virtual-storage-1", "repository-1", "gitaly-3", 0))
- require.NoError(t, gs.SetGeneration(ctx, "virtual-storage-1", "repository-2", "gitaly-2", 1))
- require.NoError(t, gs.SetGeneration(ctx, "virtual-storage-1", "repository-2", "gitaly-3", 0))
+ require.NoError(t, gs.SetGeneration(ctx, "virtual-storage-1", "repository-2", "gitaly-2", 1))
+ require.NoError(t, gs.SetGeneration(ctx, "virtual-storage-1", "repository-2", "gitaly-3", 0))
- ln, clean := listenAndServe(t, []svcRegistrar{
- registerPraefectInfoServer(info.NewServer(nil, cfg, nil, gs, nil, nil, nil))})
- defer clean()
- for _, tc := range []struct {
- desc string
- args []string
- virtualStorages []*config.VirtualStorage
- output string
- error error
- }{
- {
- desc: "positional arguments",
- args: []string{"-virtual-storage=virtual-storage-1", "positional-arg"},
- error: unexpectedPositionalArgsError{Command: "dataloss"},
- },
- {
- desc: "data loss with read-only repositories",
- args: []string{"-virtual-storage=virtual-storage-1"},
- output: fmt.Sprintf(`Virtual storage: virtual-storage-1
- Outdated repositories:
- repository-2 (read-only):
- Primary: %s
+ ln, clean := listenAndServe(t, []svcRegistrar{
+ registerPraefectInfoServer(info.NewServer(nil, cfg, nil, gs, nil, nil, nil))})
+ defer clean()
+ for _, tc := range []struct {
+ desc string
+ args []string
+ virtualStorages []*config.VirtualStorage
+ output string
+ error error
+ }{
+ {
+ desc: "positional arguments",
+ args: []string{"-virtual-storage=virtual-storage-1", "positional-arg"},
+ error: unexpectedPositionalArgsError{Command: "dataloss"},
+ },
+ {
+ desc: "data loss with unavailable repositories",
+ args: []string{"-virtual-storage=virtual-storage-1"},
+ output: `Virtual storage: virtual-storage-1
+ Repositories:
+ repository-2 (unavailable):
+ Primary: gitaly-3
In-Sync Storages:
- gitaly-2
+ gitaly-2, unhealthy
Outdated Storages:
gitaly-1 is behind by 2 changes or less, assigned host
gitaly-3 is behind by 1 change or less, assigned host
-`, scope.primaries["repository-2"]),
- },
- {
- desc: "data loss with partially replicated repositories",
- args: []string{"-virtual-storage=virtual-storage-1", "-partially-replicated"},
- output: fmt.Sprintf(`Virtual storage: virtual-storage-1
- Outdated repositories:
- repository-1 (writable):
- Primary: %s
+`,
+ },
+ {
+ desc: "data loss with partially unavailable repositories",
+ args: []string{"-virtual-storage=virtual-storage-1", "-partially-unavailable"},
+ output: `Virtual storage: virtual-storage-1
+ Repositories:
+ repository-1:
+ Primary: gitaly-1
In-Sync Storages:
gitaly-1, assigned host
Outdated Storages:
- gitaly-2 is behind by 1 change or less, assigned host
+ gitaly-2 is behind by 1 change or less, assigned host, unhealthy
gitaly-3 is behind by 1 change or less
- repository-2 (read-only):
- Primary: %s
+ repository-2 (unavailable):
+ Primary: gitaly-3
In-Sync Storages:
- gitaly-2
+ gitaly-2, unhealthy
Outdated Storages:
gitaly-1 is behind by 2 changes or less, assigned host
gitaly-3 is behind by 1 change or less, assigned host
-`, scope.primaries["repository-1"], scope.primaries["repository-2"]),
- },
- {
- desc: "multiple virtual storages with read-only repositories",
- virtualStorages: []*config.VirtualStorage{{Name: "virtual-storage-2"}, {Name: "virtual-storage-1"}},
- output: fmt.Sprintf(`Virtual storage: virtual-storage-1
- Outdated repositories:
- repository-2 (read-only):
- Primary: %s
+`,
+ },
+ {
+ desc: "multiple virtual storages with unavailable repositories",
+ virtualStorages: []*config.VirtualStorage{{Name: "virtual-storage-2"}, {Name: "virtual-storage-1"}},
+ output: `Virtual storage: virtual-storage-1
+ Repositories:
+ repository-2 (unavailable):
+ Primary: gitaly-3
In-Sync Storages:
- gitaly-2
+ gitaly-2, unhealthy
Outdated Storages:
gitaly-1 is behind by 2 changes or less, assigned host
gitaly-3 is behind by 1 change or less, assigned host
Virtual storage: virtual-storage-2
- All repositories are writable!
-`, scope.primaries["repository-2"]),
- },
- {
- desc: "multiple virtual storages with partially replicated repositories",
- args: []string{"-partially-replicated"},
- virtualStorages: []*config.VirtualStorage{{Name: "virtual-storage-2"}, {Name: "virtual-storage-1"}},
- output: fmt.Sprintf(`Virtual storage: virtual-storage-1
- Outdated repositories:
- repository-1 (writable):
- Primary: %s
+ All repositories are available!
+`,
+ },
+ {
+ desc: "multiple virtual storages with partially unavailable repositories",
+ args: []string{"-partially-unavailable"},
+ virtualStorages: []*config.VirtualStorage{{Name: "virtual-storage-2"}, {Name: "virtual-storage-1"}},
+ output: `Virtual storage: virtual-storage-1
+ Repositories:
+ repository-1:
+ Primary: gitaly-1
In-Sync Storages:
gitaly-1, assigned host
Outdated Storages:
- gitaly-2 is behind by 1 change or less, assigned host
+ gitaly-2 is behind by 1 change or less, assigned host, unhealthy
gitaly-3 is behind by 1 change or less
- repository-2 (read-only):
- Primary: %s
+ repository-2 (unavailable):
+ Primary: gitaly-3
In-Sync Storages:
- gitaly-2
+ gitaly-2, unhealthy
Outdated Storages:
gitaly-1 is behind by 2 changes or less, assigned host
gitaly-3 is behind by 1 change or less, assigned host
Virtual storage: virtual-storage-2
- All repositories are up to date!
-`, scope.primaries["repository-1"], scope.primaries["repository-2"]),
- },
- } {
- t.Run(tc.desc, func(t *testing.T) {
- cmd := newDatalossSubcommand()
- output := &bytes.Buffer{}
- cmd.output = output
+ All repositories are fully available on all assigned storages!
+`,
+ },
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ cmd := newDatalossSubcommand()
+ output := &bytes.Buffer{}
+ cmd.output = output
- fs := cmd.FlagSet()
- require.NoError(t, fs.Parse(tc.args))
- err := cmd.Exec(fs, config.Config{
- VirtualStorages: tc.virtualStorages,
- SocketPath: ln.Addr().String(),
- })
- require.Equal(t, tc.error, err, err)
- require.Equal(t, tc.output, output.String())
- })
- }
+ fs := cmd.FlagSet()
+ require.NoError(t, fs.Parse(tc.args))
+ err := cmd.Exec(fs, config.Config{
+ VirtualStorages: tc.virtualStorages,
+ SocketPath: ln.Addr().String(),
+ })
+ require.Equal(t, tc.error, err, err)
+ require.Equal(t, tc.output, output.String())
})
}
}
diff --git a/internal/praefect/datastore/repository_store.go b/internal/praefect/datastore/repository_store.go
index 7a1702b6d..278f4e3ea 100644
--- a/internal/praefect/datastore/repository_store.go
+++ b/internal/praefect/datastore/repository_store.go
@@ -116,10 +116,9 @@ type RepositoryStore interface {
ConsistentStoragesGetter
// RepositoryExists returns whether the repository exists on a virtual storage.
RepositoryExists(ctx context.Context, virtualStorage, relativePath string) (bool, error)
- // GetPartiallyReplicatedRepositories returns information on repositories which have an outdated copy on an assigned storage.
- // By default, repository specific primaries are returned in the results. If useVirtualStoragePrimaries is set, virtual storage's
- // primary is returned instead for each repository.
- GetPartiallyReplicatedRepositories(ctx context.Context, virtualStorage string, virtualStoragePrimaries bool) ([]OutdatedRepository, error)
+ // GetPartiallyAvailableRepositories returns information on repositories which have assigned replicas which
+ // are not able to serve requests at the moment.
+ GetPartiallyAvailableRepositories(ctx context.Context, virtualStorage string) ([]PartiallyAvailableRepository, error)
// DeleteInvalidRepository is a method for deleting records of invalid repositories. It deletes a storage's
// record of the invalid repository. If the storage was the only storage with the repository, the repository's
// record on the virtual storage is also deleted.
@@ -519,36 +518,45 @@ AND NOT EXISTS (
return err
}
-// OutdatedRepositoryStorageDetails represents a storage that contains or should contain a
+// StorageDetails represents a storage that contains or should contain a
// copy of the repository.
-type OutdatedRepositoryStorageDetails struct {
+type StorageDetails struct {
// Name of the storage as configured.
Name string
// BehindBy indicates how many generations the storage's copy of the repository is missing at maximum.
BehindBy int
// Assigned indicates whether the storage is an assigned host of the repository.
Assigned bool
+ // Healthy indicates whether the replica is considered healthy by the consensus of Praefect nodes.
+ Healthy bool
+ // ValidPrimary indicates whether the replica is ready to serve as the primary if necessary.
+ ValidPrimary bool
}
-// OutdatedRepository is a repository with one or more outdated assigned storages.
-type OutdatedRepository struct {
+// PartiallyAvailableRepository is a repository with one or more assigned replicas which are not
+// able to serve requests at the moment.
+type PartiallyAvailableRepository struct {
// RelativePath is the relative path of the repository.
RelativePath string
// Primary is the current primary of this repository.
Primary string
// Storages contains information of the repository on each storage that contains the repository
// or does not contain the repository but is assigned to host it.
- Storages []OutdatedRepositoryStorageDetails
+ Storages []StorageDetails
}
-func (rs *PostgresRepositoryStore) GetPartiallyReplicatedRepositories(ctx context.Context, virtualStorage string, useVirtualStoragePrimaries bool) ([]OutdatedRepository, error) {
+// GetPartiallyAvailableRepositories returns information on repositories which have assigned replicas which
+// are not able to serve requests at the moment.
+func (rs *PostgresRepositoryStore) GetPartiallyAvailableRepositories(ctx context.Context, virtualStorage string) ([]PartiallyAvailableRepository, error) {
configuredStorages, ok := rs.storages[virtualStorage]
if !ok {
return nil, fmt.Errorf("unknown virtual storage: %q", virtualStorage)
}
- // The query below gets the generations and assignments of every repository
- // which has one or more outdated assigned nodes. It works as follows:
+ // The query below gets the status of every repository which has one or more assigned replicas that
+ // are not able to serve requests at the moment. The status includes how many changes a replica is behind,
+ // whether the replica is assigned host or not, whether the replica is healthy and whether the replica is
+ // considered a valid primary candidate. It works as follows:
//
// 1. First we get all the storages which contain the repository from `storage_repositories`. We
// list every copy of the repository as the latest generation could exist on an unassigned
@@ -569,13 +577,17 @@ func (rs *PostgresRepositoryStore) GetPartiallyReplicatedRepositories(ctx contex
// and there can't be any assignments for deleted repositories, this is still needed as long as the
// fallback behavior of no assignments is in place.
//
- // 4. Finally we aggregate each repository's information in to a single row with a JSON object containing
+ // 4. We join the `healthy_storages` view to return the storages current health.
+ //
+ // 5. We join the `valid_primaries` view to return whether the storage is ready to act as a primary in case
+ // of a failover.
+ //
+ // 6. Finally we aggregate each repository's information in to a single row with a JSON object containing
// the information. This allows us to group the output already in the query and makes scanning easier
- // We filter out groups which do not have an outdated assigned storage as the replication factor on those
+ // We filter out groups which do not have an assigned storage as the replication factor on those
// is reached. Status of unassigned storages does not matter as long as they don't contain a later generation
// than the assigned ones.
//
- // If virtual storage scoped primaries are used, the primary is instead selected from the `shard_primaries` table.
rows, err := rs.db.QueryContext(ctx, `
SELECT
json_build_object (
@@ -585,20 +597,21 @@ SELECT
json_build_object(
'Name', storage,
'BehindBy', behind_by,
- 'Assigned', assigned
+ 'Assigned', assigned,
+ 'Healthy', healthy,
+ 'ValidPrimary', valid_primary
)
)
)
FROM (
SELECT
relative_path,
- CASE WHEN $3
- THEN shard_primaries.node_name
- ELSE repositories."primary"
- END AS "primary",
+ repositories.primary,
storage,
- max(storage_repositories.generation) OVER (PARTITION BY virtual_storage, relative_path) - COALESCE(storage_repositories.generation, -1) AS behind_by,
- repository_assignments.storage IS NOT NULL AS assigned
+ repository_generations.generation - COALESCE(storage_repositories.generation, -1) AS behind_by,
+ repository_assignments.storage IS NOT NULL AS assigned,
+ healthy_storages.storage IS NOT NULL AS healthy,
+ valid_primaries.storage IS NOT NULL AS valid_primary
FROM storage_repositories
FULL JOIN (
SELECT virtual_storage, relative_path, storage
@@ -613,33 +626,35 @@ FROM (
)
) AS repository_assignments USING (virtual_storage, relative_path, storage)
JOIN repositories USING (virtual_storage, relative_path)
- LEFT JOIN shard_primaries ON $3 AND shard_name = virtual_storage AND NOT demoted
+ JOIN repository_generations USING (virtual_storage, relative_path)
+ LEFT JOIN healthy_storages USING (virtual_storage, storage)
+ LEFT JOIN valid_primaries USING (virtual_storage, relative_path, storage)
WHERE virtual_storage = $1
ORDER BY relative_path, "primary", storage
) AS outdated_repositories
GROUP BY relative_path, "primary"
-HAVING max(behind_by) FILTER(WHERE assigned) > 0
+HAVING bool_or(NOT valid_primary) FILTER(WHERE assigned)
ORDER BY relative_path, "primary"
- `, virtualStorage, pq.StringArray(configuredStorages), useVirtualStoragePrimaries)
+ `, virtualStorage, pq.StringArray(configuredStorages))
if err != nil {
return nil, fmt.Errorf("query: %w", err)
}
defer rows.Close()
- var outdatedRepos []OutdatedRepository
+ var repos []PartiallyAvailableRepository
for rows.Next() {
var repositoryJSON string
if err := rows.Scan(&repositoryJSON); err != nil {
return nil, fmt.Errorf("scan: %w", err)
}
- var outdatedRepo OutdatedRepository
- if err := json.NewDecoder(strings.NewReader(repositoryJSON)).Decode(&outdatedRepo); err != nil {
+ var repo PartiallyAvailableRepository
+ if err := json.NewDecoder(strings.NewReader(repositoryJSON)).Decode(&repo); err != nil {
return nil, fmt.Errorf("decode json: %w", err)
}
- outdatedRepos = append(outdatedRepos, outdatedRepo)
+ repos = append(repos, repo)
}
- return outdatedRepos, rows.Err()
+ return repos, rows.Err()
}
diff --git a/internal/praefect/datastore/repository_store_mock.go b/internal/praefect/datastore/repository_store_mock.go
index 3c5b5f81c..876dcec73 100644
--- a/internal/praefect/datastore/repository_store_mock.go
+++ b/internal/praefect/datastore/repository_store_mock.go
@@ -5,18 +5,18 @@ import "context"
// MockRepositoryStore allows for mocking a RepositoryStore by parametrizing its behavior. All methods
// default to what could be considered success if not set.
type MockRepositoryStore struct {
- GetGenerationFunc func(ctx context.Context, virtualStorage, relativePath, storage string) (int, error)
- IncrementGenerationFunc func(ctx context.Context, virtualStorage, relativePath, primary string, secondaries []string) error
- GetReplicatedGenerationFunc func(ctx context.Context, virtualStorage, relativePath, source, target string) (int, error)
- SetGenerationFunc func(ctx context.Context, virtualStorage, relativePath, storage string, generation int) error
- CreateRepositoryFunc func(ctx context.Context, virtualStorage, relativePath, primary string, updatedSecondaries, outdatedSecondaries []string, storePrimary, storeAssignments bool) error
- DeleteRepositoryFunc func(ctx context.Context, virtualStorage, relativePath string, storages []string) error
- DeleteReplicaFunc func(ctx context.Context, virtualStorage, relativePath, storage string) error
- RenameRepositoryFunc func(ctx context.Context, virtualStorage, relativePath, storage, newRelativePath string) error
- GetConsistentStoragesFunc func(ctx context.Context, virtualStorage, relativePath string) (map[string]struct{}, error)
- GetPartiallyReplicatedRepositoriesFunc func(ctx context.Context, virtualStorage string, virtualStorageScopedPrimaries bool) ([]OutdatedRepository, error)
- DeleteInvalidRepositoryFunc func(ctx context.Context, virtualStorage, relativePath, storage string) error
- RepositoryExistsFunc func(ctx context.Context, virtualStorage, relativePath string) (bool, error)
+ GetGenerationFunc func(ctx context.Context, virtualStorage, relativePath, storage string) (int, error)
+ IncrementGenerationFunc func(ctx context.Context, virtualStorage, relativePath, primary string, secondaries []string) error
+ GetReplicatedGenerationFunc func(ctx context.Context, virtualStorage, relativePath, source, target string) (int, error)
+ SetGenerationFunc func(ctx context.Context, virtualStorage, relativePath, storage string, generation int) error
+ CreateRepositoryFunc func(ctx context.Context, virtualStorage, relativePath, primary string, updatedSecondaries, outdatedSecondaries []string, storePrimary, storeAssignments bool) error
+ DeleteRepositoryFunc func(ctx context.Context, virtualStorage, relativePath string, storages []string) error
+ DeleteReplicaFunc func(ctx context.Context, virtualStorage, relativePath, storage string) error
+ RenameRepositoryFunc func(ctx context.Context, virtualStorage, relativePath, storage, newRelativePath string) error
+ GetConsistentStoragesFunc func(ctx context.Context, virtualStorage, relativePath string) (map[string]struct{}, error)
+ GetPartiallyAvailableRepositoriesFunc func(ctx context.Context, virtualStorage string) ([]PartiallyAvailableRepository, error)
+ DeleteInvalidRepositoryFunc func(ctx context.Context, virtualStorage, relativePath, storage string) error
+ RepositoryExistsFunc func(ctx context.Context, virtualStorage, relativePath string) (bool, error)
}
func (m MockRepositoryStore) GetGeneration(ctx context.Context, virtualStorage, relativePath, storage string) (int, error) {
@@ -95,12 +95,13 @@ func (m MockRepositoryStore) GetConsistentStorages(ctx context.Context, virtualS
return m.GetConsistentStoragesFunc(ctx, virtualStorage, relativePath)
}
-func (m MockRepositoryStore) GetPartiallyReplicatedRepositories(ctx context.Context, virtualStorage string, virtualStorageScopedPrimaries bool) ([]OutdatedRepository, error) {
- if m.GetPartiallyReplicatedRepositoriesFunc == nil {
+// GetPartiallyAvailableRepositories returns the result of GetPartiallyAvailableRepositories or nil if it is unset.
+func (m MockRepositoryStore) GetPartiallyAvailableRepositories(ctx context.Context, virtualStorage string) ([]PartiallyAvailableRepository, error) {
+ if m.GetPartiallyAvailableRepositoriesFunc == nil {
return nil, nil
}
- return m.GetPartiallyReplicatedRepositoriesFunc(ctx, virtualStorage, virtualStorageScopedPrimaries)
+ return m.GetPartiallyAvailableRepositoriesFunc(ctx, virtualStorage)
}
func (m MockRepositoryStore) DeleteInvalidRepository(ctx context.Context, virtualStorage, relativePath, storage string) error {
diff --git a/internal/praefect/datastore/repository_store_test.go b/internal/praefect/datastore/repository_store_test.go
index 57e61dac0..ad5058aca 100644
--- a/internal/praefect/datastore/repository_store_test.go
+++ b/internal/praefect/datastore/repository_store_test.go
@@ -799,208 +799,249 @@ func testRepositoryStore(t *testing.T, newStore repositoryStoreFactory) {
})
}
-func TestPostgresRepositoryStore_GetPartiallyReplicatedRepositories(t *testing.T) {
- for _, scope := range []struct {
- desc string
- useVirtualStoragePrimaries bool
- primary string
+func TestPostgresRepositoryStore_GetPartiallyAvailableRepositories(t *testing.T) {
+ for _, tc := range []struct {
+ desc string
+ nonExistentRepository bool
+ unhealthyStorages map[string]struct{}
+ existingGenerations map[string]int
+ existingAssignments []string
+ storageDetails []StorageDetails
}{
- {desc: "virtual storage primaries", useVirtualStoragePrimaries: true, primary: "virtual-storage-primary"},
- {desc: "repository primaries", useVirtualStoragePrimaries: false, primary: "repository-primary"},
+ {
+ desc: "all up to date without assignments",
+ existingGenerations: map[string]int{"primary": 0, "secondary-1": 0},
+ },
+ {
+ desc: "unconfigured node outdated without assignments",
+ existingGenerations: map[string]int{"primary": 1, "secondary-1": 1, "unconfigured": 0},
+ },
+ {
+ desc: "unconfigured node contains the latest",
+ existingGenerations: map[string]int{"primary": 0, "secondary-1": 0, "unconfigured": 1},
+ storageDetails: []StorageDetails{
+ {Name: "primary", BehindBy: 1, Assigned: true, Healthy: true},
+ {Name: "secondary-1", BehindBy: 1, Assigned: true, Healthy: true},
+ {Name: "unconfigured", BehindBy: 0, Assigned: false},
+ },
+ },
+ {
+ desc: "node has no repository without assignments",
+ existingGenerations: map[string]int{"primary": 0},
+ storageDetails: []StorageDetails{
+ {Name: "primary", BehindBy: 0, Assigned: true, Healthy: true, ValidPrimary: true},
+ {Name: "secondary-1", BehindBy: 1, Assigned: true, Healthy: true},
+ },
+ },
+ {
+ desc: "node has outdated repository without assignments",
+ existingGenerations: map[string]int{"primary": 1, "secondary-1": 0},
+ storageDetails: []StorageDetails{
+ {Name: "primary", BehindBy: 0, Assigned: true, Healthy: true, ValidPrimary: true},
+ {Name: "secondary-1", BehindBy: 1, Assigned: true, Healthy: true},
+ },
+ },
+ {
+ desc: "node with no repository heavily outdated",
+ existingGenerations: map[string]int{"primary": 10},
+ storageDetails: []StorageDetails{
+ {Name: "primary", BehindBy: 0, Assigned: true, Healthy: true, ValidPrimary: true},
+ {Name: "secondary-1", BehindBy: 11, Assigned: true, Healthy: true},
+ },
+ },
+ {
+ desc: "node with a heavily outdated repository",
+ existingGenerations: map[string]int{"primary": 10, "secondary-1": 0},
+ storageDetails: []StorageDetails{
+ {Name: "primary", BehindBy: 0, Assigned: true, Healthy: true, ValidPrimary: true},
+ {Name: "secondary-1", BehindBy: 10, Assigned: true, Healthy: true},
+ },
+ },
+ {
+ desc: "outdated nodes ignored when repository should not exist",
+ nonExistentRepository: true,
+ existingGenerations: map[string]int{"primary": 1, "secondary-1": 0},
+ },
+ {
+ desc: "unassigned node has no repository",
+ existingAssignments: []string{"primary"},
+ existingGenerations: map[string]int{"primary": 0},
+ },
+ {
+ desc: "unassigned node has an outdated repository",
+ existingAssignments: []string{"primary"},
+ existingGenerations: map[string]int{"primary": 1, "secondary-1": 0},
+ },
+ {
+ desc: "assigned node has no repository",
+ existingAssignments: []string{"primary", "secondary-1"},
+ existingGenerations: map[string]int{"primary": 0},
+ storageDetails: []StorageDetails{
+ {Name: "primary", BehindBy: 0, Assigned: true, Healthy: true, ValidPrimary: true},
+ {Name: "secondary-1", BehindBy: 1, Assigned: true, Healthy: true},
+ },
+ },
+ {
+ desc: "assigned node has outdated repository",
+ existingAssignments: []string{"primary", "secondary-1"},
+ existingGenerations: map[string]int{"primary": 1, "secondary-1": 0},
+ storageDetails: []StorageDetails{
+ {Name: "primary", BehindBy: 0, Assigned: true, Healthy: true, ValidPrimary: true},
+ {Name: "secondary-1", BehindBy: 1, Assigned: true, Healthy: true},
+ },
+ },
+ {
+ desc: "unassigned node contains the latest repository",
+ existingAssignments: []string{"primary"},
+ existingGenerations: map[string]int{"primary": 0, "secondary-1": 1},
+ storageDetails: []StorageDetails{
+ {Name: "primary", BehindBy: 1, Assigned: true, Healthy: true},
+ {Name: "secondary-1", BehindBy: 0, Assigned: false, Healthy: true, ValidPrimary: true},
+ },
+ },
+ {
+ desc: "unassigned node contains the only repository",
+ existingAssignments: []string{"primary"},
+ existingGenerations: map[string]int{"secondary-1": 0},
+ storageDetails: []StorageDetails{
+ {Name: "primary", BehindBy: 1, Assigned: true, Healthy: true},
+ {Name: "secondary-1", BehindBy: 0, Assigned: false, Healthy: true, ValidPrimary: true},
+ },
+ },
+ {
+ desc: "unassigned unconfigured node contains the only repository",
+ existingAssignments: []string{"primary"},
+ existingGenerations: map[string]int{"unconfigured": 0},
+ storageDetails: []StorageDetails{
+ {Name: "primary", BehindBy: 1, Assigned: true, Healthy: true},
+ {Name: "unconfigured", BehindBy: 0, Assigned: false},
+ },
+ },
+ {
+ desc: "assigned unconfigured node has no repository",
+ existingAssignments: []string{"primary", "unconfigured"},
+ existingGenerations: map[string]int{"primary": 1},
+ },
+ {
+ desc: "assigned unconfigured node is outdated",
+ existingAssignments: []string{"primary", "unconfigured"},
+ existingGenerations: map[string]int{"primary": 1, "unconfigured": 0},
+ },
+ {
+ desc: "unconfigured node is the only assigned node",
+ existingAssignments: []string{"unconfigured"},
+ existingGenerations: map[string]int{"unconfigured": 0},
+ storageDetails: []StorageDetails{
+ {Name: "primary", BehindBy: 1, Assigned: true, Healthy: true},
+ {Name: "secondary-1", BehindBy: 1, Assigned: true, Healthy: true},
+ {Name: "unconfigured", BehindBy: 0, Assigned: false},
+ },
+ },
+ {
+ desc: "repository is fully replicated but unavailable",
+ unhealthyStorages: map[string]struct{}{"primary": {}, "secondary-1": {}},
+ existingAssignments: []string{"primary", "secondary-1"},
+ existingGenerations: map[string]int{"primary": 0, "secondary-1": 0},
+ storageDetails: []StorageDetails{
+ {Name: "primary", Assigned: true},
+ {Name: "secondary-1", Assigned: true},
+ },
+ },
+ {
+ desc: "assigned replicas unavailable but a valid unassigned primary candidate",
+ unhealthyStorages: map[string]struct{}{"primary": {}},
+ existingAssignments: []string{"primary"},
+ existingGenerations: map[string]int{"primary": 0, "secondary-1": 0},
+ storageDetails: []StorageDetails{
+ {Name: "primary", Assigned: true},
+ {Name: "secondary-1", Healthy: true, ValidPrimary: true},
+ },
+ },
+ {
+ desc: "assigned replicas available but unassigned replica unavailable",
+ unhealthyStorages: map[string]struct{}{"secondary-1": {}},
+ existingAssignments: []string{"primary"},
+ existingGenerations: map[string]int{"primary": 0, "secondary-1": 0},
+ },
+ {
+ desc: "one assigned replica unavailable",
+ unhealthyStorages: map[string]struct{}{"secondary-1": {}},
+ existingAssignments: []string{"primary", "secondary-1"},
+ existingGenerations: map[string]int{"primary": 0, "secondary-1": 0},
+ storageDetails: []StorageDetails{
+ {Name: "primary", Assigned: true, Healthy: true, ValidPrimary: true},
+ {Name: "secondary-1", Assigned: true},
+ },
+ },
} {
- t.Run(scope.desc, func(t *testing.T) {
- for _, tc := range []struct {
- desc string
- nonExistentRepository bool
- existingGenerations map[string]int
- existingAssignments []string
- storageDetails []OutdatedRepositoryStorageDetails
- }{
- {
- desc: "all up to date without assignments",
- existingGenerations: map[string]int{"primary": 0, "secondary-1": 0},
- },
- {
- desc: "unconfigured node outdated without assignments",
- existingGenerations: map[string]int{"primary": 1, "secondary-1": 1, "unconfigured": 0},
- },
- {
- desc: "unconfigured node contains the latest",
- existingGenerations: map[string]int{"primary": 0, "secondary-1": 0, "unconfigured": 1},
- storageDetails: []OutdatedRepositoryStorageDetails{
- {Name: "primary", BehindBy: 1, Assigned: true},
- {Name: "secondary-1", BehindBy: 1, Assigned: true},
- {Name: "unconfigured", BehindBy: 0, Assigned: false},
- },
- },
- {
- desc: "node has no repository without assignments",
- existingGenerations: map[string]int{"primary": 0},
- storageDetails: []OutdatedRepositoryStorageDetails{
- {Name: "primary", BehindBy: 0, Assigned: true},
- {Name: "secondary-1", BehindBy: 1, Assigned: true},
- },
- },
- {
- desc: "node has outdated repository without assignments",
- existingGenerations: map[string]int{"primary": 1, "secondary-1": 0},
- storageDetails: []OutdatedRepositoryStorageDetails{
- {Name: "primary", BehindBy: 0, Assigned: true},
- {Name: "secondary-1", BehindBy: 1, Assigned: true},
- },
- },
- {
- desc: "node with no repository heavily outdated",
- existingGenerations: map[string]int{"primary": 10},
- storageDetails: []OutdatedRepositoryStorageDetails{
- {Name: "primary", BehindBy: 0, Assigned: true},
- {Name: "secondary-1", BehindBy: 11, Assigned: true},
- },
- },
- {
- desc: "node with a heavily outdated repository",
- existingGenerations: map[string]int{"primary": 10, "secondary-1": 0},
- storageDetails: []OutdatedRepositoryStorageDetails{
- {Name: "primary", BehindBy: 0, Assigned: true},
- {Name: "secondary-1", BehindBy: 10, Assigned: true},
- },
- },
- {
- desc: "outdated nodes ignored when repository should not exist",
- nonExistentRepository: true,
- existingGenerations: map[string]int{"primary": 1, "secondary-1": 0},
- },
- {
- desc: "unassigned node has no repository",
- existingAssignments: []string{"primary"},
- existingGenerations: map[string]int{"primary": 0},
- },
- {
- desc: "unassigned node has an outdated repository",
- existingAssignments: []string{"primary"},
- existingGenerations: map[string]int{"primary": 1, "secondary-1": 0},
- },
- {
- desc: "assigned node has no repository",
- existingAssignments: []string{"primary", "secondary-1"},
- existingGenerations: map[string]int{"primary": 0},
- storageDetails: []OutdatedRepositoryStorageDetails{
- {Name: "primary", BehindBy: 0, Assigned: true},
- {Name: "secondary-1", BehindBy: 1, Assigned: true},
- },
- },
- {
- desc: "assigned node has outdated repository",
- existingAssignments: []string{"primary", "secondary-1"},
- existingGenerations: map[string]int{"primary": 1, "secondary-1": 0},
- storageDetails: []OutdatedRepositoryStorageDetails{
- {Name: "primary", BehindBy: 0, Assigned: true},
- {Name: "secondary-1", BehindBy: 1, Assigned: true},
- },
- },
- {
- desc: "unassigned node contains the latest repository",
- existingAssignments: []string{"primary"},
- existingGenerations: map[string]int{"primary": 0, "secondary-1": 1},
- storageDetails: []OutdatedRepositoryStorageDetails{
- {Name: "primary", BehindBy: 1, Assigned: true},
- {Name: "secondary-1", BehindBy: 0, Assigned: false},
- },
- },
- {
- desc: "unassigned node contains the only repository",
- existingAssignments: []string{"primary"},
- existingGenerations: map[string]int{"secondary-1": 0},
- storageDetails: []OutdatedRepositoryStorageDetails{
- {Name: "primary", BehindBy: 1, Assigned: true},
- {Name: "secondary-1", BehindBy: 0, Assigned: false},
- },
- },
- {
- desc: "unassigned unconfigured node contains the only repository",
- existingAssignments: []string{"primary"},
- existingGenerations: map[string]int{"unconfigured": 0},
- storageDetails: []OutdatedRepositoryStorageDetails{
- {Name: "primary", BehindBy: 1, Assigned: true},
- {Name: "unconfigured", BehindBy: 0, Assigned: false},
- },
- },
- {
- desc: "assigned unconfigured node has no repository",
- existingAssignments: []string{"primary", "unconfigured"},
- existingGenerations: map[string]int{"primary": 1},
- },
- {
- desc: "assigned unconfigured node is outdated",
- existingAssignments: []string{"primary", "unconfigured"},
- existingGenerations: map[string]int{"primary": 1, "unconfigured": 0},
- },
- {
- desc: "unconfigured node is the only assigned node",
- existingAssignments: []string{"unconfigured"},
- existingGenerations: map[string]int{"unconfigured": 0},
- storageDetails: []OutdatedRepositoryStorageDetails{
- {Name: "primary", BehindBy: 1, Assigned: true},
- {Name: "secondary-1", BehindBy: 1, Assigned: true},
- {Name: "unconfigured", BehindBy: 0, Assigned: false},
- },
- },
- } {
- t.Run(tc.desc, func(t *testing.T) {
- ctx, cancel := testhelper.Context()
- defer cancel()
+ t.Run(tc.desc, func(t *testing.T) {
+ ctx, cancel := testhelper.Context()
+ defer cancel()
+
+ tx, err := getDB(t).Begin()
+ require.NoError(t, err)
+ defer tx.Rollback()
+
+ configuredStorages := map[string][]string{"virtual-storage": {"primary", "secondary-1"}}
+
+ var healthyStorages []string
+ for _, storage := range configuredStorages["virtual-storage"] {
+ if _, ok := tc.unhealthyStorages[storage]; ok {
+ continue
+ }
- db := getDB(t)
+ healthyStorages = append(healthyStorages, storage)
+ }
- configuredStorages := map[string][]string{"virtual-storage": {"primary", "secondary-1"}}
+ testhelper.SetHealthyNodes(t, ctx, tx, map[string]map[string][]string{
+ "praefect-0": {"virtual-storage": healthyStorages},
+ })
- if !tc.nonExistentRepository {
- _, err := db.ExecContext(ctx, `
+ if !tc.nonExistentRepository {
+ _, err := tx.ExecContext(ctx, `
INSERT INTO repositories (virtual_storage, relative_path, "primary")
VALUES ('virtual-storage', 'relative-path', 'repository-primary')
`)
- require.NoError(t, err)
- }
+ require.NoError(t, err)
+ }
- for storage, generation := range tc.existingGenerations {
- _, err := db.ExecContext(ctx, `
+ for storage, generation := range tc.existingGenerations {
+ _, err := tx.ExecContext(ctx, `
INSERT INTO storage_repositories VALUES ('virtual-storage', 'relative-path', $1, $2)
`, storage, generation)
- require.NoError(t, err)
- }
+ require.NoError(t, err)
+ }
- for _, storage := range tc.existingAssignments {
- _, err := db.ExecContext(ctx, `
+ for _, storage := range tc.existingAssignments {
+ _, err := tx.ExecContext(ctx, `
INSERT INTO repository_assignments VALUES ('virtual-storage', 'relative-path', $1)
`, storage)
- require.NoError(t, err)
- }
+ require.NoError(t, err)
+ }
- _, err := db.ExecContext(ctx, `
+ _, err = tx.ExecContext(ctx, `
INSERT INTO shard_primaries (shard_name, node_name, elected_by_praefect, elected_at)
VALUES ('virtual-storage', 'virtual-storage-primary', 'ignored', now())
`)
- require.NoError(t, err)
-
- store := NewPostgresRepositoryStore(db, configuredStorages)
- outdated, err := store.GetPartiallyReplicatedRepositories(ctx, "virtual-storage", scope.useVirtualStoragePrimaries)
- require.NoError(t, err)
+ require.NoError(t, err)
- expected := []OutdatedRepository{
- {
- RelativePath: "relative-path",
- Primary: scope.primary,
- Storages: tc.storageDetails,
- },
- }
+ store := NewPostgresRepositoryStore(tx, configuredStorages)
+ outdated, err := store.GetPartiallyAvailableRepositories(ctx, "virtual-storage")
+ require.NoError(t, err)
- if tc.storageDetails == nil {
- expected = nil
- }
+ expected := []PartiallyAvailableRepository{
+ {
+ RelativePath: "relative-path",
+ Primary: "repository-primary",
+ Storages: tc.storageDetails,
+ },
+ }
- require.Equal(t, expected, outdated)
- })
+ if tc.storageDetails == nil {
+ expected = nil
}
+
+ require.Equal(t, expected, outdated)
})
}
}
diff --git a/internal/praefect/service/info/dataloss.go b/internal/praefect/service/info/dataloss.go
index 200f2bffb..3210884f0 100644
--- a/internal/praefect/service/info/dataloss.go
+++ b/internal/praefect/service/info/dataloss.go
@@ -3,42 +3,42 @@ package info
import (
"context"
- "gitlab.com/gitlab-org/gitaly/v14/internal/praefect/config"
"gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb"
)
func (s *Server) DatalossCheck(ctx context.Context, req *gitalypb.DatalossCheckRequest) (*gitalypb.DatalossCheckResponse, error) {
- outdatedRepos, err := s.rs.GetPartiallyReplicatedRepositories(
- ctx, req.GetVirtualStorage(), s.conf.Failover.ElectionStrategy != config.ElectionStrategyPerRepository)
+ repos, err := s.rs.GetPartiallyAvailableRepositories(ctx, req.GetVirtualStorage())
if err != nil {
return nil, err
}
- pbRepos := make([]*gitalypb.DatalossCheckResponse_Repository, 0, len(outdatedRepos))
- for _, outdatedRepo := range outdatedRepos {
- readOnly := true
+ pbRepos := make([]*gitalypb.DatalossCheckResponse_Repository, 0, len(repos))
+ for _, outdatedRepo := range repos {
+ unavailable := true
storages := make([]*gitalypb.DatalossCheckResponse_Repository_Storage, 0, len(outdatedRepo.Storages))
for _, storage := range outdatedRepo.Storages {
- if storage.Name == outdatedRepo.Primary && storage.BehindBy == 0 {
- readOnly = false
+ if storage.ValidPrimary {
+ unavailable = false
}
storages = append(storages, &gitalypb.DatalossCheckResponse_Repository_Storage{
- Name: storage.Name,
- BehindBy: int64(storage.BehindBy),
- Assigned: storage.Assigned,
+ Name: storage.Name,
+ BehindBy: int64(storage.BehindBy),
+ Assigned: storage.Assigned,
+ Healthy: storage.Healthy,
+ ValidPrimary: storage.ValidPrimary,
})
}
- if !req.IncludePartiallyReplicated && !readOnly {
+ if !req.IncludePartiallyReplicated && !unavailable {
continue
}
pbRepos = append(pbRepos, &gitalypb.DatalossCheckResponse_Repository{
RelativePath: outdatedRepo.RelativePath,
Primary: outdatedRepo.Primary,
- ReadOnly: readOnly,
+ Unavailable: unavailable,
Storages: storages,
})
}
diff --git a/proto/go/gitalypb/praefect.pb.go b/proto/go/gitalypb/praefect.pb.go
index b1ee17d2a..3049f8b73 100644
--- a/proto/go/gitalypb/praefect.pb.go
+++ b/proto/go/gitalypb/praefect.pb.go
@@ -243,10 +243,8 @@ type DatalossCheckRequest struct {
unknownFields protoimpl.UnknownFields
VirtualStorage string `protobuf:"bytes,1,opt,name=virtual_storage,json=virtualStorage,proto3" json:"virtual_storage,omitempty"`
- // include_partially_replicated decides whether to include repositories which are fully up to date
- // on the primary but are outdated on some secondaries. Such repositories are still writable and do
- // not suffer from data loss. The data on the primary is not fully replicated which increases the
- // chances of data loss following a failover.
+ // include_partially_unavailable indicates whether to include repositories which are available but
+ // are unavailable on some assigned storages.
IncludePartiallyReplicated bool `protobuf:"varint,2,opt,name=include_partially_replicated,json=includePartiallyReplicated,proto3" json:"include_partially_replicated,omitempty"`
}
@@ -624,8 +622,8 @@ type DatalossCheckResponse_Repository struct {
RelativePath string `protobuf:"bytes,1,opt,name=relative_path,json=relativePath,proto3" json:"relative_path,omitempty"`
// storages on which the repository is outdated
Storages []*DatalossCheckResponse_Repository_Storage `protobuf:"bytes,2,rep,name=storages,proto3" json:"storages,omitempty"`
- // read_only indicates whether the repository is in read-only mode.
- ReadOnly bool `protobuf:"varint,3,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"`
+ // unavailable indicates whether the repository is in unavailable.
+ Unavailable bool `protobuf:"varint,3,opt,name=unavailable,proto3" json:"unavailable,omitempty"`
// current primary storage of the repository
Primary string `protobuf:"bytes,4,opt,name=primary,proto3" json:"primary,omitempty"`
}
@@ -676,9 +674,9 @@ func (x *DatalossCheckResponse_Repository) GetStorages() []*DatalossCheckRespons
return nil
}
-func (x *DatalossCheckResponse_Repository) GetReadOnly() bool {
+func (x *DatalossCheckResponse_Repository) GetUnavailable() bool {
if x != nil {
- return x.ReadOnly
+ return x.Unavailable
}
return false
}
@@ -701,6 +699,10 @@ type DatalossCheckResponse_Repository_Storage struct {
BehindBy int64 `protobuf:"varint,2,opt,name=behind_by,json=behindBy,proto3" json:"behind_by,omitempty"`
// assigned indicates whether the storage is assigned to host the repository.
Assigned bool `protobuf:"varint,3,opt,name=assigned,proto3" json:"assigned,omitempty"`
+ // healthy indicates whether the storage is considered healthy by the consensus of Praefect nodes.
+ Healthy bool `protobuf:"varint,4,opt,name=healthy,proto3" json:"healthy,omitempty"`
+ // valid_primary indicates whether the storage is ready to act as the primary if necessary.
+ ValidPrimary bool `protobuf:"varint,5,opt,name=valid_primary,json=validPrimary,proto3" json:"valid_primary,omitempty"`
}
func (x *DatalossCheckResponse_Repository_Storage) Reset() {
@@ -756,6 +758,20 @@ func (x *DatalossCheckResponse_Repository_Storage) GetAssigned() bool {
return false
}
+func (x *DatalossCheckResponse_Repository_Storage) GetHealthy() bool {
+ if x != nil {
+ return x.Healthy
+ }
+ return false
+}
+
+func (x *DatalossCheckResponse_Repository_Storage) GetValidPrimary() bool {
+ if x != nil {
+ return x.ValidPrimary
+ }
+ return false
+}
+
type RepositoryReplicasResponse_RepositoryDetails struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -851,14 +867,14 @@ var file_praefect_proto_rawDesc = []byte{
0x65, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x70, 0x6c,
0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x69, 0x6e,
0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x52, 0x65,
- 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x22, 0xf6, 0x02, 0x0a, 0x15, 0x44, 0x61, 0x74,
+ 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x22, 0xbb, 0x03, 0x0a, 0x15, 0x44, 0x61, 0x74,
0x61, 0x6c, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0c, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x69,
0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c,
0x79, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
0x72, 0x79, 0x52, 0x0c, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73,
- 0x1a, 0x8e, 0x02, 0x0a, 0x0a, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12,
+ 0x1a, 0xd3, 0x02, 0x0a, 0x0a, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12,
0x23, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65,
0x50, 0x61, 0x74, 0x68, 0x12, 0x4c, 0x0a, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x73,
@@ -866,104 +882,108 @@ var file_praefect_proto_rawDesc = []byte{
0x44, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79,
0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67,
- 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18,
- 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12,
- 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x1a, 0x56, 0x0a, 0x07, 0x53, 0x74, 0x6f,
- 0x72, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x65, 0x68, 0x69,
- 0x6e, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x62, 0x65, 0x68,
- 0x69, 0x6e, 0x64, 0x42, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65,
- 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65,
- 0x64, 0x22, 0x4f, 0x0a, 0x19, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52,
- 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32,
- 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f,
- 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
- 0x72, 0x79, 0x22, 0xa3, 0x02, 0x0a, 0x1a, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
- 0x79, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
- 0x65, 0x12, 0x4e, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f,
- 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x52, 0x65,
- 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
- 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72,
- 0x79, 0x12, 0x50, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x02, 0x20,
- 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70,
- 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x52,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
- 0x72, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x69,
- 0x63, 0x61, 0x73, 0x1a, 0x63, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
- 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x32, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f,
- 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67,
- 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79,
- 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08,
- 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
- 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x22, 0xcf, 0x01, 0x0a, 0x17, 0x43, 0x6f, 0x6e,
- 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x5f,
- 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x76,
- 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x25, 0x0a,
- 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18,
- 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x74, 0x6f,
- 0x72, 0x61, 0x67, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
- 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x10, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
- 0x65, 0x12, 0x37, 0x0a, 0x17, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x63,
- 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01,
- 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x6e,
- 0x63, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x85, 0x02, 0x0a, 0x18, 0x43,
- 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x70, 0x6f, 0x5f,
- 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x10, 0x72, 0x65, 0x70, 0x6f, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76,
- 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f,
- 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e,
- 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x12, 0x2d,
- 0x0a, 0x12, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x68, 0x65, 0x63,
- 0x6b, 0x73, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x66, 0x65,
- 0x72, 0x65, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x12, 0x1e, 0x0a,
- 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01,
- 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6c, 0x4a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x2b, 0x0a,
- 0x11, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61,
- 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
- 0x6e, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x72,
- 0x72, 0x6f, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f,
- 0x72, 0x73, 0x32, 0xee, 0x03, 0x0a, 0x13, 0x50, 0x72, 0x61, 0x65, 0x66, 0x65, 0x63, 0x74, 0x49,
- 0x6e, 0x66, 0x6f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5b, 0x0a, 0x12, 0x52, 0x65,
- 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73,
- 0x12, 0x21, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69,
+ 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c,
+ 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c,
+ 0x61, 0x62, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18,
+ 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x1a, 0x95,
+ 0x01, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
+ 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b,
+ 0x0a, 0x09, 0x62, 0x65, 0x68, 0x69, 0x6e, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x03, 0x52, 0x08, 0x62, 0x65, 0x68, 0x69, 0x6e, 0x64, 0x42, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x61,
+ 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61,
+ 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x6c, 0x74,
+ 0x68, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68,
+ 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61,
+ 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x4f, 0x0a, 0x19, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69,
0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70,
- 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x52,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x73, 0x69,
- 0x73, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1f, 0x2e, 0x67, 0x69,
- 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x79,
- 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67,
- 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63,
- 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01,
- 0x12, 0x4c, 0x0a, 0x0d, 0x44, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63,
- 0x6b, 0x12, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x6c,
- 0x6f, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
- 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x73,
- 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a,
- 0x0a, 0x17, 0x53, 0x65, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x61, 0x74, 0x69,
- 0x76, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x61,
- 0x6c, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x61, 0x74,
- 0x69, 0x76, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
- 0x74, 0x1a, 0x27, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x75,
- 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61,
- 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, 0x53, 0x65,
- 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x63, 0x74,
- 0x6f, 0x72, 0x12, 0x23, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x52,
- 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79,
+ 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
+ 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79,
+ 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x0a, 0x72, 0x65, 0x70,
+ 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x22, 0xa3, 0x02, 0x0a, 0x1a, 0x52, 0x65, 0x70, 0x6f,
+ 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x52, 0x65,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72,
+ 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79,
+ 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x69,
+ 0x63, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6f,
+ 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x07, 0x70,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x50, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63,
+ 0x61, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c,
+ 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c,
+ 0x69, 0x63, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x70,
+ 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x08,
+ 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x1a, 0x63, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6f,
+ 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x32, 0x0a,
+ 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
+ 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x22, 0xcf, 0x01,
+ 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x43, 0x68, 0x65,
+ 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x69, 0x72,
+ 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x0e, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x53, 0x74, 0x6f, 0x72, 0x61,
+ 0x67, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x74, 0x6f,
+ 0x72, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x61, 0x72, 0x67,
+ 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x66,
+ 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x03,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x53,
+ 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x17, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
+ 0x65, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65,
+ 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22,
+ 0x85, 0x02, 0x0a, 0x18, 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x43,
+ 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x12,
+ 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61,
+ 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x72, 0x65, 0x70, 0x6f, 0x52, 0x65,
+ 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x61,
+ 0x72, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b,
+ 0x73, 0x75, 0x6d, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
+ 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x11, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73,
+ 0x75, 0x6d, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, 0x69,
+ 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6c, 0x4a, 0x6f, 0x62,
+ 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f,
+ 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x72,
+ 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12,
+ 0x16, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52,
+ 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x32, 0xee, 0x03, 0x0a, 0x13, 0x50, 0x72, 0x61, 0x65,
+ 0x66, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
+ 0x5b, 0x0a, 0x12, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x70,
+ 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52,
+ 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61,
+ 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c,
+ 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c,
+ 0x69, 0x63, 0x61, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x10,
+ 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b,
+ 0x12, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x73,
+ 0x74, 0x65, 0x6e, 0x63, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x69,
+ 0x73, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x4c, 0x0a, 0x0d, 0x44, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x73,
+ 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e,
+ 0x44, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71,
+ 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x44, 0x61,
+ 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x26,
+ 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e,
+ 0x53, 0x65, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65,
+ 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
+ 0x61, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x23, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79,
0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46,
- 0x61, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x04, 0xf0,
- 0x97, 0x28, 0x01, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x63, 0x6f,
- 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2d, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x69, 0x74,
- 0x61, 0x6c, 0x79, 0x2f, 0x76, 0x31, 0x34, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f,
- 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
- 0x33,
+ 0x61, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67,
+ 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+ 0x73, 0x65, 0x1a, 0x04, 0xf0, 0x97, 0x28, 0x01, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x6c,
+ 0x61, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2d, 0x6f, 0x72,
+ 0x67, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2f, 0x76, 0x31, 0x34, 0x2f, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x70, 0x62, 0x62, 0x06,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
diff --git a/proto/go/gitalypb/praefect_grpc.pb.go b/proto/go/gitalypb/praefect_grpc.pb.go
index 435f2e849..5abdde92d 100644
--- a/proto/go/gitalypb/praefect_grpc.pb.go
+++ b/proto/go/gitalypb/praefect_grpc.pb.go
@@ -24,7 +24,7 @@ type PraefectInfoServiceClient interface {
// back indicating which repos are consistent with the primary and which ones
// need repair.
ConsistencyCheck(ctx context.Context, in *ConsistencyCheckRequest, opts ...grpc.CallOption) (PraefectInfoService_ConsistencyCheckClient, error)
- // DatalossCheck checks for outdated repository replicas.
+ // DatalossCheck checks for unavailable repositories.
DatalossCheck(ctx context.Context, in *DatalossCheckRequest, opts ...grpc.CallOption) (*DatalossCheckResponse, error)
// SetAuthoritativeStorage sets the authoritative storage for a repository on a given virtual storage.
// This causes the current version of the repository on the authoritative storage to be considered the
@@ -127,7 +127,7 @@ type PraefectInfoServiceServer interface {
// back indicating which repos are consistent with the primary and which ones
// need repair.
ConsistencyCheck(*ConsistencyCheckRequest, PraefectInfoService_ConsistencyCheckServer) error
- // DatalossCheck checks for outdated repository replicas.
+ // DatalossCheck checks for unavailable repositories.
DatalossCheck(context.Context, *DatalossCheckRequest) (*DatalossCheckResponse, error)
// SetAuthoritativeStorage sets the authoritative storage for a repository on a given virtual storage.
// This causes the current version of the repository on the authoritative storage to be considered the
diff --git a/proto/praefect.proto b/proto/praefect.proto
index 946fef596..71198a687 100644
--- a/proto/praefect.proto
+++ b/proto/praefect.proto
@@ -17,7 +17,7 @@ service PraefectInfoService {
// need repair.
rpc ConsistencyCheck(ConsistencyCheckRequest) returns (stream ConsistencyCheckResponse);
- // DatalossCheck checks for outdated repository replicas.
+ // DatalossCheck checks for unavailable repositories.
rpc DatalossCheck(DatalossCheckRequest) returns (DatalossCheckResponse);
// SetAuthoritativeStorage sets the authoritative storage for a repository on a given virtual storage.
@@ -62,10 +62,8 @@ message SetAuthoritativeStorageResponse {}
message DatalossCheckRequest {
string virtual_storage = 1;
- // include_partially_replicated decides whether to include repositories which are fully up to date
- // on the primary but are outdated on some secondaries. Such repositories are still writable and do
- // not suffer from data loss. The data on the primary is not fully replicated which increases the
- // chances of data loss following a failover.
+ // include_partially_unavailable indicates whether to include repositories which are available but
+ // are unavailable on some assigned storages.
bool include_partially_replicated = 2;
}
@@ -78,14 +76,18 @@ message DatalossCheckResponse {
int64 behind_by = 2;
// assigned indicates whether the storage is assigned to host the repository.
bool assigned = 3;
+ // healthy indicates whether the storage is considered healthy by the consensus of Praefect nodes.
+ bool healthy = 4;
+ // valid_primary indicates whether the storage is ready to act as the primary if necessary.
+ bool valid_primary = 5;
}
// relative path of the repository with outdated replicas
string relative_path = 1;
// storages on which the repository is outdated
repeated Storage storages = 2;
- // read_only indicates whether the repository is in read-only mode.
- bool read_only = 3;
+ // unavailable indicates whether the repository is in unavailable.
+ bool unavailable = 3;
// current primary storage of the repository
string primary = 4;
diff --git a/ruby/proto/gitaly/praefect_pb.rb b/ruby/proto/gitaly/praefect_pb.rb
index 8439cc121..0e10b2162 100644
--- a/ruby/proto/gitaly/praefect_pb.rb
+++ b/ruby/proto/gitaly/praefect_pb.rb
@@ -32,13 +32,15 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
add_message "gitaly.DatalossCheckResponse.Repository" do
optional :relative_path, :string, 1
repeated :storages, :message, 2, "gitaly.DatalossCheckResponse.Repository.Storage"
- optional :read_only, :bool, 3
+ optional :unavailable, :bool, 3
optional :primary, :string, 4
end
add_message "gitaly.DatalossCheckResponse.Repository.Storage" do
optional :name, :string, 1
optional :behind_by, :int64, 2
optional :assigned, :bool, 3
+ optional :healthy, :bool, 4
+ optional :valid_primary, :bool, 5
end
add_message "gitaly.RepositoryReplicasRequest" do
optional :repository, :message, 1, "gitaly.Repository"
diff --git a/ruby/proto/gitaly/praefect_services_pb.rb b/ruby/proto/gitaly/praefect_services_pb.rb
index 9eb597889..a148f3357 100644
--- a/ruby/proto/gitaly/praefect_services_pb.rb
+++ b/ruby/proto/gitaly/praefect_services_pb.rb
@@ -20,7 +20,7 @@ module Gitaly
# back indicating which repos are consistent with the primary and which ones
# need repair.
rpc :ConsistencyCheck, Gitaly::ConsistencyCheckRequest, stream(Gitaly::ConsistencyCheckResponse)
- # DatalossCheck checks for outdated repository replicas.
+ # DatalossCheck checks for unavailable repositories.
rpc :DatalossCheck, Gitaly::DatalossCheckRequest, Gitaly::DatalossCheckResponse
# SetAuthoritativeStorage sets the authoritative storage for a repository on a given virtual storage.
# This causes the current version of the repository on the authoritative storage to be considered the