go.temporal.io/server@v1.23.0/common/xdc/ndc_history_resender.go (about)

     1  // The MIT License
     2  //
     3  // Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
     4  //
     5  // Copyright (c) 2020 Uber Technologies, Inc.
     6  //
     7  // Permission is hereby granted, free of charge, to any person obtaining a copy
     8  // of this software and associated documentation files (the "Software"), to deal
     9  // in the Software without restriction, including without limitation the rights
    10  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    11  // copies of the Software, and to permit persons to whom the Software is
    12  // furnished to do so, subject to the following conditions:
    13  //
    14  // The above copyright notice and this permission notice shall be included in
    15  // all copies or substantial portions of the Software.
    16  //
    17  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    18  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    19  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    20  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    21  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    22  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    23  // THE SOFTWARE.
    24  
    25  //go:generate mockgen -copyright_file ../../LICENSE -package $GOPACKAGE -source $GOFILE -destination ndc_history_resender_mock.go
    26  
    27  package xdc
    28  
    29  import (
    30  	"context"
    31  	"time"
    32  
    33  	commonpb "go.temporal.io/api/common/v1"
    34  
    35  	"go.temporal.io/server/api/adminservice/v1"
    36  	historyspb "go.temporal.io/server/api/history/v1"
    37  	"go.temporal.io/server/api/historyservice/v1"
    38  	"go.temporal.io/server/client"
    39  	"go.temporal.io/server/common/collection"
    40  	"go.temporal.io/server/common/dynamicconfig"
    41  	"go.temporal.io/server/common/log"
    42  	"go.temporal.io/server/common/log/tag"
    43  	"go.temporal.io/server/common/namespace"
    44  	"go.temporal.io/server/common/persistence/serialization"
    45  	"go.temporal.io/server/common/rpc"
    46  )
    47  
    48  const (
    49  	resendContextTimeout = 30 * time.Second
    50  )
    51  
    52  type (
    53  	// nDCHistoryReplicationFn provides the functionality to deliver replication raw history request to history
    54  	// the provided func should be thread safe
    55  	nDCHistoryReplicationFn func(ctx context.Context, request *historyservice.ReplicateEventsV2Request) error
    56  
    57  	// NDCHistoryResender is the interface for resending history events to remote
    58  	NDCHistoryResender interface {
    59  		// SendSingleWorkflowHistory sends multiple run IDs's history events to remote
    60  		SendSingleWorkflowHistory(
    61  			ctx context.Context,
    62  			remoteClusterName string,
    63  			namespaceID namespace.ID,
    64  			workflowID string,
    65  			runID string,
    66  			startEventID int64,
    67  			startEventVersion int64,
    68  			endEventID int64,
    69  			endEventVersion int64,
    70  		) error
    71  	}
    72  
    73  	// NDCHistoryResenderImpl is the implementation of NDCHistoryResender
    74  	NDCHistoryResenderImpl struct {
    75  		namespaceRegistry    namespace.Registry
    76  		clientBean           client.Bean
    77  		historyReplicationFn nDCHistoryReplicationFn
    78  		serializer           serialization.Serializer
    79  		rereplicationTimeout dynamicconfig.DurationPropertyFnWithNamespaceIDFilter
    80  		logger               log.Logger
    81  	}
    82  
    83  	historyBatch struct {
    84  		versionHistory *historyspb.VersionHistory
    85  		rawEventBatch  *commonpb.DataBlob
    86  	}
    87  )
    88  
    89  const (
    90  	defaultPageSize = int32(100)
    91  )
    92  
    93  // NewNDCHistoryResender create a new NDCHistoryResenderImpl
    94  func NewNDCHistoryResender(
    95  	namespaceRegistry namespace.Registry,
    96  	clientBean client.Bean,
    97  	historyReplicationFn nDCHistoryReplicationFn,
    98  	serializer serialization.Serializer,
    99  	rereplicationTimeout dynamicconfig.DurationPropertyFnWithNamespaceIDFilter,
   100  	logger log.Logger,
   101  ) *NDCHistoryResenderImpl {
   102  
   103  	return &NDCHistoryResenderImpl{
   104  		namespaceRegistry:    namespaceRegistry,
   105  		clientBean:           clientBean,
   106  		historyReplicationFn: historyReplicationFn,
   107  		serializer:           serializer,
   108  		rereplicationTimeout: rereplicationTimeout,
   109  		logger:               logger,
   110  	}
   111  }
   112  
   113  // SendSingleWorkflowHistory sends one run IDs's history events to remote
   114  func (n *NDCHistoryResenderImpl) SendSingleWorkflowHistory(
   115  	ctx context.Context,
   116  	remoteClusterName string,
   117  	namespaceID namespace.ID,
   118  	workflowID string,
   119  	runID string,
   120  	startEventID int64,
   121  	startEventVersion int64,
   122  	endEventID int64,
   123  	endEventVersion int64,
   124  ) error {
   125  
   126  	resendCtx := context.Background()
   127  	var cancel context.CancelFunc
   128  	if n.rereplicationTimeout != nil {
   129  		resendContextTimeout := n.rereplicationTimeout(namespaceID.String())
   130  		if resendContextTimeout > 0 {
   131  			resendCtx, cancel = context.WithTimeout(resendCtx, resendContextTimeout)
   132  			defer cancel()
   133  		}
   134  	}
   135  	resendCtx = rpc.CopyContextValues(resendCtx, ctx)
   136  
   137  	historyIterator := collection.NewPagingIterator(n.getPaginationFn(
   138  		resendCtx,
   139  		remoteClusterName,
   140  		namespaceID,
   141  		workflowID,
   142  		runID,
   143  		startEventID,
   144  		startEventVersion,
   145  		endEventID,
   146  		endEventVersion,
   147  	))
   148  
   149  	for historyIterator.HasNext() {
   150  		batch, err := historyIterator.Next()
   151  		if err != nil {
   152  			n.logger.Error("failed to get history events",
   153  				tag.WorkflowNamespaceID(namespaceID.String()),
   154  				tag.WorkflowID(workflowID),
   155  				tag.WorkflowRunID(runID),
   156  				tag.Error(err))
   157  			return err
   158  		}
   159  
   160  		replicationRequest := n.createReplicationRawRequest(
   161  			namespaceID,
   162  			workflowID,
   163  			runID,
   164  			batch.rawEventBatch,
   165  			batch.versionHistory.GetItems())
   166  
   167  		err = n.sendReplicationRawRequest(resendCtx, replicationRequest)
   168  		if err != nil {
   169  			n.logger.Error("failed to replicate events",
   170  				tag.WorkflowNamespaceID(namespaceID.String()),
   171  				tag.WorkflowID(workflowID),
   172  				tag.WorkflowRunID(runID),
   173  				tag.Error(err))
   174  			return err
   175  		}
   176  	}
   177  	return nil
   178  }
   179  
   180  func (n *NDCHistoryResenderImpl) getPaginationFn(
   181  	ctx context.Context,
   182  	remoteClusterName string,
   183  	namespaceID namespace.ID,
   184  	workflowID string,
   185  	runID string,
   186  	startEventID int64,
   187  	startEventVersion int64,
   188  	endEventID int64,
   189  	endEventVersion int64,
   190  ) collection.PaginationFn[historyBatch] {
   191  
   192  	return func(paginationToken []byte) ([]historyBatch, []byte, error) {
   193  
   194  		response, err := n.getHistory(
   195  			ctx,
   196  			remoteClusterName,
   197  			namespaceID,
   198  			workflowID,
   199  			runID,
   200  			startEventID,
   201  			startEventVersion,
   202  			endEventID,
   203  			endEventVersion,
   204  			paginationToken,
   205  			defaultPageSize,
   206  		)
   207  		if err != nil {
   208  			return nil, nil, err
   209  		}
   210  
   211  		batches := make([]historyBatch, 0, len(response.GetHistoryBatches()))
   212  		versionHistory := response.GetVersionHistory()
   213  		for _, history := range response.GetHistoryBatches() {
   214  			batch := historyBatch{
   215  				versionHistory: versionHistory,
   216  				rawEventBatch:  history,
   217  			}
   218  			batches = append(batches, batch)
   219  		}
   220  		return batches, response.NextPageToken, nil
   221  	}
   222  }
   223  
   224  func (n *NDCHistoryResenderImpl) createReplicationRawRequest(
   225  	namespaceID namespace.ID,
   226  	workflowID string,
   227  	runID string,
   228  	historyBlob *commonpb.DataBlob,
   229  	versionHistoryItems []*historyspb.VersionHistoryItem,
   230  ) *historyservice.ReplicateEventsV2Request {
   231  
   232  	request := &historyservice.ReplicateEventsV2Request{
   233  		NamespaceId: namespaceID.String(),
   234  		WorkflowExecution: &commonpb.WorkflowExecution{
   235  			WorkflowId: workflowID,
   236  			RunId:      runID,
   237  		},
   238  		Events:              historyBlob,
   239  		VersionHistoryItems: versionHistoryItems,
   240  	}
   241  	return request
   242  }
   243  
   244  func (n *NDCHistoryResenderImpl) sendReplicationRawRequest(
   245  	ctx context.Context,
   246  	request *historyservice.ReplicateEventsV2Request,
   247  ) error {
   248  
   249  	ctx, cancel := context.WithTimeout(ctx, resendContextTimeout)
   250  	defer cancel()
   251  	return n.historyReplicationFn(ctx, request)
   252  }
   253  
   254  func (n *NDCHistoryResenderImpl) getHistory(
   255  	ctx context.Context,
   256  	remoteClusterName string,
   257  	namespaceID namespace.ID,
   258  	workflowID string,
   259  	runID string,
   260  	startEventID int64,
   261  	startEventVersion int64,
   262  	endEventID int64,
   263  	endEventVersion int64,
   264  	token []byte,
   265  	pageSize int32,
   266  ) (*adminservice.GetWorkflowExecutionRawHistoryV2Response, error) {
   267  
   268  	logger := log.With(n.logger, tag.WorkflowRunID(runID))
   269  
   270  	ctx, cancel := rpc.NewContextFromParentWithTimeoutAndVersionHeaders(ctx, resendContextTimeout)
   271  	defer cancel()
   272  
   273  	adminClient, err := n.clientBean.GetRemoteAdminClient(remoteClusterName)
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  
   278  	response, err := adminClient.GetWorkflowExecutionRawHistoryV2(ctx, &adminservice.GetWorkflowExecutionRawHistoryV2Request{
   279  		NamespaceId: namespaceID.String(),
   280  		Execution: &commonpb.WorkflowExecution{
   281  			WorkflowId: workflowID,
   282  			RunId:      runID,
   283  		},
   284  		StartEventId:      startEventID,
   285  		StartEventVersion: startEventVersion,
   286  		EndEventId:        endEventID,
   287  		EndEventVersion:   endEventVersion,
   288  		MaximumPageSize:   pageSize,
   289  		NextPageToken:     token,
   290  	})
   291  	if err != nil {
   292  		logger.Error("error getting history", tag.Error(err))
   293  		return nil, err
   294  	}
   295  
   296  	return response, nil
   297  }