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:
Diffstat (limited to 'internal/gitaly/storage/storagemgr/transaction_manager_housekeeping_test.go')
-rw-r--r--internal/gitaly/storage/storagemgr/transaction_manager_housekeeping_test.go2862
1 files changed, 2862 insertions, 0 deletions
diff --git a/internal/gitaly/storage/storagemgr/transaction_manager_housekeeping_test.go b/internal/gitaly/storage/storagemgr/transaction_manager_housekeeping_test.go
index d776185a7..ef3d1a2b1 100644
--- a/internal/gitaly/storage/storagemgr/transaction_manager_housekeeping_test.go
+++ b/internal/gitaly/storage/storagemgr/transaction_manager_housekeeping_test.go
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitaly/v16/internal/git"
"gitlab.com/gitlab-org/gitaly/v16/internal/git/gittest"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/git/housekeeping"
"gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/config"
"gitlab.com/gitlab-org/gitaly/v16/internal/helper/perm"
"gitlab.com/gitlab-org/gitaly/v16/internal/testhelper"
@@ -1092,3 +1093,2864 @@ func generateHousekeepingPackRefsTests(t *testing.T, ctx context.Context, testPa
},
}
}
+
+// A shortcut to return a digest hash correspondingly to the current testing object hash format. Names of
+// packfiles have digest hashes. The following tests examine on-disk packfiles of WAL log entry's luggage and
+// the destination repository. We could use an incremental names for attached packfiles of the log entry,
+// eventually, they will be applied into the repository. The actual on-disk packfiles should match the their
+// IDs, which are specified in the *.idx files. So, it's essential to include the digest hash in the tests
+// although it's annoying to switch between different hash formats.
+func hash(tb testing.TB, sha1 string, sha256 string) string {
+ return gittest.ObjectHashDependent(tb, map[string]string{
+ git.ObjectHashSHA1.Format: sha1,
+ git.ObjectHashSHA256.Format: sha256,
+ })
+}
+
+type walDirectoryState struct {
+ lsn LSN
+ includePackfiles []string
+ includeMultiIndexes []string
+ includeCommitGraphs []string
+ includeObjects []git.ObjectID
+}
+
+func generateDirectoryState(cfg config.Cfg, stats []*walDirectoryState) testhelper.DirectoryState {
+ state := testhelper.DirectoryState{}
+ if len(stats) == 0 {
+ return state
+ }
+ state["/"] = testhelper.DirectoryEntry{Mode: fs.ModeDir | perm.PrivateDir}
+ state["/wal"] = testhelper.DirectoryEntry{Mode: fs.ModeDir | perm.PrivateDir}
+ for _, stat := range stats {
+ walDir := fmt.Sprintf("/wal/%d", stat.lsn)
+ state[walDir] = testhelper.DirectoryEntry{Mode: fs.ModeDir | perm.PrivateDir}
+ for _, packfile := range stat.includePackfiles {
+ state[filepath.Join(walDir, packfile+".pack")] = anyDirectoryEntry(cfg)
+ state[filepath.Join(walDir, packfile+".idx")] = anyDirectoryEntry(cfg)
+ state[filepath.Join(walDir, packfile+".rev")] = anyDirectoryEntry(cfg)
+ }
+ for _, index := range stat.includeMultiIndexes {
+ state[filepath.Join(walDir, "multi-pack-index")] = anyDirectoryEntryWithPerm(cfg, perm.SharedFile)
+ state[filepath.Join(walDir, index+".bitmap")] = anyDirectoryEntry(cfg)
+ }
+ for _, graph := range stat.includeCommitGraphs {
+ state[filepath.Join(walDir, "commit-graphs")] = testhelper.DirectoryEntry{Mode: fs.ModeDir | perm.PrivateDir}
+ state[filepath.Join(walDir, "commit-graphs", "commit-graph-chain")] = anyDirectoryEntry(cfg)
+ state[filepath.Join(walDir, "commit-graphs", graph+".graph")] = anyDirectoryEntry(cfg)
+ }
+ if len(stat.includeObjects) != 0 {
+ state[filepath.Join(walDir, "objects.idx")] = indexFileDirectoryEntry(cfg)
+ state[filepath.Join(walDir, "objects.rev")] = reverseIndexFileDirectoryEntry(cfg)
+ state[filepath.Join(walDir, "objects.pack")] = packFileDirectoryEntry(cfg, stat.includeObjects)
+ }
+ }
+ return state
+}
+
+// generateHousekeepingRepackingStrategyTests returns a set of tests which run repacking with different strategies and
+// settings.
+func generateHousekeepingRepackingStrategyTests(t *testing.T, ctx context.Context, testPartitionID partitionID, relativePath string) []transactionTestCase {
+ customSetup := func(t *testing.T, ctx context.Context, testPartitionID partitionID, relativePath string) testTransactionSetup {
+ setup := setupTest(t, ctx, testPartitionID, relativePath)
+ gittest.WriteRef(t, setup.Config, setup.RepositoryPath, "refs/heads/main", setup.Commits.Third.OID)
+ gittest.WriteRef(t, setup.Config, setup.RepositoryPath, "refs/heads/branch", setup.Commits.Diverging.OID)
+ setup.Commits.Unreachable = testTransactionCommit{
+ OID: gittest.WriteCommit(t, setup.Config, setup.RepositoryPath, gittest.WithParents(setup.Commits.Second.OID), gittest.WithMessage("unreachable commit")),
+ }
+ setup.Commits.Orphan = testTransactionCommit{
+ OID: gittest.WriteCommit(t, setup.Config, setup.RepositoryPath, gittest.WithParents(), gittest.WithMessage("orphan commit")),
+ }
+ // Create an initial packfile, but keep loose objects intact.
+ // gittest.Exec(t, setup.Config, "-C", setup.RepositoryPath, "repack", "-ad")
+ return setup
+ }
+ setup := customSetup(t, ctx, testPartitionID, relativePath)
+
+ defaultReferences := map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.Third.OID,
+ "refs/heads/branch": setup.Commits.Diverging.OID,
+ }
+ defaultReachableObjects := []git.ObjectID{
+ gittest.DefaultObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Third.OID,
+ setup.Commits.Diverging.OID,
+ }
+ return []transactionTestCase{
+ {
+ desc: "run repacking (IncrementalWithUnreachable)",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{
+ ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
+ repoPath := filepath.Join(storagePath, setup.RelativePath)
+ gittest.Exec(tb, cfg, "-C", repoPath, "repack", "-ad")
+ },
+ },
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyIncrementalWithUnreachable,
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{LooseReferences: defaultReferences},
+ Packfiles: &PackfilesState{
+ // Loose objects stay intact.
+ LooseObjects: []git.ObjectID{
+ setup.Commits.Orphan.OID,
+ setup.Commits.Unreachable.OID,
+ },
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-5f624d294fda1b8df86f1c286c6a66757b44126e",
+ "pack-c57ed22f16c0a35f04febe26eac0fe8974b2b4ab3469d1ece0bc2983588ad44e",
+ ): {
+ Objects: defaultReachableObjects,
+ HasBitmap: true,
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: false,
+ HasCommitGraphs: false,
+ },
+ },
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
+ },
+ },
+ },
+ {
+ desc: "run repacking (FullWithUnreachable) on a repository with an existing packfile",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{
+ ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
+ repoPath := filepath.Join(storagePath, setup.RelativePath)
+ gittest.Exec(tb, cfg, "-C", repoPath, "repack", "-ad")
+ },
+ },
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithUnreachable,
+ WriteBitmap: true,
+ WriteMultiPackIndex: true,
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{LooseReferences: defaultReferences},
+ Packfiles: &PackfilesState{
+ // Unreachable objects are packed.
+ LooseObjects: nil,
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-689b1fa746246c50a8b0f3469a06c7ae68af9926",
+ "pack-3506da99c69e8bbb4e3122636a486ffcc3506f08d24426823a2a394a7fb16b94",
+ ): {
+ Objects: append(defaultReachableObjects,
+ setup.Commits.Orphan.OID,
+ setup.Commits.Unreachable.OID,
+ ),
+ HasBitmap: false,
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: true,
+ HasCommitGraphs: false,
+ },
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includePackfiles: []string{hash(t,
+ "pack-689b1fa746246c50a8b0f3469a06c7ae68af9926",
+ "pack-3506da99c69e8bbb4e3122636a486ffcc3506f08d24426823a2a394a7fb16b94",
+ )},
+ includeMultiIndexes: []string{hash(t,
+ "multi-pack-index-dece3dfef114aa668c61339e0d4eb081af62ce68",
+ "multi-pack-index-bf9ee4098624aeb3fae4990d943443f5759d6d63c8cca686b19fb48e3c6a6f25",
+ )},
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking (FullWithUnreachable) on a repository without any packfile",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithUnreachable,
+ WriteBitmap: true,
+ WriteMultiPackIndex: true,
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{LooseReferences: defaultReferences},
+ Packfiles: &PackfilesState{
+ // Interestingly, loose unreachable objects stay untouched!
+ LooseObjects: []git.ObjectID{
+ setup.Commits.Orphan.OID,
+ setup.Commits.Unreachable.OID,
+ },
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-a81cd79eb9f32ce0afbdc15dec51c7141029e54c",
+ "pack-ce649b013f4191c500c7c4de5fe407120314c83354944e5639bf1a33a2c94110",
+ ): {
+ Objects: defaultReachableObjects,
+ HasBitmap: false,
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: true,
+ HasCommitGraphs: false,
+ },
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includePackfiles: []string{hash(t,
+ "pack-a81cd79eb9f32ce0afbdc15dec51c7141029e54c",
+ "pack-ce649b013f4191c500c7c4de5fe407120314c83354944e5639bf1a33a2c94110",
+ )},
+ includeMultiIndexes: []string{hash(t,
+ "multi-pack-index-f881063fbb14e481b5be5619df02c9874dbe5d3b",
+ "multi-pack-index-67d1f13534c85393277dc006444eee9b6670b6f1554faa43e051fa9402efa3a8",
+ )},
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking (Geometric) on a repository without any packfile",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyGeometric,
+ WriteBitmap: true,
+ WriteMultiPackIndex: true,
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{LooseReferences: defaultReferences},
+ Packfiles: &PackfilesState{
+ LooseObjects: nil,
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-e1b234fb89567714fc382281c7f89a363f4ac115",
+ "pack-d7214dae50142c99e75bf21d679b8cc14bc5d82cdb84dc23f39120101a6ed5e9",
+ ): {
+ Objects: append(defaultReachableObjects,
+ setup.Commits.Orphan.OID,
+ setup.Commits.Unreachable.OID,
+ ),
+ HasBitmap: false,
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: true,
+ HasCommitGraphs: false,
+ },
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includePackfiles: []string{hash(t,
+ "pack-e1b234fb89567714fc382281c7f89a363f4ac115",
+ "pack-d7214dae50142c99e75bf21d679b8cc14bc5d82cdb84dc23f39120101a6ed5e9",
+ )},
+ includeMultiIndexes: []string{hash(t,
+ "multi-pack-index-70fc88df37859b5f9c0d68a6b4ed42e9a6d3819e",
+ "multi-pack-index-d55aca104a7164aa65e984e53ebe2633c515eba475a68dccc982156fefaf9c51",
+ )},
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking (Geometric) on a repository having an existing packfile",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{
+ ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
+ repoPath := filepath.Join(storagePath, setup.RelativePath)
+ gittest.Exec(tb, cfg, "-C", repoPath, "repack", "-ad")
+ },
+ },
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyGeometric,
+ WriteBitmap: true,
+ WriteMultiPackIndex: true,
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{LooseReferences: defaultReferences},
+ Packfiles: &PackfilesState{
+ LooseObjects: nil,
+ Packfiles: map[string]*PackfileState{
+ // Initial packfile.
+ hash(t,
+ "pack-5f624d294fda1b8df86f1c286c6a66757b44126e",
+ "pack-c57ed22f16c0a35f04febe26eac0fe8974b2b4ab3469d1ece0bc2983588ad44e",
+ ): {
+ Objects: defaultReachableObjects,
+ HasBitmap: false,
+ HasReverseIndex: true,
+ },
+ // New packfile that contains unreachable objects. This
+ // is a co-incident, it follows the geometric
+ // progression.
+ hash(t,
+ "pack-f20a6e68adae9088db85f994838091d53fbaf608",
+ "pack-aa6d40f5f019492a7cc11291ab68666ae7ac2a23e66762905581c44523bb12bd",
+ ): {
+ Objects: []git.ObjectID{
+ setup.Commits.Orphan.OID,
+ setup.Commits.Unreachable.OID,
+ },
+ HasBitmap: false,
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: true,
+ HasCommitGraphs: false,
+ },
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includePackfiles: []string{hash(t,
+ "pack-f20a6e68adae9088db85f994838091d53fbaf608",
+ "pack-aa6d40f5f019492a7cc11291ab68666ae7ac2a23e66762905581c44523bb12bd",
+ )},
+ includeMultiIndexes: []string{hash(t,
+ "multi-pack-index-8b9315908033879678e7a6d7ff16d8cf3f419181",
+ "multi-pack-index-c2be71e50a69e0706c7818e608144ad07eca4425a4d6446d4b4f1a667f756500",
+ )},
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking (FullWithCruft) on a repository having all loose objects",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithCruft,
+ WriteBitmap: true,
+ WriteMultiPackIndex: true,
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{LooseReferences: defaultReferences},
+ Packfiles: &PackfilesState{
+ // Interestingly, loose unreachable objects stay untouched!
+ LooseObjects: []git.ObjectID{
+ setup.Commits.Orphan.OID,
+ setup.Commits.Unreachable.OID,
+ },
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-a81cd79eb9f32ce0afbdc15dec51c7141029e54c",
+ "pack-ce649b013f4191c500c7c4de5fe407120314c83354944e5639bf1a33a2c94110",
+ ): {
+ Objects: defaultReachableObjects,
+ HasBitmap: false,
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: true,
+ HasCommitGraphs: true,
+ },
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includePackfiles: []string{hash(t,
+ "pack-a81cd79eb9f32ce0afbdc15dec51c7141029e54c",
+ "pack-ce649b013f4191c500c7c4de5fe407120314c83354944e5639bf1a33a2c94110",
+ )},
+ includeMultiIndexes: []string{hash(t,
+ "multi-pack-index-f881063fbb14e481b5be5619df02c9874dbe5d3b",
+ "multi-pack-index-67d1f13534c85393277dc006444eee9b6670b6f1554faa43e051fa9402efa3a8",
+ )},
+ includeCommitGraphs: []string{hash(t,
+ "graph-5eddc89b8217451ecd51182f91ddf6f58b20f0f7",
+ "graph-d7a6f93863d026b02376bf869ab4fa23a7cd6bdbc013543741352b574cc19606",
+ )},
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking (FullWithCruft) on a repository whose objects are packed",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{
+ ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
+ repoPath := filepath.Join(storagePath, setup.RelativePath)
+ gittest.Exec(tb, cfg, "-C", repoPath, "repack", "-adl")
+ gittest.Exec(tb, cfg, "-C", repoPath, "repack", "-adl", "--keep-unreachable")
+ },
+ },
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithCruft,
+ WriteBitmap: true,
+ WriteMultiPackIndex: true,
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{LooseReferences: defaultReferences},
+ Packfiles: &PackfilesState{
+ // Unreachable objects are pruned.
+ LooseObjects: nil,
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-a81cd79eb9f32ce0afbdc15dec51c7141029e54c",
+ "pack-ce649b013f4191c500c7c4de5fe407120314c83354944e5639bf1a33a2c94110",
+ ): {
+ Objects: defaultReachableObjects,
+ HasBitmap: false,
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: true,
+ HasCommitGraphs: true,
+ },
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includePackfiles: []string{hash(t,
+ "pack-a81cd79eb9f32ce0afbdc15dec51c7141029e54c",
+ "pack-ce649b013f4191c500c7c4de5fe407120314c83354944e5639bf1a33a2c94110",
+ )},
+ includeMultiIndexes: []string{hash(t,
+ "multi-pack-index-f881063fbb14e481b5be5619df02c9874dbe5d3b",
+ "multi-pack-index-67d1f13534c85393277dc006444eee9b6670b6f1554faa43e051fa9402efa3a8",
+ )},
+ includeCommitGraphs: []string{hash(t,
+ "graph-5eddc89b8217451ecd51182f91ddf6f58b20f0f7",
+ "graph-d7a6f93863d026b02376bf869ab4fa23a7cd6bdbc013543741352b574cc19606",
+ )},
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking (FullWithCruft) on a repository having both packfile and loose unreachable objects",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{
+ ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
+ repoPath := filepath.Join(storagePath, setup.RelativePath)
+ gittest.Exec(tb, cfg, "-C", repoPath, "repack", "-adl")
+ },
+ },
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithCruft,
+ WriteBitmap: true,
+ WriteMultiPackIndex: true,
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{LooseReferences: defaultReferences},
+ Packfiles: &PackfilesState{
+ // Interestingly, loose unreachable objects stay untouched!
+ LooseObjects: []git.ObjectID{
+ setup.Commits.Orphan.OID,
+ setup.Commits.Unreachable.OID,
+ },
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-a81cd79eb9f32ce0afbdc15dec51c7141029e54c",
+ "pack-ce649b013f4191c500c7c4de5fe407120314c83354944e5639bf1a33a2c94110",
+ ): {
+ Objects: defaultReachableObjects,
+ HasBitmap: false,
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: true,
+ HasCommitGraphs: true,
+ },
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includePackfiles: []string{hash(t,
+ "pack-a81cd79eb9f32ce0afbdc15dec51c7141029e54c",
+ "pack-ce649b013f4191c500c7c4de5fe407120314c83354944e5639bf1a33a2c94110",
+ )},
+ includeMultiIndexes: []string{hash(t,
+ "multi-pack-index-f881063fbb14e481b5be5619df02c9874dbe5d3b",
+ "multi-pack-index-67d1f13534c85393277dc006444eee9b6670b6f1554faa43e051fa9402efa3a8",
+ )},
+ includeCommitGraphs: []string{hash(t,
+ "graph-5eddc89b8217451ecd51182f91ddf6f58b20f0f7",
+ "graph-d7a6f93863d026b02376bf869ab4fa23a7cd6bdbc013543741352b574cc19606",
+ )},
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking without bitmap and multi-pack-index",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{
+ ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
+ repoPath := filepath.Join(storagePath, setup.RelativePath)
+ gittest.Exec(tb, cfg, "-c", "repack.writeBitmaps=false", "-C", repoPath, "repack", "-ad")
+ },
+ },
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyGeometric,
+ WriteBitmap: false,
+ WriteMultiPackIndex: false,
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{LooseReferences: defaultReferences},
+ Packfiles: &PackfilesState{
+ LooseObjects: nil,
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-5f624d294fda1b8df86f1c286c6a66757b44126e",
+ "pack-c57ed22f16c0a35f04febe26eac0fe8974b2b4ab3469d1ece0bc2983588ad44e",
+ ): {
+ Objects: defaultReachableObjects,
+ HasBitmap: false,
+ HasReverseIndex: true,
+ },
+ hash(t,
+ "pack-f20a6e68adae9088db85f994838091d53fbaf608",
+ "pack-aa6d40f5f019492a7cc11291ab68666ae7ac2a23e66762905581c44523bb12bd",
+ ): {
+ Objects: []git.ObjectID{
+ setup.Commits.Orphan.OID,
+ setup.Commits.Unreachable.OID,
+ },
+ HasBitmap: false,
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: false,
+ HasCommitGraphs: false,
+ },
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includePackfiles: []string{hash(t,
+ "pack-f20a6e68adae9088db85f994838091d53fbaf608",
+ "pack-aa6d40f5f019492a7cc11291ab68666ae7ac2a23e66762905581c44523bb12bd",
+ )},
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking (Geometric) on a repository having existing commit-graphs",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{
+ ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
+ repoPath := filepath.Join(storagePath, setup.RelativePath)
+ gittest.Exec(tb, cfg, "-c", "repack.writeBitmaps=false", "-C", repoPath, "repack", "-ad")
+ gittest.Exec(tb, cfg, "-C", repoPath, "commit-graph", "write", "--reachable", "--changed-paths", "--size-multiple=4", "--split=replace")
+ },
+ },
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyGeometric,
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{LooseReferences: defaultReferences},
+ Packfiles: &PackfilesState{
+ LooseObjects: nil,
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-5f624d294fda1b8df86f1c286c6a66757b44126e",
+ "pack-c57ed22f16c0a35f04febe26eac0fe8974b2b4ab3469d1ece0bc2983588ad44e",
+ ): {
+ Objects: defaultReachableObjects,
+ HasBitmap: false,
+ HasReverseIndex: true,
+ },
+ hash(t,
+ "pack-f20a6e68adae9088db85f994838091d53fbaf608",
+ "pack-aa6d40f5f019492a7cc11291ab68666ae7ac2a23e66762905581c44523bb12bd",
+ ): {
+ Objects: []git.ObjectID{
+ setup.Commits.Orphan.OID,
+ setup.Commits.Unreachable.OID,
+ },
+ HasBitmap: false,
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: false,
+ HasCommitGraphs: true,
+ },
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includePackfiles: []string{hash(t,
+ "pack-f20a6e68adae9088db85f994838091d53fbaf608",
+ "pack-aa6d40f5f019492a7cc11291ab68666ae7ac2a23e66762905581c44523bb12bd",
+ )},
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking (FullWithUnreachable) on a repository having existing commit-graphs",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{
+ ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
+ repoPath := filepath.Join(storagePath, setup.RelativePath)
+ gittest.Exec(tb, cfg, "-c", "repack.writeBitmaps=false", "-C", repoPath, "repack", "-ad")
+ gittest.Exec(tb, cfg, "-C", repoPath, "commit-graph", "write", "--reachable", "--changed-paths", "--size-multiple=4", "--split=replace")
+ },
+ },
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithUnreachable,
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{LooseReferences: defaultReferences},
+ Packfiles: &PackfilesState{
+ LooseObjects: nil,
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-689b1fa746246c50a8b0f3469a06c7ae68af9926",
+ "pack-3506da99c69e8bbb4e3122636a486ffcc3506f08d24426823a2a394a7fb16b94",
+ ): {
+ Objects: append(defaultReachableObjects,
+ setup.Commits.Orphan.OID,
+ setup.Commits.Unreachable.OID,
+ ),
+ HasBitmap: false,
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: false,
+ HasCommitGraphs: true,
+ },
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includePackfiles: []string{hash(t,
+ "pack-689b1fa746246c50a8b0f3469a06c7ae68af9926",
+ "pack-3506da99c69e8bbb4e3122636a486ffcc3506f08d24426823a2a394a7fb16b94",
+ )},
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking (FullWithCruft) on a repository having existing commit-graphs",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{
+ ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
+ repoPath := filepath.Join(storagePath, setup.RelativePath)
+ gittest.Exec(tb, cfg, "-C", repoPath, "repack", "-adl")
+ gittest.Exec(tb, cfg, "-C", repoPath, "commit-graph", "write", "--reachable", "--changed-paths", "--size-multiple=4", "--split=replace")
+ },
+ },
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithCruft,
+ WriteBitmap: true,
+ WriteMultiPackIndex: true,
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{LooseReferences: defaultReferences},
+ Packfiles: &PackfilesState{
+ // Interestingly, loose unreachable objects stay untouched!
+ LooseObjects: []git.ObjectID{
+ setup.Commits.Orphan.OID,
+ setup.Commits.Unreachable.OID,
+ },
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-a81cd79eb9f32ce0afbdc15dec51c7141029e54c",
+ "pack-ce649b013f4191c500c7c4de5fe407120314c83354944e5639bf1a33a2c94110",
+ ): {
+ Objects: defaultReachableObjects,
+ HasBitmap: false,
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: true,
+ HasCommitGraphs: true,
+ },
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includePackfiles: []string{hash(t,
+ "pack-a81cd79eb9f32ce0afbdc15dec51c7141029e54c",
+ "pack-ce649b013f4191c500c7c4de5fe407120314c83354944e5639bf1a33a2c94110",
+ )},
+ includeMultiIndexes: []string{hash(t,
+ "multi-pack-index-f881063fbb14e481b5be5619df02c9874dbe5d3b",
+ "multi-pack-index-67d1f13534c85393277dc006444eee9b6670b6f1554faa43e051fa9402efa3a8",
+ )},
+ includeCommitGraphs: []string{hash(t,
+ "graph-5eddc89b8217451ecd51182f91ddf6f58b20f0f7",
+ "graph-d7a6f93863d026b02376bf869ab4fa23a7cd6bdbc013543741352b574cc19606",
+ )},
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking twice with the same setting",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{
+ ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
+ repoPath := filepath.Join(storagePath, setup.RelativePath)
+ gittest.Exec(tb, cfg, "-C", repoPath, "repack", "-adl")
+ },
+ },
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithUnreachable,
+ WriteBitmap: true,
+ WriteMultiPackIndex: true,
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ RunRepack{
+ TransactionID: 2,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithUnreachable,
+ WriteBitmap: true,
+ WriteMultiPackIndex: true,
+ },
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{LooseReferences: defaultReferences},
+ Packfiles: &PackfilesState{
+ LooseObjects: nil,
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-689b1fa746246c50a8b0f3469a06c7ae68af9926",
+ "pack-3506da99c69e8bbb4e3122636a486ffcc3506f08d24426823a2a394a7fb16b94",
+ ): {
+ Objects: append(defaultReachableObjects,
+ setup.Commits.Orphan.OID,
+ setup.Commits.Unreachable.OID,
+ ),
+ HasBitmap: false,
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: true,
+ HasCommitGraphs: false,
+ },
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includePackfiles: []string{hash(t,
+ "pack-689b1fa746246c50a8b0f3469a06c7ae68af9926",
+ "pack-3506da99c69e8bbb4e3122636a486ffcc3506f08d24426823a2a394a7fb16b94",
+ )},
+ includeMultiIndexes: []string{hash(t,
+ "multi-pack-index-dece3dfef114aa668c61339e0d4eb081af62ce68",
+ "multi-pack-index-bf9ee4098624aeb3fae4990d943443f5759d6d63c8cca686b19fb48e3c6a6f25",
+ )},
+ },
+ {
+ lsn: 2,
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking in the same transaction including other changes",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithUnreachable,
+ WriteBitmap: true,
+ WriteMultiPackIndex: true,
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID},
+ },
+ ExpectedError: errHousekeepingConflictOtherUpdates,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{},
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{LooseReferences: defaultReferences},
+ Packfiles: &PackfilesState{
+ LooseObjects: append(defaultReachableObjects, setup.Commits.Unreachable.OID, setup.Commits.Orphan.OID),
+ Packfiles: map[string]*PackfileState{},
+ HasMultiPackIndex: false,
+ HasCommitGraphs: false,
+ },
+ },
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ },
+ },
+ },
+ }
+}
+
+// generateHousekeepingRepackingConcurrentTests returns a set of tests which run repacking before, after, or alongside
+// with other transactions.
+func generateHousekeepingRepackingConcurrentTests(t *testing.T, ctx context.Context, setup testTransactionSetup) []transactionTestCase {
+ return []transactionTestCase{
+ {
+ desc: "run repacking on an empty repository",
+ steps: steps{
+ Prune{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithCruft,
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {lsn: 1},
+ }),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{},
+ },
+ Packfiles: &PackfilesState{
+ Packfiles: map[string]*PackfileState{},
+ HasCommitGraphs: false,
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "run repacking after some changes including both reachable and unreachable objects",
+ steps: steps{
+ Prune{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 1,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.First.Pack,
+ setup.Commits.Second.Pack,
+ setup.Commits.Diverging.Pack, // This commit is not reachable
+ },
+ IncludeObjects: []git.ObjectID{setup.Commits.Diverging.OID},
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ RunRepack{
+ TransactionID: 2,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithCruft,
+ },
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includeObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Diverging.OID,
+ },
+ },
+ {
+ lsn: 2,
+ includePackfiles: []string{hash(t,
+ "pack-98be7bb46e97ddbe7e3093e0cc5bca60f37f9b09",
+ "pack-53630df54431a48f6d87f1bbe0d054327f8eb1964f813de1821d15bc5dcb1621",
+ )},
+ includeCommitGraphs: []string{hash(t,
+ "graph-8cd59940f998ecb90f7935b6b7adc8df46d9174e",
+ "graph-8cbbd20f75bc45c5337718fe4ab8498e1ce7524e87d6bd7fc9581bc08c119562",
+ )},
+ },
+ }),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.Second.OID,
+ },
+ },
+ Packfiles: &PackfilesState{
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-98be7bb46e97ddbe7e3093e0cc5bca60f37f9b09",
+ "pack-53630df54431a48f6d87f1bbe0d054327f8eb1964f813de1821d15bc5dcb1621",
+ ): {
+ // Diverging commit is gone.
+ Objects: []git.ObjectID{
+ gittest.DefaultObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ HasReverseIndex: true,
+ },
+ },
+ HasCommitGraphs: true,
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "run repacking before another transaction that produce new packfiles",
+ steps: steps{
+ Prune{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 1,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.First.Pack,
+ setup.Commits.Second.Pack,
+ setup.Commits.Diverging.Pack, // This commit is not reachable
+ },
+ IncludeObjects: []git.ObjectID{setup.Commits.Diverging.OID},
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ RunRepack{
+ TransactionID: 2,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithCruft,
+ },
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 2,
+ },
+ Commit{
+ TransactionID: 3,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.Second.OID, NewOID: setup.Commits.Third.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.Third.Pack,
+ },
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(3).toProto(),
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includeObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Diverging.OID,
+ },
+ },
+ {
+ lsn: 2,
+ includePackfiles: []string{hash(t,
+ "pack-98be7bb46e97ddbe7e3093e0cc5bca60f37f9b09",
+ "pack-53630df54431a48f6d87f1bbe0d054327f8eb1964f813de1821d15bc5dcb1621",
+ )},
+ includeCommitGraphs: []string{hash(t,
+ "graph-8cd59940f998ecb90f7935b6b7adc8df46d9174e",
+ "graph-8cbbd20f75bc45c5337718fe4ab8498e1ce7524e87d6bd7fc9581bc08c119562",
+ )},
+ },
+ {
+ lsn: 3,
+ includeObjects: []git.ObjectID{
+ setup.Commits.Third.OID,
+ },
+ },
+ }),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.Third.OID,
+ },
+ },
+ Packfiles: &PackfilesState{
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-98be7bb46e97ddbe7e3093e0cc5bca60f37f9b09",
+ "pack-53630df54431a48f6d87f1bbe0d054327f8eb1964f813de1821d15bc5dcb1621",
+ ): {
+ // Diverging commit is gone.
+ Objects: []git.ObjectID{
+ gittest.DefaultObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ HasReverseIndex: true,
+ },
+ hash(t,
+ "pack-529ec37accbc126425efe69abdf91153411532a6",
+ "pack-895b4eade6c459f47a382a0d637ef1ce34a661c76f003c7d7a38a7420e3afc69",
+ ): {
+ Objects: []git.ObjectID{
+ setup.Commits.Third.OID,
+ },
+ HasReverseIndex: true,
+ },
+ },
+ HasCommitGraphs: true,
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "run repacking concurrently with another transaction that produce new packfiles",
+ steps: steps{
+ Prune{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 1,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.First.Pack,
+ setup.Commits.Second.Pack,
+ setup.Commits.Diverging.Pack,
+ },
+ IncludeObjects: []git.ObjectID{setup.Commits.Diverging.OID},
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ RunRepack{
+ TransactionID: 2,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithCruft,
+ },
+ },
+ Commit{
+ TransactionID: 3,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.Second.OID, NewOID: setup.Commits.Third.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.Third.Pack,
+ },
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(3).toProto(),
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includeObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Diverging.OID,
+ },
+ },
+ {
+ lsn: 2,
+ includeObjects: []git.ObjectID{
+ setup.Commits.Third.OID,
+ },
+ },
+ {
+ lsn: 3,
+ includePackfiles: []string{hash(t,
+ "pack-98be7bb46e97ddbe7e3093e0cc5bca60f37f9b09",
+ "pack-53630df54431a48f6d87f1bbe0d054327f8eb1964f813de1821d15bc5dcb1621",
+ )},
+ includeCommitGraphs: []string{hash(t,
+ "graph-8cd59940f998ecb90f7935b6b7adc8df46d9174e",
+ "graph-8cbbd20f75bc45c5337718fe4ab8498e1ce7524e87d6bd7fc9581bc08c119562",
+ )},
+ },
+ }),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.Third.OID,
+ },
+ },
+ Packfiles: &PackfilesState{
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-98be7bb46e97ddbe7e3093e0cc5bca60f37f9b09",
+ "pack-53630df54431a48f6d87f1bbe0d054327f8eb1964f813de1821d15bc5dcb1621",
+ ): {
+ Objects: []git.ObjectID{
+ gittest.DefaultObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ HasReverseIndex: true,
+ },
+ hash(t,
+ "pack-529ec37accbc126425efe69abdf91153411532a6",
+ "pack-895b4eade6c459f47a382a0d637ef1ce34a661c76f003c7d7a38a7420e3afc69",
+ ): {
+ Objects: []git.ObjectID{
+ setup.Commits.Third.OID,
+ },
+ HasReverseIndex: true,
+ },
+ },
+ HasCommitGraphs: true,
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "run repacking concurrently with another transaction that points to a survived object",
+ steps: steps{
+ Prune{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 1,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.First.Pack,
+ setup.Commits.Second.Pack,
+ setup.Commits.Diverging.Pack,
+ },
+ IncludeObjects: []git.ObjectID{setup.Commits.Diverging.OID},
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ RunRepack{
+ TransactionID: 2,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithCruft,
+ },
+ },
+ Commit{
+ TransactionID: 3,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.Second.OID, NewOID: setup.Commits.First.OID},
+ },
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(3).toProto(),
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includeObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Diverging.OID,
+ },
+ },
+ {
+ lsn: 3,
+ includePackfiles: []string{hash(t,
+ "pack-98be7bb46e97ddbe7e3093e0cc5bca60f37f9b09",
+ "pack-53630df54431a48f6d87f1bbe0d054327f8eb1964f813de1821d15bc5dcb1621",
+ )},
+ includeCommitGraphs: []string{hash(t,
+ "graph-8cd59940f998ecb90f7935b6b7adc8df46d9174e",
+ "graph-8cbbd20f75bc45c5337718fe4ab8498e1ce7524e87d6bd7fc9581bc08c119562",
+ )},
+ },
+ }),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ },
+ Packfiles: &PackfilesState{
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-98be7bb46e97ddbe7e3093e0cc5bca60f37f9b09",
+ "pack-53630df54431a48f6d87f1bbe0d054327f8eb1964f813de1821d15bc5dcb1621",
+ ): {
+ Objects: []git.ObjectID{
+ gittest.DefaultObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ HasReverseIndex: true,
+ },
+ },
+ HasCommitGraphs: true,
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "run repacking that spans through multiple transactions",
+ steps: steps{
+ Prune{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 1,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.First.Pack,
+ },
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ RunRepack{
+ TransactionID: 2,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithCruft,
+ },
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ Commit{
+ TransactionID: 3,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.Second.Pack,
+ },
+ },
+ Begin{
+ TransactionID: 4,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 2,
+ },
+ Commit{
+ TransactionID: 4,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.Second.OID, NewOID: setup.Commits.Third.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.Third.Pack,
+ },
+ },
+ Begin{
+ TransactionID: 5,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 3,
+ },
+ Commit{
+ TransactionID: 5,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.Third.OID, NewOID: setup.Commits.Diverging.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.Diverging.Pack,
+ },
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(5).toProto(),
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includeObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ },
+ {
+ lsn: 2,
+ includeObjects: []git.ObjectID{
+ setup.Commits.Second.OID,
+ },
+ },
+ {
+ lsn: 3,
+ includeObjects: []git.ObjectID{
+ setup.Commits.Third.OID,
+ },
+ },
+ {
+ lsn: 4,
+ includeObjects: []git.ObjectID{
+ setup.Commits.Diverging.OID,
+ },
+ },
+ {
+ lsn: 5,
+ },
+ }),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.Diverging.OID,
+ },
+ },
+ Packfiles: &PackfilesState{
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-452292f7e0c6bcca1b42c53aaac4537416b5dbb9",
+ "pack-735ad245db57a16c41525c9101c42594d090c7021b51aa12d9104a4eea4223c5",
+ ): {
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ HasReverseIndex: true,
+ },
+ hash(t,
+ "pack-407172c9edc9b3cef89f8fb341262155b6b401ae",
+ "pack-8c9a31ee3c6493a1883f96fe629925b6f94c00d810eb6c80d5e2502fba646d3a",
+ ): {
+ Objects: []git.ObjectID{
+ setup.Commits.Diverging.OID,
+ },
+ HasReverseIndex: true,
+ },
+ hash(t,
+ "pack-529ec37accbc126425efe69abdf91153411532a6",
+ "pack-895b4eade6c459f47a382a0d637ef1ce34a661c76f003c7d7a38a7420e3afc69",
+ ): {
+ Objects: []git.ObjectID{
+ setup.Commits.Third.OID,
+ },
+ HasReverseIndex: true,
+ },
+ hash(t,
+ "pack-5a422f6c469963ffa026bf15cfd151751fba6e5f",
+ "pack-3ed0b733b17d82f87a350b856f7fd6d6a781d85c5d8d36fad64b459124444f11",
+ ): {
+ Objects: []git.ObjectID{
+ setup.Commits.Second.OID,
+ },
+ HasReverseIndex: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "run repacking (FullWithUnreachable) concurrently with another transaction pointing new reference to packed objects",
+ steps: steps{
+ Prune{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 1,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
+ "refs/heads/branch": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.First.Pack,
+ setup.Commits.Second.Pack,
+ },
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ Commit{
+ TransactionID: 2,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch": {OldOID: setup.Commits.Second.OID, NewOID: setup.ObjectHash.ZeroOID},
+ },
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 2,
+ },
+ Begin{
+ TransactionID: 4,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 2,
+ },
+ RunRepack{
+ TransactionID: 3,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithUnreachable,
+ },
+ },
+ Commit{
+ TransactionID: 4,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID},
+ },
+ },
+ Commit{
+ TransactionID: 3,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(4).toProto(),
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includeObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ },
+ {
+ lsn: 4,
+ includePackfiles: []string{hash(t,
+ "pack-df5e3e230b167b4ce31a30f389e0f1908ae40f2b",
+ "pack-6a4d9d6b54438754effb555adec435cd9031a01cba7515bdf8b73a0e2714c6ff",
+ )},
+ },
+ }),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.Second.OID,
+ },
+ },
+ Packfiles: &PackfilesState{
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-df5e3e230b167b4ce31a30f389e0f1908ae40f2b",
+ "pack-6a4d9d6b54438754effb555adec435cd9031a01cba7515bdf8b73a0e2714c6ff",
+ ): {
+ Objects: []git.ObjectID{
+ gittest.DefaultObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ HasReverseIndex: true,
+ },
+ },
+ HasCommitGraphs: false,
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "run repacking (Geometric) concurrently with another transaction pointing new reference to packed objects",
+ steps: steps{
+ Prune{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 1,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
+ "refs/heads/branch": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.First.Pack,
+ setup.Commits.Second.Pack,
+ },
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ Commit{
+ TransactionID: 2,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch": {OldOID: setup.Commits.Second.OID, NewOID: setup.ObjectHash.ZeroOID},
+ },
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 2,
+ },
+ Begin{
+ TransactionID: 4,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 2,
+ },
+ RunRepack{
+ TransactionID: 3,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyGeometric,
+ },
+ },
+ Commit{
+ TransactionID: 4,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID},
+ },
+ },
+ Commit{
+ TransactionID: 3,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(4).toProto(),
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includeObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ },
+ {
+ // No new packfiles
+ lsn: 4,
+ },
+ }),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.Second.OID,
+ },
+ },
+ Packfiles: &PackfilesState{
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-4274682fcb6a4dbb1a59ba7dd8577402e61ccbd2",
+ "pack-8ebabff3c37210ed37c4343255992f62a2ce113f7fb11f757de3bca157379d40",
+ ): {
+ Objects: []git.ObjectID{
+ gittest.DefaultObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ HasReverseIndex: true,
+ },
+ },
+ HasCommitGraphs: false,
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "run repacking (FullWithCruft) concurrently with another transaction pointing new reference to pruned objects",
+ steps: steps{
+ Prune{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 1,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
+ "refs/heads/branch": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.First.Pack,
+ setup.Commits.Second.Pack,
+ },
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ Commit{
+ TransactionID: 2,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch": {OldOID: setup.Commits.Second.OID, NewOID: setup.ObjectHash.ZeroOID},
+ },
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 2,
+ },
+ Begin{
+ TransactionID: 4,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 2,
+ },
+ RunRepack{
+ TransactionID: 3,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithCruft,
+ },
+ },
+ Commit{
+ TransactionID: 4,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID},
+ },
+ },
+ Commit{
+ TransactionID: 3,
+ ExpectedError: errRepackConflictPrunedObject,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(3).toProto(),
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includeObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ },
+ }),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.Second.OID,
+ },
+ },
+ Packfiles: &PackfilesState{
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-4274682fcb6a4dbb1a59ba7dd8577402e61ccbd2",
+ "pack-8ebabff3c37210ed37c4343255992f62a2ce113f7fb11f757de3bca157379d40",
+ ): {
+ Objects: []git.ObjectID{
+ gittest.DefaultObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ HasReverseIndex: true,
+ },
+ },
+ HasCommitGraphs: false,
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "run repacking (FullWithUnreachable) on an alternate member",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ Packs: [][]byte{setup.Commits.First.Pack},
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ Commit{
+ TransactionID: 3,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Third.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.Second.Pack,
+ setup.Commits.Third.Pack,
+ },
+ },
+ Begin{
+ TransactionID: 4,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 3,
+ },
+ Commit{
+ TransactionID: 4,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch": {OldOID: setup.Commits.Third.OID, NewOID: setup.Commits.Second.OID},
+ },
+ },
+ Begin{
+ TransactionID: 5,
+ RelativePath: "pool",
+ ExpectedSnapshotLSN: 4,
+ },
+ Commit{
+ TransactionID: 5,
+ QuarantinedPacks: [][]byte{
+ setup.Commits.Second.Pack,
+ },
+ IncludeObjects: []git.ObjectID{setup.Commits.Second.OID},
+ },
+ Begin{
+ TransactionID: 6,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 5,
+ },
+ RunRepack{
+ TransactionID: 6,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithUnreachable,
+ },
+ },
+ Commit{
+ TransactionID: 6,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(6).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ Packfiles: &PackfilesState{
+ LooseObjects: nil,
+ // First commit and its tree object.
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-452292f7e0c6bcca1b42c53aaac4537416b5dbb9",
+ "pack-735ad245db57a16c41525c9101c42594d090c7021b51aa12d9104a4eea4223c5",
+ ): {
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ HasReverseIndex: true,
+ },
+ hash(t,
+ "pack-5a422f6c469963ffa026bf15cfd151751fba6e5f",
+ "pack-3ed0b733b17d82f87a350b856f7fd6d6a781d85c5d8d36fad64b459124444f11",
+ ): {
+ Objects: []git.ObjectID{
+ setup.Commits.Second.OID,
+ },
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: false,
+ HasCommitGraphs: false,
+ },
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ },
+ },
+ "member": {
+ Packfiles: &PackfilesState{
+ LooseObjects: nil,
+ Packfiles: map[string]*PackfileState{
+ // Packfile containing second commit (reachable) and
+ // third commit (unreachable). Redundant objects in
+ // quarantined packs are removed.
+ hash(t,
+ "pack-529ec37accbc126425efe69abdf91153411532a6",
+ "pack-895b4eade6c459f47a382a0d637ef1ce34a661c76f003c7d7a38a7420e3afc69",
+ ): {
+ Objects: []git.ObjectID{
+ setup.Commits.Third.OID,
+ },
+ HasReverseIndex: true,
+ },
+ },
+ PooledObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ // Both member and pool have second commit. It's
+ // deduplicated and the member inherits it from the
+ // pool.
+ setup.Commits.Second.OID,
+ },
+ HasMultiPackIndex: false,
+ HasCommitGraphs: false,
+ },
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/branch": setup.Commits.Second.OID,
+ },
+ },
+ Alternate: "../../pool/objects",
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includeObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ },
+ {
+ lsn: 3,
+ includeObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Third.OID,
+ },
+ },
+ {
+ lsn: 5,
+ includeObjects: []git.ObjectID{
+ setup.Commits.Second.OID,
+ },
+ },
+ {
+ lsn: 6,
+ includePackfiles: []string{hash(t,
+ "pack-529ec37accbc126425efe69abdf91153411532a6",
+ "pack-895b4eade6c459f47a382a0d637ef1ce34a661c76f003c7d7a38a7420e3afc69",
+ )},
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking (FullWithUnreachable) on an alternate pool",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ Packs: [][]byte{setup.Commits.First.Pack},
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "pool",
+ ExpectedSnapshotLSN: 2,
+ },
+ Commit{
+ TransactionID: 3,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Third.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.Second.Pack,
+ setup.Commits.Third.Pack,
+ },
+ },
+ Begin{
+ TransactionID: 4,
+ RelativePath: "pool",
+ ExpectedSnapshotLSN: 3,
+ },
+ Commit{
+ TransactionID: 4,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch": {OldOID: setup.Commits.Third.OID, NewOID: setup.Commits.Second.OID},
+ },
+ },
+ Begin{
+ TransactionID: 5,
+ RelativePath: "pool",
+ ExpectedSnapshotLSN: 4,
+ },
+ RunRepack{
+ TransactionID: 5,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithUnreachable,
+ },
+ },
+ Commit{
+ TransactionID: 5,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(5).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ Packfiles: &PackfilesState{
+ LooseObjects: nil,
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-144f9890c312a4cf5e66895bf721606d0f691083",
+ "pack-1d8b96ae9cc5301db6024e5d87974c960da6c017a9cf1bbed52bf8fe51e085de",
+ ): {
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Third.OID,
+ },
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: false,
+ HasCommitGraphs: false,
+ },
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ "refs/heads/branch": setup.Commits.Second.OID,
+ },
+ },
+ },
+ "member": {
+ Packfiles: &PackfilesState{
+ LooseObjects: nil,
+ Packfiles: make(map[string]*PackfileState),
+ // All objects are accessible in member.
+ PooledObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Third.OID,
+ },
+ HasMultiPackIndex: false,
+ HasCommitGraphs: false,
+ },
+ References: nil,
+ Alternate: "../../pool/objects",
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includeObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ },
+ {
+ lsn: 3,
+ includeObjects: []git.ObjectID{
+ setup.Commits.Second.OID,
+ setup.Commits.Third.OID,
+ },
+ },
+ {
+ lsn: 5,
+ includePackfiles: []string{hash(t,
+ "pack-144f9890c312a4cf5e66895bf721606d0f691083",
+ "pack-1d8b96ae9cc5301db6024e5d87974c960da6c017a9cf1bbed52bf8fe51e085de",
+ )},
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking (Geometric) on an alternate member",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ Packs: [][]byte{setup.Commits.First.Pack},
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ Commit{
+ TransactionID: 3,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.Second.Pack,
+ },
+ },
+ Begin{
+ TransactionID: 4,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 3,
+ },
+ Commit{
+ TransactionID: 4,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch": {OldOID: setup.Commits.Second.OID, NewOID: setup.Commits.Third.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.Third.Pack,
+ },
+ },
+ Begin{
+ TransactionID: 5,
+ RelativePath: "pool",
+ ExpectedSnapshotLSN: 4,
+ },
+ Commit{
+ TransactionID: 5,
+ QuarantinedPacks: [][]byte{
+ setup.Commits.Second.Pack,
+ },
+ IncludeObjects: []git.ObjectID{setup.Commits.Second.OID},
+ },
+ Begin{
+ TransactionID: 6,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 5,
+ },
+ RunRepack{
+ TransactionID: 6,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyGeometric,
+ },
+ },
+ Commit{
+ TransactionID: 6,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(6).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ Packfiles: &PackfilesState{
+ LooseObjects: nil,
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-452292f7e0c6bcca1b42c53aaac4537416b5dbb9",
+ "pack-735ad245db57a16c41525c9101c42594d090c7021b51aa12d9104a4eea4223c5",
+ ): {
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ HasReverseIndex: true,
+ },
+ hash(t,
+ "pack-5a422f6c469963ffa026bf15cfd151751fba6e5f",
+ "pack-3ed0b733b17d82f87a350b856f7fd6d6a781d85c5d8d36fad64b459124444f11",
+ ): {
+ Objects: []git.ObjectID{
+ setup.Commits.Second.OID,
+ },
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: false,
+ HasCommitGraphs: false,
+ },
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ },
+ },
+ "member": {
+ Packfiles: &PackfilesState{
+ LooseObjects: nil,
+ Packfiles: map[string]*PackfileState{
+ // This packfile matches the quarantined pack of
+ // transaction 3. Geometric repacking does not
+ // deduplicate second commit.
+ hash(t,
+ "pack-4274682fcb6a4dbb1a59ba7dd8577402e61ccbd2",
+ "pack-8ebabff3c37210ed37c4343255992f62a2ce113f7fb11f757de3bca157379d40",
+ ): {
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ HasReverseIndex: true,
+ },
+ // This packfile matches the quarantined pack of
+ // transaction 4.
+ hash(t,
+ "pack-529ec37accbc126425efe69abdf91153411532a6",
+ "pack-895b4eade6c459f47a382a0d637ef1ce34a661c76f003c7d7a38a7420e3afc69",
+ ): {
+ Objects: []git.ObjectID{
+ setup.Commits.Third.OID,
+ },
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: false,
+ HasCommitGraphs: false,
+ },
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/branch": setup.Commits.Third.OID,
+ },
+ },
+ Alternate: "../../pool/objects",
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includeObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ },
+ {
+ lsn: 3,
+ includeObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ },
+ },
+ {
+ lsn: 4,
+ includeObjects: []git.ObjectID{
+ setup.Commits.Third.OID,
+ },
+ },
+ {
+ lsn: 5,
+ includeObjects: []git.ObjectID{
+ setup.Commits.Second.OID,
+ },
+ },
+ {
+ // As they form geometric progression, the repacking task keeps them intact.
+ lsn: 6,
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking (FullWithCruft) on an alternate member",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ Packs: [][]byte{setup.Commits.First.Pack},
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ Commit{
+ TransactionID: 3,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch-1": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID},
+ "refs/heads/branch-2": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.Third.OID},
+ },
+ QuarantinedPacks: [][]byte{
+ setup.Commits.Second.Pack,
+ setup.Commits.Third.Pack,
+ setup.Commits.Diverging.Pack,
+ },
+ IncludeObjects: []git.ObjectID{setup.Commits.Diverging.OID},
+ },
+ Begin{
+ TransactionID: 4,
+ RelativePath: "pool",
+ ExpectedSnapshotLSN: 3,
+ },
+ Commit{
+ TransactionID: 4,
+ QuarantinedPacks: [][]byte{
+ setup.Commits.Second.Pack,
+ },
+ IncludeObjects: []git.ObjectID{setup.Commits.Second.OID},
+ },
+ Begin{
+ TransactionID: 5,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 4,
+ },
+ RunRepack{
+ TransactionID: 5,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithCruft,
+ },
+ },
+ Commit{
+ TransactionID: 5,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(5).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ Packfiles: &PackfilesState{
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-452292f7e0c6bcca1b42c53aaac4537416b5dbb9",
+ "pack-735ad245db57a16c41525c9101c42594d090c7021b51aa12d9104a4eea4223c5",
+ ): {
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ HasReverseIndex: true,
+ },
+ hash(t,
+ "pack-5a422f6c469963ffa026bf15cfd151751fba6e5f",
+ "pack-3ed0b733b17d82f87a350b856f7fd6d6a781d85c5d8d36fad64b459124444f11",
+ ): {
+ Objects: []git.ObjectID{
+ setup.Commits.Second.OID,
+ },
+ HasReverseIndex: true,
+ },
+ },
+ HasMultiPackIndex: false,
+ HasCommitGraphs: false,
+ },
+ DefaultBranch: "refs/heads/main",
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ },
+ },
+ "member": {
+ Packfiles: &PackfilesState{
+ Packfiles: map[string]*PackfileState{
+ hash(t,
+ "pack-529ec37accbc126425efe69abdf91153411532a6",
+ "pack-895b4eade6c459f47a382a0d637ef1ce34a661c76f003c7d7a38a7420e3afc69",
+ ): {
+ Objects: []git.ObjectID{
+ // Diverging commit is pruned.
+ setup.Commits.Third.OID,
+ },
+ HasReverseIndex: true,
+ },
+ },
+ PooledObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ // Second commit is deduplicated.
+ setup.Commits.Second.OID,
+ },
+ HasMultiPackIndex: false,
+ HasCommitGraphs: true,
+ },
+ References: &ReferencesState{
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/branch-1": setup.Commits.Second.OID,
+ "refs/heads/branch-2": setup.Commits.Third.OID,
+ },
+ },
+ Alternate: "../../pool/objects",
+ },
+ },
+ Directory: generateDirectoryState(setup.Config, []*walDirectoryState{
+ {
+ lsn: 1,
+ includeObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ },
+ {
+ lsn: 3,
+ includeObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Third.OID,
+ setup.Commits.Diverging.OID,
+ },
+ },
+ {
+ lsn: 4,
+ includeObjects: []git.ObjectID{
+ setup.Commits.Second.OID,
+ },
+ },
+ {
+ lsn: 5,
+ includePackfiles: []string{hash(t,
+ "pack-529ec37accbc126425efe69abdf91153411532a6",
+ "pack-895b4eade6c459f47a382a0d637ef1ce34a661c76f003c7d7a38a7420e3afc69",
+ )},
+ includeCommitGraphs: []string{hash(t,
+ "graph-5cd3399b1657ded0a67d1dc3f9fef739fd648116",
+ "graph-cc6c67a3c5e19b7b15b1f1551363a9d163fd14b13f11b6aeedcc5a9f40ffb590",
+ )},
+ },
+ }),
+ },
+ },
+ {
+ desc: "run repacking concurrently with other repacking task",
+ steps: steps{
+ StartManager{
+ ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
+ repoPath := filepath.Join(storagePath, setup.RelativePath)
+ gittest.Exec(tb, cfg, "-C", repoPath, "repack", "-ad")
+ },
+ },
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithUnreachable,
+ },
+ },
+ RunRepack{
+ TransactionID: 2,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithUnreachable,
+ },
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Commit{
+ TransactionID: 1,
+ ExpectedError: errHousekeepingConflictConcurrent,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ Packfiles: &PackfilesState{
+ // Unreachable objects are packed.
+ LooseObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Third.OID,
+ setup.Commits.Diverging.OID,
+ },
+ Packfiles: map[string]*PackfileState{},
+ },
+ },
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
+ },
+ },
+ },
+ {
+ desc: "run repacking concurrently with other housekeeping task",
+ steps: steps{
+ StartManager{
+ ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
+ repoPath := filepath.Join(storagePath, setup.RelativePath)
+ gittest.Exec(tb, cfg, "-C", repoPath, "repack", "-ad")
+ },
+ },
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ },
+ RunRepack{
+ TransactionID: 1,
+ Config: housekeeping.RepackObjectsConfig{
+ Strategy: housekeeping.RepackObjectsStrategyFullWithUnreachable,
+ },
+ },
+ RunPackRefs{
+ TransactionID: 2,
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Commit{
+ TransactionID: 1,
+ ExpectedError: errHousekeepingConflictConcurrent,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ Packfiles: &PackfilesState{
+ // Unreachable objects are packed.
+ LooseObjects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ setup.Commits.Second.OID,
+ setup.Commits.Third.OID,
+ setup.Commits.Diverging.OID,
+ },
+ Packfiles: map[string]*PackfileState{},
+ },
+ },
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1/packed-refs": anyDirectoryEntry(setup.Config),
+ },
+ },
+ },
+ }
+}