@@ -68,6 +68,7 @@ type (
 	Context interface {
 		GetNamespace() namespace.Name
 		GetNamespaceID() namespace.ID
+		GetNamespaceEntry() (*namespace.Namespace, error)
 		GetExecution() *commonpb.WorkflowExecution
 
 		LoadWorkflowExecution() (MutableState, error)
// 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.

//go:generate mockgen -copyright_file ../../../LICENSE -package $GOPACKAGE -source $GOFILE -destination context_mock.go

package workflow

import (
	"context"
	"fmt"
	"time"

	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"

	"go.temporal.io/server/api/adminservice/v1"
	persistencespb "go.temporal.io/server/api/persistence/v1"
	"go.temporal.io/server/common"
	"go.temporal.io/server/common/clock"
	"go.temporal.io/server/common/locks"
	"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/rpc"
	"go.temporal.io/server/service/history/configs"
	"go.temporal.io/server/service/history/consts"
	"go.temporal.io/server/service/history/shard"
	"go.temporal.io/server/service/history/tasks"
)

const (
	defaultRemoteCallTimeout = 30 * time.Second
)

const (
	CallerTypeAPI  CallerType = 0
	CallerTypeTask CallerType = 1
)

type (
	CallerType int

	Context interface {
		GetNamespace() namespace.Name
		GetNamespaceID() namespace.ID
		GetExecution() *commonpb.WorkflowExecution

		LoadWorkflowExecution() (MutableState, error)
		LoadWorkflowExecutionForReplication(incomingVersion int64) (MutableState, error)
		LoadExecutionStats() (*persistencespb.ExecutionStats, error)
		Clear()

		Lock(ctx context.Context, caller CallerType) error
		Unlock(caller CallerType)

		GetHistorySize() int64
		SetHistorySize(size int64)

		ReapplyEvents(
			eventBatches []*persistence.WorkflowEvents,
		) error

		PersistWorkflowEvents(
			workflowEvents *persistence.WorkflowEvents,
		) (int64, error)

		CreateWorkflowExecution(
			now time.Time,
			createMode persistence.CreateWorkflowMode,
			prevRunID string,
			prevLastWriteVersion int64,
			newMutableState MutableState,
			newWorkflow *persistence.WorkflowSnapshot,
			newWorkflowEvents []*persistence.WorkflowEvents,
		) error
		ConflictResolveWorkflowExecution(
			now time.Time,
			conflictResolveMode persistence.ConflictResolveWorkflowMode,
			resetMutableState MutableState,
			newContext Context,
			newMutableState MutableState,
			currentContext Context,
			currentMutableState MutableState,
			currentTransactionPolicy *TransactionPolicy,
		) error
		UpdateWorkflowExecutionAsActive(
			now time.Time,
		) error
		UpdateWorkflowExecutionWithNewAsActive(
			now time.Time,
			newContext Context,
			newMutableState MutableState,
		) error
		UpdateWorkflowExecutionAsPassive(
			now time.Time,
		) error
		UpdateWorkflowExecutionWithNewAsPassive(
			now time.Time,
			newContext Context,
			newMutableState MutableState,
		) error
		UpdateWorkflowExecutionWithNew(
			now time.Time,
			updateMode persistence.UpdateWorkflowMode,
			newContext Context,
			newMutableState MutableState,
			currentWorkflowTransactionPolicy TransactionPolicy,
			newWorkflowTransactionPolicy *TransactionPolicy,
		) error
	}
)

type (
	ContextImpl struct {
		namespaceID       namespace.ID
		workflowExecution commonpb.WorkflowExecution
		shard             shard.Context
		logger            log.Logger
		metricsClient     metrics.Client
		timeSource        clock.TimeSource
		config            *configs.Config
		transaction       Transaction

		mutex        locks.PriorityMutex
		MutableState MutableState
		stats        *persistencespb.ExecutionStats
	}
)

var _ Context = (*ContextImpl)(nil)

var (
	PersistenceOperationRetryPolicy = common.CreatePersistenceRetryPolicy()
)

func NewContext(
	namespaceID namespace.ID,
	execution commonpb.WorkflowExecution,
	shard shard.Context,
	logger log.Logger,
) *ContextImpl {
	return &ContextImpl{
		namespaceID:       namespaceID,
		workflowExecution: execution,
		shard:             shard,
		logger:            logger,
		metricsClient:     shard.GetMetricsClient(),
		timeSource:        shard.GetTimeSource(),
		config:            shard.GetConfig(),
		mutex:             locks.NewPriorityMutex(),
		transaction:       NewTransaction(shard),
		stats: &persistencespb.ExecutionStats{
			HistorySize: 0,
		},
	}
}

func (c *ContextImpl) Lock(
	ctx context.Context,
	caller CallerType,
) error {
	switch caller {
	case CallerTypeAPI:
		return c.mutex.LockHigh(ctx)
	case CallerTypeTask:
		return c.mutex.LockLow(ctx)
	default:
		panic(fmt.Sprintf("unknown caller type: %v", caller))
	}
}

func (c *ContextImpl) Unlock(
	caller CallerType,
) {
	switch caller {
	case CallerTypeAPI:
		c.mutex.UnlockHigh()
	case CallerTypeTask:
		c.mutex.UnlockLow()
	default:
		panic(fmt.Sprintf("unknown caller type: %v", caller))
	}
}

func (c *ContextImpl) Clear() {
	c.metricsClient.IncCounter(metrics.WorkflowContextScope, metrics.WorkflowContextCleared)
	if c.MutableState != nil {
		c.MutableState.GetQueryRegistry().Clear()
	}
	c.MutableState = nil
	c.stats = &persistencespb.ExecutionStats{
		HistorySize: 0,
	}
}

func (c *ContextImpl) GetNamespaceID() namespace.ID {
	return c.namespaceID
}

func (c *ContextImpl) GetExecution() *commonpb.WorkflowExecution {
	return &c.workflowExecution
}

func (c *ContextImpl) GetNamespace() namespace.Name {
	namespaceEntry, err := c.shard.GetNamespaceRegistry().GetNamespaceByID(c.namespaceID)
	if err != nil {
		return ""
	}
	return namespaceEntry.Name()
}

func (c *ContextImpl) GetHistorySize() int64 {
	return c.stats.HistorySize
}

func (c *ContextImpl) SetHistorySize(size int64) {
	c.stats.HistorySize = size
}

func (c *ContextImpl) LoadExecutionStats() (*persistencespb.ExecutionStats, error) {
	_, err := c.LoadWorkflowExecution()
	if err != nil {
		return nil, err
	}
	return c.stats, nil
}

func (c *ContextImpl) LoadWorkflowExecutionForReplication(
	incomingVersion int64,
) (MutableState, error) {

	namespaceEntry, err := c.shard.GetNamespaceRegistry().GetNamespaceByID(c.namespaceID)
	if err != nil {
		return nil, err
	}

	if c.MutableState == nil {
		response, err := getWorkflowExecutionWithRetry(c.shard, &persistence.GetWorkflowExecutionRequest{
			ShardID:     c.shard.GetShardID(),
			NamespaceID: c.namespaceID.String(),
			WorkflowID:  c.workflowExecution.GetWorkflowId(),
			RunID:       c.workflowExecution.GetRunId(),
		})
		if err != nil {
			return nil, err
		}

		c.MutableState, err = newMutableStateBuilderFromDB(
			c.shard,
			c.shard.GetEventsCache(),
			c.logger,
			namespaceEntry,
			response.State,
			response.DBRecordVersion,
		)
		if err != nil {
			return nil, err
		}

		c.stats = response.State.ExecutionInfo.ExecutionStats
	}

	lastWriteVersion, err := c.MutableState.GetLastWriteVersion()
	if err != nil {
		return nil, err
	}

	if lastWriteVersion == incomingVersion {
		err = c.MutableState.StartTransactionSkipWorkflowTaskFail(namespaceEntry)
		if err != nil {
			return nil, err
		}
	} else {
		flushBeforeReady, err := c.MutableState.StartTransaction(namespaceEntry)
		if err != nil {
			return nil, err
		}
		if !flushBeforeReady {
			return c.MutableState, nil
		}

		if err = c.UpdateWorkflowExecutionAsActive(
			c.shard.GetTimeSource().Now(),
		); err != nil {
			return nil, err
		}

		flushBeforeReady, err = c.MutableState.StartTransaction(namespaceEntry)
		if err != nil {
			return nil, err
		}
		if flushBeforeReady {
			return nil, serviceerror.NewInternal("Context counter flushBeforeReady status after loading mutable state from DB")
		}
	}
	return c.MutableState, nil
}

func (c *ContextImpl) LoadWorkflowExecution() (MutableState, error) {

	namespaceEntry, err := c.shard.GetNamespaceRegistry().GetNamespaceByID(c.namespaceID)
	if err != nil {
		return nil, err
	}

	if c.MutableState == nil {
		response, err := getWorkflowExecutionWithRetry(c.shard, &persistence.GetWorkflowExecutionRequest{
			ShardID:     c.shard.GetShardID(),
			NamespaceID: c.namespaceID.String(),
			WorkflowID:  c.workflowExecution.GetWorkflowId(),
			RunID:       c.workflowExecution.GetRunId(),
		})
		if err != nil {
			return nil, err
		}

		c.MutableState, err = newMutableStateBuilderFromDB(
			c.shard,
			c.shard.GetEventsCache(),
			c.logger,
			namespaceEntry,
			response.State,
			response.DBRecordVersion,
		)
		if err != nil {
			return nil, err
		}

		c.stats = response.State.ExecutionInfo.ExecutionStats
	}

	flushBeforeReady, err := c.MutableState.StartTransaction(namespaceEntry)
	if err != nil {
		return nil, err
	}
	if !flushBeforeReady {
		return c.MutableState, nil
	}

	if err = c.UpdateWorkflowExecutionAsActive(
		c.shard.GetTimeSource().Now(),
	); err != nil {
		return nil, err
	}

	flushBeforeReady, err = c.MutableState.StartTransaction(namespaceEntry)
	if err != nil {
		return nil, err
	}
	if flushBeforeReady {
		return nil, serviceerror.NewInternal("Context counter flushBeforeReady status after loading mutable state from DB")
	}

	return c.MutableState, nil
}

func (c *ContextImpl) PersistWorkflowEvents(
	workflowEvents *persistence.WorkflowEvents,
) (int64, error) {
	return PersistWorkflowEvents(c.shard, workflowEvents)
}

func (c *ContextImpl) CreateWorkflowExecution(
	_ time.Time,
	createMode persistence.CreateWorkflowMode,
	prevRunID string,
	prevLastWriteVersion int64,
	newMutableState MutableState,
	newWorkflow *persistence.WorkflowSnapshot,
	newWorkflowEvents []*persistence.WorkflowEvents,
) (retError error) {

	defer func() {
		if retError != nil {
			c.Clear()
		}
	}()

	createRequest := &persistence.CreateWorkflowExecutionRequest{
		ShardID: c.shard.GetShardID(),
		// workflow create mode & prev run ID & version
		Mode:                     createMode,
		PreviousRunID:            prevRunID,
		PreviousLastWriteVersion: prevLastWriteVersion,

		NewWorkflowSnapshot: *newWorkflow,
		NewWorkflowEvents:   newWorkflowEvents,
	}

	resp, err := createWorkflowExecutionWithRetry(
		c.shard,
		createRequest,
	)
	if err != nil {
		return err
	}
	c.SetHistorySize(int64(resp.NewMutableStateStats.HistoryStatistics.SizeDiff))

	engine, err := c.shard.GetEngine()
	if err != nil {
		return err
	}
	NotifyWorkflowSnapshotTasks(engine, newWorkflow)
	emitStateTransitionCount(c.metricsClient, newMutableState)

	return nil
}

func (c *ContextImpl) ConflictResolveWorkflowExecution(
	now time.Time,
	conflictResolveMode persistence.ConflictResolveWorkflowMode,
	resetMutableState MutableState,
	newContext Context,
	newMutableState MutableState,
	currentContext Context,
	currentMutableState MutableState,
	currentTransactionPolicy *TransactionPolicy,
) (retError error) {

	defer func() {
		if retError != nil {
			c.Clear()
		}
	}()

	resetWorkflow, resetWorkflowEventsSeq, err := resetMutableState.CloseTransactionAsSnapshot(
		now,
		TransactionPolicyPassive,
	)
	if err != nil {
		return err
	}
	resetWorkflow.ExecutionInfo.ExecutionStats = &persistencespb.ExecutionStats{
		HistorySize: c.GetHistorySize(),
	}

	var newWorkflow *persistence.WorkflowSnapshot
	var newWorkflowEventsSeq []*persistence.WorkflowEvents
	if newContext != nil && newMutableState != nil {

		defer func() {
			if retError != nil {
				newContext.Clear()
			}
		}()

		newWorkflow, newWorkflowEventsSeq, err = newMutableState.CloseTransactionAsSnapshot(
			now,
			TransactionPolicyPassive,
		)
		if err != nil {
			return err
		}
		newWorkflow.ExecutionInfo.ExecutionStats = &persistencespb.ExecutionStats{
			HistorySize: newContext.GetHistorySize(),
		}
	}

	var currentWorkflow *persistence.WorkflowMutation
	var currentWorkflowEventsSeq []*persistence.WorkflowEvents
	if currentContext != nil && currentMutableState != nil && currentTransactionPolicy != nil {

		defer func() {
			if retError != nil {
				currentContext.Clear()
			}
		}()

		currentWorkflow, currentWorkflowEventsSeq, err = currentMutableState.CloseTransactionAsMutation(
			now,
			*currentTransactionPolicy,
		)
		if err != nil {
			return err
		}
		currentWorkflow.ExecutionInfo.ExecutionStats = &persistencespb.ExecutionStats{
			HistorySize: currentContext.GetHistorySize(),
		}
	}

	if err := c.conflictResolveEventReapply(
		conflictResolveMode,
		resetWorkflowEventsSeq,
		newWorkflowEventsSeq,
		// current workflow events will not participate in the events reapplication
	); err != nil {
		return err
	}

	if resetWorkflowSizeDiff, newWorkflowSizeDiff, currentWorkflowSizeDiff, err := c.transaction.ConflictResolveWorkflowExecution(
		conflictResolveMode,
		resetWorkflow,
		resetWorkflowEventsSeq,
		newWorkflow,
		newWorkflowEventsSeq,
		currentWorkflow,
		currentWorkflowEventsSeq,
	); err != nil {
		return err
	} else {
		c.SetHistorySize(c.GetHistorySize() + resetWorkflowSizeDiff)
		if newContext != nil {
			newContext.SetHistorySize(newContext.GetHistorySize() + newWorkflowSizeDiff)
		}
		if currentContext != nil {
			currentContext.SetHistorySize(currentContext.GetHistorySize() + currentWorkflowSizeDiff)
		}
	}

	emitStateTransitionCount(c.metricsClient, resetMutableState)
	emitStateTransitionCount(c.metricsClient, newMutableState)
	emitStateTransitionCount(c.metricsClient, currentMutableState)

	return nil
}

func (c *ContextImpl) UpdateWorkflowExecutionAsActive(
	now time.Time,
) error {

	// We only perform this check on active cluster for the namespace
	forceTerminate, err := c.enforceSizeCheck()
	if err != nil {
		return err
	}

	if err := c.UpdateWorkflowExecutionWithNew(
		now,
		persistence.UpdateWorkflowModeUpdateCurrent,
		nil,
		nil,
		TransactionPolicyActive,
		nil,
	); err != nil {
		return err
	}

	if forceTerminate {
		// Returns ResourceExhausted error back to caller after workflow execution is forced terminated
		// Retrying the operation will give appropriate semantics operation should expect in the case of workflow
		// execution being closed.
		return consts.ErrSizeExceedsLimit
	}

	return nil
}

func (c *ContextImpl) UpdateWorkflowExecutionWithNewAsActive(
	now time.Time,
	newContext Context,
	newMutableState MutableState,
) error {

	return c.UpdateWorkflowExecutionWithNew(
		now,
		persistence.UpdateWorkflowModeUpdateCurrent,
		newContext,
		newMutableState,
		TransactionPolicyActive,
		TransactionPolicyActive.Ptr(),
	)
}

func (c *ContextImpl) UpdateWorkflowExecutionAsPassive(
	now time.Time,
) error {

	return c.UpdateWorkflowExecutionWithNew(
		now,
		persistence.UpdateWorkflowModeUpdateCurrent,
		nil,
		nil,
		TransactionPolicyPassive,
		nil,
	)
}

func (c *ContextImpl) UpdateWorkflowExecutionWithNewAsPassive(
	now time.Time,
	newContext Context,
	newMutableState MutableState,
) error {

	return c.UpdateWorkflowExecutionWithNew(
		now,
		persistence.UpdateWorkflowModeUpdateCurrent,
		newContext,
		newMutableState,
		TransactionPolicyPassive,
		TransactionPolicyPassive.Ptr(),
	)
}

func (c *ContextImpl) UpdateWorkflowExecutionWithNew(
	now time.Time,
	updateMode persistence.UpdateWorkflowMode,
	newContext Context,
	newMutableState MutableState,
	currentWorkflowTransactionPolicy TransactionPolicy,
	newWorkflowTransactionPolicy *TransactionPolicy,
) (retError error) {

	defer func() {
		if retError != nil {
			c.Clear()
		}
	}()

	currentWorkflow, currentWorkflowEventsSeq, err := c.MutableState.CloseTransactionAsMutation(
		now,
		currentWorkflowTransactionPolicy,
	)
	if err != nil {
		return err
	}
	currentWorkflow.ExecutionInfo.ExecutionStats = &persistencespb.ExecutionStats{
		HistorySize: c.GetHistorySize(),
	}

	var newWorkflow *persistence.WorkflowSnapshot
	var newWorkflowEventsSeq []*persistence.WorkflowEvents
	if newContext != nil && newMutableState != nil && newWorkflowTransactionPolicy != nil {
		defer func() {
			if retError != nil {
				newContext.Clear()
			}
		}()

		newWorkflow, newWorkflowEventsSeq, err = newMutableState.CloseTransactionAsSnapshot(
			now,
			*newWorkflowTransactionPolicy,
		)
		if err != nil {
			return err
		}
		newWorkflow.ExecutionInfo.ExecutionStats = &persistencespb.ExecutionStats{
			HistorySize: newContext.GetHistorySize(),
		}
	}

	if err := c.mergeContinueAsNewReplicationTasks(
		updateMode,
		currentWorkflow,
		newWorkflow,
	); err != nil {
		return err
	}

	if err := c.updateWorkflowExecutionEventReapply(
		updateMode,
		currentWorkflowEventsSeq,
		newWorkflowEventsSeq,
	); err != nil {
		return err
	}

	if currentWorkflowSizeDiff, newWorkflowSizeDiff, err := c.transaction.UpdateWorkflowExecution(
		updateMode,
		currentWorkflow,
		currentWorkflowEventsSeq,
		newWorkflow,
		newWorkflowEventsSeq,
	); err != nil {
		return err
	} else {
		c.SetHistorySize(c.GetHistorySize() + currentWorkflowSizeDiff)
		if newContext != nil {
			newContext.SetHistorySize(newContext.GetHistorySize() + newWorkflowSizeDiff)
		}
	}

	emitStateTransitionCount(c.metricsClient, c.MutableState)
	emitStateTransitionCount(c.metricsClient, newMutableState)

	// finally emit session stats
	namespace := c.GetNamespace()
	emitWorkflowHistoryStats(
		c.metricsClient,
		namespace,
		int(c.GetHistorySize()),
		int(c.MutableState.GetNextEventID()-1),
	)

	return nil
}

func (c *ContextImpl) mergeContinueAsNewReplicationTasks(
	updateMode persistence.UpdateWorkflowMode,
	currentWorkflowMutation *persistence.WorkflowMutation,
	newWorkflowSnapshot *persistence.WorkflowSnapshot,
) error {

	if currentWorkflowMutation.ExecutionState.Status != enumspb.WORKFLOW_EXECUTION_STATUS_CONTINUED_AS_NEW {
		return nil
	} else if updateMode == persistence.UpdateWorkflowModeBypassCurrent && newWorkflowSnapshot == nil {
		// update current workflow as zombie & continue as new without new zombie workflow
		// this case can be valid if new workflow is already created by resend
		return nil
	}

	// current workflow is doing continue as new

	// it is possible that continue as new is done as part of passive logic
	if len(currentWorkflowMutation.ReplicationTasks) == 0 {
		return nil
	}

	if newWorkflowSnapshot == nil || len(newWorkflowSnapshot.ReplicationTasks) != 1 {
		return serviceerror.NewInternal("unable to find replication task from new workflow for continue as new replication")
	}

	// merge the new run first event batch replication task
	// to current event batch replication task
	newRunTask := newWorkflowSnapshot.ReplicationTasks[0].(*tasks.HistoryReplicationTask)
	newWorkflowSnapshot.ReplicationTasks = nil

	newRunBranchToken := newRunTask.BranchToken
	taskUpdated := false
	for _, replicationTask := range currentWorkflowMutation.ReplicationTasks {
		if task, ok := replicationTask.(*tasks.HistoryReplicationTask); ok {
			taskUpdated = true
			task.NewRunBranchToken = newRunBranchToken
		}
	}
	if !taskUpdated {
		return serviceerror.NewInternal("unable to find replication task from current workflow for continue as new replication")
	}
	return nil
}

func (c *ContextImpl) updateWorkflowExecutionEventReapply(
	updateMode persistence.UpdateWorkflowMode,
	eventBatch1 []*persistence.WorkflowEvents,
	eventBatch2 []*persistence.WorkflowEvents,
) error {

	if updateMode != persistence.UpdateWorkflowModeBypassCurrent {
		return nil
	}

	var eventBatches []*persistence.WorkflowEvents
	eventBatches = append(eventBatches, eventBatch1...)
	eventBatches = append(eventBatches, eventBatch2...)
	return c.ReapplyEvents(eventBatches)
}

func (c *ContextImpl) conflictResolveEventReapply(
	conflictResolveMode persistence.ConflictResolveWorkflowMode,
	eventBatch1 []*persistence.WorkflowEvents,
	eventBatch2 []*persistence.WorkflowEvents,
) error {

	if conflictResolveMode != persistence.ConflictResolveWorkflowModeBypassCurrent {
		return nil
	}

	var eventBatches []*persistence.WorkflowEvents
	eventBatches = append(eventBatches, eventBatch1...)
	eventBatches = append(eventBatches, eventBatch2...)
	return c.ReapplyEvents(eventBatches)
}

func (c *ContextImpl) ReapplyEvents(
	eventBatches []*persistence.WorkflowEvents,
) error {

	// NOTE: this function should only be used to workflow which is
	// not the caller, or otherwise deadlock will appear

	if len(eventBatches) == 0 {
		return nil
	}

	namespaceID := namespace.ID(eventBatches[0].NamespaceID)
	workflowID := eventBatches[0].WorkflowID
	runID := eventBatches[0].RunID
	var reapplyEvents []*historypb.HistoryEvent
	for _, events := range eventBatches {
		if namespace.ID(events.NamespaceID) != namespaceID ||
			events.WorkflowID != workflowID {
			return serviceerror.NewInternal("Context encountered mismatch namespaceID / workflowID in events reapplication.")
		}

		for _, e := range events.Events {
			event := e
			switch event.GetEventType() {
			case enumspb.EVENT_TYPE_WORKFLOW_EXECUTION_SIGNALED:
				reapplyEvents = append(reapplyEvents, event)
			}
		}
	}
	if len(reapplyEvents) == 0 {
		return nil
	}

	// Reapply events only reapply to the current run.
	// The run id is only used for reapply event de-duplication
	execution := &commonpb.WorkflowExecution{
		WorkflowId: workflowID,
		RunId:      runID,
	}
	namespaceRegistry := c.shard.GetNamespaceRegistry()
	serializer := c.shard.GetPayloadSerializer()
	namespaceEntry, err := namespaceRegistry.GetNamespaceByID(namespaceID)
	if err != nil {
		return err
	}

	ctx, cancel := context.WithTimeout(context.Background(), defaultRemoteCallTimeout)
	defer cancel()

	activeCluster := namespaceEntry.ActiveClusterName()
	if activeCluster == c.shard.GetClusterMetadata().GetCurrentClusterName() {
		engine, err := c.shard.GetEngine()
		if err != nil {
			return err
		}
		return engine.ReapplyEvents(
			ctx,
			namespaceID,
			workflowID,
			runID,
			reapplyEvents,
		)
	}

	// The active cluster of the namespace is the same as current cluster.
	// Use the history from the same cluster to reapply events
	reapplyEventsDataBlob, err := serializer.SerializeEvents(reapplyEvents, enumspb.ENCODING_TYPE_PROTO3)
	if err != nil {
		return err
	}
	// The active cluster of the namespace is differ from the current cluster
	// Use frontend client to route this request to the active cluster
	// Reapplication only happens in active cluster
	sourceCluster := c.shard.GetRemoteAdminClient(activeCluster)
	if sourceCluster == nil {
		return serviceerror.NewInternal(fmt.Sprintf("cannot find cluster config %v to do reapply", activeCluster))
	}
	ctx2, cancel2 := rpc.NewContextWithTimeoutAndHeaders(defaultRemoteCallTimeout)
	defer cancel2()
	_, err = sourceCluster.ReapplyEvents(
		ctx2,
		&adminservice.ReapplyEventsRequest{
			Namespace:         namespaceEntry.Name().String(),
			WorkflowExecution: execution,
			Events:            reapplyEventsDataBlob,
		},
	)

	return err
}

// Returns true if execution is forced terminated
func (c *ContextImpl) enforceSizeCheck() (bool, error) {
	namespaceName := c.GetNamespace().String()
	historySizeLimitWarn := c.config.HistorySizeLimitWarn(namespaceName)
	historySizeLimitError := c.config.HistorySizeLimitError(namespaceName)
	historyCountLimitWarn := c.config.HistoryCountLimitWarn(namespaceName)
	historyCountLimitError := c.config.HistoryCountLimitError(namespaceName)

	historySize := int(c.GetHistorySize())
	historyCount := int(c.MutableState.GetNextEventID() - 1)

	// Hard terminate workflow if still running and breached size or count limit
	if (historySize > historySizeLimitError || historyCount > historyCountLimitError) &&
		c.MutableState.IsWorkflowExecutionRunning() {
		c.logger.Error("history size exceeds error limit.",
			tag.WorkflowNamespaceID(c.namespaceID.String()),
			tag.WorkflowID(c.workflowExecution.GetWorkflowId()),
			tag.WorkflowRunID(c.workflowExecution.GetRunId()),
			tag.WorkflowHistorySize(historySize),
			tag.WorkflowEventCount(historyCount))

		// Discard pending changes in MutableState so we can apply terminate state transition
		c.Clear()

		// Reload mutable state
		mutableState, err := c.LoadWorkflowExecution()
		if err != nil {
			return false, err
		}

		// Terminate workflow is written as a separate batch and might result in more than one event as we close the
		// outstanding workflow task before terminating the workflow
		eventBatchFirstEventID := mutableState.GetNextEventID()
		if err := TerminateWorkflow(
			mutableState,
			eventBatchFirstEventID,
			common.FailureReasonSizeExceedsLimit,
			nil,
			consts.IdentityHistoryService,
		); err != nil {
			return false, err
		}

		// Return true to caller to indicate workflow state is overwritten to force terminate execution on update
		return true, nil
	}

	if historySize > historySizeLimitWarn || historyCount > historyCountLimitWarn {
		c.logger.Warn("history size exceeds warn limit.",
			tag.WorkflowNamespaceID(c.MutableState.GetExecutionInfo().NamespaceId),
			tag.WorkflowID(c.MutableState.GetExecutionInfo().WorkflowId),
			tag.WorkflowRunID(c.MutableState.GetExecutionState().RunId),
			tag.WorkflowHistorySize(historySize),
			tag.WorkflowEventCount(historyCount))
	}

	return false, nil
}

func emitStateTransitionCount(
	metricsClient metrics.Client,
	mutableState MutableState,
) {
	if mutableState == nil {
		return
	}

	metricsClient.Scope(
		metrics.WorkflowContextScope,
		metrics.NamespaceTag(mutableState.GetNamespaceEntry().Name().String()),
	).RecordDistribution(metrics.StateTransitionCount, int(mutableState.GetExecutionInfo().StateTransitionCount))
}

mutable state already have a get namespace method, can you use that one?