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:
authorSami Hiltunen <shiltunen@gitlab.com>2021-02-17 18:17:38 +0300
committerSami Hiltunen <shiltunen@gitlab.com>2021-02-17 19:50:30 +0300
commit08024c36dcedbbc8f0ad172737cc4b6351522eba (patch)
tree33f20e7706f7bd4875ede534edb718f7f43c13b9
parent98fa07272517b699bfcd5c2515bb81fd56b65f7d (diff)
implement DeleteReplica on RepositoryStore
RepositoryStore has DeleteRepository for deleting a repository from the store. It deletes the repository's record from the `repositories` table to indicate the repository should not exist on any storages anymore. It then also deletes the storage's record of having a replica of the repository. This works fine for the flow where it is currently called, namely when Rails deletes a repository. In that case, the deletion should be replicated everywhere and we should delete any records of it from the database. Reconciler is gaining support for deleting repositories from storages which should not contain them anymore. In this case, we only want to delete the replica of the repository on a given storage. We don't want to replicate the deletion anywhere else nor do we want to delete the repository's entry in `repositories` table as the repository should still exist and be accessible via other storages. To support this use case, this commit introduces a DeleteReplica method on the Repository Store to delete a record of a replica from a given storage without affecting any other state in the database.
-rw-r--r--internal/praefect/datastore/repository_store.go22
-rw-r--r--internal/praefect/datastore/repository_store_mock.go10
-rw-r--r--internal/praefect/datastore/repository_store_test.go75
3 files changed, 104 insertions, 3 deletions
diff --git a/internal/praefect/datastore/repository_store.go b/internal/praefect/datastore/repository_store.go
index bc2c8c8ba..4ed5abe54 100644
--- a/internal/praefect/datastore/repository_store.go
+++ b/internal/praefect/datastore/repository_store.go
@@ -111,6 +111,8 @@ type RepositoryStore interface {
// RepositoryNotExistsError when trying to delete a repository which has no record in the virtual storage
// or the storage.
DeleteRepository(ctx context.Context, virtualStorage, relativePath, storage string) error
+ // DeleteReplica deletes a replica of a repository from a storage without affecting other state in the virtual storage.
+ DeleteReplica(ctx context.Context, virtualStorage, relativePath, storage string) error
// RenameRepository updates a repository's relative path. It renames the virtual storage wide record as well
// as the storage's which is calling it. Returns RepositoryNotExistsError when trying to rename a repository
// which has no record in the virtual storage or the storage.
@@ -355,7 +357,7 @@ VALUES ($1, $2, $3, 0)
}
func (rs *PostgresRepositoryStore) DeleteRepository(ctx context.Context, virtualStorage, relativePath, storage string) error {
- const q = `
+ return rs.delete(ctx, `
WITH repo AS (
DELETE FROM repositories
WHERE virtual_storage = $1
@@ -366,9 +368,23 @@ DELETE FROM storage_repositories
WHERE virtual_storage = $1
AND relative_path = $2
AND storage = $3
-`
+ `, virtualStorage, relativePath, storage,
+ )
+}
+
+// DeleteReplica deletes a record from the `storage_repositories`. See the interface documentation for details.
+func (rs *PostgresRepositoryStore) DeleteReplica(ctx context.Context, virtualStorage, relativePath, storage string) error {
+ return rs.delete(ctx, `
+DELETE FROM storage_repositories
+WHERE virtual_storage = $1
+AND relative_path = $2
+AND storage = $3
+ `, virtualStorage, relativePath, storage,
+ )
+}
- result, err := rs.db.ExecContext(ctx, q, virtualStorage, relativePath, storage)
+func (rs *PostgresRepositoryStore) delete(ctx context.Context, query, virtualStorage, relativePath, storage string) error {
+ result, err := rs.db.ExecContext(ctx, query, virtualStorage, relativePath, storage)
if err != nil {
return err
}
diff --git a/internal/praefect/datastore/repository_store_mock.go b/internal/praefect/datastore/repository_store_mock.go
index 8df265ef5..13dcd2eab 100644
--- a/internal/praefect/datastore/repository_store_mock.go
+++ b/internal/praefect/datastore/repository_store_mock.go
@@ -11,6 +11,7 @@ type MockRepositoryStore struct {
SetGenerationFunc func(ctx context.Context, virtualStorage, relativePath, storage string, generation int) error
CreateRepositoryFunc func(ctx context.Context, virtualStorage, relativePath, primary string, secondaries []string, storePrimary, storeAssignments bool) error
DeleteRepositoryFunc func(ctx context.Context, virtualStorage, relativePath, storage string) error
+ DeleteReplicaFunc func(ctx context.Context, virtualStorage, relativePath, storage string) error
RenameRepositoryFunc func(ctx context.Context, virtualStorage, relativePath, storage, newRelativePath string) error
GetConsistentStoragesFunc func(ctx context.Context, virtualStorage, relativePath string) (map[string]struct{}, error)
GetPartiallyReplicatedRepositoriesFunc func(ctx context.Context, virtualStorage string, virtualStorageScopedPrimaries bool) ([]OutdatedRepository, error)
@@ -68,6 +69,15 @@ func (m MockRepositoryStore) DeleteRepository(ctx context.Context, virtualStorag
return m.DeleteRepositoryFunc(ctx, virtualStorage, relativePath, storage)
}
+// DeleteReplica runs the mock's DeleteReplicaFunc.
+func (m MockRepositoryStore) DeleteReplica(ctx context.Context, virtualStorage, relativePath, storage string) error {
+ if m.DeleteReplicaFunc == nil {
+ return nil
+ }
+
+ return m.DeleteReplicaFunc(ctx, virtualStorage, relativePath, storage)
+}
+
func (m MockRepositoryStore) RenameRepository(ctx context.Context, virtualStorage, relativePath, storage, newRelativePath string) error {
if m.RenameRepositoryFunc == nil {
return nil
diff --git a/internal/praefect/datastore/repository_store_test.go b/internal/praefect/datastore/repository_store_test.go
index 365260a03..acbe3f3c7 100644
--- a/internal/praefect/datastore/repository_store_test.go
+++ b/internal/praefect/datastore/repository_store_test.go
@@ -474,6 +474,81 @@ func testRepositoryStore(t *testing.T, newStore repositoryStoreFactory) {
})
})
+ t.Run("DeleteReplica", func(t *testing.T) {
+ rs, requireState := newStore(t, nil)
+
+ t.Run("delete non-existing", func(t *testing.T) {
+ require.Equal(t,
+ RepositoryNotExistsError{"virtual-storage-1", "relative-path-1", "storage-1"},
+ rs.DeleteReplica(ctx, "virtual-storage-1", "relative-path-1", "storage-1"),
+ )
+ })
+
+ t.Run("delete existing", func(t *testing.T) {
+ require.NoError(t, rs.SetGeneration(ctx, "virtual-storage-1", "relative-path-1", "storage-1", 0))
+ require.NoError(t, rs.SetGeneration(ctx, "virtual-storage-1", "relative-path-1", "storage-2", 0))
+ require.NoError(t, rs.SetGeneration(ctx, "virtual-storage-1", "relative-path-2", "storage-1", 0))
+ require.NoError(t, rs.SetGeneration(ctx, "virtual-storage-2", "relative-path-1", "storage-1", 0))
+
+ requireState(t, ctx,
+ virtualStorageState{
+ "virtual-storage-1": {
+ "relative-path-1": repositoryRecord{},
+ "relative-path-2": repositoryRecord{},
+ },
+ "virtual-storage-2": {
+ "relative-path-1": repositoryRecord{},
+ },
+ },
+ storageState{
+ "virtual-storage-1": {
+ "relative-path-1": {
+ "storage-1": 0,
+ "storage-2": 0,
+ },
+ "relative-path-2": {
+ "storage-1": 0,
+ },
+ },
+ "virtual-storage-2": {
+ "relative-path-1": {
+ "storage-1": 0,
+ },
+ },
+ },
+ )
+
+ require.NoError(t, rs.DeleteReplica(ctx, "virtual-storage-1", "relative-path-1", "storage-1"))
+
+ requireState(t, ctx,
+ virtualStorageState{
+ "virtual-storage-1": {
+ "relative-path-1": repositoryRecord{},
+ "relative-path-2": repositoryRecord{},
+ },
+ "virtual-storage-2": {
+ "relative-path-1": repositoryRecord{},
+ },
+ },
+ storageState{
+ "virtual-storage-1": {
+ "relative-path-1": {
+ "storage-2": 0,
+ },
+ "relative-path-2": {
+ "storage-1": 0,
+ },
+ },
+ "virtual-storage-2": {
+ "relative-path-1": {
+ "storage-1": 0,
+ },
+ },
+ },
+ )
+ })
+ })
+
t.Run("RenameRepository", func(t *testing.T) {
t.Run("rename non-existing", func(t *testing.T) {
rs, _ := newStore(t, nil)