Files
tasks/tests/xdc/stream_based_replication_test.go
Hai ZhaoandGitHub 09e53340fe fix flaky xdc stream based replication test (#6898)
## What changed?
<!-- Describe what has changed in this PR -->
Fix flaky xdc stream based replication test and re-enable it.

## Why?
<!-- Tell your future self why have you made these changes -->
When state-based replication is enabled, if there are workflow tasks in
fly and namespace failover version changes, these workflow tasks will be
failed. This will generate events and replication tasks.

## How did you test it?
<!-- How have you verified this change? Tested locally? Added a unit
test? Checked in staging env? -->
Unit test.

## Potential risks
<!-- Assuming the worst case, what can be broken when deploying this
change to production? -->

## Documentation
<!-- Have you made sure this change doesn't falsify anything currently
stated in `docs/`? If significant
new behavior is added, have you described that in `docs/`? -->

## Is hotfix candidate?
<!-- Is this PR a hotfix candidate or does it require a notification to
be sent to the broader community? (Yes/No) -->
2024-11-27 10:56:53 -08:00

832 lines
26 KiB
Go

// The MIT License
//
// Copyright (c) 2020 Temporal Technologies Inc. All rights reserved.
//
// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package xdc
import (
"context"
"errors"
"fmt"
"sync"
"testing"
"time"
"github.com/pborman/uuid"
"github.com/stretchr/testify/suite"
commandpb "go.temporal.io/api/command/v1"
commonpb "go.temporal.io/api/common/v1"
enumspb "go.temporal.io/api/enums/v1"
historypb "go.temporal.io/api/history/v1"
"go.temporal.io/api/serviceerror"
taskqueuepb "go.temporal.io/api/taskqueue/v1"
"go.temporal.io/api/workflowservice/v1"
"go.temporal.io/server/api/adminservice/v1"
historyspb "go.temporal.io/server/api/history/v1"
"go.temporal.io/server/api/historyservice/v1"
"go.temporal.io/server/client"
"go.temporal.io/server/client/history"
"go.temporal.io/server/common"
"go.temporal.io/server/common/config"
"go.temporal.io/server/common/dynamicconfig"
"go.temporal.io/server/common/log"
"go.temporal.io/server/common/namespace"
"go.temporal.io/server/common/payloads"
"go.temporal.io/server/common/persistence/serialization"
"go.temporal.io/server/common/primitives"
test "go.temporal.io/server/common/testing"
"go.temporal.io/server/service/history/replication/eventhandler"
"go.temporal.io/server/tests/testcore"
"go.uber.org/fx"
"go.uber.org/mock/gomock"
"google.golang.org/protobuf/types/known/durationpb"
)
type (
streamBasedReplicationTestSuite struct {
xdcBaseSuite
controller *gomock.Controller
namespaceName string
namespaceID string
serializer serialization.Serializer
generator test.Generator
once sync.Once
enableTransitionHistory bool
}
)
func TestStreamBasedReplicationTestSuite(t *testing.T) {
for _, tc := range []struct {
name string
enableTransitionHistory bool
}{
{
name: "DisableTransitionHistory",
enableTransitionHistory: false,
},
{
name: "EnableTransitionHistory",
enableTransitionHistory: true,
},
} {
t.Run(tc.name, func(t *testing.T) {
s := &streamBasedReplicationTestSuite{
namespaceName: "replication-test-" + common.GenerateRandomString(5),
enableTransitionHistory: tc.enableTransitionHistory,
}
suite.Run(t, s)
})
}
}
func (s *streamBasedReplicationTestSuite) SetupSuite() {
s.controller = gomock.NewController(s.T())
s.dynamicConfigOverrides = map[dynamicconfig.Key]any{
dynamicconfig.EnableReplicationStream.Key(): true,
dynamicconfig.EnableEagerNamespaceRefresher.Key(): true,
dynamicconfig.EnableReplicationTaskBatching.Key(): true,
dynamicconfig.EnableReplicateLocalGeneratedEvents.Key(): true,
dynamicconfig.EnableTransitionHistory.Key(): s.enableTransitionHistory,
}
s.logger = log.NewTestLogger()
s.serializer = serialization.NewSerializer()
s.setupSuite(
[]string{
"active",
"standby",
},
testcore.WithFxOptionsForService(primitives.AllServices,
fx.Decorate(
func() config.DCRedirectionPolicy {
return config.DCRedirectionPolicy{Policy: "noop"}
},
),
),
)
}
func (s *streamBasedReplicationTestSuite) TearDownSuite() {
if s.generator != nil {
s.generator.Reset()
}
s.controller.Finish()
s.tearDownSuite()
}
func (s *streamBasedReplicationTestSuite) SetupTest() {
s.setupTest()
s.once.Do(func() {
ctx := context.Background()
_, err := s.cluster1.FrontendClient().RegisterNamespace(ctx, &workflowservice.RegisterNamespaceRequest{
Namespace: s.namespaceName,
Clusters: s.clusterReplicationConfig(),
// The first cluster is the active cluster.
ActiveClusterName: s.clusterNames[0],
// Needed so that the namespace is replicated.
IsGlobalNamespace: true,
// This is a required parameter.
WorkflowExecutionRetentionPeriod: durationpb.New(time.Hour * 24),
})
s.Require().NoError(err)
err = s.waitUntilNamespaceReplicated(ctx, s.namespaceName)
s.Require().NoError(err)
nsRes, _ := s.cluster1.FrontendClient().DescribeNamespace(ctx, &workflowservice.DescribeNamespaceRequest{
Namespace: s.namespaceName,
})
s.namespaceID = nsRes.NamespaceInfo.GetId()
s.generator = test.InitializeHistoryEventGenerator("namespace", "ns-id", 1)
})
}
func (s *streamBasedReplicationTestSuite) TestReplicateHistoryEvents_ForceReplicationScenario() {
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, testTimeout)
defer cancel()
var versions []int64
if s.enableTransitionHistory {
// Use versions for cluster1 (active) so we can update workflows
// Use same versions to prevent workflow tasks from being failed due to WORKFLOW_TASK_FAILED_CAUSE_FAILOVER_CLOSE_COMMAND
versions = []int64{1, 1, 1, 1, 1, 1, 1, 1, 1}
} else {
versions = []int64{2, 12, 22, 32, 2, 1, 5, 8, 9}
}
// let's import some events into cluster 1
historyClient1 := s.cluster1.HistoryClient()
executions := s.importTestEvents(historyClient1, namespace.Name(s.namespaceName), namespace.ID(s.namespaceID), versions)
// let's trigger replication by calling GenerateLastHistoryReplicationTasks. This is also used by force replication logic
for _, execution := range executions {
_, err := historyClient1.GenerateLastHistoryReplicationTasks(ctx, &historyservice.GenerateLastHistoryReplicationTasksRequest{
NamespaceId: s.namespaceID,
Execution: execution,
})
s.NoError(err)
}
s.waitForClusterSynced()
for _, execution := range executions {
err := s.assertHistoryEvents(ctx, s.namespaceID, execution.GetWorkflowId(), execution.GetRunId())
s.NoError(err)
}
}
func (s *streamBasedReplicationTestSuite) importTestEvents(
historyClient historyservice.HistoryServiceClient,
namespaceName namespace.Name,
namespaceId namespace.ID,
versions []int64,
) []*commonpb.WorkflowExecution {
executions := []*commonpb.WorkflowExecution{}
s.generator.Reset()
isCloseEvent := func(event *historypb.HistoryEvent) bool {
eventType := event.GetEventType()
if eventType == enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED ||
eventType == enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_FAILED ||
eventType == enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED ||
eventType == enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_TERMINATED ||
eventType == enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_CONTINUED_AS_NEW ||
eventType == enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_TIMED_OUT {
return true
}
return false
}
var runID string
for _, version := range versions {
workflowID := "xdc-stream-replication-test-" + uuid.New()
runID = uuid.New()
var historyBatch []*historypb.History
s.generator = test.InitializeHistoryEventGenerator(namespaceName, namespaceId, version)
ImportLoop:
for s.generator.HasNextVertex() {
events := s.generator.GetNextVertices()
historyEvents := &historypb.History{}
for _, event := range events {
historyEvents.Events = append(historyEvents.Events, event.GetData().(*historypb.HistoryEvent))
}
if isCloseEvent(historyEvents.Events[len(historyEvents.Events)-1]) {
break ImportLoop
}
historyBatch = append(historyBatch, historyEvents)
}
versionHistory, err := testcore.EventBatchesToVersionHistory(nil, historyBatch)
s.NoError(err)
s.importEvents(
workflowID,
runID,
versionHistory,
historyBatch,
historyClient,
true,
)
if s.enableTransitionHistory {
// signal the workflow to make sure the TransitionHistory is updated
signalName := "my signal"
signalInput := payloads.EncodeString("my signal input")
client1 := s.cluster1.FrontendClient() // active
_, err = client1.SignalWorkflowExecution(context.Background(), &workflowservice.SignalWorkflowExecutionRequest{
Namespace: s.namespaceName,
WorkflowExecution: &commonpb.WorkflowExecution{WorkflowId: workflowID, RunId: runID},
SignalName: signalName,
Input: signalInput,
Identity: "worker1",
})
s.NoError(err)
}
executions = append(executions, &commonpb.WorkflowExecution{WorkflowId: workflowID, RunId: runID})
}
return executions
}
func (s *streamBasedReplicationTestSuite) waitUntilNamespaceReplicated(
ctx context.Context,
namespaceName string,
) error {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
_, err := s.cluster2.FrontendClient().DescribeNamespace(ctx, &workflowservice.DescribeNamespaceRequest{
Namespace: namespaceName,
})
if err != nil {
continue
}
return nil
}
}
}
func (s *streamBasedReplicationTestSuite) assertHistoryEvents(
ctx context.Context,
namespaceId string,
workflowId string,
runId string,
) error {
mockClientBean := client.NewMockBean(s.controller)
mockClientBean.
EXPECT().
GetRemoteAdminClient("cluster1").
Return(s.cluster1.AdminClient(), nil).
AnyTimes()
mockClientBean.EXPECT().GetRemoteAdminClient("cluster2").Return(s.cluster2.AdminClient(), nil).AnyTimes()
serializer := serialization.NewSerializer()
cluster1Fetcher := eventhandler.NewHistoryPaginatedFetcher(
nil,
mockClientBean,
serializer,
s.logger,
)
cluster2Fetcher := eventhandler.NewHistoryPaginatedFetcher(
nil,
mockClientBean,
serializer,
s.logger,
)
iterator1 := cluster1Fetcher.GetSingleWorkflowHistoryPaginatedIteratorExclusive(
ctx, "cluster1", namespace.ID(namespaceId), workflowId, runId, 0, 1, 0, 0)
iterator2 := cluster2Fetcher.GetSingleWorkflowHistoryPaginatedIteratorExclusive(
ctx, "cluster2", namespace.ID(namespaceId), workflowId, runId, 0, 1, 0, 0)
for iterator1.HasNext() {
s.True(iterator2.HasNext())
batch1, err := iterator1.Next()
s.NoError(err)
batch2, err := iterator2.Next()
s.NoError(err)
getMsg := func() string {
events1, _ := s.serializer.DeserializeEvents(batch1.RawEventBatch)
events2, _ := s.serializer.DeserializeEvents(batch2.RawEventBatch)
return fmt.Sprintf("Not equal \nevents1: %v \nevents2: %v", events1, events2)
}
s.Equal(batch1.RawEventBatch, batch2.RawEventBatch, getMsg())
s.Equal(batch1.VersionHistory.Items, batch2.VersionHistory.Items)
}
s.False(iterator2.HasNext())
return nil
}
func (s *streamBasedReplicationTestSuite) importEvents(
workflowID string,
runID string,
versionHistory *historyspb.VersionHistory,
eventBatches []*historypb.History,
historyClient historyservice.HistoryServiceClient,
verifyWorkflowNotExists bool,
) {
if len(eventBatches) == 0 {
return
}
historyClient = history.NewRetryableClient(
historyClient,
common.CreateHistoryClientRetryPolicy(),
common.IsResourceExhausted,
)
var token []byte
for _, batch := range eventBatches {
blob, err := s.serializer.SerializeEvents(batch.Events, enumspb.ENCODING_TYPE_PROTO3)
s.NoError(err)
req := &historyservice.ImportWorkflowExecutionRequest{
NamespaceId: s.namespaceID,
Execution: &commonpb.WorkflowExecution{
WorkflowId: workflowID,
RunId: runID,
},
VersionHistory: versionHistory,
HistoryBatches: []*commonpb.DataBlob{blob},
Token: token,
}
resp, err := historyClient.ImportWorkflowExecution(context.Background(), req)
s.NoError(err, "Failed to import history event")
token = resp.Token
}
if verifyWorkflowNotExists {
_, err := historyClient.GetMutableState(context.Background(), &historyservice.GetMutableStateRequest{
NamespaceId: s.namespaceID,
Execution: &commonpb.WorkflowExecution{
WorkflowId: workflowID,
RunId: runID,
},
})
s.IsType(&serviceerror.NotFound{}, err)
}
req := &historyservice.ImportWorkflowExecutionRequest{
NamespaceId: s.namespaceID,
Execution: &commonpb.WorkflowExecution{
WorkflowId: workflowID,
RunId: runID,
},
VersionHistory: versionHistory,
HistoryBatches: []*commonpb.DataBlob{},
Token: token,
}
resp, err := historyClient.ImportWorkflowExecution(context.Background(), req)
s.NoError(err, "Failed to import history event")
s.Nil(resp.Token)
}
func (s *streamBasedReplicationTestSuite) TestForceReplicateResetWorkflow_BaseWorkflowNotFound() {
ns := "test-force-replicate-reset-" + common.GenerateRandomString(5)
client1 := s.cluster1.FrontendClient() // active
regReq := &workflowservice.RegisterNamespaceRequest{
Namespace: ns,
IsGlobalNamespace: true,
Clusters: s.clusterReplicationConfig(),
ActiveClusterName: s.clusterNames[0],
WorkflowExecutionRetentionPeriod: durationpb.New(1 * time.Hour * 24),
}
_, err := client1.RegisterNamespace(testcore.NewContext(), regReq)
s.NoError(err)
// Wait for namespace cache to pick the change
time.Sleep(cacheRefreshInterval)
descReq := &workflowservice.DescribeNamespaceRequest{
Namespace: ns,
}
resp, err := client1.DescribeNamespace(testcore.NewContext(), descReq)
s.NoError(err)
s.NotNil(resp)
// Start a workflow
id := "force-replicate-reset-test"
wt := "force-replicate-reset-test-type"
tl := "force-replicate-reset-test-tq"
identity := "worker1"
workflowType := &commonpb.WorkflowType{Name: wt}
taskQueue := &taskqueuepb.TaskQueue{Name: tl, Kind: enumspb.TASK_QUEUE_KIND_NORMAL}
startReq := &workflowservice.StartWorkflowExecutionRequest{
RequestId: uuid.New(),
Namespace: ns,
WorkflowId: id,
WorkflowType: workflowType,
TaskQueue: taskQueue,
Input: nil,
WorkflowRunTimeout: durationpb.New(300 * time.Second),
WorkflowTaskTimeout: durationpb.New(1 * time.Second),
Identity: identity,
}
we, err := client1.StartWorkflowExecution(testcore.NewContext(), startReq)
s.NoError(err)
s.NotNil(we.GetRunId())
wtHandler := func(task *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) {
return []*commandpb.Command{{
CommandType: enumspb.COMMAND_TYPE_COMPLETE_WORKFLOW_EXECUTION,
Attributes: &commandpb.Command_CompleteWorkflowExecutionCommandAttributes{CompleteWorkflowExecutionCommandAttributes: &commandpb.CompleteWorkflowExecutionCommandAttributes{
Result: payloads.EncodeString("Done"),
}},
}}, nil
}
poller := &testcore.TaskPoller{
Client: client1,
Namespace: ns,
TaskQueue: taskQueue,
Identity: identity,
WorkflowTaskHandler: wtHandler,
Logger: s.logger,
T: s.T(),
}
// Process start event in cluster 1
_, err = poller.PollAndProcessWorkflowTask()
s.NoError(err)
resetResp, err := client1.ResetWorkflowExecution(testcore.NewContext(), &workflowservice.ResetWorkflowExecutionRequest{
Namespace: ns,
WorkflowExecution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: we.GetRunId(),
},
Reason: "test",
WorkflowTaskFinishEventId: 3,
RequestId: uuid.New(),
})
s.NoError(err)
_, err = poller.PollAndProcessWorkflowTask()
s.NoError(err)
_, err = client1.DeleteWorkflowExecution(testcore.NewContext(), &workflowservice.DeleteWorkflowExecutionRequest{
Namespace: ns,
WorkflowExecution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: we.GetRunId(),
},
})
s.NoError(err)
client2 := s.cluster2.FrontendClient()
_, err = client2.DeleteWorkflowExecution(testcore.NewContext(), &workflowservice.DeleteWorkflowExecutionRequest{
Namespace: ns,
WorkflowExecution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: we.GetRunId(),
},
})
s.NoError(err)
_, err = client2.DeleteWorkflowExecution(testcore.NewContext(), &workflowservice.DeleteWorkflowExecutionRequest{
Namespace: ns,
WorkflowExecution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: resetResp.GetRunId(),
},
})
s.NoError(err)
time.Sleep(time.Second)
_, err = client2.DescribeWorkflowExecution(testcore.NewContext(), &workflowservice.DescribeWorkflowExecutionRequest{
Namespace: ns,
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: resetResp.GetRunId(),
},
})
s.Error(err)
_, err = s.cluster1.HistoryClient().GenerateLastHistoryReplicationTasks(testcore.NewContext(), &historyservice.GenerateLastHistoryReplicationTasksRequest{
NamespaceId: resp.NamespaceInfo.GetId(),
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: resetResp.GetRunId(),
},
})
s.NoError(err)
for i := 0; i < 5; i++ {
wfExec, err := client2.DescribeWorkflowExecution(testcore.NewContext(), &workflowservice.DescribeWorkflowExecutionRequest{
Namespace: ns,
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: resetResp.GetRunId(),
},
})
if err != nil {
time.Sleep(100 * time.Millisecond)
continue
}
s.Equal(enumspb.WORKFLOW_EXECUTION_STATUS_COMPLETED, wfExec.WorkflowExecutionInfo.Status)
return
}
s.Fail("Cannot replicate reset workflow to target cluster.")
}
func (s *streamBasedReplicationTestSuite) TestResetWorkflow_SyncWorkflowState() {
ns := "test--reset-sync-workflow-sate" + common.GenerateRandomString(5)
client1 := s.cluster1.FrontendClient() // active
regReq := &workflowservice.RegisterNamespaceRequest{
Namespace: ns,
IsGlobalNamespace: true,
Clusters: s.clusterReplicationConfig(),
ActiveClusterName: s.clusterNames[0],
WorkflowExecutionRetentionPeriod: durationpb.New(1 * time.Hour * 24),
}
_, err := client1.RegisterNamespace(testcore.NewContext(), regReq)
s.NoError(err)
// Wait for namespace cache to pick the change
var resp *workflowservice.DescribeNamespaceResponse
s.Eventually(func() bool {
descReq := &workflowservice.DescribeNamespaceRequest{
Namespace: ns,
}
var err error
resp, err = client1.DescribeNamespace(testcore.NewContext(), descReq)
return err == nil
}, cacheRefreshInterval, time.Second)
// Start a workflow
id := "reset-test"
wt := "reset-test-type"
tl := "reset-test-tq"
identity := "worker1"
workflowType := &commonpb.WorkflowType{Name: wt}
taskQueue := &taskqueuepb.TaskQueue{Name: tl, Kind: enumspb.TASK_QUEUE_KIND_NORMAL}
startReq := &workflowservice.StartWorkflowExecutionRequest{
RequestId: uuid.New(),
Namespace: ns,
WorkflowId: id,
WorkflowType: workflowType,
TaskQueue: taskQueue,
Input: nil,
WorkflowRunTimeout: durationpb.New(300 * time.Second),
WorkflowTaskTimeout: durationpb.New(1 * time.Second),
Identity: identity,
}
we, err := client1.StartWorkflowExecution(testcore.NewContext(), startReq)
s.NoError(err)
s.NotNil(we.GetRunId())
wtHandler := func(task *workflowservice.PollWorkflowTaskQueueResponse) ([]*commandpb.Command, error) {
return []*commandpb.Command{{
CommandType: enumspb.COMMAND_TYPE_COMPLETE_WORKFLOW_EXECUTION,
Attributes: &commandpb.Command_CompleteWorkflowExecutionCommandAttributes{CompleteWorkflowExecutionCommandAttributes: &commandpb.CompleteWorkflowExecutionCommandAttributes{
Result: payloads.EncodeString("Done"),
}},
}}, nil
}
poller := &testcore.TaskPoller{
Client: client1,
Namespace: ns,
TaskQueue: taskQueue,
Identity: identity,
WorkflowTaskHandler: wtHandler,
Logger: s.logger,
T: s.T(),
}
// Process start event in cluster 1
_, err = poller.PollAndProcessWorkflowTask()
s.NoError(err)
resetResp1, err := client1.ResetWorkflowExecution(testcore.NewContext(), &workflowservice.ResetWorkflowExecutionRequest{
Namespace: ns,
WorkflowExecution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: we.GetRunId(),
},
Reason: "test",
WorkflowTaskFinishEventId: 4,
RequestId: uuid.New(),
})
s.NoError(err)
_, err = poller.PollAndProcessWorkflowTask()
s.NoError(err)
resetResp2, err := client1.ResetWorkflowExecution(testcore.NewContext(), &workflowservice.ResetWorkflowExecutionRequest{
Namespace: ns,
WorkflowExecution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: resetResp1.GetRunId(),
},
Reason: "test",
WorkflowTaskFinishEventId: 7,
RequestId: uuid.New(),
})
s.NoError(err)
_, err = poller.PollAndProcessWorkflowTask()
s.NoError(err)
s.Eventually(func() bool {
_, err = s.cluster2.AdminClient().DescribeMutableState(
testcore.NewContext(),
&adminservice.DescribeMutableStateRequest{
Namespace: ns,
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: we.GetRunId(),
},
})
return err == nil
},
time.Second*10,
time.Second)
s.Eventually(func() bool {
_, err = s.cluster2.AdminClient().DescribeMutableState(
testcore.NewContext(),
&adminservice.DescribeMutableStateRequest{
Namespace: ns,
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: resetResp1.GetRunId(),
},
})
return err == nil
},
time.Second*10,
time.Second)
s.Eventually(func() bool {
_, err = s.cluster2.AdminClient().DescribeMutableState(
testcore.NewContext(),
&adminservice.DescribeMutableStateRequest{
Namespace: ns,
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: resetResp2.GetRunId(),
},
})
return err == nil
},
time.Second*10,
time.Second)
// Delete reset workflows
_, err = s.cluster2.AdminClient().DeleteWorkflowExecution(testcore.NewContext(), &adminservice.DeleteWorkflowExecutionRequest{
Namespace: ns,
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: we.GetRunId(),
},
})
s.NoError(err)
_, err = s.cluster2.AdminClient().DeleteWorkflowExecution(testcore.NewContext(), &adminservice.DeleteWorkflowExecutionRequest{
Namespace: ns,
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: resetResp1.GetRunId(),
},
})
s.NoError(err)
_, err = s.cluster2.AdminClient().DeleteWorkflowExecution(testcore.NewContext(), &adminservice.DeleteWorkflowExecutionRequest{
Namespace: ns,
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: resetResp2.GetRunId(),
},
})
s.NoError(err)
s.Eventually(func() bool {
_, err = s.cluster2.AdminClient().DescribeMutableState(
testcore.NewContext(),
&adminservice.DescribeMutableStateRequest{
Namespace: ns,
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: we.GetRunId(),
},
})
var expectedErr *serviceerror.NotFound
return errors.As(err, &expectedErr)
},
time.Second*10,
time.Second)
s.Eventually(func() bool {
_, err = s.cluster2.AdminClient().DescribeMutableState(
testcore.NewContext(),
&adminservice.DescribeMutableStateRequest{
Namespace: ns,
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: resetResp1.GetRunId(),
},
})
var expectedErr *serviceerror.NotFound
return errors.As(err, &expectedErr)
},
time.Second*10,
time.Second)
s.Eventually(func() bool {
_, err = s.cluster2.AdminClient().DescribeMutableState(
testcore.NewContext(),
&adminservice.DescribeMutableStateRequest{
Namespace: ns,
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: resetResp2.GetRunId(),
},
})
var expectedErr *serviceerror.NotFound
return errors.As(err, &expectedErr)
},
time.Second*10,
time.Second)
_, err = s.cluster1.HistoryClient().GenerateLastHistoryReplicationTasks(testcore.NewContext(), &historyservice.GenerateLastHistoryReplicationTasksRequest{
NamespaceId: resp.NamespaceInfo.GetId(),
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: we.GetRunId(),
},
})
s.NoError(err)
s.Eventually(func() bool {
_, err = s.cluster2.AdminClient().DescribeMutableState(
testcore.NewContext(),
&adminservice.DescribeMutableStateRequest{
Namespace: ns,
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: we.GetRunId(),
},
})
return err == nil
},
time.Second*10,
time.Second)
_, err = s.cluster1.HistoryClient().GenerateLastHistoryReplicationTasks(testcore.NewContext(), &historyservice.GenerateLastHistoryReplicationTasksRequest{
NamespaceId: resp.NamespaceInfo.GetId(),
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: resetResp1.GetRunId(),
},
})
s.NoError(err)
s.Eventually(func() bool {
_, err = s.cluster2.AdminClient().DescribeMutableState(
testcore.NewContext(),
&adminservice.DescribeMutableStateRequest{
Namespace: ns,
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: resetResp1.GetRunId(),
},
})
return err == nil
},
time.Second*10,
time.Second)
_, err = s.cluster1.HistoryClient().GenerateLastHistoryReplicationTasks(testcore.NewContext(), &historyservice.GenerateLastHistoryReplicationTasksRequest{
NamespaceId: resp.NamespaceInfo.GetId(),
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: resetResp2.GetRunId(),
},
})
s.NoError(err)
s.Eventually(func() bool {
_, err = s.cluster2.AdminClient().DescribeMutableState(
testcore.NewContext(),
&adminservice.DescribeMutableStateRequest{
Namespace: ns,
Execution: &commonpb.WorkflowExecution{
WorkflowId: id,
RunId: resetResp2.GetRunId(),
},
})
return err == nil
},
time.Second*10,
time.Second)
}