github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/pkg/externalresource/internal/bucket/resource_controller_test.go (about) 1 // Copyright 2022 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package bucket 15 16 import ( 17 "context" 18 "fmt" 19 "testing" 20 21 "github.com/pingcap/tiflow/engine/pkg/externalresource/internal" 22 resModel "github.com/pingcap/tiflow/engine/pkg/externalresource/model" 23 "github.com/pingcap/tiflow/pkg/errors" 24 "github.com/stretchr/testify/require" 25 ) 26 27 const ( 28 numTemporaryResources = 10 29 numPersistedResources = 10 30 ) 31 32 func TestS3ResourceController(t *testing.T) { 33 t.Parallel() 34 35 ctx, cancel := context.WithTimeout(context.Background(), caseTimeout) 36 defer cancel() 37 38 temproraryResNames := make([]resModel.ResourceName, numTemporaryResources) 39 for i := 0; i < numTemporaryResources; i++ { 40 resID := fmt.Sprintf("/s3/temporary-resource-%d", i) 41 _, resName, err := resModel.ParseResourceID(resID) 42 require.NoError(t, err) 43 temproraryResNames[i] = resName 44 } 45 46 persistedResNames := make([]resModel.ResourceName, numPersistedResources) 47 persistedResMetas := []*resModel.ResourceMeta{} 48 for i := 0; i < numPersistedResources; i++ { 49 resID := fmt.Sprintf("/s3/persisted-resource-%d", i) 50 _, resName, err := resModel.ParseResourceID(resID) 51 require.NoError(t, err) 52 persistedResNames[i] = resName 53 } 54 55 fm, factory := NewFileManagerForUT(t.TempDir(), MockExecutorID) 56 workers := []string{"worker-1", "worker-2", "worker-3"} 57 // generate mock data 58 for _, worker := range workers { 59 scope := internal.ResourceScope{ 60 Executor: MockExecutorID, 61 WorkerID: worker, 62 } 63 64 for _, persistedResName := range persistedResNames { 65 ident := internal.ResourceIdent{ 66 ResourceScope: scope, 67 Name: persistedResName, 68 } 69 _, err := fm.CreateResource(ctx, ident) 70 require.NoError(t, err) 71 72 err = fm.SetPersisted(ctx, ident) 73 require.NoError(t, err) 74 75 persistedResMetas = append(persistedResMetas, &resModel.ResourceMeta{ 76 ID: resModel.BuildResourceID(resModel.ResourceTypeS3, persistedResName), 77 Executor: MockExecutorID, 78 Worker: worker, 79 }) 80 } 81 82 for _, tempResName := range temproraryResNames { 83 _, err := fm.CreateResource(ctx, internal.ResourceIdent{ 84 ResourceScope: scope, 85 Name: tempResName, 86 }) 87 require.NoError(t, err) 88 } 89 } 90 91 checkWorker := func(worker string, removed bool) { 92 scope := internal.ResourceScope{ 93 Executor: MockExecutorID, 94 WorkerID: worker, 95 } 96 for _, persistedResName := range persistedResNames { 97 ident := internal.ResourceIdent{ 98 ResourceScope: scope, 99 Name: persistedResName, 100 } 101 _, err := fm.GetPersistedResource(ctx, ident) 102 require.NoError(t, err) 103 } 104 105 for _, tempResName := range temproraryResNames { 106 _, err := fm.GetPersistedResource(ctx, internal.ResourceIdent{ 107 ResourceScope: scope, 108 Name: tempResName, 109 }) 110 if removed { 111 require.True(t, errors.Is(err, errors.ErrResourceFilesNotFound)) 112 } else { 113 require.NoError(t, err) 114 } 115 } 116 } 117 checkWorker(workers[0], false) 118 checkWorker(workers[1], false) 119 checkWorker(workers[2], false) 120 121 // test GCExecutor 122 fm1 := NewFileManagerForUTFromSharedStorageFactory("leader-controller", factory) 123 controller := &resourceController{fm: fm1} 124 gcExecutor := func() { 125 err := controller.GCExecutor(ctx, persistedResMetas, MockExecutorID) 126 require.NoError(t, err) 127 checkWorker(workers[0], true) 128 checkWorker(workers[1], true) 129 checkWorker(workers[2], true) 130 } 131 gcExecutor() 132 // test for idempotency 133 gcExecutor() 134 135 // test GCSingleResource 136 for _, res := range persistedResMetas { 137 _, resName, err := resModel.ParseResourceID(res.ID) 138 require.NoError(t, err) 139 ident := internal.ResourceIdent{ 140 ResourceScope: internal.ResourceScope{ 141 Executor: MockExecutorID, 142 WorkerID: res.Worker, 143 }, 144 Name: resName, 145 } 146 _, err = fm.GetPersistedResource(ctx, ident) 147 require.NoError(t, err) 148 149 require.NoError(t, controller.GCSingleResource(ctx, res)) 150 _, err = fm.GetPersistedResource(ctx, ident) 151 require.True(t, errors.Is(err, errors.ErrResourceFilesNotFound)) 152 153 // test for idempotency 154 require.NoError(t, controller.GCSingleResource(ctx, res)) 155 _, err = fm.GetPersistedResource(ctx, ident) 156 require.True(t, errors.Is(err, errors.ErrResourceFilesNotFound)) 157 } 158 }