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:
authorJustin Tobler <jtobler@gitlab.com>2023-08-14 16:09:59 +0300
committerJustin Tobler <jtobler@gitlab.com>2023-08-14 16:09:59 +0300
commite8bd279636709887e70420ef64276022f25724f0 (patch)
tree2076d7e8047b5b7bd9bb0c1b1c58b16d570c0a89
parent050416be5af659e666f9c174b00f65b24c5d3ed8 (diff)
parentabbad87683f06013f0f0e3116416885f810c5b62 (diff)
Merge branch 'wc/repo-count-dedup' into 'master'
counter: Key metric on storage path instead of name See merge request https://gitlab.com/gitlab-org/gitaly/-/merge_requests/6188 Merged-by: Justin Tobler <jtobler@gitlab.com> Approved-by: Steve Xuereb <sxuereb@gitlab.com> Approved-by: Quang-Minh Nguyen <qmnguyen@gitlab.com> Approved-by: Justin Tobler <jtobler@gitlab.com> Reviewed-by: Steve Xuereb <sxuereb@gitlab.com> Reviewed-by: Will Chandler <wchandler@gitlab.com> Reviewed-by: Quang-Minh Nguyen <qmnguyen@gitlab.com> Co-authored-by: Will Chandler <wchandler@gitlab.com>
-rw-r--r--internal/backup/backup_test.go8
-rw-r--r--internal/cli/gitaly/serve.go4
-rw-r--r--internal/gitaly/repoutil/create_test.go2
-rw-r--r--internal/gitaly/repoutil/remove_test.go2
-rw-r--r--internal/gitaly/storage/counter/counter.go57
-rw-r--r--internal/gitaly/storage/counter/counter_test.go105
-rw-r--r--internal/testhelper/testserver/gitaly.go2
7 files changed, 123 insertions, 57 deletions
diff --git a/internal/backup/backup_test.go b/internal/backup/backup_test.go
index 46a8e840d..af905e9d0 100644
--- a/internal/backup/backup_test.go
+++ b/internal/backup/backup_test.go
@@ -70,7 +70,7 @@ func TestManager_Create(t *testing.T) {
cfg.SocketPath = testserver.RunGitalyServer(t, cfg, setup.RegisterAll)
ctx := testhelper.Context(t)
- repoCounter := counter.NewRepositoryCounter()
+ repoCounter := counter.NewRepositoryCounter(cfg.Storages)
for _, managerTC := range []struct {
desc string
@@ -238,7 +238,7 @@ func TestManager_Create_incremental(t *testing.T) {
cfg.SocketPath = testserver.RunGitalyServer(t, cfg, setup.RegisterAll)
ctx := testhelper.Context(t)
- repoCounter := counter.NewRepositoryCounter()
+ repoCounter := counter.NewRepositoryCounter(cfg.Storages)
for _, managerTC := range []struct {
desc string
@@ -382,7 +382,7 @@ func TestManager_Restore_latest(t *testing.T) {
cfg := testcfg.Build(t)
testcfg.BuildGitalyHooks(t, cfg)
cfg.SocketPath = testserver.RunGitalyServer(t, cfg, setup.RegisterAll)
- repoCounter := counter.NewRepositoryCounter()
+ repoCounter := counter.NewRepositoryCounter(cfg.Storages)
for _, managerTC := range []struct {
desc string
@@ -710,7 +710,7 @@ func TestManager_Restore_specific(t *testing.T) {
cfg := testcfg.Build(t)
testcfg.BuildGitalyHooks(t, cfg)
cfg.SocketPath = testserver.RunGitalyServer(t, cfg, setup.RegisterAll)
- repoCounter := counter.NewRepositoryCounter()
+ repoCounter := counter.NewRepositoryCounter(cfg.Storages)
for _, managerTC := range []struct {
desc string
diff --git a/internal/cli/gitaly/serve.go b/internal/cli/gitaly/serve.go
index b0a0c3833..5f80d28e5 100644
--- a/internal/cli/gitaly/serve.go
+++ b/internal/cli/gitaly/serve.go
@@ -230,9 +230,9 @@ func run(cfg config.Cfg) error {
locator := config.NewLocator(cfg)
- repoCounter := counter.NewRepositoryCounter()
+ repoCounter := counter.NewRepositoryCounter(cfg.Storages)
prometheus.MustRegister(repoCounter)
- repoCounter.StartCountingRepositories(ctx, locator, cfg.Storages, log.StandardLogger())
+ repoCounter.StartCountingRepositories(ctx, locator, log.StandardLogger())
tempdir.StartCleaning(locator, cfg.Storages, time.Hour)
diff --git a/internal/gitaly/repoutil/create_test.go b/internal/gitaly/repoutil/create_test.go
index 614782342..08d2d43bc 100644
--- a/internal/gitaly/repoutil/create_test.go
+++ b/internal/gitaly/repoutil/create_test.go
@@ -322,7 +322,7 @@ func TestCreate(t *testing.T) {
// manager's data.
ctx := ctx
*txManager = transaction.MockManager{}
- repoCounter := counter.NewRepositoryCounter()
+ repoCounter := counter.NewRepositoryCounter(cfg.Storages)
repo := &gitalypb.Repository{
StorageName: cfg.Storages[0].Name,
diff --git a/internal/gitaly/repoutil/remove_test.go b/internal/gitaly/repoutil/remove_test.go
index 7d59b78c2..ae46b4d25 100644
--- a/internal/gitaly/repoutil/remove_test.go
+++ b/internal/gitaly/repoutil/remove_test.go
@@ -94,7 +94,7 @@ func TestRemove(t *testing.T) {
cfg := testcfg.Build(t)
locator := config.NewLocator(cfg)
txManager := transaction.NewTrackingManager()
- repoCounter := counter.NewRepositoryCounter()
+ repoCounter := counter.NewRepositoryCounter(cfg.Storages)
repo, repoPath := tc.createRepo(t, ctx, cfg)
diff --git a/internal/gitaly/storage/counter/counter.go b/internal/gitaly/storage/counter/counter.go
index 117e21dfb..86accfeb6 100644
--- a/internal/gitaly/storage/counter/counter.go
+++ b/internal/gitaly/storage/counter/counter.go
@@ -22,20 +22,23 @@ import (
type RepositoryCounter struct {
reposTotal *prometheus.GaugeVec
suppressMetric atomic.Bool
+ // Lookup path associated with a storage.
+ storageNameToPath map[string]string
}
// NewRepositoryCounter constructs a RepositoryCounter object.
-func NewRepositoryCounter() *RepositoryCounter {
+func NewRepositoryCounter(storages []config.Storage) *RepositoryCounter {
c := &RepositoryCounter{
reposTotal: prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "gitaly_total_repositories_count",
- Help: "Gauge of number of repositories by storage and path",
+ Help: "Gauge of number of repositories by storage path and repository prefix",
},
- []string{"storage", "prefix"},
+ []string{"path", "prefix"},
),
}
c.suppressMetric.Store(true)
+ c.storageNameToPath = nameToPath(storages)
return c
}
@@ -58,29 +61,36 @@ func (c *RepositoryCounter) Collect(metrics chan<- prometheus.Metric) {
func (c *RepositoryCounter) StartCountingRepositories(
ctx context.Context,
locator storage.Locator,
- storages []config.Storage,
logger log.FieldLogger,
) {
dontpanic.Go(func() {
- c.countRepositories(ctx, locator, storages, logger)
+ c.countRepositories(ctx, locator, logger)
})
}
func (c *RepositoryCounter) countRepositories(
ctx context.Context,
locator storage.Locator,
- storages []config.Storage,
logger log.FieldLogger,
) {
defer func() {
c.suppressMetric.Store(false)
}()
+ // Multiple storage names may share a single path, ensure we walk each path only once.
+ uniquePaths := make(map[string]string)
+ for name, path := range c.storageNameToPath {
+ _, ok := uniquePaths[path]
+ if !ok {
+ uniquePaths[path] = name
+ }
+ }
+
logger.Info("counting repositories")
totalStart := time.Now()
- for _, stor := range storages {
- logger.Infof("starting to count repositories in storage %q", stor.Name)
+ for storPath, name := range uniquePaths {
+ logger.Infof("starting to count repositories in path %q", storPath)
storageStart := time.Now()
paths := make(map[string]float64)
@@ -89,24 +99,23 @@ func (c *RepositoryCounter) countRepositories(
if err != nil {
// Encountering a malformed path should not block us from continuing
// to count.
- logger.WithError(err).Warnf("counting repositories: walking storage %q", stor)
+ logger.WithError(err).Warnf("counting repositories: walking path %q", storPath)
return nil
}
paths[prefix]++
return nil
}
- ctx := context.Background()
- if err := walk.FindRepositories(ctx, locator, stor.Name, incrementPrefix); err != nil {
- log.WithError(err).Errorf("failed to count repositories in storage %q", stor.Name)
+ if err := walk.FindRepositories(ctx, locator, name, incrementPrefix); err != nil {
+ log.WithError(err).Errorf("failed to count repositories in path %q", storPath)
}
for prefix, ct := range paths {
- c.reposTotal.WithLabelValues(stor.Name, prefix).Add(ct)
+ c.reposTotal.WithLabelValues(storPath, prefix).Add(ct)
}
- logger.Infof("completed counting repositories in storage %q after %s", stor.Name, time.Since(storageStart))
+ logger.Infof("completed counting repositories in path %q after %s", storPath, time.Since(storageStart))
}
logger.Infof("completed counting all repositories after %s", time.Since(totalStart))
@@ -123,8 +132,10 @@ func (c *RepositoryCounter) Decrement(repo storage.Repository) {
}
// DeleteStorage removes metrics associated with a storage.
-func (c *RepositoryCounter) DeleteStorage(storage string) {
- c.reposTotal.DeletePartialMatch(prometheus.Labels{"storage": storage})
+func (c *RepositoryCounter) DeleteStorage(storageName string) {
+ if path, exist := c.storageNameToPath[storageName]; exist {
+ c.reposTotal.DeletePartialMatch(prometheus.Labels{"path": path})
+ }
}
func (c *RepositoryCounter) add(repo storage.Repository, ct float64) {
@@ -132,8 +143,20 @@ func (c *RepositoryCounter) add(repo storage.Repository, ct float64) {
if err != nil {
return
}
+ if path, exist := c.storageNameToPath[repo.GetStorageName()]; exist {
+ c.reposTotal.WithLabelValues(path, prefix).Add(ct)
+ }
+}
+
+// Create a map of storage name to paths for quick lookup.
+func nameToPath(storages []config.Storage) map[string]string {
+ m := make(map[string]string)
+
+ for _, stor := range storages {
+ m[stor.Name] = stor.Path
+ }
- c.reposTotal.WithLabelValues(repo.GetStorageName(), prefix).Add(ct)
+ return m
}
func getPrefix(path string) (string, error) {
diff --git a/internal/gitaly/storage/counter/counter_test.go b/internal/gitaly/storage/counter/counter_test.go
index f06692041..78d80e0cb 100644
--- a/internal/gitaly/storage/counter/counter_test.go
+++ b/internal/gitaly/storage/counter/counter_test.go
@@ -16,8 +16,8 @@ import (
)
type metric struct {
- storage, prefix string
- count int
+ path, prefix string
+ count int
}
func TestCountStorages(t *testing.T) {
@@ -26,46 +26,79 @@ func TestCountStorages(t *testing.T) {
for _, tc := range []struct {
name string
storageNames []string
+ sharedPath bool
repos []string
files []string
- expectedMetrics []metric
+ expectedMetrics func(map[string]string) []metric
}{
{
name: "non-praefect paths",
storageNames: []string{"foo"},
repos: []string{"@hashed/aa/bb/repo-1.git", "@hashed/01/23/repo-2.git"},
- expectedMetrics: []metric{
- {storage: "foo", prefix: "@hashed", count: 2},
+ expectedMetrics: func(storagePaths map[string]string) []metric {
+ path, ok := storagePaths["foo"]
+ require.True(t, ok)
+ return []metric{{path: path, prefix: "@hashed", count: 2}}
},
},
{
name: "multiple prefixes",
storageNames: []string{"foo"},
repos: []string{"@hashed/aa/bb/repo-1.git", "@snippets/01/23/repo-2.git", "@groups/ee/ff/wiki.git"},
- expectedMetrics: []metric{
- {storage: "foo", prefix: "@hashed", count: 1},
- {storage: "foo", prefix: "@snippets", count: 1},
- {storage: "foo", prefix: "@groups", count: 1},
+ expectedMetrics: func(storagePaths map[string]string) []metric {
+ path, ok := storagePaths["foo"]
+ require.True(t, ok)
+ return []metric{
+ {path: path, prefix: "@hashed", count: 1},
+ {path: path, prefix: "@snippets", count: 1},
+ {path: path, prefix: "@groups", count: 1},
+ }
},
},
{
name: "@cluster paths",
storageNames: []string{"foo"},
repos: []string{"@cluster/bar/01/23/1234", "@cluster/baz/45/67/89ab"},
- expectedMetrics: []metric{
- {storage: "foo", prefix: "bar", count: 1},
- {storage: "foo", prefix: "baz", count: 1},
+ expectedMetrics: func(storagePaths map[string]string) []metric {
+ path, ok := storagePaths["foo"]
+ require.True(t, ok)
+ return []metric{
+ {path: path, prefix: "bar", count: 1},
+ {path: path, prefix: "baz", count: 1},
+ }
},
},
{
name: "multiple storages",
storageNames: []string{"foo", "bar"},
repos: []string{"@hashed/aa/bb/repo-1.git", "@snippets/01/23/repo-2.git"},
- expectedMetrics: []metric{
- {storage: "foo", prefix: "@hashed", count: 1},
- {storage: "foo", prefix: "@snippets", count: 1},
- {storage: "bar", prefix: "@hashed", count: 1},
- {storage: "bar", prefix: "@snippets", count: 1},
+ expectedMetrics: func(storagePaths map[string]string) []metric {
+ fooPath, ok := storagePaths["foo"]
+ require.True(t, ok)
+ barPath, ok := storagePaths["bar"]
+ require.True(t, ok)
+
+ return []metric{
+ {path: fooPath, prefix: "@hashed", count: 1},
+ {path: fooPath, prefix: "@snippets", count: 1},
+ {path: barPath, prefix: "@hashed", count: 1},
+ {path: barPath, prefix: "@snippets", count: 1},
+ }
+ },
+ },
+ {
+ name: "storages sharing a path are deduped",
+ storageNames: []string{"foo", "bar"},
+ sharedPath: true,
+ repos: []string{"@hashed/aa/bb/repo-1.git", "@snippets/01/23/repo-2.git"},
+ expectedMetrics: func(storagePaths map[string]string) []metric {
+ fooPath, ok := storagePaths["foo"]
+ require.True(t, ok)
+
+ return []metric{
+ {path: fooPath, prefix: "@hashed", count: 1},
+ {path: fooPath, prefix: "@snippets", count: 1},
+ }
},
},
} {
@@ -73,12 +106,15 @@ func TestCountStorages(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
-
ctx := testhelper.Context(t)
cfg := testcfg.Build(t, testcfg.WithStorages(tc.storageNames[0], tc.storageNames[1:]...))
locator := config.NewLocator(cfg)
logger := testhelper.NewDiscardingLogger(t)
+ if tc.sharedPath {
+ cfg.Storages[1].Path = cfg.Storages[0].Path
+ }
+
for _, storage := range cfg.Storages {
for _, path := range tc.repos {
_, _ = gittest.CreateRepository(t, ctx, cfg, gittest.CreateRepositoryConfig{
@@ -89,12 +125,14 @@ func TestCountStorages(t *testing.T) {
}
}
- c := NewRepositoryCounter()
+ c := NewRepositoryCounter(cfg.Storages)
+
+ c.countRepositories(ctx, locator, logger)
- c.countRepositories(ctx, locator, cfg.Storages, logger)
+ storages := nameToPath(cfg.Storages)
require.NoError(t, testutil.CollectAndCompare(
- c, buildMetrics(t, tc.expectedMetrics), "gitaly_total_repositories_count"))
+ c, buildMetrics(t, tc.expectedMetrics(storages)), "gitaly_total_repositories_count"))
})
}
}
@@ -103,6 +141,7 @@ func TestCounter(t *testing.T) {
t.Parallel()
const storageName = "default"
+ const storagePath = "/repos"
repo := &gitalypb.Repository{
StorageName: storageName,
RelativePath: gittest.NewRepositoryName(t),
@@ -119,14 +158,14 @@ func TestCounter(t *testing.T) {
setup: func(t *testing.T, c *RepositoryCounter) {
c.Increment(repo)
},
- expectedMetrics: []metric{{storage: storageName, prefix: "@hashed", count: 1}},
+ expectedMetrics: []metric{{path: storagePath, prefix: "@hashed", count: 1}},
},
{
name: "decrement",
setup: func(t *testing.T, c *RepositoryCounter) {
c.Decrement(repo)
},
- expectedMetrics: []metric{{storage: storageName, prefix: "@hashed", count: -1}},
+ expectedMetrics: []metric{{path: storagePath, prefix: "@hashed", count: -1}},
},
{
name: "object pool",
@@ -137,7 +176,7 @@ func TestCounter(t *testing.T) {
}
c.Increment(poolRepo)
},
- expectedMetrics: []metric{{storage: storageName, prefix: "@pools", count: 1}},
+ expectedMetrics: []metric{{path: storagePath, prefix: "@pools", count: 1}},
},
{
name: "praefect",
@@ -154,8 +193,8 @@ func TestCounter(t *testing.T) {
c.Increment(praefectPool)
},
expectedMetrics: []metric{
- {storage: storageName, prefix: "repositories", count: 1},
- {storage: storageName, prefix: "pools", count: 1},
+ {path: storagePath, prefix: "repositories", count: 1},
+ {path: storagePath, prefix: "pools", count: 1},
},
},
{
@@ -170,7 +209,7 @@ func TestCounter(t *testing.T) {
c.DeleteStorage(storageName)
},
- expectedMetrics: []metric{{storage: "other", prefix: "@hashed", count: 1}},
+ expectedMetrics: []metric{{path: "/other", prefix: "@hashed", count: 1}},
},
{
name: "invalid path ignored",
@@ -195,7 +234,11 @@ func TestCounter(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
- c := NewRepositoryCounter()
+ storages := []config.Storage{
+ {Name: storageName, Path: storagePath},
+ {Name: "other", Path: "/other"},
+ }
+ c := NewRepositoryCounter(storages)
c.suppressMetric.Store(tc.suppress)
tc.setup(t, c)
@@ -210,14 +253,14 @@ func buildMetrics(t *testing.T, metrics []metric) *strings.Reader {
t.Helper()
var builder strings.Builder
- _, err := builder.WriteString("# HELP gitaly_total_repositories_count Gauge of number of repositories by storage and path\n")
+ _, err := builder.WriteString("# HELP gitaly_total_repositories_count Gauge of number of repositories by storage path and repository prefix\n")
require.NoError(t, err)
_, err = builder.WriteString("# TYPE gitaly_total_repositories_count gauge\n")
require.NoError(t, err)
for _, item := range metrics {
- _, err = builder.WriteString(fmt.Sprintf("gitaly_total_repositories_count{prefix=%q, storage=%q} %d\n",
- item.prefix, item.storage, item.count))
+ _, err = builder.WriteString(fmt.Sprintf("gitaly_total_repositories_count{path=%q, prefix=%q} %d\n",
+ item.path, item.prefix, item.count))
require.NoError(t, err)
}
return strings.NewReader(builder.String())
diff --git a/internal/testhelper/testserver/gitaly.go b/internal/testhelper/testserver/gitaly.go
index 4f17f66c4..0ce7dbb59 100644
--- a/internal/testhelper/testserver/gitaly.go
+++ b/internal/testhelper/testserver/gitaly.go
@@ -341,7 +341,7 @@ func (gsd *gitalyServerDeps) createDependencies(ctx context.Context, tb testing.
}
if gsd.repositoryCounter == nil {
- gsd.repositoryCounter = counter.NewRepositoryCounter()
+ gsd.repositoryCounter = counter.NewRepositoryCounter(cfg.Storages)
}
if gsd.git2goExecutor == nil {