Files
tasks/service/history/history_engine.go
Stephan BehnkeandGitHub cf0577124a MultiOperation support for terminate/fail conflict policy (#5800)
## What changed?
<!-- Describe what has changed in this PR -->

Added support for workflow id conflict policies "terminate-existing" and
"fail".

## Why?
<!-- Tell your future self why have you made these changes -->

Complement the support for policy "use-existing".

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

Added new tests.

## 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-05-02 19:31:33 -07:00

980 lines
36 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 history
import (
"context"
"sync/atomic"
"time"
"go.opentelemetry.io/otel/trace"
commonpb "go.temporal.io/api/common/v1"
historypb "go.temporal.io/api/history/v1"
historyspb "go.temporal.io/server/api/history/v1"
"go.temporal.io/server/api/historyservice/v1"
"go.temporal.io/server/api/matchingservice/v1"
replicationspb "go.temporal.io/server/api/replication/v1"
workflowpb "go.temporal.io/server/api/workflow/v1"
"go.temporal.io/server/client"
"go.temporal.io/server/common"
"go.temporal.io/server/common/clock"
"go.temporal.io/server/common/cluster"
"go.temporal.io/server/common/collection"
"go.temporal.io/server/common/definition"
"go.temporal.io/server/common/headers"
"go.temporal.io/server/common/log"
"go.temporal.io/server/common/log/tag"
"go.temporal.io/server/common/metrics"
"go.temporal.io/server/common/namespace"
"go.temporal.io/server/common/persistence"
"go.temporal.io/server/common/persistence/serialization"
"go.temporal.io/server/common/persistence/visibility"
"go.temporal.io/server/common/persistence/visibility/manager"
"go.temporal.io/server/common/primitives/timestamp"
"go.temporal.io/server/common/sdk"
"go.temporal.io/server/common/searchattribute"
"go.temporal.io/server/service/history/api"
"go.temporal.io/server/service/history/api/addtasks"
"go.temporal.io/server/service/history/api/deleteworkflow"
"go.temporal.io/server/service/history/api/describemutablestate"
"go.temporal.io/server/service/history/api/describeworkflow"
"go.temporal.io/server/service/history/api/getworkflowexecutionhistory"
"go.temporal.io/server/service/history/api/getworkflowexecutionhistoryreverse"
"go.temporal.io/server/service/history/api/getworkflowexecutionrawhistory"
"go.temporal.io/server/service/history/api/getworkflowexecutionrawhistoryv2"
"go.temporal.io/server/service/history/api/isactivitytaskvalid"
"go.temporal.io/server/service/history/api/isworkflowtaskvalid"
"go.temporal.io/server/service/history/api/listtasks"
"go.temporal.io/server/service/history/api/multioperation"
"go.temporal.io/server/service/history/api/pollupdate"
"go.temporal.io/server/service/history/api/queryworkflow"
"go.temporal.io/server/service/history/api/reapplyevents"
"go.temporal.io/server/service/history/api/recordactivitytaskheartbeat"
"go.temporal.io/server/service/history/api/recordactivitytaskstarted"
"go.temporal.io/server/service/history/api/recordchildworkflowcompleted"
"go.temporal.io/server/service/history/api/refreshworkflow"
"go.temporal.io/server/service/history/api/removesignalmutablestate"
replicationapi "go.temporal.io/server/service/history/api/replication"
"go.temporal.io/server/service/history/api/replicationadmin"
"go.temporal.io/server/service/history/api/requestcancelworkflow"
"go.temporal.io/server/service/history/api/resetstickytaskqueue"
"go.temporal.io/server/service/history/api/resetworkflow"
"go.temporal.io/server/service/history/api/respondactivitytaskcanceled"
"go.temporal.io/server/service/history/api/respondactivitytaskcompleted"
"go.temporal.io/server/service/history/api/respondactivitytaskfailed"
"go.temporal.io/server/service/history/api/signalwithstartworkflow"
"go.temporal.io/server/service/history/api/signalworkflow"
"go.temporal.io/server/service/history/api/startworkflow"
"go.temporal.io/server/service/history/api/terminateworkflow"
"go.temporal.io/server/service/history/api/updateworkflow"
"go.temporal.io/server/service/history/api/verifychildworkflowcompletionrecorded"
"go.temporal.io/server/service/history/configs"
"go.temporal.io/server/service/history/consts"
"go.temporal.io/server/service/history/deletemanager"
"go.temporal.io/server/service/history/events"
"go.temporal.io/server/service/history/hsm"
"go.temporal.io/server/service/history/ndc"
"go.temporal.io/server/service/history/queues"
"go.temporal.io/server/service/history/replication"
"go.temporal.io/server/service/history/shard"
"go.temporal.io/server/service/history/tasks"
"go.temporal.io/server/service/history/workflow"
wcache "go.temporal.io/server/service/history/workflow/cache"
)
const (
activityCancellationMsgActivityNotStarted = "ACTIVITY_ID_NOT_STARTED"
)
type (
historyEngineImpl struct {
status int32
currentClusterName string
shardContext shard.Context
timeSource clock.TimeSource
workflowTaskHandler workflowTaskHandlerCallbacks
clusterMetadata cluster.Metadata
executionManager persistence.ExecutionManager
queueProcessors map[tasks.Category]queues.Queue
replicationAckMgr replication.AckManager
nDCHistoryReplicator ndc.HistoryReplicator
nDCHistoryImporter ndc.HistoryImporter
nDCActivityStateReplicator ndc.ActivityStateReplicator
nDCWorkflowStateReplicator ndc.WorkflowStateReplicator
replicationProcessorMgr replication.TaskProcessor
eventNotifier events.Notifier
tokenSerializer common.TaskTokenSerializer
metricsHandler metrics.Handler
logger log.Logger
throttledLogger log.Logger
config *configs.Config
workflowRebuilder workflowRebuilder
workflowResetter ndc.WorkflowResetter
sdkClientFactory sdk.ClientFactory
eventsReapplier ndc.EventsReapplier
matchingClient matchingservice.MatchingServiceClient
rawMatchingClient matchingservice.MatchingServiceClient
replicationDLQHandler replication.DLQHandler
persistenceVisibilityMgr manager.VisibilityManager
searchAttributesValidator *searchattribute.Validator
workflowDeleteManager deletemanager.DeleteManager
eventSerializer serialization.Serializer
workflowConsistencyChecker api.WorkflowConsistencyChecker
versionChecker headers.VersionChecker
tracer trace.Tracer
taskCategoryRegistry tasks.TaskCategoryRegistry
commandHandlerRegistry *workflow.CommandHandlerRegistry
stateMachineEnvironment *stateMachineEnvironment
}
)
// NewEngineWithShardContext creates an instance of history engine
func NewEngineWithShardContext(
shard shard.Context,
clientBean client.Bean,
matchingClient matchingservice.MatchingServiceClient,
sdkClientFactory sdk.ClientFactory,
eventNotifier events.Notifier,
config *configs.Config,
rawMatchingClient matchingservice.MatchingServiceClient,
workflowCache wcache.Cache,
eventSerializer serialization.Serializer,
queueProcessorFactories []QueueFactory,
replicationTaskFetcherFactory replication.TaskFetcherFactory,
replicationTaskExecutorProvider replication.TaskExecutorProvider,
workflowConsistencyChecker api.WorkflowConsistencyChecker,
tracerProvider trace.TracerProvider,
persistenceVisibilityMgr manager.VisibilityManager,
eventBlobCache persistence.XDCCache,
taskCategoryRegistry tasks.TaskCategoryRegistry,
dlqWriter replication.DLQWriter,
commandHandlerRegistry *workflow.CommandHandlerRegistry,
) shard.Engine {
currentClusterName := shard.GetClusterMetadata().GetCurrentClusterName()
logger := shard.GetLogger()
executionManager := shard.GetExecutionManager()
workflowDeleteManager := deletemanager.NewDeleteManager(
shard,
workflowCache,
config,
shard.GetTimeSource(),
persistenceVisibilityMgr,
)
historyEngImpl := &historyEngineImpl{
status: common.DaemonStatusInitialized,
currentClusterName: currentClusterName,
shardContext: shard,
clusterMetadata: shard.GetClusterMetadata(),
timeSource: shard.GetTimeSource(),
executionManager: executionManager,
tokenSerializer: common.NewProtoTaskTokenSerializer(),
logger: log.With(logger, tag.ComponentHistoryEngine),
throttledLogger: log.With(shard.GetThrottledLogger(), tag.ComponentHistoryEngine),
metricsHandler: shard.GetMetricsHandler(),
eventNotifier: eventNotifier,
config: config,
sdkClientFactory: sdkClientFactory,
matchingClient: matchingClient,
rawMatchingClient: rawMatchingClient,
persistenceVisibilityMgr: persistenceVisibilityMgr,
workflowDeleteManager: workflowDeleteManager,
eventSerializer: eventSerializer,
workflowConsistencyChecker: workflowConsistencyChecker,
versionChecker: headers.NewDefaultVersionChecker(),
tracer: tracerProvider.Tracer(consts.LibraryName),
taskCategoryRegistry: taskCategoryRegistry,
commandHandlerRegistry: commandHandlerRegistry,
stateMachineEnvironment: &stateMachineEnvironment{
shardContext: shard,
cache: workflowCache,
metricsHandler: shard.GetMetricsHandler(),
logger: logger,
},
}
historyEngImpl.queueProcessors = make(map[tasks.Category]queues.Queue)
for _, factory := range queueProcessorFactories {
processor := factory.CreateQueue(shard, workflowCache)
historyEngImpl.queueProcessors[processor.Category()] = processor
}
historyEngImpl.eventsReapplier = ndc.NewEventsReapplier(shard.GetMetricsHandler(), logger)
if shard.GetClusterMetadata().IsGlobalNamespaceEnabled() {
historyEngImpl.replicationAckMgr = replication.NewAckManager(
shard,
workflowCache,
eventBlobCache,
executionManager,
logger,
)
historyEngImpl.nDCHistoryReplicator = ndc.NewHistoryReplicator(
shard,
workflowCache,
historyEngImpl.eventsReapplier,
eventSerializer,
logger,
)
historyEngImpl.nDCHistoryImporter = ndc.NewHistoryImporter(
shard,
workflowCache,
logger,
)
historyEngImpl.nDCActivityStateReplicator = ndc.NewActivityStateReplicator(
shard,
workflowCache,
logger,
)
historyEngImpl.nDCWorkflowStateReplicator = ndc.NewWorkflowStateReplicator(
shard,
workflowCache,
historyEngImpl.eventsReapplier,
eventSerializer,
logger,
)
}
historyEngImpl.workflowRebuilder = NewWorkflowRebuilder(
shard,
workflowCache,
logger,
)
historyEngImpl.workflowResetter = ndc.NewWorkflowResetter(
shard,
workflowCache,
logger,
)
historyEngImpl.searchAttributesValidator = searchattribute.NewValidator(
shard.GetSearchAttributesProvider(),
shard.GetSearchAttributesMapperProvider(),
config.SearchAttributesNumberOfKeysLimit,
config.SearchAttributesSizeOfValueLimit,
config.SearchAttributesTotalSizeLimit,
persistenceVisibilityMgr,
visibility.AllowListForValidation(
persistenceVisibilityMgr.GetStoreNames(),
config.VisibilityAllowList,
),
config.SuppressErrorSetSystemSearchAttribute,
)
historyEngImpl.workflowTaskHandler = newWorkflowTaskHandlerCallback(historyEngImpl)
historyEngImpl.replicationDLQHandler = replication.NewLazyDLQHandler(
shard,
workflowDeleteManager,
workflowCache,
clientBean,
replicationTaskExecutorProvider,
)
historyEngImpl.replicationProcessorMgr = replication.NewTaskProcessorManager(
config,
shard,
historyEngImpl,
workflowCache,
workflowDeleteManager,
clientBean,
eventSerializer,
replicationTaskFetcherFactory,
replicationTaskExecutorProvider,
dlqWriter,
)
return historyEngImpl
}
// Start will spin up all the components needed to start serving this shard.
// Make sure all the components are loaded lazily so start can return immediately. This is important because
// ShardController calls start sequentially for all the shards for a given host during startup.
func (e *historyEngineImpl) Start() {
if !atomic.CompareAndSwapInt32(
&e.status,
common.DaemonStatusInitialized,
common.DaemonStatusStarted,
) {
return
}
e.logger.Info("", tag.LifeCycleStarting)
defer e.logger.Info("", tag.LifeCycleStarted)
e.registerNamespaceStateChangeCallback()
for _, queueProcessor := range e.queueProcessors {
queueProcessor.Start()
}
e.replicationProcessorMgr.Start()
}
// Stop the service.
func (e *historyEngineImpl) Stop() {
if !atomic.CompareAndSwapInt32(
&e.status,
common.DaemonStatusStarted,
common.DaemonStatusStopped,
) {
return
}
e.logger.Info("", tag.LifeCycleStopping)
defer e.logger.Info("", tag.LifeCycleStopped)
for _, queueProcessor := range e.queueProcessors {
queueProcessor.Stop()
}
e.replicationProcessorMgr.Stop()
// unset the failover callback
e.shardContext.GetNamespaceRegistry().UnregisterStateChangeCallback(e)
}
func (e *historyEngineImpl) registerNamespaceStateChangeCallback() {
e.shardContext.GetNamespaceRegistry().RegisterStateChangeCallback(e, func(ns *namespace.Namespace, deletedFromDb bool) {
if e.shardContext.GetClusterMetadata().IsGlobalNamespaceEnabled() {
e.shardContext.UpdateHandoverNamespace(ns, deletedFromDb)
}
if deletedFromDb {
return
}
if ns.IsGlobalNamespace() &&
ns.ReplicationPolicy() == namespace.ReplicationPolicyMultiCluster &&
ns.ActiveClusterName() == e.currentClusterName {
for _, queueProcessor := range e.queueProcessors {
queueProcessor.FailoverNamespace(ns.ID().String())
}
}
})
}
// StartWorkflowExecution starts a workflow execution
// Consistency guarantee: always write
func (e *historyEngineImpl) StartWorkflowExecution(
ctx context.Context,
startRequest *historyservice.StartWorkflowExecutionRequest,
) (resp *historyservice.StartWorkflowExecutionResponse, retError error) {
starter, err := startworkflow.NewStarter(
e.shardContext,
e.workflowConsistencyChecker,
e.tokenSerializer,
e.persistenceVisibilityMgr,
startRequest,
)
if err != nil {
return nil, err
}
return starter.Invoke(ctx, startworkflow.BeforeCreateHookNoop)
}
func (e *historyEngineImpl) ExecuteMultiOperation(
ctx context.Context,
request *historyservice.ExecuteMultiOperationRequest,
) (*historyservice.ExecuteMultiOperationResponse, error) {
return multioperation.Invoke(
ctx,
request,
e.shardContext,
e.workflowConsistencyChecker,
e.tokenSerializer,
e.persistenceVisibilityMgr,
e.matchingClient,
)
}
// GetMutableState retrieves the mutable state of the workflow execution
func (e *historyEngineImpl) GetMutableState(
ctx context.Context,
request *historyservice.GetMutableStateRequest,
) (*historyservice.GetMutableStateResponse, error) {
return api.GetOrPollMutableState(ctx, e.shardContext, request, e.workflowConsistencyChecker, e.eventNotifier)
}
// PollMutableState retrieves the mutable state of the workflow execution with long polling
func (e *historyEngineImpl) PollMutableState(
ctx context.Context,
request *historyservice.PollMutableStateRequest,
) (*historyservice.PollMutableStateResponse, error) {
response, err := api.GetOrPollMutableState(
ctx,
e.shardContext,
&historyservice.GetMutableStateRequest{
NamespaceId: request.GetNamespaceId(),
Execution: request.Execution,
ExpectedNextEventId: request.ExpectedNextEventId,
CurrentBranchToken: request.CurrentBranchToken,
VersionHistoryItem: request.GetVersionHistoryItem(),
},
e.workflowConsistencyChecker,
e.eventNotifier,
)
if err != nil {
return nil, err
}
return &historyservice.PollMutableStateResponse{
Execution: response.Execution,
WorkflowType: response.WorkflowType,
NextEventId: response.NextEventId,
PreviousStartedEventId: response.PreviousStartedEventId,
LastFirstEventId: response.LastFirstEventId,
LastFirstEventTxnId: response.LastFirstEventTxnId,
TaskQueue: response.TaskQueue,
StickyTaskQueue: response.StickyTaskQueue,
StickyTaskQueueScheduleToStartTimeout: response.StickyTaskQueueScheduleToStartTimeout,
CurrentBranchToken: response.CurrentBranchToken,
VersionHistories: response.VersionHistories,
WorkflowState: response.WorkflowState,
WorkflowStatus: response.WorkflowStatus,
FirstExecutionRunId: response.FirstExecutionRunId,
}, nil
}
func (e *historyEngineImpl) QueryWorkflow(
ctx context.Context,
request *historyservice.QueryWorkflowRequest,
) (_ *historyservice.QueryWorkflowResponse, retErr error) {
return queryworkflow.Invoke(ctx, request, e.shardContext, e.workflowConsistencyChecker, e.rawMatchingClient, e.matchingClient)
}
func (e *historyEngineImpl) DescribeMutableState(
ctx context.Context,
request *historyservice.DescribeMutableStateRequest,
) (response *historyservice.DescribeMutableStateResponse, retError error) {
return describemutablestate.Invoke(ctx, request, e.shardContext, e.workflowConsistencyChecker)
}
// ResetStickyTaskQueue reset the volatile information in mutable state of a given workflow.
// Volatile information are the information related to client, such as:
// 1. StickyTaskQueue
// 2. StickyScheduleToStartTimeout
func (e *historyEngineImpl) ResetStickyTaskQueue(
ctx context.Context,
resetRequest *historyservice.ResetStickyTaskQueueRequest,
) (*historyservice.ResetStickyTaskQueueResponse, error) {
return resetstickytaskqueue.Invoke(ctx, resetRequest, e.shardContext, e.workflowConsistencyChecker)
}
// DescribeWorkflowExecution returns information about the specified workflow execution.
func (e *historyEngineImpl) DescribeWorkflowExecution(
ctx context.Context,
request *historyservice.DescribeWorkflowExecutionRequest,
) (_ *historyservice.DescribeWorkflowExecutionResponse, retError error) {
return describeworkflow.Invoke(
ctx,
request,
e.shardContext,
e.workflowConsistencyChecker,
e.persistenceVisibilityMgr,
)
}
func (e *historyEngineImpl) RecordActivityTaskStarted(
ctx context.Context,
request *historyservice.RecordActivityTaskStartedRequest,
) (*historyservice.RecordActivityTaskStartedResponse, error) {
return recordactivitytaskstarted.Invoke(ctx, request, e.shardContext, e.workflowConsistencyChecker)
}
// ScheduleWorkflowTask schedules a workflow task if no outstanding workflow task found
func (e *historyEngineImpl) ScheduleWorkflowTask(
ctx context.Context,
req *historyservice.ScheduleWorkflowTaskRequest,
) error {
return e.workflowTaskHandler.handleWorkflowTaskScheduled(ctx, req)
}
func (e *historyEngineImpl) VerifyFirstWorkflowTaskScheduled(
ctx context.Context,
request *historyservice.VerifyFirstWorkflowTaskScheduledRequest,
) (retError error) {
return e.workflowTaskHandler.verifyFirstWorkflowTaskScheduled(ctx, request)
}
// RecordWorkflowTaskStarted starts a workflow task
func (e *historyEngineImpl) RecordWorkflowTaskStarted(
ctx context.Context,
request *historyservice.RecordWorkflowTaskStartedRequest,
) (*historyservice.RecordWorkflowTaskStartedResponse, error) {
return e.workflowTaskHandler.handleWorkflowTaskStarted(ctx, request)
}
// RespondWorkflowTaskCompleted completes a workflow task
func (e *historyEngineImpl) RespondWorkflowTaskCompleted(
ctx context.Context,
req *historyservice.RespondWorkflowTaskCompletedRequest,
) (*historyservice.RespondWorkflowTaskCompletedResponse, error) {
return e.workflowTaskHandler.handleWorkflowTaskCompleted(ctx, req)
}
// RespondWorkflowTaskFailed fails a workflow task
func (e *historyEngineImpl) RespondWorkflowTaskFailed(
ctx context.Context,
req *historyservice.RespondWorkflowTaskFailedRequest,
) error {
return e.workflowTaskHandler.handleWorkflowTaskFailed(ctx, req)
}
// RespondActivityTaskCompleted completes an activity task.
func (e *historyEngineImpl) RespondActivityTaskCompleted(
ctx context.Context,
req *historyservice.RespondActivityTaskCompletedRequest,
) (*historyservice.RespondActivityTaskCompletedResponse, error) {
return respondactivitytaskcompleted.Invoke(ctx, req, e.shardContext, e.workflowConsistencyChecker)
}
// RespondActivityTaskFailed completes an activity task failure.
func (e *historyEngineImpl) RespondActivityTaskFailed(
ctx context.Context,
req *historyservice.RespondActivityTaskFailedRequest,
) (*historyservice.RespondActivityTaskFailedResponse, error) {
return respondactivitytaskfailed.Invoke(ctx, req, e.shardContext, e.workflowConsistencyChecker)
}
// RespondActivityTaskCanceled completes an activity task failure.
func (e *historyEngineImpl) RespondActivityTaskCanceled(
ctx context.Context,
req *historyservice.RespondActivityTaskCanceledRequest,
) (*historyservice.RespondActivityTaskCanceledResponse, error) {
return respondactivitytaskcanceled.Invoke(ctx, req, e.shardContext, e.workflowConsistencyChecker)
}
// RecordActivityTaskHeartbeat records an hearbeat for a task.
// This method can be used for two purposes.
// - For reporting liveness of the activity.
// - For reporting progress of the activity, this can be done even if the liveness is not configured.
func (e *historyEngineImpl) RecordActivityTaskHeartbeat(
ctx context.Context,
req *historyservice.RecordActivityTaskHeartbeatRequest,
) (*historyservice.RecordActivityTaskHeartbeatResponse, error) {
return recordactivitytaskheartbeat.Invoke(ctx, req, e.shardContext, e.workflowConsistencyChecker)
}
// RequestCancelWorkflowExecution records request cancellation event for workflow execution
func (e *historyEngineImpl) RequestCancelWorkflowExecution(
ctx context.Context,
req *historyservice.RequestCancelWorkflowExecutionRequest,
) (resp *historyservice.RequestCancelWorkflowExecutionResponse, retError error) {
return requestcancelworkflow.Invoke(ctx, req, e.shardContext, e.workflowConsistencyChecker)
}
func (e *historyEngineImpl) SignalWorkflowExecution(
ctx context.Context,
req *historyservice.SignalWorkflowExecutionRequest,
) (resp *historyservice.SignalWorkflowExecutionResponse, retError error) {
return signalworkflow.Invoke(ctx, req, e.shardContext, e.workflowConsistencyChecker)
}
// SignalWithStartWorkflowExecution signals current workflow (if running) or creates & signals a new workflow
// Consistency guarantee: always write
func (e *historyEngineImpl) SignalWithStartWorkflowExecution(
ctx context.Context,
req *historyservice.SignalWithStartWorkflowExecutionRequest,
) (_ *historyservice.SignalWithStartWorkflowExecutionResponse, retError error) {
return signalwithstartworkflow.Invoke(ctx, req, e.shardContext, e.workflowConsistencyChecker)
}
func (e *historyEngineImpl) UpdateWorkflowExecution(
ctx context.Context,
req *historyservice.UpdateWorkflowExecutionRequest,
) (*historyservice.UpdateWorkflowExecutionResponse, error) {
updater := updateworkflow.NewUpdater(
e.shardContext,
e.workflowConsistencyChecker,
e.matchingClient,
req,
)
return updater.Invoke(ctx)
}
func (e *historyEngineImpl) PollWorkflowExecutionUpdate(
ctx context.Context,
req *historyservice.PollWorkflowExecutionUpdateRequest,
) (*historyservice.PollWorkflowExecutionUpdateResponse, error) {
return pollupdate.Invoke(ctx, req, e.shardContext, e.workflowConsistencyChecker)
}
// RemoveSignalMutableState remove the signal request id in signal_requested for deduplicate
func (e *historyEngineImpl) RemoveSignalMutableState(
ctx context.Context,
req *historyservice.RemoveSignalMutableStateRequest,
) (*historyservice.RemoveSignalMutableStateResponse, error) {
return removesignalmutablestate.Invoke(ctx, req, e.shardContext, e.workflowConsistencyChecker)
}
func (e *historyEngineImpl) TerminateWorkflowExecution(
ctx context.Context,
req *historyservice.TerminateWorkflowExecutionRequest,
) (*historyservice.TerminateWorkflowExecutionResponse, error) {
return terminateworkflow.Invoke(ctx, req, e.shardContext, e.workflowConsistencyChecker)
}
func (e *historyEngineImpl) DeleteWorkflowExecution(
ctx context.Context,
request *historyservice.DeleteWorkflowExecutionRequest,
) (*historyservice.DeleteWorkflowExecutionResponse, error) {
return deleteworkflow.Invoke(ctx, request, e.shardContext, e.workflowConsistencyChecker, e.workflowDeleteManager)
}
// RecordChildExecutionCompleted records the completion of child execution into parent execution history
func (e *historyEngineImpl) RecordChildExecutionCompleted(
ctx context.Context,
req *historyservice.RecordChildExecutionCompletedRequest,
) (*historyservice.RecordChildExecutionCompletedResponse, error) {
return recordchildworkflowcompleted.Invoke(ctx, req, e.shardContext, e.workflowConsistencyChecker)
}
// IsActivityTaskValid - whether activity task is still valid
func (e *historyEngineImpl) IsActivityTaskValid(
ctx context.Context,
req *historyservice.IsActivityTaskValidRequest,
) (*historyservice.IsActivityTaskValidResponse, error) {
return isactivitytaskvalid.Invoke(ctx, req, e.shardContext, e.workflowConsistencyChecker)
}
// IsWorkflowTaskValid - whether workflow task is still valid
func (e *historyEngineImpl) IsWorkflowTaskValid(
ctx context.Context,
req *historyservice.IsWorkflowTaskValidRequest,
) (*historyservice.IsWorkflowTaskValidResponse, error) {
return isworkflowtaskvalid.Invoke(ctx, req, e.shardContext, e.workflowConsistencyChecker)
}
func (e *historyEngineImpl) VerifyChildExecutionCompletionRecorded(
ctx context.Context,
req *historyservice.VerifyChildExecutionCompletionRecordedRequest,
) (*historyservice.VerifyChildExecutionCompletionRecordedResponse, error) {
return verifychildworkflowcompletionrecorded.Invoke(ctx, req, e.shardContext, e.workflowConsistencyChecker)
}
func (e *historyEngineImpl) ReplicateEventsV2(
ctx context.Context,
replicateRequest *historyservice.ReplicateEventsV2Request,
) error {
return e.nDCHistoryReplicator.ApplyEvents(ctx, replicateRequest)
}
func (e *historyEngineImpl) ReplicateHistoryEvents(
ctx context.Context,
workflowKey definition.WorkflowKey,
baseExecutionInfo *workflowpb.BaseExecutionInfo,
versionHistoryItems []*historyspb.VersionHistoryItem,
historyEvents [][]*historypb.HistoryEvent,
newEvents []*historypb.HistoryEvent,
newRunID string,
) error {
return e.nDCHistoryReplicator.ReplicateHistoryEvents(
ctx,
workflowKey,
baseExecutionInfo,
versionHistoryItems,
historyEvents,
newEvents,
newRunID,
)
}
func (e *historyEngineImpl) SyncActivity(
ctx context.Context,
request *historyservice.SyncActivityRequest,
) (retError error) {
return e.nDCActivityStateReplicator.SyncActivityState(ctx, request)
}
func (e *historyEngineImpl) SyncActivities(
ctx context.Context,
request *historyservice.SyncActivitiesRequest,
) (retError error) {
return e.nDCActivityStateReplicator.SyncActivitiesState(ctx, request)
}
// ReplicateWorkflowState is an experimental method to replicate workflow state. This should not expose outside of history service role.
func (e *historyEngineImpl) ReplicateWorkflowState(
ctx context.Context,
request *historyservice.ReplicateWorkflowStateRequest,
) error {
return e.nDCWorkflowStateReplicator.SyncWorkflowState(ctx, request)
}
func (e *historyEngineImpl) ImportWorkflowExecution(
ctx context.Context,
request *historyservice.ImportWorkflowExecutionRequest,
) (*historyservice.ImportWorkflowExecutionResponse, error) {
historyEvents, err := ndc.DeserializeBlobs(e.eventSerializer, request.HistoryBatches)
if err != nil {
return nil, err
}
token, eventsApplied, err := e.nDCHistoryImporter.ImportWorkflow(
ctx,
definition.NewWorkflowKey(
request.NamespaceId,
request.Execution.GetWorkflowId(),
request.Execution.GetRunId(),
),
request.VersionHistory.Items,
historyEvents,
request.Token,
)
if err != nil {
return nil, err
}
return &historyservice.ImportWorkflowExecutionResponse{
Token: token,
EventsApplied: eventsApplied,
}, nil
}
func (e *historyEngineImpl) SyncShardStatus(
ctx context.Context,
request *historyservice.SyncShardStatusRequest,
) error {
clusterName := request.GetSourceCluster()
now := timestamp.TimeValue(request.GetStatusTime())
// here there are 3 main things
// 1. update the view of remote cluster's shard time
// 2. notify the timer gate in the timer queue standby processor
// 3, notify the transfer (essentially a no op, just put it here so it looks symmetric)
e.shardContext.SetCurrentTime(clusterName, now)
for _, processor := range e.queueProcessors {
processor.NotifyNewTasks([]tasks.Task{})
}
return nil
}
// ResetWorkflowExecution terminates current workflow (if running) and replay & create new workflow
// Consistency guarantee: always write
func (e *historyEngineImpl) ResetWorkflowExecution(
ctx context.Context,
req *historyservice.ResetWorkflowExecutionRequest,
) (*historyservice.ResetWorkflowExecutionResponse, error) {
return resetworkflow.Invoke(ctx, req, e.shardContext, e.workflowConsistencyChecker)
}
func (e *historyEngineImpl) NotifyNewHistoryEvent(
notification *events.Notification,
) {
e.eventNotifier.NotifyNewHistoryEvent(notification)
}
func (e *historyEngineImpl) NotifyNewTasks(
newTasks map[tasks.Category][]tasks.Task,
) {
for category, tasksByCategory := range newTasks {
// TODO: make replicatorProcessor part of queueProcessors list
// and get rid of the special case here.
if category == tasks.CategoryReplication {
if e.replicationAckMgr != nil {
e.replicationAckMgr.NotifyNewTasks(tasksByCategory)
}
continue
}
if len(tasksByCategory) > 0 {
e.queueProcessors[category].NotifyNewTasks(tasksByCategory)
}
}
}
func (e *historyEngineImpl) AddSpeculativeWorkflowTaskTimeoutTask(task *tasks.WorkflowTaskTimeoutTask) {
e.queueProcessors[tasks.CategoryMemoryTimer].NotifyNewTasks([]tasks.Task{task})
}
func (e *historyEngineImpl) GetReplicationMessages(
ctx context.Context,
pollingCluster string,
ackMessageID int64,
ackTimestamp time.Time,
queryMessageID int64,
) (*replicationspb.ReplicationMessages, error) {
return replicationapi.GetTasks(ctx, e.shardContext, e.replicationAckMgr, pollingCluster, ackMessageID, ackTimestamp, queryMessageID)
}
func (e *historyEngineImpl) SubscribeReplicationNotification() (<-chan struct{}, string) {
return e.replicationAckMgr.SubscribeNotification()
}
func (e *historyEngineImpl) UnsubscribeReplicationNotification(subscriberID string) {
e.replicationAckMgr.UnsubscribeNotification(subscriberID)
}
func (e *historyEngineImpl) ConvertReplicationTask(
ctx context.Context,
task tasks.Task,
) (*replicationspb.ReplicationTask, error) {
return e.replicationAckMgr.ConvertTask(ctx, task)
}
func (e *historyEngineImpl) GetReplicationTasksIter(
ctx context.Context,
pollingCluster string,
minInclusiveTaskID int64,
maxExclusiveTaskID int64,
) (collection.Iterator[tasks.Task], error) {
return e.replicationAckMgr.GetReplicationTasksIter(ctx, pollingCluster, minInclusiveTaskID, maxExclusiveTaskID)
}
func (e *historyEngineImpl) GetDLQReplicationMessages(
ctx context.Context,
taskInfos []*replicationspb.ReplicationTaskInfo,
) ([]*replicationspb.ReplicationTask, error) {
return replicationapi.GetDLQTasks(ctx, e.shardContext, e.replicationAckMgr, taskInfos)
}
func (e *historyEngineImpl) ReapplyEvents(
ctx context.Context,
namespaceUUID namespace.ID,
workflowID string,
runID string,
reapplyEvents []*historypb.HistoryEvent,
) error {
return reapplyevents.Invoke(ctx, namespaceUUID, workflowID, runID, reapplyEvents, e.shardContext, e.workflowConsistencyChecker, e.workflowResetter, e.eventsReapplier)
}
func (e *historyEngineImpl) GetDLQMessages(
ctx context.Context,
request *historyservice.GetDLQMessagesRequest,
) (*historyservice.GetDLQMessagesResponse, error) {
return replicationadmin.GetDLQ(ctx, request, e.shardContext, e.replicationDLQHandler)
}
func (e *historyEngineImpl) PurgeDLQMessages(
ctx context.Context,
request *historyservice.PurgeDLQMessagesRequest,
) (*historyservice.PurgeDLQMessagesResponse, error) {
return replicationadmin.PurgeDLQ(ctx, request, e.shardContext, e.replicationDLQHandler)
}
func (e *historyEngineImpl) MergeDLQMessages(
ctx context.Context,
request *historyservice.MergeDLQMessagesRequest,
) (*historyservice.MergeDLQMessagesResponse, error) {
return replicationadmin.MergeDLQ(ctx, request, e.shardContext, e.replicationDLQHandler)
}
func (e *historyEngineImpl) RebuildMutableState(
ctx context.Context,
namespaceUUID namespace.ID,
execution *commonpb.WorkflowExecution,
) error {
return e.workflowRebuilder.rebuild(
ctx,
definition.NewWorkflowKey(
namespaceUUID.String(),
execution.GetWorkflowId(),
execution.GetRunId(),
),
)
}
func (e *historyEngineImpl) RefreshWorkflowTasks(
ctx context.Context,
namespaceUUID namespace.ID,
execution *commonpb.WorkflowExecution,
) (retError error) {
return refreshworkflow.Invoke(
ctx,
definition.NewWorkflowKey(namespaceUUID.String(), execution.WorkflowId, execution.RunId),
e.shardContext,
e.workflowConsistencyChecker,
)
}
func (e *historyEngineImpl) GenerateLastHistoryReplicationTasks(
ctx context.Context,
request *historyservice.GenerateLastHistoryReplicationTasksRequest,
) (_ *historyservice.GenerateLastHistoryReplicationTasksResponse, retError error) {
return replicationapi.GenerateTask(ctx, request, e.shardContext, e.workflowConsistencyChecker)
}
func (e *historyEngineImpl) GetReplicationStatus(
ctx context.Context,
request *historyservice.GetReplicationStatusRequest,
) (_ *historyservice.ShardReplicationStatus, retError error) {
return replicationapi.GetStatus(ctx, request, e.shardContext, e.replicationAckMgr)
}
func (e *historyEngineImpl) GetWorkflowExecutionHistory(
ctx context.Context,
request *historyservice.GetWorkflowExecutionHistoryRequest,
) (_ *historyservice.GetWorkflowExecutionHistoryResponse, retError error) {
return getworkflowexecutionhistory.Invoke(ctx, e.shardContext, e.workflowConsistencyChecker, e.versionChecker, e.eventNotifier, request, e.persistenceVisibilityMgr)
}
func (e *historyEngineImpl) GetWorkflowExecutionHistoryReverse(
ctx context.Context,
request *historyservice.GetWorkflowExecutionHistoryReverseRequest,
) (_ *historyservice.GetWorkflowExecutionHistoryReverseResponse, retError error) {
return getworkflowexecutionhistoryreverse.Invoke(ctx, e.shardContext, e.workflowConsistencyChecker, e.eventNotifier, request, e.persistenceVisibilityMgr)
}
func (e *historyEngineImpl) GetWorkflowExecutionRawHistory(ctx context.Context, request *historyservice.GetWorkflowExecutionRawHistoryRequest) (*historyservice.GetWorkflowExecutionRawHistoryResponse, error) {
return getworkflowexecutionrawhistory.Invoke(ctx, e.shardContext, e.workflowConsistencyChecker, e.eventNotifier, request)
}
func (e *historyEngineImpl) GetWorkflowExecutionRawHistoryV2(
ctx context.Context,
request *historyservice.GetWorkflowExecutionRawHistoryV2Request,
) (_ *historyservice.GetWorkflowExecutionRawHistoryV2Response, retError error) {
return getworkflowexecutionrawhistoryv2.Invoke(ctx, e.shardContext, e.workflowConsistencyChecker, e.eventNotifier, request)
}
func (e *historyEngineImpl) AddTasks(
ctx context.Context,
request *historyservice.AddTasksRequest,
) (_ *historyservice.AddTasksResponse, retError error) {
return addtasks.Invoke(
ctx,
e.shardContext,
e.eventSerializer,
int(e.config.NumberOfShards),
request,
e.taskCategoryRegistry,
)
}
func (e *historyEngineImpl) ListTasks(
ctx context.Context,
request *historyservice.ListTasksRequest,
) (_ *historyservice.ListTasksResponse, retError error) {
return listtasks.Invoke(
ctx,
e.taskCategoryRegistry,
e.executionManager,
request,
)
}
// StateMachineEnvironment implements shard.Engine.
func (e *historyEngineImpl) StateMachineEnvironment() hsm.Environment {
return e.stateMachineEnvironment
}