Files
tasks/service/history/ndc/mutable_state_initializer.go
zeekay 5a601d081b rebrand: go.temporal.io/server -> github.com/hanzoai/tasks
Full module path rebrand from Temporal to Hanzo Tasks:
- go.mod module: github.com/hanzoai/tasks
- All internal Go imports updated (1906 files)
- cmd/server renamed to cmd/tasksd
- Binary name: tasksd (was temporal-server)
- Docker images: hanzoai/tasks (was temporalio/server)
- README rewritten with Hanzo Tasks branding
- LLM.md created with integration notes
- Makefile, goreleaser, CI workflows updated
- go.temporal.io/sdk and go.temporal.io/api imports preserved (separate repos)

Fork of Temporal.io (MIT license).
2026-03-19 10:36:40 -07:00

256 lines
7.3 KiB
Go

package ndc
import (
"context"
"encoding/json"
"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"
persistencespb "github.com/hanzoai/tasks/api/persistence/v1"
"github.com/hanzoai/tasks/chasm"
"github.com/hanzoai/tasks/common/definition"
"github.com/hanzoai/tasks/common/locks"
"github.com/hanzoai/tasks/common/log"
"github.com/hanzoai/tasks/common/log/tag"
"github.com/hanzoai/tasks/common/namespace"
"github.com/hanzoai/tasks/common/persistence/serialization"
historyi "github.com/hanzoai/tasks/service/history/interfaces"
"github.com/hanzoai/tasks/service/history/workflow"
wcache "github.com/hanzoai/tasks/service/history/workflow/cache"
"google.golang.org/protobuf/proto"
)
type (
MutableStateToken struct {
ExistsInDB bool
DBRecordVersion int64
DBHistorySize int64
MutableStateRow []byte
}
MutableStateInitializationSpec struct {
ExistsInDB bool
IsBrandNew bool
DBRecordVersion int64
DBHistorySize int64
}
MutableStateInitializer interface {
Initialize(
ctx context.Context,
workflowKey definition.WorkflowKey,
token []byte,
) (Workflow, bool, error)
}
MutableStateInitializerImpl struct {
shardContext historyi.ShardContext
namespaceCache namespace.Registry
workflowCache wcache.Cache
logger log.Logger
}
)
func NewMutableStateInitializer(
shardContext historyi.ShardContext,
workflowCache wcache.Cache,
logger log.Logger,
) *MutableStateInitializerImpl {
return &MutableStateInitializerImpl{
shardContext: shardContext,
namespaceCache: shardContext.GetNamespaceRegistry(),
workflowCache: workflowCache,
logger: logger,
}
}
func (r *MutableStateInitializerImpl) Initialize(
ctx context.Context,
workflowKey definition.WorkflowKey,
token []byte,
) (Workflow, MutableStateInitializationSpec, error) {
namespaceEntry, err := r.namespaceCache.GetNamespaceByID(namespace.ID(workflowKey.NamespaceID))
if err != nil {
return nil, MutableStateInitializationSpec{}, err
}
if len(token) == 0 {
return r.InitializeFromDB(ctx, namespaceEntry, workflowKey)
}
return r.InitializeFromToken(ctx, namespaceEntry, workflowKey, token)
}
func (r *MutableStateInitializerImpl) InitializeFromDB(
ctx context.Context,
namespaceEntry *namespace.Namespace,
workflowKey definition.WorkflowKey,
) (Workflow, MutableStateInitializationSpec, error) {
wfContext, releaseFn, err := r.workflowCache.GetOrCreateWorkflowExecution(
ctx,
r.shardContext,
namespace.ID(workflowKey.NamespaceID),
&commonpb.WorkflowExecution{
WorkflowId: workflowKey.WorkflowID,
RunId: workflowKey.RunID,
},
locks.PriorityHigh,
)
if err != nil {
return nil, MutableStateInitializationSpec{}, err
}
mutableState, err := wfContext.LoadMutableState(ctx, r.shardContext)
switch err.(type) {
case nil:
mutableState, err = r.flushBufferEvents(ctx, wfContext, mutableState)
if err != nil {
releaseFn(err)
return nil, MutableStateInitializationSpec{}, err
}
_, dbRecordVersion := mutableState.GetUpdateCondition()
dbHistorySize := mutableState.GetHistorySize()
return NewWorkflow(
r.shardContext.GetClusterMetadata(),
wfContext,
mutableState,
releaseFn,
), MutableStateInitializationSpec{
ExistsInDB: true,
IsBrandNew: false,
DBRecordVersion: dbRecordVersion,
DBHistorySize: dbHistorySize,
}, nil
case *serviceerror.NotFound:
return NewWorkflow(
r.shardContext.GetClusterMetadata(),
wfContext,
workflow.NewMutableState(
r.shardContext,
r.shardContext.GetEventsCache(),
r.logger,
namespaceEntry,
workflowKey.WorkflowID,
workflowKey.RunID,
time.Now().UTC(),
),
releaseFn,
), MutableStateInitializationSpec{
ExistsInDB: false,
IsBrandNew: true,
DBRecordVersion: 1,
DBHistorySize: 0,
}, nil
default:
releaseFn(err)
return nil, MutableStateInitializationSpec{}, err
}
}
func (r *MutableStateInitializerImpl) InitializeFromToken(
_ context.Context,
namespaceEntry *namespace.Namespace,
workflowKey definition.WorkflowKey,
token []byte,
) (Workflow, MutableStateInitializationSpec, error) {
wfContext := workflow.NewContext(
r.shardContext.GetConfig(),
workflowKey,
chasm.WorkflowArchetypeID,
r.logger,
r.shardContext.GetThrottledLogger(),
r.shardContext.GetMetricsHandler(),
)
mutableStateRow, dbRecordVersion, dbHistorySize, existsInDB, err := r.deserializeBackfillToken(token)
if err != nil {
return nil, MutableStateInitializationSpec{}, err
}
mutableState, err := workflow.NewMutableStateFromDB(
r.shardContext,
r.shardContext.GetEventsCache(),
r.logger,
namespaceEntry,
mutableStateRow,
dbRecordVersion,
)
if err != nil {
return nil, MutableStateInitializationSpec{}, err
}
return NewWorkflow(
r.shardContext.GetClusterMetadata(),
wfContext,
mutableState,
wcache.NoopReleaseFn,
), MutableStateInitializationSpec{
ExistsInDB: existsInDB,
IsBrandNew: false,
DBRecordVersion: dbRecordVersion,
DBHistorySize: dbHistorySize,
}, nil
}
func (r *MutableStateInitializerImpl) flushBufferEvents(
ctx context.Context,
wfContext historyi.WorkflowContext,
mutableState historyi.MutableState,
) (historyi.MutableState, error) {
flusher := NewBufferEventFlusher(r.shardContext, wfContext, mutableState, r.logger)
_, mutableState, err := flusher.flush(ctx)
if err != nil {
r.logger.Error(
"MutableStateMapping::FlushBufferEvents unable to flush buffer events",
tag.Error(err),
)
return nil, err
}
return mutableState, err
}
func (r *MutableStateInitializerImpl) serializeBackfillToken(
mutableState *persistencespb.WorkflowMutableState,
dbRecordVersion int64,
dbHistorySize int64,
existsInDB bool,
) ([]byte, error) {
mutableStateRow, err := mutableState.Marshal()
if err != nil {
return nil, err
}
return json.Marshal(MutableStateToken{
MutableStateRow: mutableStateRow,
DBRecordVersion: dbRecordVersion,
DBHistorySize: dbHistorySize,
ExistsInDB: existsInDB,
})
}
func (r *MutableStateInitializerImpl) deserializeBackfillToken(
token []byte,
) (_ *persistencespb.WorkflowMutableState, _ int64, _ int64, _ bool, _ error) {
mutableState := &persistencespb.WorkflowMutableState{
ActivityInfos: make(map[int64]*persistencespb.ActivityInfo),
TimerInfos: make(map[string]*persistencespb.TimerInfo),
ChildExecutionInfos: make(map[int64]*persistencespb.ChildExecutionInfo),
RequestCancelInfos: make(map[int64]*persistencespb.RequestCancelInfo),
SignalInfos: make(map[int64]*persistencespb.SignalInfo),
SignalRequestedIds: make([]string, 0),
ExecutionInfo: nil,
ExecutionState: nil,
NextEventId: 0,
BufferedEvents: make([]*historypb.HistoryEvent, 0),
Checksum: nil,
}
historyBackfillToken := &MutableStateToken{}
if err := json.Unmarshal(token, historyBackfillToken); err != nil {
return nil, 0, 0, false, serialization.NewDeserializationError(enumspb.ENCODING_TYPE_JSON, err)
}
err := proto.Unmarshal(historyBackfillToken.MutableStateRow, mutableState)
if err != nil {
return nil, 0, 0, false, serialization.NewDeserializationError(enumspb.ENCODING_TYPE_PROTO3, err)
}
return mutableState, historyBackfillToken.DBRecordVersion, historyBackfillToken.DBHistorySize, historyBackfillToken.ExistsInDB, nil
}