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:
authorPatrick Steinhardt <psteinhardt@gitlab.com>2023-01-30 10:21:21 +0300
committerPatrick Steinhardt <psteinhardt@gitlab.com>2023-01-30 10:21:21 +0300
commit9e9717d60e83bc08e6bcb3ca0e636edf1668a9b3 (patch)
tree56c4c2f19c17014f00eec60fc5063a8ace2d039f
parenta966c74ae41b0c749ea0433501cc39dbff96ce3f (diff)
parentef10b419db050e8a43d2c345bce167bc0a1d15ce (diff)
Merge branch 'pks-housekeeping-write-multi-pack-index' into 'master'
housekeeping: Enable writing of multi-pack-indices Closes #2661 See merge request https://gitlab.com/gitlab-org/gitaly/-/merge_requests/5275 Merged-by: Patrick Steinhardt <psteinhardt@gitlab.com> Approved-by: Will Chandler <wchandler@gitlab.com> Approved-by: Justin Tobler <jtobler@gitlab.com> Reviewed-by: Patrick Steinhardt <psteinhardt@gitlab.com> Reviewed-by: Will Chandler <wchandler@gitlab.com>
-rw-r--r--internal/git/housekeeping/optimization_strategy.go37
-rw-r--r--internal/git/housekeeping/optimization_strategy_test.go92
-rw-r--r--internal/git/housekeeping/optimize_repository.go4
-rw-r--r--internal/git/housekeeping/optimize_repository_ext_test.go105
-rw-r--r--internal/git/housekeeping/optimize_repository_test.go296
-rw-r--r--internal/git/objectpool/fetch_test.go32
-rw-r--r--internal/gitaly/service/objectpool/fetch_into_object_pool_test.go20
-rw-r--r--internal/gitaly/service/repository/optimize_test.go5
-rw-r--r--internal/metadata/featureflag/ff_write_multi_pack_index.go10
9 files changed, 474 insertions, 127 deletions
diff --git a/internal/git/housekeeping/optimization_strategy.go b/internal/git/housekeeping/optimization_strategy.go
index e1c7df86b..4fe51a863 100644
--- a/internal/git/housekeeping/optimization_strategy.go
+++ b/internal/git/housekeeping/optimization_strategy.go
@@ -5,6 +5,7 @@ import (
"math"
"gitlab.com/gitlab-org/gitaly/v15/internal/git/stats"
+ "gitlab.com/gitlab-org/gitaly/v15/internal/metadata/featureflag"
)
// OptimizationStrategy is an interface to determine which parts of a repository should be
@@ -42,7 +43,7 @@ func NewHeuristicalOptimizationStrategy(info stats.RepositoryInfo) HeuristicalOp
// ShouldRepackObjects checks whether the repository's objects need to be repacked. This uses a
// set of heuristics that scales with the size of the object database: the larger the repository,
// the less frequent does it get a full repack.
-func (s HeuristicalOptimizationStrategy) ShouldRepackObjects(context.Context) (bool, RepackObjectsConfig) {
+func (s HeuristicalOptimizationStrategy) ShouldRepackObjects(ctx context.Context) (bool, RepackObjectsConfig) {
// If there are neither packfiles nor loose objects in this repository then there is no need
// to repack anything.
if s.info.Packfiles.Count == 0 && s.info.LooseObjects.Count == 0 {
@@ -59,7 +60,12 @@ func (s HeuristicalOptimizationStrategy) ShouldRepackObjects(context.Context) (b
// a bitmap on their own. We do not yet use multi-pack indices, and in that case Git can
// only use one bitmap. We already generate this bitmap in the pool, so member of it
// shouldn't have another bitmap on their own.
- if !s.info.Packfiles.Bitmap.Exists && len(s.info.Alternates) == 0 {
+ //
+ // Addendum: this isn't true anymore as we're rolling out multi-pack-indices. For one, they
+ // allow us to have bitmaps even in repositories that are linked via alternates. And second,
+ // they don't require a full repack in order to write the bitmap. So once we have fully
+ // rolled out multi-pack-indices this whole condition can go away.
+ if featureflag.WriteMultiPackIndex.IsDisabled(ctx) && !s.info.Packfiles.Bitmap.Exists && len(s.info.Alternates) == 0 {
return true, RepackObjectsConfig{
FullRepack: true,
WriteBitmap: true,
@@ -107,8 +113,9 @@ func (s HeuristicalOptimizationStrategy) ShouldRepackObjects(context.Context) (b
if uint64(math.Max(lowerLimit,
math.Log(float64(s.info.Packfiles.Size/1024/1024))/math.Log(log))) <= s.info.Packfiles.Count {
return true, RepackObjectsConfig{
- FullRepack: true,
- WriteBitmap: len(s.info.Alternates) == 0,
+ FullRepack: true,
+ WriteBitmap: len(s.info.Alternates) == 0 || featureflag.WriteMultiPackIndex.IsEnabled(ctx),
+ WriteMultiPackIndex: featureflag.WriteMultiPackIndex.IsEnabled(ctx),
}
}
@@ -127,8 +134,19 @@ func (s HeuristicalOptimizationStrategy) ShouldRepackObjects(context.Context) (b
// it is necessary on the client side. We thus take a much stricter limit of 1024 objects.
if s.info.LooseObjects.Count > looseObjectLimit {
return true, RepackObjectsConfig{
- FullRepack: false,
- WriteBitmap: false,
+ FullRepack: false,
+ WriteBitmap: featureflag.WriteMultiPackIndex.IsEnabled(ctx),
+ WriteMultiPackIndex: featureflag.WriteMultiPackIndex.IsEnabled(ctx),
+ }
+ }
+
+ // In case both packfiles and loose objects are in a good state, but we don't yet have a
+ // multi-pack-index we perform an incremental repack to generate one.
+ if featureflag.WriteMultiPackIndex.IsEnabled(ctx) && !s.info.Packfiles.HasMultiPackIndex {
+ return true, RepackObjectsConfig{
+ FullRepack: false,
+ WriteBitmap: true,
+ WriteMultiPackIndex: true,
}
}
@@ -246,10 +264,11 @@ func NewEagerOptimizationStrategy(info stats.RepositoryInfo) EagerOptimizationSt
// ShouldRepackObjects always instructs the caller to repack objects. The strategy will always be to
// repack all objects into a single packfile. The bitmap will be written in case the repository does
// not have any alterantes.
-func (s EagerOptimizationStrategy) ShouldRepackObjects(context.Context) (bool, RepackObjectsConfig) {
+func (s EagerOptimizationStrategy) ShouldRepackObjects(ctx context.Context) (bool, RepackObjectsConfig) {
return true, RepackObjectsConfig{
- FullRepack: true,
- WriteBitmap: len(s.info.Alternates) == 0,
+ FullRepack: true,
+ WriteBitmap: len(s.info.Alternates) == 0 || featureflag.WriteMultiPackIndex.IsEnabled(ctx),
+ WriteMultiPackIndex: featureflag.WriteMultiPackIndex.IsEnabled(ctx),
}
}
diff --git a/internal/git/housekeeping/optimization_strategy_test.go b/internal/git/housekeeping/optimization_strategy_test.go
index 405b3de84..b1ba6cc54 100644
--- a/internal/git/housekeeping/optimization_strategy_test.go
+++ b/internal/git/housekeeping/optimization_strategy_test.go
@@ -7,13 +7,17 @@ import (
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitaly/v15/internal/git/stats"
+ "gitlab.com/gitlab-org/gitaly/v15/internal/metadata/featureflag"
"gitlab.com/gitlab-org/gitaly/v15/internal/testhelper"
)
func TestHeuristicalOptimizationStrategy_ShouldRepackObjects(t *testing.T) {
t.Parallel()
+ testhelper.NewFeatureSets(featureflag.WriteMultiPackIndex).Run(t, testHeuristicalOptimizationStrategyShouldRepackObjects)
+}
- ctx := testhelper.Context(t)
+func testHeuristicalOptimizationStrategyShouldRepackObjects(t *testing.T, ctx context.Context) {
+ t.Parallel()
for _, tc := range []struct {
desc string
@@ -40,8 +44,9 @@ func TestHeuristicalOptimizationStrategy_ShouldRepackObjects(t *testing.T) {
},
expectedNeeded: true,
expectedConfig: RepackObjectsConfig{
- FullRepack: true,
- WriteBitmap: true,
+ FullRepack: featureflag.WriteMultiPackIndex.IsDisabled(ctx),
+ WriteBitmap: true,
+ WriteMultiPackIndex: featureflag.WriteMultiPackIndex.IsEnabled(ctx),
},
},
{
@@ -60,10 +65,23 @@ func TestHeuristicalOptimizationStrategy_ShouldRepackObjects(t *testing.T) {
// If we have no bitmap in the repository we'd normally want to fully repack
// the repository. But because we have an alternates file we know that the
// repository must not have a bitmap anyway, so we can skip the repack here.
- expectedNeeded: false,
+ //
+ // This changes though with multi-pack-indices, which allow for bitmaps to
+ // exist in pooled repositories.
+ expectedNeeded: featureflag.WriteMultiPackIndex.IsEnabled(ctx),
+ expectedConfig: func() RepackObjectsConfig {
+ if featureflag.WriteMultiPackIndex.IsEnabled(ctx) {
+ return RepackObjectsConfig{
+ WriteBitmap: true,
+ WriteMultiPackIndex: true,
+ }
+ }
+
+ return RepackObjectsConfig{}
+ }(),
},
{
- desc: "no repack needed",
+ desc: "no repack needed without multi-pack-index",
strategy: HeuristicalOptimizationStrategy{
info: stats.RepositoryInfo{
Packfiles: stats.PackfilesInfo{
@@ -74,7 +92,41 @@ func TestHeuristicalOptimizationStrategy_ShouldRepackObjects(t *testing.T) {
},
},
},
- expectedNeeded: false,
+ expectedNeeded: featureflag.WriteMultiPackIndex.IsEnabled(ctx),
+ expectedConfig: func() RepackObjectsConfig {
+ if featureflag.WriteMultiPackIndex.IsEnabled(ctx) {
+ return RepackObjectsConfig{
+ FullRepack: false,
+ WriteBitmap: true,
+ WriteMultiPackIndex: true,
+ }
+ }
+
+ return RepackObjectsConfig{}
+ }(),
+ },
+ {
+ desc: "no repack needed with multi-pack-index",
+ strategy: HeuristicalOptimizationStrategy{
+ info: stats.RepositoryInfo{
+ Packfiles: stats.PackfilesInfo{
+ Count: 1,
+ HasMultiPackIndex: true,
+ },
+ },
+ },
+ expectedNeeded: featureflag.WriteMultiPackIndex.IsDisabled(ctx),
+ expectedConfig: func() RepackObjectsConfig {
+ if featureflag.WriteMultiPackIndex.IsDisabled(ctx) {
+ return RepackObjectsConfig{
+ FullRepack: true,
+ WriteBitmap: true,
+ WriteMultiPackIndex: false,
+ }
+ }
+
+ return RepackObjectsConfig{}
+ }(),
},
} {
t.Run(tc.desc, func(t *testing.T) {
@@ -159,6 +211,7 @@ func TestHeuristicalOptimizationStrategy_ShouldRepackObjects(t *testing.T) {
Bitmap: stats.BitmapInfo{
Exists: true,
},
+ HasMultiPackIndex: true,
},
Alternates: tc.alternates,
},
@@ -174,8 +227,9 @@ func TestHeuristicalOptimizationStrategy_ShouldRepackObjects(t *testing.T) {
repackNeeded, repackCfg := strategy.ShouldRepackObjects(ctx)
require.True(t, repackNeeded)
require.Equal(t, RepackObjectsConfig{
- FullRepack: true,
- WriteBitmap: len(tc.alternates) == 0,
+ FullRepack: true,
+ WriteBitmap: len(tc.alternates) == 0 || featureflag.WriteMultiPackIndex.IsEnabled(ctx),
+ WriteMultiPackIndex: featureflag.WriteMultiPackIndex.IsEnabled(ctx),
}, repackCfg)
})
}
@@ -234,6 +288,7 @@ func TestHeuristicalOptimizationStrategy_ShouldRepackObjects(t *testing.T) {
Bitmap: stats.BitmapInfo{
Exists: true,
},
+ HasMultiPackIndex: true,
},
},
}
@@ -241,8 +296,9 @@ func TestHeuristicalOptimizationStrategy_ShouldRepackObjects(t *testing.T) {
repackNeeded, repackCfg := strategy.ShouldRepackObjects(ctx)
require.Equal(t, outerTC.expectedRepack, repackNeeded)
require.Equal(t, RepackObjectsConfig{
- FullRepack: false,
- WriteBitmap: false,
+ FullRepack: false,
+ WriteBitmap: repackNeeded && featureflag.WriteMultiPackIndex.IsEnabled(ctx),
+ WriteMultiPackIndex: repackNeeded && featureflag.WriteMultiPackIndex.IsEnabled(ctx),
}, repackCfg)
})
}
@@ -527,8 +583,11 @@ func TestHeuristicalOptimizationStrategy_NeedsWriteCommitGraph(t *testing.T) {
func TestEagerOptimizationStrategy(t *testing.T) {
t.Parallel()
+ testhelper.NewFeatureSets(featureflag.WriteMultiPackIndex).Run(t, testEagerOptimizationStrategy)
+}
- ctx := testhelper.Context(t)
+func testEagerOptimizationStrategy(t *testing.T, ctx context.Context) {
+ t.Parallel()
for _, tc := range []struct {
desc string
@@ -548,6 +607,7 @@ func TestEagerOptimizationStrategy(t *testing.T) {
Alternates: []string{"path/to/alternate"},
},
},
+ expectWriteBitmap: featureflag.WriteMultiPackIndex.IsEnabled(ctx),
expectShouldPruneObjects: true,
},
{
@@ -557,7 +617,8 @@ func TestEagerOptimizationStrategy(t *testing.T) {
IsObjectPool: true,
},
},
- expectWriteBitmap: true,
+ expectWriteBitmap: true,
+ expectShouldPruneObjects: false,
},
{
desc: "object pool with alternate",
@@ -567,14 +628,17 @@ func TestEagerOptimizationStrategy(t *testing.T) {
Alternates: []string{"path/to/alternate"},
},
},
+ expectWriteBitmap: featureflag.WriteMultiPackIndex.IsEnabled(ctx),
+ expectShouldPruneObjects: false,
},
} {
t.Run(tc.desc, func(t *testing.T) {
shouldRepackObjects, repackObjectsCfg := tc.strategy.ShouldRepackObjects(ctx)
require.True(t, shouldRepackObjects)
require.Equal(t, RepackObjectsConfig{
- FullRepack: true,
- WriteBitmap: tc.expectWriteBitmap,
+ FullRepack: true,
+ WriteBitmap: tc.expectWriteBitmap,
+ WriteMultiPackIndex: featureflag.WriteMultiPackIndex.IsEnabled(ctx),
}, repackObjectsCfg)
shouldWriteCommitGraph, writeCommitGraphCfg := tc.strategy.ShouldWriteCommitGraph(ctx)
diff --git a/internal/git/housekeeping/optimize_repository.go b/internal/git/housekeeping/optimize_repository.go
index 1238e74f7..4d9d5e50a 100644
--- a/internal/git/housekeeping/optimize_repository.go
+++ b/internal/git/housekeeping/optimize_repository.go
@@ -152,6 +152,7 @@ func optimizeRepository(
optimizations["packed_objects_full"] = "failure"
optimizations["packed_objects_incremental"] = "failure"
optimizations["written_bitmap"] = "failure"
+ optimizations["written_multi_pack_index"] = "failure"
return fmt.Errorf("could not repack: %w", err)
}
if didRepack {
@@ -163,6 +164,9 @@ func optimizeRepository(
if repackCfg.WriteBitmap {
optimizations["written_bitmap"] = "success"
}
+ if repackCfg.WriteMultiPackIndex {
+ optimizations["written_multi_pack_index"] = "success"
+ }
}
timer.ObserveDuration()
diff --git a/internal/git/housekeeping/optimize_repository_ext_test.go b/internal/git/housekeeping/optimize_repository_ext_test.go
index a956afb9d..087cf2fc1 100644
--- a/internal/git/housekeeping/optimize_repository_ext_test.go
+++ b/internal/git/housekeeping/optimize_repository_ext_test.go
@@ -25,7 +25,10 @@ import (
func TestPruneIfNeeded(t *testing.T) {
t.Parallel()
- testhelper.NewFeatureSets(featureflag.WriteBitmapLookupTable).Run(t, testPruneIfNeeded)
+ testhelper.NewFeatureSets(
+ featureflag.WriteBitmapLookupTable,
+ featureflag.WriteMultiPackIndex,
+ ).Run(t, testPruneIfNeeded)
}
func testPruneIfNeeded(t *testing.T, ctx context.Context) {
@@ -34,6 +37,13 @@ func testPruneIfNeeded(t *testing.T, ctx context.Context) {
cfg := testcfg.Build(t)
cfg.SocketPath = testserver.RunGitalyServer(t, cfg, nil, setup.RegisterAll)
+ midxEnabledOrDisabled := func(enabled, disabled map[string]string) map[string]string {
+ if featureflag.WriteMultiPackIndex.IsEnabled(ctx) {
+ return enabled
+ }
+ return disabled
+ }
+
for _, tc := range []struct {
desc string
isPool bool
@@ -51,20 +61,34 @@ func testPruneIfNeeded(t *testing.T, ctx context.Context) {
looseObjects: []string{
filepath.Join("ab/12345"),
},
- expectedLogEntries: map[string]string{
- "packed_objects_full": "success",
- "written_bitmap": "success",
- },
+ expectedLogEntries: midxEnabledOrDisabled(
+ map[string]string{
+ "packed_objects_incremental": "success",
+ "written_bitmap": "success",
+ "written_multi_pack_index": "success",
+ },
+ map[string]string{
+ "packed_objects_full": "success",
+ "written_bitmap": "success",
+ },
+ ),
},
{
desc: "object in 17 shard",
looseObjects: []string{
filepath.Join("17/12345"),
},
- expectedLogEntries: map[string]string{
- "packed_objects_full": "success",
- "written_bitmap": "success",
- },
+ expectedLogEntries: midxEnabledOrDisabled(
+ map[string]string{
+ "packed_objects_incremental": "success",
+ "written_bitmap": "success",
+ "written_multi_pack_index": "success",
+ },
+ map[string]string{
+ "packed_objects_full": "success",
+ "written_bitmap": "success",
+ },
+ ),
},
{
desc: "objects in different shards",
@@ -74,10 +98,17 @@ func testPruneIfNeeded(t *testing.T, ctx context.Context) {
filepath.Join("12/12345"),
filepath.Join("17/12345"),
},
- expectedLogEntries: map[string]string{
- "packed_objects_full": "success",
- "written_bitmap": "success",
- },
+ expectedLogEntries: midxEnabledOrDisabled(
+ map[string]string{
+ "packed_objects_incremental": "success",
+ "written_bitmap": "success",
+ "written_multi_pack_index": "success",
+ },
+ map[string]string{
+ "packed_objects_full": "success",
+ "written_bitmap": "success",
+ },
+ ),
},
{
desc: "exceeding boundary on pool",
@@ -93,10 +124,17 @@ func testPruneIfNeeded(t *testing.T, ctx context.Context) {
return looseObjects
}(),
- expectedLogEntries: map[string]string{
- "packed_objects_full": "success",
- "written_bitmap": "success",
- },
+ expectedLogEntries: midxEnabledOrDisabled(
+ map[string]string{
+ "packed_objects_incremental": "success",
+ "written_bitmap": "success",
+ "written_multi_pack_index": "success",
+ },
+ map[string]string{
+ "packed_objects_full": "success",
+ "written_bitmap": "success",
+ },
+ ),
},
{
desc: "on boundary shouldn't prune",
@@ -115,10 +153,17 @@ func testPruneIfNeeded(t *testing.T, ctx context.Context) {
t := time.Now().Add(stats.StaleObjectsGracePeriod).Add(-1 * time.Minute)
return &t
}(),
- expectedLogEntries: map[string]string{
- "packed_objects_full": "success",
- "written_bitmap": "success",
- },
+ expectedLogEntries: midxEnabledOrDisabled(
+ map[string]string{
+ "packed_objects_incremental": "success",
+ "written_bitmap": "success",
+ "written_multi_pack_index": "success",
+ },
+ map[string]string{
+ "packed_objects_full": "success",
+ "written_bitmap": "success",
+ },
+ ),
},
{
desc: "exceeding boundary should prune",
@@ -137,11 +182,19 @@ func testPruneIfNeeded(t *testing.T, ctx context.Context) {
t := time.Now().Add(stats.StaleObjectsGracePeriod).Add(-1 * time.Minute)
return &t
}(),
- expectedLogEntries: map[string]string{
- "packed_objects_full": "success",
- "pruned_objects": "success",
- "written_bitmap": "success",
- },
+ expectedLogEntries: midxEnabledOrDisabled(
+ map[string]string{
+ "packed_objects_incremental": "success",
+ "pruned_objects": "success",
+ "written_bitmap": "success",
+ "written_multi_pack_index": "success",
+ },
+ map[string]string{
+ "packed_objects_full": "success",
+ "pruned_objects": "success",
+ "written_bitmap": "success",
+ },
+ ),
},
} {
tc := tc
diff --git a/internal/git/housekeeping/optimize_repository_test.go b/internal/git/housekeeping/optimize_repository_test.go
index caddf732b..c13f08ead 100644
--- a/internal/git/housekeeping/optimize_repository_test.go
+++ b/internal/git/housekeeping/optimize_repository_test.go
@@ -28,7 +28,10 @@ import (
func TestRepackIfNeeded(t *testing.T) {
t.Parallel()
- testhelper.NewFeatureSets(featureflag.WriteBitmapLookupTable).Run(t, testRepackIfNeeded)
+ testhelper.NewFeatureSets(
+ featureflag.WriteBitmapLookupTable,
+ featureflag.WriteMultiPackIndex,
+ ).Run(t, testRepackIfNeeded)
}
func testRepackIfNeeded(t *testing.T, ctx context.Context) {
@@ -154,7 +157,10 @@ func TestPackRefsIfNeeded(t *testing.T) {
func TestOptimizeRepository(t *testing.T) {
t.Parallel()
- testhelper.NewFeatureSets(featureflag.WriteBitmapLookupTable).Run(t, testOptimizeRepository)
+ testhelper.NewFeatureSets(
+ featureflag.WriteBitmapLookupTable,
+ featureflag.WriteMultiPackIndex,
+ ).Run(t, testOptimizeRepository)
}
func testOptimizeRepository(t *testing.T, ctx context.Context) {
@@ -163,12 +169,24 @@ func testOptimizeRepository(t *testing.T, ctx context.Context) {
cfg := testcfg.Build(t)
txManager := transaction.NewManager(cfg, backchannel.NewRegistry())
+ type metric struct {
+ name, status string
+ count int
+ }
+
+ midxEnabledOrDisabled := func(enabled, disabled []metric) []metric {
+ if featureflag.WriteMultiPackIndex.IsEnabled(ctx) {
+ return enabled
+ }
+ return disabled
+ }
+
for _, tc := range []struct {
desc string
setup func(t *testing.T, relativePath string) *gitalypb.Repository
expectedErr error
- expectedMetrics string
- expectedMetricsForPool string
+ expectedMetrics []metric
+ expectedMetricsForPool []metric
}{
{
desc: "empty repository does nothing",
@@ -179,10 +197,9 @@ func testOptimizeRepository(t *testing.T, ctx context.Context) {
})
return repo
},
- expectedMetrics: `# HELP gitaly_housekeeping_tasks_total Total number of housekeeping tasks performed in the repository
-# TYPE gitaly_housekeeping_tasks_total counter
-gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
-`,
+ expectedMetrics: []metric{
+ {name: "total", status: "success", count: 1},
+ },
},
{
desc: "repository without bitmap repacks objects",
@@ -194,13 +211,21 @@ gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"))
return repo
},
- expectedMetrics: `# HELP gitaly_housekeeping_tasks_total Total number of housekeeping tasks performed in the repository
-# TYPE gitaly_housekeeping_tasks_total counter
-gitaly_housekeeping_tasks_total{housekeeping_task="packed_objects_full", status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="written_commit_graph_full", status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="written_bitmap", status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
-`,
+ expectedMetrics: midxEnabledOrDisabled(
+ []metric{
+ {name: "packed_objects_incremental", status: "success", count: 1},
+ {name: "written_commit_graph_full", status: "success", count: 1},
+ {name: "written_bitmap", status: "success", count: 1},
+ {name: "written_multi_pack_index", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ []metric{
+ {name: "packed_objects_full", status: "success", count: 1},
+ {name: "written_commit_graph_full", status: "success", count: 1},
+ {name: "written_bitmap", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ ),
},
{
desc: "repository without commit-graph writes commit-graph",
@@ -213,11 +238,19 @@ gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
gittest.Exec(t, cfg, "-C", repoPath, "repack", "-A", "-d", "--write-bitmap-index")
return repo
},
- expectedMetrics: `# HELP gitaly_housekeeping_tasks_total Total number of housekeeping tasks performed in the repository
-# TYPE gitaly_housekeeping_tasks_total counter
-gitaly_housekeeping_tasks_total{housekeeping_task="written_commit_graph_full", status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
-`,
+ expectedMetrics: midxEnabledOrDisabled(
+ []metric{
+ {name: "packed_objects_incremental", status: "success", count: 1},
+ {name: "written_bitmap", status: "success", count: 1},
+ {name: "written_commit_graph_full", status: "success", count: 1},
+ {name: "written_multi_pack_index", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ []metric{
+ {name: "written_commit_graph_full", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ ),
},
{
desc: "repository with commit-graph without generation data writes commit-graph",
@@ -231,11 +264,44 @@ gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
gittest.Exec(t, cfg, "-c", "commitGraph.generationVersion=1", "-C", repoPath, "commit-graph", "write", "--split", "--changed-paths")
return repo
},
- expectedMetrics: `# HELP gitaly_housekeeping_tasks_total Total number of housekeeping tasks performed in the repository
-# TYPE gitaly_housekeeping_tasks_total counter
-gitaly_housekeeping_tasks_total{housekeeping_task="written_commit_graph_full", status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
-`,
+ expectedMetrics: midxEnabledOrDisabled(
+ []metric{
+ {name: "packed_objects_incremental", status: "success", count: 1},
+ {name: "written_bitmap", status: "success", count: 1},
+ {name: "written_commit_graph_full", status: "success", count: 1},
+ {name: "written_multi_pack_index", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ []metric{
+ {name: "written_commit_graph_full", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ ),
+ },
+ {
+ desc: "repository without multi-pack-index performs incremental repack",
+ setup: func(t *testing.T, relativePath string) *gitalypb.Repository {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg, gittest.CreateRepositoryConfig{
+ SkipCreationViaService: true,
+ RelativePath: relativePath,
+ })
+ gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"))
+ gittest.Exec(t, cfg, "-C", repoPath, "repack", "-A", "-d", "-b")
+ return repo
+ },
+ expectedMetrics: midxEnabledOrDisabled(
+ []metric{
+ {name: "packed_objects_incremental", status: "success", count: 1},
+ {name: "written_bitmap", status: "success", count: 1},
+ {name: "written_commit_graph_full", status: "success", count: 1},
+ {name: "written_multi_pack_index", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ []metric{
+ {name: "written_commit_graph_full", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ ),
},
{
desc: "repository with multiple packfiles packs only for object pool",
@@ -257,17 +323,33 @@ gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
return repo
},
- expectedMetrics: `# HELP gitaly_housekeeping_tasks_total Total number of housekeeping tasks performed in the repository
-# TYPE gitaly_housekeeping_tasks_total counter
-gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
-`,
- expectedMetricsForPool: `# HELP gitaly_housekeeping_tasks_total Total number of housekeeping tasks performed in the repository
-# TYPE gitaly_housekeeping_tasks_total counter
-gitaly_housekeeping_tasks_total{housekeeping_task="packed_objects_full", status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="written_bitmap", status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="written_commit_graph_incremental", status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
-`,
+ expectedMetrics: midxEnabledOrDisabled(
+ []metric{
+ {name: "packed_objects_incremental", status: "success", count: 1},
+ {name: "written_bitmap", status: "success", count: 1},
+ {name: "written_commit_graph_incremental", status: "success", count: 1},
+ {name: "written_multi_pack_index", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ []metric{
+ {name: "total", status: "success", count: 1},
+ },
+ ),
+ expectedMetricsForPool: midxEnabledOrDisabled(
+ []metric{
+ {name: "packed_objects_full", status: "success", count: 1},
+ {name: "written_bitmap", status: "success", count: 1},
+ {name: "written_commit_graph_incremental", status: "success", count: 1},
+ {name: "written_multi_pack_index", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ []metric{
+ {name: "packed_objects_full", status: "success", count: 1},
+ {name: "written_bitmap", status: "success", count: 1},
+ {name: "written_commit_graph_incremental", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ ),
},
{
desc: "well-packed repository does not optimize",
@@ -281,10 +363,42 @@ gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
gittest.Exec(t, cfg, "-c", "commitGraph.generationVersion=2", "-C", repoPath, "commit-graph", "write", "--split", "--changed-paths")
return repo
},
- expectedMetrics: `# HELP gitaly_housekeeping_tasks_total Total number of housekeeping tasks performed in the repository
-# TYPE gitaly_housekeeping_tasks_total counter
-gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
-`,
+ expectedMetrics: midxEnabledOrDisabled(
+ []metric{
+ {name: "packed_objects_incremental", status: "success", count: 1},
+ {name: "written_bitmap", status: "success", count: 1},
+ {name: "written_commit_graph_incremental", status: "success", count: 1},
+ {name: "written_multi_pack_index", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ []metric{
+ {name: "total", status: "success", count: 1},
+ },
+ ),
+ },
+ {
+ desc: "well-packed repository with multi-pack-index does not optimize",
+ setup: func(t *testing.T, relativePath string) *gitalypb.Repository {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg, gittest.CreateRepositoryConfig{
+ SkipCreationViaService: true,
+ RelativePath: relativePath,
+ })
+ gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("main"))
+ gittest.Exec(t, cfg, "-C", repoPath, "repack", "-A", "-d", "--write-bitmap-index", "--write-midx")
+ gittest.Exec(t, cfg, "-c", "commitGraph.generationVersion=2", "-C", repoPath, "commit-graph", "write", "--split", "--changed-paths")
+ return repo
+ },
+ expectedMetrics: midxEnabledOrDisabled(
+ []metric{
+ {name: "total", status: "success", count: 1},
+ },
+ []metric{
+ {name: "packed_objects_full", status: "success", count: 1},
+ {name: "written_bitmap", status: "success", count: 1},
+ {name: "written_commit_graph_incremental", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ ),
},
{
desc: "recent loose objects don't get pruned",
@@ -314,12 +428,20 @@ gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
return repo
},
- expectedMetrics: `# HELP gitaly_housekeeping_tasks_total Total number of housekeeping tasks performed in the repository
-# TYPE gitaly_housekeeping_tasks_total counter
-gitaly_housekeeping_tasks_total{housekeeping_task="packed_objects_incremental", status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="written_commit_graph_incremental", status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
-`,
+ expectedMetrics: midxEnabledOrDisabled(
+ []metric{
+ {name: "packed_objects_incremental", status: "success", count: 1},
+ {name: "written_bitmap", status: "success", count: 1},
+ {name: "written_commit_graph_incremental", status: "success", count: 1},
+ {name: "written_multi_pack_index", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ []metric{
+ {name: "packed_objects_incremental", status: "success", count: 1},
+ {name: "written_commit_graph_incremental", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ ),
},
{
desc: "old loose objects get pruned",
@@ -346,20 +468,37 @@ gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
return repo
},
- expectedMetrics: `# HELP gitaly_housekeeping_tasks_total Total number of housekeeping tasks performed in the repository
-# TYPE gitaly_housekeeping_tasks_total counter
-gitaly_housekeeping_tasks_total{housekeeping_task="packed_objects_incremental", status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="written_commit_graph_full", status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="pruned_objects",status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
-`,
+ expectedMetrics: midxEnabledOrDisabled(
+ []metric{
+ {name: "packed_objects_incremental", status: "success", count: 1},
+ {name: "pruned_objects", status: "success", count: 1},
+ {name: "written_commit_graph_full", status: "success", count: 1},
+ {name: "written_bitmap", status: "success", count: 1},
+ {name: "written_multi_pack_index", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ []metric{
+ {name: "packed_objects_incremental", status: "success", count: 1},
+ {name: "written_commit_graph_full", status: "success", count: 1},
+ {name: "pruned_objects", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ ),
// Object pools never prune objects.
- expectedMetricsForPool: `# HELP gitaly_housekeeping_tasks_total Total number of housekeeping tasks performed in the repository
-# TYPE gitaly_housekeeping_tasks_total counter
-gitaly_housekeeping_tasks_total{housekeeping_task="packed_objects_incremental", status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="written_commit_graph_incremental", status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
-`,
+ expectedMetricsForPool: midxEnabledOrDisabled(
+ []metric{
+ {name: "packed_objects_incremental", status: "success", count: 1},
+ {name: "written_bitmap", status: "success", count: 1},
+ {name: "written_commit_graph_incremental", status: "success", count: 1},
+ {name: "written_multi_pack_index", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ []metric{
+ {name: "packed_objects_incremental", status: "success", count: 1},
+ {name: "written_commit_graph_incremental", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ ),
},
{
desc: "loose refs get packed",
@@ -378,11 +517,20 @@ gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
return repo
},
- expectedMetrics: `# HELP gitaly_housekeeping_tasks_total Total number of housekeeping tasks performed in the repository
-# TYPE gitaly_housekeeping_tasks_total counter
-gitaly_housekeeping_tasks_total{housekeeping_task="packed_refs", status="success"} 1
-gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
-`,
+ expectedMetrics: midxEnabledOrDisabled(
+ []metric{
+ {name: "packed_objects_incremental", status: "success", count: 1},
+ {name: "packed_refs", status: "success", count: 1},
+ {name: "written_commit_graph_incremental", status: "success", count: 1},
+ {name: "written_bitmap", status: "success", count: 1},
+ {name: "written_multi_pack_index", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ []metric{
+ {name: "packed_refs", status: "success", count: 1},
+ {name: "total", status: "success", count: 1},
+ },
+ ),
},
} {
tc := tc
@@ -399,14 +547,26 @@ gitaly_housekeeping_tasks_total{housekeeping_task="total", status="success"} 1
require.Equal(t, tc.expectedErr, err)
expectedMetrics := tc.expectedMetrics
- if stats.IsPoolRepository(repoProto) && tc.expectedMetricsForPool != "" {
+ if stats.IsPoolRepository(repoProto) && tc.expectedMetricsForPool != nil {
expectedMetrics = tc.expectedMetricsForPool
}
+ var buf bytes.Buffer
+ _, err = buf.WriteString("# HELP gitaly_housekeeping_tasks_total Total number of housekeeping tasks performed in the repository\n")
+ require.NoError(t, err)
+ _, err = buf.WriteString("# TYPE gitaly_housekeeping_tasks_total counter\n")
+ require.NoError(t, err)
+
+ for _, metric := range expectedMetrics {
+ _, err := buf.WriteString(fmt.Sprintf(
+ "gitaly_housekeeping_tasks_total{housekeeping_task=%q, status=%q} %d\n",
+ metric.name, metric.status, metric.count,
+ ))
+ require.NoError(t, err)
+ }
+
require.NoError(t, testutil.CollectAndCompare(
- manager.tasksTotal,
- bytes.NewBufferString(expectedMetrics),
- "gitaly_housekeeping_tasks_total",
+ manager.tasksTotal, &buf, "gitaly_housekeeping_tasks_total",
))
})
}
diff --git a/internal/git/objectpool/fetch_test.go b/internal/git/objectpool/fetch_test.go
index 06e097a36..8ca529aa3 100644
--- a/internal/git/objectpool/fetch_test.go
+++ b/internal/git/objectpool/fetch_test.go
@@ -24,7 +24,10 @@ import (
func TestFetchFromOrigin_dangling(t *testing.T) {
t.Parallel()
- testhelper.NewFeatureSets(featureflag.WriteBitmapLookupTable).Run(t, testFetchFromOriginDangling)
+ testhelper.NewFeatureSets(
+ featureflag.WriteBitmapLookupTable,
+ featureflag.WriteMultiPackIndex,
+ ).Run(t, testFetchFromOriginDangling)
}
func testFetchFromOriginDangling(t *testing.T, ctx context.Context) {
@@ -113,7 +116,10 @@ func TestFetchFromOrigin_fsck(t *testing.T) {
func TestFetchFromOrigin_deltaIslands(t *testing.T) {
t.Parallel()
- testhelper.NewFeatureSets(featureflag.WriteBitmapLookupTable).Run(t, testFetchFromOriginDeltaIslands)
+ testhelper.NewFeatureSets(
+ featureflag.WriteBitmapLookupTable,
+ featureflag.WriteMultiPackIndex,
+ ).Run(t, testFetchFromOriginDeltaIslands)
}
func testFetchFromOriginDeltaIslands(t *testing.T, ctx context.Context) {
@@ -126,6 +132,13 @@ func testFetchFromOriginDeltaIslands(t *testing.T, ctx context.Context) {
require.NoError(t, pool.FetchFromOrigin(ctx, repo), "seed pool")
require.NoError(t, pool.Link(ctx, repo))
+ // With multi-pack-indices we don't do full repacks of repositories that
+ // aggressively anymore, but in order to test delta islands we need to trigger one.
+ // We thus write a second packfile so that `OptimizeRepository()` decides to
+ // rewrite packfiles.
+ gittest.WriteCommit(t, cfg, poolPath, gittest.WithBranch("irrelevant"))
+ gittest.Exec(t, cfg, "-C", poolPath, "repack")
+
// The setup of delta islands is done in the normal repository, and thus we pass `false`
// for `isPoolRepo`. Verification whether we correctly handle repacking though happens in
// the pool repository.
@@ -136,7 +149,10 @@ func testFetchFromOriginDeltaIslands(t *testing.T, ctx context.Context) {
func TestFetchFromOrigin_bitmapHashCache(t *testing.T) {
t.Parallel()
- testhelper.NewFeatureSets(featureflag.WriteBitmapLookupTable).Run(t, testFetchFromOriginBitmapHashCache)
+ testhelper.NewFeatureSets(
+ featureflag.WriteBitmapLookupTable,
+ featureflag.WriteMultiPackIndex,
+ ).Run(t, testFetchFromOriginBitmapHashCache)
}
func testFetchFromOriginBitmapHashCache(t *testing.T, ctx context.Context) {
@@ -166,7 +182,10 @@ func testFetchFromOriginBitmapHashCache(t *testing.T, ctx context.Context) {
func TestFetchFromOrigin_refUpdates(t *testing.T) {
t.Parallel()
- testhelper.NewFeatureSets(featureflag.WriteBitmapLookupTable).Run(t, testFetchFromOriginRefUpdates)
+ testhelper.NewFeatureSets(
+ featureflag.WriteBitmapLookupTable,
+ featureflag.WriteMultiPackIndex,
+ ).Run(t, testFetchFromOriginRefUpdates)
}
func testFetchFromOriginRefUpdates(t *testing.T, ctx context.Context) {
@@ -217,7 +236,10 @@ func testFetchFromOriginRefUpdates(t *testing.T, ctx context.Context) {
func TestFetchFromOrigin_refs(t *testing.T) {
t.Parallel()
- testhelper.NewFeatureSets(featureflag.WriteBitmapLookupTable).Run(t, testFetchFromOriginRefs)
+ testhelper.NewFeatureSets(
+ featureflag.WriteBitmapLookupTable,
+ featureflag.WriteMultiPackIndex,
+ ).Run(t, testFetchFromOriginRefs)
}
func testFetchFromOriginRefs(t *testing.T, ctx context.Context) {
diff --git a/internal/gitaly/service/objectpool/fetch_into_object_pool_test.go b/internal/gitaly/service/objectpool/fetch_into_object_pool_test.go
index a50f49e00..2d9bec21c 100644
--- a/internal/gitaly/service/objectpool/fetch_into_object_pool_test.go
+++ b/internal/gitaly/service/objectpool/fetch_into_object_pool_test.go
@@ -38,7 +38,10 @@ import (
func TestFetchIntoObjectPool_Success(t *testing.T) {
t.Parallel()
- testhelper.NewFeatureSets(featureflag.WriteBitmapLookupTable).Run(t, testFetchIntoObjectPoolSuccess)
+ testhelper.NewFeatureSets(
+ featureflag.WriteBitmapLookupTable,
+ featureflag.WriteMultiPackIndex,
+ ).Run(t, testFetchIntoObjectPoolSuccess)
}
func testFetchIntoObjectPoolSuccess(t *testing.T, ctx context.Context) {
@@ -91,7 +94,10 @@ func testFetchIntoObjectPoolSuccess(t *testing.T, ctx context.Context) {
func TestFetchIntoObjectPool_transactional(t *testing.T) {
t.Parallel()
- testhelper.NewFeatureSets(featureflag.WriteBitmapLookupTable).Run(t, testFetchIntoObjectPoolTransactional)
+ testhelper.NewFeatureSets(
+ featureflag.WriteBitmapLookupTable,
+ featureflag.WriteMultiPackIndex,
+ ).Run(t, testFetchIntoObjectPoolTransactional)
}
func testFetchIntoObjectPoolTransactional(t *testing.T, ctx context.Context) {
@@ -207,7 +213,10 @@ func testFetchIntoObjectPoolTransactional(t *testing.T, ctx context.Context) {
func TestFetchIntoObjectPool_CollectLogStatistics(t *testing.T) {
t.Parallel()
- testhelper.NewFeatureSets(featureflag.WriteBitmapLookupTable).Run(t, testFetchIntoObjectPoolCollectLogStatistics)
+ testhelper.NewFeatureSets(
+ featureflag.WriteBitmapLookupTable,
+ featureflag.WriteMultiPackIndex,
+ ).Run(t, testFetchIntoObjectPoolCollectLogStatistics)
}
func testFetchIntoObjectPoolCollectLogStatistics(t *testing.T, ctx context.Context) {
@@ -303,7 +312,10 @@ func TestFetchIntoObjectPool_Failure(t *testing.T) {
func TestFetchIntoObjectPool_dfConflict(t *testing.T) {
t.Parallel()
- testhelper.NewFeatureSets(featureflag.WriteBitmapLookupTable).Run(t, testFetchIntoObjectPoolDfConflict)
+ testhelper.NewFeatureSets(
+ featureflag.WriteBitmapLookupTable,
+ featureflag.WriteMultiPackIndex,
+ ).Run(t, testFetchIntoObjectPoolDfConflict)
}
func testFetchIntoObjectPoolDfConflict(t *testing.T, ctx context.Context) {
diff --git a/internal/gitaly/service/repository/optimize_test.go b/internal/gitaly/service/repository/optimize_test.go
index f713de3c2..618a6f2b5 100644
--- a/internal/gitaly/service/repository/optimize_test.go
+++ b/internal/gitaly/service/repository/optimize_test.go
@@ -28,7 +28,10 @@ import (
func TestOptimizeRepository(t *testing.T) {
t.Parallel()
- testhelper.NewFeatureSets(featureflag.WriteBitmapLookupTable).Run(t, testOptimizeRepository)
+ testhelper.NewFeatureSets(
+ featureflag.WriteBitmapLookupTable,
+ featureflag.WriteMultiPackIndex,
+ ).Run(t, testOptimizeRepository)
}
func testOptimizeRepository(t *testing.T, ctx context.Context) {
diff --git a/internal/metadata/featureflag/ff_write_multi_pack_index.go b/internal/metadata/featureflag/ff_write_multi_pack_index.go
new file mode 100644
index 000000000..9f2565788
--- /dev/null
+++ b/internal/metadata/featureflag/ff_write_multi_pack_index.go
@@ -0,0 +1,10 @@
+package featureflag
+
+// WriteMultiPackIndex determines whether Gitaly should write multi-pack-indices in
+// OptimizeRepository.
+var WriteMultiPackIndex = NewFeatureFlag(
+ "write_multi_pack_index",
+ "v15.9.0",
+ "https://gitlab.com/gitlab-org/gitaly/-/issues/4741",
+ false,
+)