github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/pkg/externalresource/model/model.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 model 15 16 import ( 17 "encoding/hex" 18 "fmt" 19 "path" 20 "strings" 21 22 "github.com/pingcap/log" 23 pb "github.com/pingcap/tiflow/engine/enginepb" 24 "github.com/pingcap/tiflow/engine/model" 25 ormModel "github.com/pingcap/tiflow/engine/pkg/orm/model" 26 "github.com/pingcap/tiflow/engine/pkg/tenant" 27 "github.com/pingcap/tiflow/pkg/errors" 28 "go.uber.org/zap" 29 ) 30 31 type ( 32 // WorkerID alias worker id string 33 WorkerID = string 34 // JobID alias job id string 35 JobID = model.JobID 36 // ExecutorID alias model.ExecutorID 37 ExecutorID = model.ExecutorID 38 39 // ResourceType represents the type of the resource 40 ResourceType string 41 // ResourceID should be in the form of `/<type>/<unique-name>`, currently 42 // only local type is available. 43 ResourceID = string 44 // ResourceName is a string encoding raw resource name in hexadecimal. 45 // The raw resource name is the ResourceID with its type prefix removed. 46 // For example, the raw resource name of `/local/resource-1` is `resource-1`. 47 ResourceName = string 48 ) 49 50 // ResourceUpdateColumns is used in gorm update 51 var ResourceUpdateColumns = []string{ 52 "updated_at", 53 "project_id", 54 "tenant_id", 55 "id", 56 "job_id", 57 "worker_id", 58 "executor_id", 59 "deleted", 60 } 61 62 // ResourceKey is the unique identifier for the resource 63 type ResourceKey struct { 64 JobID JobID 65 ID ResourceID 66 } 67 68 // ToResourceKeys converts resource requirements in pb to resource keys 69 func ToResourceKeys(requires []*pb.ResourceKey) []ResourceKey { 70 if len(requires) == 0 { 71 return nil 72 } 73 rks := make([]ResourceKey, 0, len(requires)) 74 for _, require := range requires { 75 rks = append(rks, ResourceKey{JobID: require.GetJobId(), ID: require.GetResourceId()}) 76 } 77 78 return rks 79 } 80 81 // ToResourceRequirement return the resource requirement of pb 82 func ToResourceRequirement(jobID JobID, resourceIDs ...ResourceID) []*pb.ResourceKey { 83 if len(resourceIDs) == 0 { 84 return nil 85 } 86 ress := make([]*pb.ResourceKey, 0, len(resourceIDs)) 87 for _, rid := range resourceIDs { 88 ress = append(ress, &pb.ResourceKey{JobId: jobID, ResourceId: rid}) 89 } 90 91 return ress 92 } 93 94 // ResourceMeta is the records stored in the metastore. 95 type ResourceMeta struct { 96 ormModel.Model 97 ProjectID tenant.ProjectID `json:"project-id" gorm:"column:project_id;type:varchar(128) not null;"` 98 TenantID tenant.Tenant `json:"tenant-id" gorm:"column:tenant_id;type:varchar(128) not null;"` 99 ID ResourceID `json:"id" gorm:"column:id;type:varchar(128) not null;uniqueIndex:uidx_rid,priority:2;index:idx_rei,priority:2"` 100 Job JobID `json:"job" gorm:"column:job_id;type:varchar(128) not null;uniqueIndex:uidx_rid,priority:1"` 101 Worker WorkerID `json:"worker" gorm:"column:worker_id;type:varchar(128) not null"` 102 Executor ExecutorID `json:"executor" gorm:"column:executor_id;type:varchar(128) not null;index:idx_rei,priority:1"` 103 GCPending bool `json:"gc-pending" gorm:"column:gc_pending;type:BOOLEAN"` 104 105 // TODO soft delete has not be implemented, because it requires modifying too many 106 // unit tests in engine/pkg/orm 107 Deleted bool `json:"deleted" gorm:"column:deleted;type:BOOLEAN"` 108 } 109 110 // GetID implements dataset.DataEntry 111 func (m *ResourceMeta) GetID() string { 112 return m.ID 113 } 114 115 // ToQueryResourceResponse converts the ResourceMeta to pb.QueryResourceResponse 116 func (m *ResourceMeta) ToQueryResourceResponse() *pb.QueryResourceResponse { 117 return &pb.QueryResourceResponse{ 118 CreatorExecutor: string(m.Executor), 119 JobId: m.Job, 120 CreatorWorkerId: m.Worker, 121 } 122 } 123 124 // Map is used in gorm update 125 func (m *ResourceMeta) Map() map[string]interface{} { 126 return map[string]interface{}{ 127 "project_id": m.ProjectID, 128 "tenant_id": m.TenantID, 129 "id": m.ID, 130 "job_id": m.Job, 131 "worker_id": m.Worker, 132 "executor_id": m.Executor, 133 "deleted": m.Deleted, 134 } 135 } 136 137 // Define all supported resource types. 138 const ( 139 ResourceTypeLocalFile = ResourceType("local") 140 ResourceTypeS3 = ResourceType("s3") 141 ResourceTypeGCS = ResourceType("gs") 142 ResourceTypeNone = ResourceType("none") 143 ) 144 145 // BuildPrefix returns the prefix of the resource type. 146 func (r ResourceType) BuildPrefix() string { 147 // For local file, the prefix is `/local`. For S3, the prefix is `/s3`. 148 return fmt.Sprintf("/%s", r) 149 } 150 151 // ParseResourceID returns the ResourceType and the path suffix. 152 func ParseResourceID(rpath ResourceID) (ResourceType, ResourceName, error) { 153 if !strings.HasPrefix(rpath, "/") { 154 return "", "", errors.ErrIllegalResourcePath.GenWithStackByArgs(rpath) 155 } 156 rpath = strings.TrimPrefix(rpath, "/") 157 segments := strings.Split(rpath, "/") 158 if len(segments) == 0 { 159 return "", "", errors.ErrIllegalResourcePath.GenWithStackByArgs(rpath) 160 } 161 162 var resourceType ResourceType 163 switch ResourceType(segments[0]) { 164 case ResourceTypeLocalFile: 165 resourceType = ResourceTypeLocalFile 166 case ResourceTypeS3: 167 resourceType = ResourceTypeS3 168 case ResourceTypeGCS: 169 resourceType = ResourceTypeGCS 170 default: 171 return "", "", errors.ErrIllegalResourcePath.GenWithStackByArgs(rpath) 172 } 173 174 suffix := path.Join(segments[1:]...) 175 return resourceType, EncodeResourceName(suffix), nil 176 } 177 178 // BuildResourceID returns an ResourceID based on given ResourceType and ResourceName. 179 func BuildResourceID(rtype ResourceType, resName ResourceName) ResourceID { 180 name, err := DecodeResourceName(resName) 181 if err != nil { 182 log.Panic("invalid resource name", zap.Error(err)) 183 } 184 return path.Join("/"+string(rtype), name) 185 } 186 187 // EncodeResourceName encodes raw resource name to a valid resource name. 188 func EncodeResourceName(rawResName string) ResourceName { 189 resName := hex.EncodeToString([]byte(rawResName)) 190 return resName 191 } 192 193 // DecodeResourceName decodes resource name to raw resource name. 194 func DecodeResourceName(resName ResourceName) (string, error) { 195 rawResName, err := hex.DecodeString(resName) 196 if err != nil { 197 return "", err 198 } 199 return string(rawResName), nil 200 }