github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/pkg/externalresource/manager/service.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 manager 15 16 import ( 17 "context" 18 19 "github.com/pingcap/log" 20 pb "github.com/pingcap/tiflow/engine/enginepb" 21 "github.com/pingcap/tiflow/engine/model" 22 resModel "github.com/pingcap/tiflow/engine/pkg/externalresource/model" 23 pkgOrm "github.com/pingcap/tiflow/engine/pkg/orm" 24 "github.com/pingcap/tiflow/engine/pkg/tenant" 25 "github.com/pingcap/tiflow/pkg/errors" 26 "go.uber.org/zap" 27 "google.golang.org/grpc/codes" 28 "google.golang.org/grpc/status" 29 ) 30 31 var _ pb.ResourceManagerServer = (*Service)(nil) 32 33 // Service implements pb.ResourceManagerServer 34 type Service struct { 35 metaclient pkgOrm.Client 36 } 37 38 // NewService creates a new externalresource manage service 39 func NewService(metaclient pkgOrm.Client) *Service { 40 return &Service{ 41 metaclient: metaclient, 42 } 43 } 44 45 // QueryResource implements ResourceManagerClient.QueryResource 46 func (s *Service) QueryResource( 47 ctx context.Context, 48 request *pb.QueryResourceRequest, 49 ) (*pb.QueryResourceResponse, error) { 50 jobID := request.GetResourceKey().GetJobId() 51 resourceID := request.GetResourceKey().GetResourceId() 52 53 if err := checkArguments(resourceID, jobID); err != nil { 54 return nil, err 55 } 56 57 record, err := s.metaclient.GetResourceByID(ctx, pkgOrm.ResourceKey{JobID: jobID, ID: resourceID}) 58 if err != nil { 59 if pkgOrm.IsNotFoundError(err) { 60 return nil, errors.ErrResourceDoesNotExist.GenWithStackByArgs(resourceID) 61 } 62 return nil, errors.ErrResourceMetastoreError.Wrap(err).GenWithStackByArgs() 63 } 64 65 if record.Deleted { 66 // This logic is currently not used. 67 return nil, status.Error(codes.NotFound, "resource marked as deleted") 68 } 69 return record.ToQueryResourceResponse(), nil 70 } 71 72 // CreateResource implements ResourceManagerClient.CreateResource 73 func (s *Service) CreateResource( 74 ctx context.Context, 75 request *pb.CreateResourceRequest, 76 ) (*pb.CreateResourceResponse, error) { 77 if err := checkArguments(request.GetResourceId(), request.GetJobId()); err != nil { 78 return nil, err 79 } 80 81 resourceRecord := &resModel.ResourceMeta{ 82 ProjectID: tenant.NewProjectInfo(request.GetProjectInfo().TenantId, request.GetProjectInfo().ProjectId).UniqueID(), 83 ID: request.GetResourceId(), 84 Job: request.GetJobId(), 85 Worker: request.GetCreatorWorkerId(), 86 Executor: resModel.ExecutorID(request.GetCreatorExecutor()), 87 Deleted: false, 88 } 89 90 err := s.metaclient.CreateResource(ctx, resourceRecord) 91 if errors.Is(err, errors.ErrDuplicateResourceID) { 92 return nil, errors.ErrResourceAlreadyExists.GenWithStackByArgs(request.GetResourceId()) 93 } 94 if err != nil { 95 return nil, errors.ErrResourceMetastoreError.Wrap(err).GenWithStackByArgs() 96 } 97 98 return &pb.CreateResourceResponse{}, nil 99 } 100 101 // RemoveResource implements ResourceManagerClient.RemoveResource 102 func (s *Service) RemoveResource( 103 ctx context.Context, 104 request *pb.RemoveResourceRequest, 105 ) (*pb.RemoveResourceResponse, error) { 106 jobID := request.GetResourceKey().GetJobId() 107 resourceID := request.GetResourceKey().GetResourceId() 108 if err := checkArguments(resourceID, jobID); err != nil { 109 return nil, err 110 } 111 112 res, err := s.metaclient.DeleteResource(ctx, pkgOrm.ResourceKey{JobID: jobID, ID: resourceID}) 113 if err != nil { 114 return nil, errors.ErrResourceMetastoreError.Wrap(err).GenWithStackByArgs() 115 } 116 117 if res.RowsAffected() == 0 { 118 return nil, errors.ErrResourceDoesNotExist.GenWithStackByArgs(resourceID) 119 } 120 if res.RowsAffected() > 1 { 121 log.Panic("unexpected RowsAffected", 122 zap.String("job-id", jobID), 123 zap.String("resource-id", resourceID)) 124 } 125 126 return &pb.RemoveResourceResponse{}, nil 127 } 128 129 // GetPlacementConstraint is called by the Scheduler to determine whether 130 // a resource the worker relies on requires the worker running on a specific 131 // executor. 132 // Returns: 133 // (1) A local resource is required and the resource exists: (executorID, true, nil) 134 // (2) A local resource is required but the resource is not found: ("", false, ErrResourceDoesNotExist) 135 // (3) No placement constraint is needed: ("", false, nil) 136 // (4) Other errors: ("", false, err) 137 func (s *Service) GetPlacementConstraint( 138 ctx context.Context, 139 resourceKey resModel.ResourceKey, 140 ) (resModel.ExecutorID, bool, error) { 141 logger := log.L().With( 142 zap.String("job-id", resourceKey.JobID), 143 zap.String("resource-id", resourceKey.ID)) 144 145 rType, _, err := resModel.ParseResourceID(resourceKey.ID) 146 if err != nil { 147 return "", false, err 148 } 149 150 if rType != resModel.ResourceTypeLocalFile { 151 logger.Info("Resource does not need a constraint", 152 zap.String("resource-id", resourceKey.ID), zap.String("type", string(rType))) 153 return "", false, nil 154 } 155 156 record, err := s.metaclient.GetResourceByID(ctx, pkgOrm.ResourceKey{JobID: resourceKey.JobID, ID: resourceKey.ID}) 157 if err != nil { 158 if pkgOrm.IsNotFoundError(err) { 159 return "", false, errors.ErrResourceDoesNotExist.GenWithStackByArgs(resourceKey.ID) 160 } 161 return "", false, err 162 } 163 164 if record.Deleted { 165 logger.Info("Resource meta is marked as deleted", zap.Any("record", record)) 166 return "", false, errors.ErrResourceDoesNotExist.GenWithStackByArgs(resourceKey.ID) 167 } 168 return record.Executor, true, nil 169 } 170 171 func checkArguments(resourceID resModel.ResourceID, jobID model.JobID) error { 172 if resourceID == "" { 173 return errors.ErrInvalidArgument.GenWithStackByArgs("resource-id") 174 } 175 176 if jobID == "" { 177 return errors.ErrInvalidArgument.GenWithStackByArgs("job-id") 178 } 179 return nil 180 }