github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/pkg/externalresource/broker/storage_handle.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 broker 15 16 import ( 17 "context" 18 19 "github.com/pingcap/log" 20 brStorage "github.com/pingcap/tidb/br/pkg/storage" 21 pb "github.com/pingcap/tiflow/engine/enginepb" 22 "github.com/pingcap/tiflow/engine/pkg/client" 23 "github.com/pingcap/tiflow/engine/pkg/externalresource/internal" 24 resModel "github.com/pingcap/tiflow/engine/pkg/externalresource/model" 25 "github.com/pingcap/tiflow/pkg/errors" 26 "go.uber.org/atomic" 27 "go.uber.org/zap" 28 ) 29 30 // Handle defines an interface for interact with framework 31 type Handle interface { 32 ID() resModel.ResourceID 33 BrExternalStorage() brStorage.ExternalStorage 34 Persist(ctx context.Context) error 35 Discard(ctx context.Context) error 36 } 37 38 // ResourceHandle contains a brStorage.ExternalStorage. 39 // It helps Dataflow Engine reuse the external storage facilities 40 // implemented in Br. 41 type ResourceHandle struct { 42 executorID resModel.ExecutorID 43 jobID resModel.JobID 44 client client.ResourceManagerClient 45 46 fileManager internal.FileManager 47 desc internal.ResourceDescriptor 48 inner brStorage.ExternalStorage 49 50 // isPersisted should be set to true if the 51 // resource has been registered with the servermaster. 52 isPersisted atomic.Bool 53 isInvalid atomic.Bool 54 } 55 56 func newResourceHandle( 57 jobID resModel.JobID, 58 executorID resModel.ExecutorID, 59 fm internal.FileManager, 60 desc internal.ResourceDescriptor, 61 isPersisted bool, 62 client client.ResourceManagerClient, 63 ) (*ResourceHandle, error) { 64 // TODO(maxshuang): check, may need a context for BrExternalStorage 65 inner, err := desc.ExternalStorage(context.Background()) 66 if err != nil { 67 return nil, err 68 } 69 70 h := &ResourceHandle{ 71 executorID: executorID, 72 jobID: jobID, 73 client: client, 74 75 desc: desc, 76 fileManager: fm, 77 inner: inner, 78 } 79 80 if isPersisted { 81 h.isPersisted.Store(true) 82 } 83 return h, nil 84 } 85 86 // ID implements Handle.ID 87 func (h *ResourceHandle) ID() resModel.ResourceID { 88 return h.desc.ID() 89 } 90 91 // BrExternalStorage implements Handle.BrExternalStorage 92 func (h *ResourceHandle) BrExternalStorage() brStorage.ExternalStorage { 93 return h.inner 94 } 95 96 // Persist implements Handle.Persist 97 func (h *ResourceHandle) Persist(ctx context.Context) error { 98 if h.isInvalid.Load() { 99 // Trying to persist invalid resource. 100 return errors.ErrInvalidResourceHandle.FastGenByArgs() 101 } 102 103 creatorExecutor := h.desc.ResourceIdent().Executor 104 if h.isPersisted.Load() { 105 log.Warn("Trying to persist a resource that has already been persisted", 106 zap.Any("resourceDesc", h.desc), 107 zap.Any("creatorExecutor", string(creatorExecutor)), 108 zap.String("currentExecutor", string(h.executorID))) 109 return nil 110 } 111 112 if creatorExecutor != h.executorID { 113 // in this case, the resource should have been persisted by the creator. 114 log.Panic("Trying to persist a resource that is not created by current executor", 115 zap.Any("resourceDesc", h.desc), 116 zap.Any("creator", string(creatorExecutor)), 117 zap.String("currentExecutor", string(h.executorID))) 118 } 119 120 err := h.client.CreateResource(ctx, &pb.CreateResourceRequest{ 121 ProjectInfo: &pb.ProjectInfo{ 122 TenantId: h.desc.ResourceIdent().TenantID(), 123 ProjectId: h.desc.ResourceIdent().ProjectID(), 124 }, 125 ResourceId: h.desc.ID(), 126 CreatorExecutor: string(h.executorID), 127 JobId: h.jobID, 128 CreatorWorkerId: h.desc.ResourceIdent().WorkerID, 129 }) 130 if err != nil { 131 // The RPC could have succeeded on server's side. 132 // We do not need to handle it for now, as the 133 // dangling meta records will be cleaned up by 134 // garbage collection eventually. 135 // TODO proper retrying. 136 return errors.Trace(err) 137 } 138 err = h.fileManager.SetPersisted(ctx, h.desc.ResourceIdent()) 139 if err != nil { 140 return errors.Trace(err) 141 } 142 h.isPersisted.Store(true) 143 return nil 144 } 145 146 // Discard implements Handle.Discard 147 // Note that the current design does not allow multiple workers to hold 148 // persistent resources simultaneously. 149 func (h *ResourceHandle) Discard(ctx context.Context) error { 150 if h.isInvalid.Load() { 151 // Trying to discard invalid resource. 152 return errors.ErrInvalidResourceHandle.FastGenByArgs() 153 } 154 155 err := h.fileManager.RemoveResource(ctx, h.desc.ResourceIdent()) 156 if err != nil { 157 return err 158 } 159 160 if h.isPersisted.Load() { 161 err := h.client.RemoveResource(ctx, &pb.RemoveResourceRequest{ 162 ResourceKey: &pb.ResourceKey{ 163 JobId: h.jobID, 164 ResourceId: h.desc.ID(), 165 }, 166 }) 167 if err != nil { 168 // TODO proper retrying. 169 return errors.Trace(err) 170 } 171 h.isPersisted.Store(false) 172 } 173 174 h.isInvalid.Store(true) 175 return nil 176 }