go.temporal.io/server@v1.23.0/common/xdc/ndc_history_resender_test.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 package xdc 26 27 import ( 28 "context" 29 "testing" 30 "time" 31 32 "github.com/golang/mock/gomock" 33 "github.com/pborman/uuid" 34 "github.com/stretchr/testify/require" 35 "github.com/stretchr/testify/suite" 36 commonpb "go.temporal.io/api/common/v1" 37 enumspb "go.temporal.io/api/enums/v1" 38 historypb "go.temporal.io/api/history/v1" 39 "google.golang.org/protobuf/types/known/timestamppb" 40 41 "go.temporal.io/server/api/adminservice/v1" 42 "go.temporal.io/server/api/adminservicemock/v1" 43 historyspb "go.temporal.io/server/api/history/v1" 44 "go.temporal.io/server/api/historyservice/v1" 45 "go.temporal.io/server/api/historyservicemock/v1" 46 persistencespb "go.temporal.io/server/api/persistence/v1" 47 "go.temporal.io/server/client" 48 "go.temporal.io/server/common" 49 "go.temporal.io/server/common/cluster" 50 "go.temporal.io/server/common/log" 51 "go.temporal.io/server/common/namespace" 52 "go.temporal.io/server/common/persistence/serialization" 53 "go.temporal.io/server/common/primitives/timestamp" 54 serviceerrors "go.temporal.io/server/common/serviceerror" 55 ) 56 57 type ( 58 nDCHistoryResenderSuite struct { 59 suite.Suite 60 *require.Assertions 61 62 controller *gomock.Controller 63 mockClusterMetadata *cluster.MockMetadata 64 mockNamespaceCache *namespace.MockRegistry 65 mockClientBean *client.MockBean 66 mockAdminClient *adminservicemock.MockAdminServiceClient 67 mockHistoryClient *historyservicemock.MockHistoryServiceClient 68 69 namespaceID namespace.ID 70 namespace namespace.Name 71 72 serializer serialization.Serializer 73 logger log.Logger 74 75 rereplicator *NDCHistoryResenderImpl 76 } 77 ) 78 79 func TestNDCHistoryResenderSuite(t *testing.T) { 80 s := new(nDCHistoryResenderSuite) 81 suite.Run(t, s) 82 } 83 84 func (s *nDCHistoryResenderSuite) SetupSuite() { 85 } 86 87 func (s *nDCHistoryResenderSuite) TearDownSuite() { 88 89 } 90 91 func (s *nDCHistoryResenderSuite) SetupTest() { 92 s.Assertions = require.New(s.T()) 93 94 s.controller = gomock.NewController(s.T()) 95 s.mockClusterMetadata = cluster.NewMockMetadata(s.controller) 96 s.mockClientBean = client.NewMockBean(s.controller) 97 s.mockAdminClient = adminservicemock.NewMockAdminServiceClient(s.controller) 98 s.mockHistoryClient = historyservicemock.NewMockHistoryServiceClient(s.controller) 99 s.mockNamespaceCache = namespace.NewMockRegistry(s.controller) 100 101 s.mockClientBean.EXPECT().GetRemoteAdminClient(gomock.Any()).Return(s.mockAdminClient, nil).AnyTimes() 102 103 s.logger = log.NewTestLogger() 104 s.mockClusterMetadata.EXPECT().IsGlobalNamespaceEnabled().Return(true).AnyTimes() 105 106 s.namespaceID = namespace.ID(uuid.New()) 107 s.namespace = "some random namespace name" 108 namespaceEntry := namespace.NewGlobalNamespaceForTest( 109 &persistencespb.NamespaceInfo{Id: s.namespaceID.String(), Name: s.namespace.String()}, 110 &persistencespb.NamespaceConfig{Retention: timestamp.DurationFromDays(1)}, 111 &persistencespb.NamespaceReplicationConfig{ 112 ActiveClusterName: cluster.TestCurrentClusterName, 113 Clusters: []string{ 114 cluster.TestCurrentClusterName, 115 cluster.TestAlternativeClusterName, 116 }, 117 }, 118 1234, 119 ) 120 s.mockNamespaceCache.EXPECT().GetNamespaceByID(s.namespaceID).Return(namespaceEntry, nil).AnyTimes() 121 s.mockNamespaceCache.EXPECT().GetNamespace(s.namespace).Return(namespaceEntry, nil).AnyTimes() 122 s.serializer = serialization.NewSerializer() 123 124 s.rereplicator = NewNDCHistoryResender( 125 s.mockNamespaceCache, 126 s.mockClientBean, 127 func(ctx context.Context, request *historyservice.ReplicateEventsV2Request) error { 128 _, err := s.mockHistoryClient.ReplicateEventsV2(ctx, request) 129 return err 130 }, 131 serialization.NewSerializer(), 132 nil, 133 s.logger, 134 ) 135 } 136 137 func (s *nDCHistoryResenderSuite) TearDownTest() { 138 s.controller.Finish() 139 } 140 141 func (s *nDCHistoryResenderSuite) TestSendSingleWorkflowHistory() { 142 workflowID := "some random workflow ID" 143 runID := uuid.New() 144 startEventID := int64(123) 145 startEventVersion := int64(100) 146 token := []byte{1} 147 pageSize := defaultPageSize 148 eventBatch := []*historypb.HistoryEvent{ 149 { 150 EventId: 2, 151 Version: 123, 152 EventTime: timestamppb.New(time.Now().UTC()), 153 EventType: enumspb.EVENT_TYPE_WORKFLOW_TASK_SCHEDULED, 154 }, 155 { 156 EventId: 3, 157 Version: 123, 158 EventTime: timestamppb.New(time.Now().UTC()), 159 EventType: enumspb.EVENT_TYPE_WORKFLOW_TASK_STARTED, 160 }, 161 } 162 blob := s.serializeEvents(eventBatch) 163 versionHistoryItems := []*historyspb.VersionHistoryItem{ 164 { 165 EventId: 1, 166 Version: 1, 167 }, 168 } 169 170 s.mockAdminClient.EXPECT().GetWorkflowExecutionRawHistoryV2( 171 gomock.Any(), 172 &adminservice.GetWorkflowExecutionRawHistoryV2Request{ 173 NamespaceId: s.namespaceID.String(), 174 Execution: &commonpb.WorkflowExecution{ 175 WorkflowId: workflowID, 176 RunId: runID, 177 }, 178 StartEventId: startEventID, 179 StartEventVersion: startEventVersion, 180 EndEventId: common.EmptyEventID, 181 EndEventVersion: common.EmptyVersion, 182 MaximumPageSize: pageSize, 183 NextPageToken: nil, 184 }).Return(&adminservice.GetWorkflowExecutionRawHistoryV2Response{ 185 HistoryBatches: []*commonpb.DataBlob{blob}, 186 NextPageToken: token, 187 VersionHistory: &historyspb.VersionHistory{ 188 Items: versionHistoryItems, 189 }, 190 }, nil) 191 192 s.mockAdminClient.EXPECT().GetWorkflowExecutionRawHistoryV2( 193 gomock.Any(), 194 &adminservice.GetWorkflowExecutionRawHistoryV2Request{ 195 NamespaceId: s.namespaceID.String(), 196 Execution: &commonpb.WorkflowExecution{ 197 WorkflowId: workflowID, 198 RunId: runID, 199 }, 200 StartEventId: startEventID, 201 StartEventVersion: startEventVersion, 202 EndEventId: common.EmptyEventID, 203 EndEventVersion: common.EmptyVersion, 204 MaximumPageSize: pageSize, 205 NextPageToken: token, 206 }).Return(&adminservice.GetWorkflowExecutionRawHistoryV2Response{ 207 HistoryBatches: []*commonpb.DataBlob{blob}, 208 NextPageToken: nil, 209 VersionHistory: &historyspb.VersionHistory{ 210 Items: versionHistoryItems, 211 }, 212 }, nil) 213 214 s.mockHistoryClient.EXPECT().ReplicateEventsV2( 215 gomock.Any(), 216 &historyservice.ReplicateEventsV2Request{ 217 NamespaceId: s.namespaceID.String(), 218 WorkflowExecution: &commonpb.WorkflowExecution{ 219 WorkflowId: workflowID, 220 RunId: runID, 221 }, 222 VersionHistoryItems: versionHistoryItems, 223 Events: blob, 224 }).Return(nil, nil).Times(2) 225 226 err := s.rereplicator.SendSingleWorkflowHistory( 227 context.Background(), 228 cluster.TestCurrentClusterName, 229 s.namespaceID, 230 workflowID, 231 runID, 232 startEventID, 233 startEventVersion, 234 common.EmptyEventID, 235 common.EmptyVersion, 236 ) 237 238 s.Nil(err) 239 } 240 241 func (s *nDCHistoryResenderSuite) TestCreateReplicateRawEventsRequest() { 242 workflowID := "some random workflow ID" 243 runID := uuid.New() 244 blob := &commonpb.DataBlob{ 245 EncodingType: enumspb.ENCODING_TYPE_PROTO3, 246 Data: []byte("some random history blob"), 247 } 248 versionHistoryItems := []*historyspb.VersionHistoryItem{ 249 { 250 EventId: 1, 251 Version: 1, 252 }, 253 } 254 255 s.Equal(&historyservice.ReplicateEventsV2Request{ 256 NamespaceId: s.namespaceID.String(), 257 WorkflowExecution: &commonpb.WorkflowExecution{ 258 WorkflowId: workflowID, 259 RunId: runID, 260 }, 261 VersionHistoryItems: versionHistoryItems, 262 Events: blob, 263 }, s.rereplicator.createReplicationRawRequest( 264 s.namespaceID, 265 workflowID, 266 runID, 267 blob, 268 versionHistoryItems)) 269 } 270 271 func (s *nDCHistoryResenderSuite) TestSendReplicationRawRequest() { 272 workflowID := "some random workflow ID" 273 runID := uuid.New() 274 item := &historyspb.VersionHistoryItem{ 275 EventId: 1, 276 Version: 1, 277 } 278 request := &historyservice.ReplicateEventsV2Request{ 279 NamespaceId: s.namespaceID.String(), 280 WorkflowExecution: &commonpb.WorkflowExecution{ 281 WorkflowId: workflowID, 282 RunId: runID, 283 }, 284 Events: &commonpb.DataBlob{ 285 EncodingType: enumspb.ENCODING_TYPE_PROTO3, 286 Data: []byte("some random history blob"), 287 }, 288 VersionHistoryItems: []*historyspb.VersionHistoryItem{item}, 289 } 290 291 s.mockHistoryClient.EXPECT().ReplicateEventsV2(gomock.Any(), request).Return(nil, nil) 292 err := s.rereplicator.sendReplicationRawRequest(context.Background(), request) 293 s.Nil(err) 294 } 295 296 func (s *nDCHistoryResenderSuite) TestSendReplicationRawRequest_Err() { 297 workflowID := "some random workflow ID" 298 runID := uuid.New() 299 item := &historyspb.VersionHistoryItem{ 300 EventId: 1, 301 Version: 1, 302 } 303 request := &historyservice.ReplicateEventsV2Request{ 304 NamespaceId: s.namespaceID.String(), 305 WorkflowExecution: &commonpb.WorkflowExecution{ 306 WorkflowId: workflowID, 307 RunId: runID, 308 }, 309 Events: &commonpb.DataBlob{ 310 EncodingType: enumspb.ENCODING_TYPE_PROTO3, 311 Data: []byte("some random history blob"), 312 }, 313 VersionHistoryItems: []*historyspb.VersionHistoryItem{item}, 314 } 315 retryErr := serviceerrors.NewRetryReplication( 316 "", 317 s.namespaceID.String(), 318 workflowID, 319 runID, 320 common.EmptyEventID, 321 common.EmptyVersion, 322 common.EmptyEventID, 323 common.EmptyVersion, 324 ) 325 326 s.mockHistoryClient.EXPECT().ReplicateEventsV2(gomock.Any(), request).Return(nil, retryErr) 327 err := s.rereplicator.sendReplicationRawRequest(context.Background(), request) 328 s.Equal(retryErr, err) 329 } 330 331 func (s *nDCHistoryResenderSuite) TestGetHistory() { 332 workflowID := "some random workflow ID" 333 runID := uuid.New() 334 startEventID := int64(123) 335 endEventID := int64(345) 336 version := int64(20) 337 nextTokenIn := []byte("some random next token in") 338 nextTokenOut := []byte("some random next token out") 339 pageSize := int32(59) 340 blob := []byte("some random events blob") 341 342 response := &adminservice.GetWorkflowExecutionRawHistoryV2Response{ 343 HistoryBatches: []*commonpb.DataBlob{{ 344 EncodingType: enumspb.ENCODING_TYPE_PROTO3, 345 Data: blob, 346 }}, 347 NextPageToken: nextTokenOut, 348 } 349 s.mockAdminClient.EXPECT().GetWorkflowExecutionRawHistoryV2(gomock.Any(), &adminservice.GetWorkflowExecutionRawHistoryV2Request{ 350 NamespaceId: s.namespaceID.String(), 351 Execution: &commonpb.WorkflowExecution{ 352 WorkflowId: workflowID, 353 RunId: runID, 354 }, 355 StartEventId: startEventID, 356 StartEventVersion: version, 357 EndEventId: endEventID, 358 EndEventVersion: version, 359 MaximumPageSize: pageSize, 360 NextPageToken: nextTokenIn, 361 }).Return(response, nil) 362 363 out, err := s.rereplicator.getHistory( 364 context.Background(), 365 cluster.TestCurrentClusterName, 366 s.namespaceID, 367 workflowID, 368 runID, 369 startEventID, 370 version, 371 endEventID, 372 version, 373 nextTokenIn, 374 pageSize) 375 s.Nil(err) 376 s.Equal(response, out) 377 } 378 379 func (s *nDCHistoryResenderSuite) serializeEvents(events []*historypb.HistoryEvent) *commonpb.DataBlob { 380 blob, err := s.serializer.SerializeEvents(events, enumspb.ENCODING_TYPE_PROTO3) 381 s.Nil(err) 382 return &commonpb.DataBlob{ 383 EncodingType: enumspb.ENCODING_TYPE_PROTO3, 384 Data: blob.Data, 385 } 386 }