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 }