diff options
author | Justin Tobler <jtobler@gitlab.com> | 2023-11-08 01:36:38 +0300 |
---|---|---|
committer | Justin Tobler <jtobler@gitlab.com> | 2023-11-08 01:36:38 +0300 |
commit | d33d508716f3f212a52a0266f81e9070513401eb (patch) | |
tree | bdc5805c4a127d7587eb7de39f8a4f19fa9471ab | |
parent | 530480eb17f12a44ed3227f34f5828085dfd9473 (diff) |
praefect: Support object pool disconnection in Praefect replicator
When a secondary repository replica in Praefect is out of sync with the
primary replica, Praefect creates a replication job that performs the
`ReplicateRepository` RPC to replicate the up-to-date primary onto the
secondary. As part of this operation the replicator also checks if the
source repository is linked to an object pool and if so links the target
to the matching object pool on the secondary.
If the primary is not linked to an object pool and the secondary is
linked to an object pool, no change is made to the target repository
object pool relationship and the replication job continues normally.
This is problematic because it allows secondary repositories to diverge
from the primary. Also if the repository attempts to link to a new
object pool via the `LinkRepositoryToObjectPool` RPC, the secondary
repositories will always fail due to already being linked to an object
pool.
To prevent this from occurring, the target repository should also
perform the `DisconnectGitAlternates` RPC if it is determined that the
source repository is not linked to an object pool. This change updates
the Praefect replicator to perform object pool disconnects to ensure the
target repository remains consistent with the source.
-rw-r--r-- | internal/praefect/replicator.go | 15 | ||||
-rw-r--r-- | internal/praefect/replicator_test.go | 26 |
2 files changed, 39 insertions, 2 deletions
diff --git a/internal/praefect/replicator.go b/internal/praefect/replicator.go index 42b3eebee..b19d17481 100644 --- a/internal/praefect/replicator.go +++ b/internal/praefect/replicator.go @@ -102,8 +102,19 @@ func (dr defaultReplicator) Replicate(ctx context.Context, event datastore.Repli sourceObjectPool := resp.GetObjectPool() - if sourceObjectPool != nil { - targetObjectPoolClient := gitalypb.NewObjectPoolServiceClient(targetCC) + targetObjectPoolClient := gitalypb.NewObjectPoolServiceClient(targetCC) + + if sourceObjectPool == nil { + // If the source repository is not linked to an object pool, the target repository + // should also not be linked to any object pool to ensure consistency. + if _, err := targetObjectPoolClient.DisconnectGitAlternates(ctx, &gitalypb.DisconnectGitAlternatesRequest{ + Repository: targetRepository, + }); err != nil { + return err + } + } else { + // If the source repository is linked to an object pool, the target repository + // should link to the same object pool. targetObjectPool := proto.Clone(sourceObjectPool).(*gitalypb.ObjectPool) targetObjectPool.GetRepository().StorageName = targetRepository.GetStorageName() if _, err := targetObjectPoolClient.LinkRepositoryToObjectPool(ctx, &gitalypb.LinkRepositoryToObjectPoolRequest{ diff --git a/internal/praefect/replicator_test.go b/internal/praefect/replicator_test.go index da06bb09d..369064ee6 100644 --- a/internal/praefect/replicator_test.go +++ b/internal/praefect/replicator_test.go @@ -313,6 +313,32 @@ func testDefaultReplicatorReplicate(t *testing.T, ctx context.Context) { } }, }, + { + desc: "target disconnected from alternate to match source", + setup: func(t *testing.T) setupData { + // Create repository on source Gitaly that does not have a link to + // an object pool. + sourceRepo, _ := gittest.CreateRepository(t, ctx, sourceCfg) + + // Create repository on target Gitaly with the same relative path as + // the source repository and link it to an object pool. + targetRepo, _ := gittest.CreateRepository(t, ctx, targetCfg, gittest.CreateRepositoryConfig{ + RelativePath: sourceRepo.RelativePath, + }) + gittest.CreateObjectPool(t, ctx, targetCfg, targetRepo, gittest.CreateObjectPoolConfig{ + LinkRepositoryToObjectPool: true, + }) + + return setupData{ + job: datastore.ReplicationJob{ + ReplicaPath: sourceRepo.RelativePath, + TargetNodeStorage: targetStorage, + SourceNodeStorage: sourceStorage, + }, + expectedPool: nil, + } + }, + }, } { t.Run(tc.desc, func(t *testing.T) { testSetup := tc.setup(t) |