Skip to content

Commit fc07bc7

Browse files
committed
Add CHASM Visibility component to CHASM Workflow
1 parent f53f7e1 commit fc07bc7

9 files changed

Lines changed: 136 additions & 5 deletions

File tree

chasm/lib/workflow/library.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,13 @@ func workflowContextFromChasm(ctx chasm.Context) *workflowContext {
6464

6565
func (l *library) Components() []*chasm.RegistrableComponent {
6666
return []*chasm.RegistrableComponent{
67-
chasm.NewRegistrableComponent[*Workflow](chasm.WorkflowComponentName, chasm.WithContextValues(map[any]any{
68-
ctxKeyWorkflowContext: &workflowContext{registry: l.registry},
69-
})),
67+
chasm.NewRegistrableComponent[*Workflow](
68+
chasm.WorkflowComponentName,
69+
chasm.WithBusinessIDAlias("WorkflowId"),
70+
chasm.WithContextValues(map[any]any{
71+
ctxKeyWorkflowContext: &workflowContext{registry: l.registry},
72+
}),
73+
),
7074
chasm.NewRegistrableComponent[*WorkflowUpdate]("update"),
7175
}
7276
}

chasm/lib/workflow/workflow.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
callbackspb "go.temporal.io/server/chasm/lib/callback/gen/callbackpb/v1"
1313
"go.temporal.io/server/chasm/lib/nexusoperation"
1414
chasmworkflowpb "go.temporal.io/server/chasm/lib/workflow/gen/workflowpb/v1"
15+
"go.temporal.io/server/common/log/tag"
16+
"go.temporal.io/server/common/softassert"
1517
"go.temporal.io/server/service/history/historybuilder"
1618
"google.golang.org/protobuf/types/known/emptypb"
1719
"google.golang.org/protobuf/types/known/timestamppb"
@@ -27,6 +29,9 @@ type Workflow struct {
2729
// MSPointer is a special in-memory field for accessing the underlying mutable state.
2830
chasm.MSPointer
2931

32+
// Visibility to store custom search attributes and memo.
33+
Visibility chasm.Field[*chasm.Visibility]
34+
3035
// Callbacks map is used to store the callbacks for the workflow.
3136
Callbacks chasm.Map[string, *callback.Callback]
3237

@@ -72,6 +77,73 @@ func (w *Workflow) Terminate(
7277
return chasm.TerminateComponentResponse{}, serviceerror.NewInternal("workflow root Terminate should not be called")
7378
}
7479

80+
// SearchAttributes returns the predefined search attributes set in the underlying mutable state.
81+
func (w *Workflow) SearchAttributes(ctx chasm.Context) []chasm.SearchAttributeKeyValue {
82+
searchAttributes, err := w.GetPredefinedSearchAttributes()
83+
softassert.That(
84+
ctx.Logger(),
85+
err == nil,
86+
"failed to retrieve search attributes from mutable state execution info",
87+
tag.Error(err),
88+
)
89+
90+
var res []chasm.SearchAttributeKeyValue
91+
for saName, value := range searchAttributes {
92+
res = append(res, chasm.SearchAttributeKeyValue{
93+
Alias: saName,
94+
Field: saName,
95+
Value: value,
96+
})
97+
}
98+
return res
99+
}
100+
101+
// CustomSearchAttributes returns the custom search attributes.
102+
func (w *Workflow) CustomSearchAttributes(ctx chasm.Context) map[string]*commonpb.Payload {
103+
if vis, ok := w.Visibility.TryGet(ctx); ok {
104+
return vis.CustomSearchAttributes(ctx)
105+
}
106+
return nil
107+
}
108+
109+
// CustomMemo returns the custom memo.
110+
func (w *Workflow) CustomMemo(ctx chasm.Context) map[string]*commonpb.Payload {
111+
if vis, ok := w.Visibility.TryGet(ctx); ok {
112+
return vis.CustomMemo(ctx)
113+
}
114+
return nil
115+
}
116+
117+
// UpsertCustomSearchAttributes merges the provided custom search attributes into the existing one.
118+
// For details of the merge, see [chasm.Visibility.MergeCustomSearchAttributes].
119+
func (w *Workflow) UpsertCustomSearchAttributes(
120+
ctx chasm.MutableContext,
121+
customSearchAttributes map[string]*commonpb.Payload,
122+
) error {
123+
if vis, ok := w.Visibility.TryGet(ctx); ok {
124+
vis.MergeCustomSearchAttributes(ctx, customSearchAttributes)
125+
} else {
126+
vis := chasm.NewVisibilityWithData(ctx, customSearchAttributes, nil)
127+
w.Visibility = chasm.NewComponentField(ctx, vis)
128+
}
129+
return nil
130+
}
131+
132+
// UpsertCustomMemo merges the provided custom memo into the existing one.
133+
// For details of the merge, see [chasm.Visibility.MergeCustomMemo].
134+
func (w *Workflow) UpsertCustomMemo(
135+
ctx chasm.MutableContext,
136+
customMemo map[string]*commonpb.Payload,
137+
) error {
138+
if vis, ok := w.Visibility.TryGet(ctx); ok {
139+
vis.MergeCustomMemo(ctx, customMemo)
140+
} else {
141+
vis := chasm.NewVisibilityWithData(ctx, nil, customMemo)
142+
w.Visibility = chasm.NewComponentField(ctx, vis)
143+
}
144+
return nil
145+
}
146+
75147
// ProcessCloseCallbacks triggers "WorkflowClosed" callbacks using the CHASM implementation.
76148
// It schedules all workflow-level and update-level callbacks that are in STANDBY state.
77149
func (w *Workflow) ProcessCloseCallbacks(ctx chasm.MutableContext) error {

chasm/ms_pointer.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ package chasm
33
import (
44
"time"
55

6+
commonpb "go.temporal.io/api/common/v1"
67
enumspb "go.temporal.io/api/enums/v1"
78
historypb "go.temporal.io/api/history/v1"
89
"go.temporal.io/server/common/nexus/nexusrpc"
10+
"go.temporal.io/server/common/searchattribute/sadefs"
11+
"google.golang.org/protobuf/proto"
912
)
1013

1114
// MSPointer is a special CHASM type which components can use to access their Node's underlying backend (i.e. mutable
@@ -61,3 +64,24 @@ func (m MSPointer) GetWorkflowTypeName() string {
6164
func (m MSPointer) GetNexusUpdateCompletion(ctx Context, updateID string, requestID string) (nexusrpc.CompleteOperationOptions, error) {
6265
return m.backend.GetNexusUpdateCompletion(ctx.goContext(), updateID, requestID)
6366
}
67+
68+
// GetPredefinedSearchAttributes retrieves the predefined search attributes from the underlying mutable state.
69+
func (m MSPointer) GetPredefinedSearchAttributes() (map[string]VisibilityValue, error) {
70+
msSearchAttributes := m.backend.GetExecutionInfo().GetSearchAttributes()
71+
predefinedWithType := make(map[string]*commonpb.Payload)
72+
for saName, saPayload := range msSearchAttributes {
73+
if saType, ok := predefinedSearchAttributes[saName]; ok {
74+
if sadefs.GetMetadataType(saPayload) == enumspb.INDEXED_VALUE_TYPE_UNSPECIFIED {
75+
saPayload = proto.CloneOf(saPayload)
76+
sadefs.SetMetadataType(saPayload, saType)
77+
}
78+
predefinedWithType[saName] = saPayload
79+
}
80+
}
81+
saMap, err := newSearchAttributesMapFromProto(
82+
&commonpb.SearchAttributes{
83+
IndexedFields: predefinedWithType,
84+
},
85+
)
86+
return saMap.values, err
87+
}

chasm/node_backend_mock.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var _ NodeBackend = (*MockNodeBackend)(nil)
2222
// fields (thread-safe).
2323
type MockNodeBackend struct {
2424
// Optional function overrides. If nil, methods return zero-values.
25+
HandleGetExecutionStateUpdated func() bool
2526
HandleGetExecutionState func() *persistencespb.WorkflowExecutionState
2627
HandleGetExecutionInfo func() *persistencespb.WorkflowExecutionInfo
2728
HandleGetCurrentVersion func() int64
@@ -50,6 +51,13 @@ type MockNodeBackend struct {
5051
}
5152
}
5253

54+
func (m *MockNodeBackend) ExecutionStateUpdated() bool {
55+
if m.HandleGetExecutionStateUpdated != nil {
56+
return m.HandleGetExecutionStateUpdated()
57+
}
58+
return false
59+
}
60+
5361
func (m *MockNodeBackend) GetExecutionState() *persistencespb.WorkflowExecutionState {
5462
if m.HandleGetExecutionState != nil {
5563
return m.HandleGetExecutionState()

chasm/search_attribute.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import (
99
"go.temporal.io/server/common/searchattribute/sadefs"
1010
)
1111

12+
// Make a copy of predefined search attributes for CHASM internal usage.
13+
var predefinedSearchAttributes = sadefs.Predefined()
14+
1215
// CHASM Search Attribute User Guide:
1316
//
1417
// This contains CHASM search attribute field constants. These predefined fields correspond to the exact column name in Visibility storage.

chasm/tree.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ type (
198198
// where MutableState is defined.
199199
NodeBackend interface {
200200
// TODO: Add methods needed from MutateState here.
201+
ExecutionStateUpdated() bool
201202
GetExecutionState() *persistencespb.WorkflowExecutionState
202203
GetExecutionInfo() *persistencespb.WorkflowExecutionInfo
203204
GetApproximatePersistedSize() int
@@ -1521,7 +1522,7 @@ func (n *Node) CloseTransaction() (NodesMutation, error) {
15211522
return NodesMutation{}, err
15221523
}
15231524

1524-
if n.isActiveStateDirty {
1525+
if n.isActiveStateDirty || rootLifecycleChanged {
15251526
if err := n.closeTransactionForceUpdateVisibility(immutableContext, rootLifecycleChanged); err != nil {
15261527
return NodesMutation{}, err
15271528
}
@@ -1591,7 +1592,7 @@ func (n *Node) closeTransactionHandleRootLifecycleChange(
15911592
) (bool, error) {
15921593
if n.backend.IsWorkflow() {
15931594
// Workflow manages its lifecycle directly in mutable state.
1594-
return false, nil
1595+
return n.backend.ExecutionStateUpdated(), nil
15951596
}
15961597

15971598
if n.valueState != valueStateNeedSerialize {

service/history/interfaces/mutable_state.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ type (
178178
GetLastEventVersion() (int64, error)
179179
GetExecutionInfo() *persistencespb.WorkflowExecutionInfo
180180
GetExecutionState() *persistencespb.WorkflowExecutionState
181+
ExecutionStateUpdated() bool
181182
GetStartedWorkflowTask() *WorkflowTaskInfo
182183
GetPendingWorkflowTask() *WorkflowTaskInfo
183184
GetLastFirstEventIDTxnID() (int64, int64)

service/history/interfaces/mutable_state_mock.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

service/history/workflow/mutable_state_impl.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,10 @@ func (ms *MutableStateImpl) GetExecutionState() *persistencespb.WorkflowExecutio
11131113
return ms.executionState
11141114
}
11151115

1116+
func (ms *MutableStateImpl) ExecutionStateUpdated() bool {
1117+
return ms.stateInDB != ms.executionState.GetState()
1118+
}
1119+
11161120
func (ms *MutableStateImpl) FlushBufferedEvents() {
11171121
if ms.HasStartedWorkflowTask() {
11181122
return

0 commit comments

Comments
 (0)