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  }