Files
tasks/service/history/replication/executable_delete_execution_task.go
Jiechen ZhongandGitHub fcb0946c45 Use ActiveClusterName to assert on active cluster (#10476)
## What changed?

- Use `ActiveClusterName(namespace.RoutingKey{ID: workflowID})` instead
of `ActiveInCluster` when deciding whether to emit delete execution
replication tasks.
- Skip delete execution replication task execution when the current
cluster is active for the workflow/business ID.

## Why?

Delete execution replication should be produced by the workflow’s active
cluster and applied only on passive clusters.

`ActiveClusterName` should be used to determine active workflow.
```
// Note: Do not use this to determine if a workflow is active in the cluster.
// Use ActiveClusterName(businessID) instead.
```

## How did you test it?
- [ ] built
- [ ] run locally and tested manually
- [x] covered by existing tests
- [x] added new unit test(s)
- [ ] added new functional test(s)
2026-06-02 09:14:23 -07:00

204 lines
5.8 KiB
Go

package replication
import (
"context"
"fmt"
"time"
commonpb "go.temporal.io/api/common/v1"
"go.temporal.io/api/serviceerror"
enumsspb "go.temporal.io/server/api/enums/v1"
"go.temporal.io/server/api/historyservice/v1"
persistencespb "go.temporal.io/server/api/persistence/v1"
replicationspb "go.temporal.io/server/api/replication/v1"
"go.temporal.io/server/chasm"
"go.temporal.io/server/common/definition"
"go.temporal.io/server/common/headers"
"go.temporal.io/server/common/log/tag"
"go.temporal.io/server/common/metrics"
"go.temporal.io/server/common/namespace"
"go.temporal.io/server/common/softassert"
ctasks "go.temporal.io/server/common/tasks"
)
type ExecutableDeleteExecutionTask struct {
ProcessToolBox
chasm.ComponentRef
ExecutableTask
}
var _ ctasks.Task = (*ExecutableDeleteExecutionTask)(nil)
var _ TrackableExecutableTask = (*ExecutableDeleteExecutionTask)(nil)
func NewExecutableDeleteExecutionTask(
processToolBox ProcessToolBox,
taskID int64,
taskCreationTime time.Time,
sourceClusterName string,
sourceShardKey ClusterShardKey,
replicationTask *replicationspb.ReplicationTask,
) *ExecutableDeleteExecutionTask {
rawInfo := replicationTask.GetRawTaskInfo()
// ArchetypeID should never be unspecified. Default to WorkflowArchetypeID.
archetypeID := chasm.WorkflowArchetypeID
if rawInfo != nil && rawInfo.ArchetypeId != chasm.UnspecifiedArchetypeID {
archetypeID = rawInfo.ArchetypeId
} else {
softassert.That(processToolBox.Logger, false, "delete execution replication task has unspecified archetype ID")
}
return &ExecutableDeleteExecutionTask{
ProcessToolBox: processToolBox,
ComponentRef: chasm.NewComponentRefByArchetypeID(
chasm.ExecutionKey{
NamespaceID: rawInfo.GetNamespaceId(),
BusinessID: rawInfo.GetWorkflowId(),
RunID: rawInfo.GetRunId(),
},
archetypeID,
),
ExecutableTask: NewExecutableTask(
processToolBox,
taskID,
metrics.DeleteExecutionReplicationTaskScope,
taskCreationTime,
time.Now().UTC(),
sourceClusterName,
sourceShardKey,
replicationTask,
),
}
}
func (e *ExecutableDeleteExecutionTask) QueueID() any {
return definition.NewWorkflowKey(e.NamespaceID, e.BusinessID, e.RunID)
}
func (e *ExecutableDeleteExecutionTask) Execute() error {
if e.TerminalState() {
return nil
}
e.MarkExecutionStart()
callerInfo := getReplicaitonCallerInfo(e.GetPriority())
namespaceName, apply, err := e.GetNamespaceInfo(headers.SetCallerInfo(
context.Background(),
callerInfo,
), e.NamespaceID, e.BusinessID)
if err != nil {
return err
} else if !apply {
e.Logger.Warn("Skipping the replication task",
tag.WorkflowNamespaceID(e.NamespaceID),
tag.WorkflowID(e.BusinessID),
tag.WorkflowRunID(e.RunID),
tag.TaskID(e.TaskID()),
)
metrics.ReplicationTasksSkipped.With(e.MetricsHandler).Record(
1,
metrics.OperationTag(metrics.DeleteExecutionReplicationTaskScope),
metrics.NamespaceTag(namespaceName),
)
return nil
}
namespaceEntry, err := e.NamespaceCache.GetNamespaceByID(namespace.ID(e.NamespaceID))
if err != nil {
return err
}
currentCluster := e.ClusterMetadata.GetCurrentClusterName()
if namespaceEntry.ActiveClusterName(namespace.RoutingKey{ID: e.BusinessID}) == currentCluster {
e.Logger.Warn("Skipping delete execution replication task on active cluster",
tag.WorkflowNamespaceID(e.NamespaceID),
tag.WorkflowID(e.BusinessID),
tag.WorkflowRunID(e.RunID),
tag.TaskID(e.TaskID()),
tag.ClusterName(currentCluster),
)
metrics.ReplicationTasksSkipped.With(e.MetricsHandler).Record(
1,
metrics.OperationTag(metrics.DeleteExecutionReplicationTaskScope),
metrics.NamespaceTag(namespaceName),
)
return nil
}
ctx, cancel := newTaskContext(namespaceName, e.Config.ReplicationTaskApplyTimeout(), callerInfo)
defer cancel()
archetypeID, err := e.ArchetypeID(e.ChasmRegistry)
if err != nil {
return err
}
switch archetypeID {
case chasm.WorkflowArchetypeID:
return e.deleteWorkflowExecution(ctx)
default:
return e.deleteChasmExecution(ctx)
}
}
func (e *ExecutableDeleteExecutionTask) deleteWorkflowExecution(ctx context.Context) error {
shardContext, err := e.ShardController.GetShardByNamespaceWorkflow(
namespace.ID(e.NamespaceID),
e.BusinessID,
)
if err != nil {
return err
}
engine, err := shardContext.GetEngine(ctx)
if err != nil {
return err
}
_, err = engine.DeleteWorkflowExecution(ctx, &historyservice.DeleteWorkflowExecutionRequest{
NamespaceId: e.NamespaceID,
WorkflowExecution: &commonpb.WorkflowExecution{
WorkflowId: e.BusinessID,
RunId: e.RunID,
},
})
return err
}
func (e *ExecutableDeleteExecutionTask) deleteChasmExecution(ctx context.Context) error {
return e.ChasmEngine.DeleteExecution(ctx, e.ComponentRef, chasm.DeleteExecutionRequest{})
}
func (e *ExecutableDeleteExecutionTask) HandleErr(err error) error {
metrics.ReplicationTasksErrorByType.With(e.MetricsHandler).Record(
1,
metrics.OperationTag(metrics.DeleteExecutionReplicationTaskScope),
metrics.NamespaceTag(e.NamespaceName()),
metrics.ServiceErrorTypeTag(err),
)
switch err.(type) {
case nil, *serviceerror.NotFound:
return nil
default:
e.Logger.Error("delete execution replication task encountered error",
tag.WorkflowNamespaceID(e.NamespaceID),
tag.WorkflowID(e.BusinessID),
tag.WorkflowRunID(e.RunID),
tag.TaskID(e.TaskID()),
tag.Error(err),
)
return fmt.Errorf("delete execution replication task error: %w", err)
}
}
func (e *ExecutableDeleteExecutionTask) MarkPoisonPill() error {
if e.ReplicationTask().GetRawTaskInfo() == nil {
e.ReplicationTask().RawTaskInfo = &persistencespb.ReplicationTaskInfo{
NamespaceId: e.NamespaceID,
WorkflowId: e.BusinessID,
RunId: e.RunID,
TaskId: e.TaskID(),
TaskType: enumsspb.TASK_TYPE_REPLICATION_DELETE_EXECUTION,
}
}
return e.ExecutableTask.MarkPoisonPill()
}