diff --git a/common/persistence/visibility/store/elasticsearch/visibility_store.go b/common/persistence/visibility/store/elasticsearch/visibility_store.go index 97079623bd6..ca4c5fb08d2 100644 --- a/common/persistence/visibility/store/elasticsearch/visibility_store.go +++ b/common/persistence/visibility/store/elasticsearch/visibility_store.go @@ -953,10 +953,14 @@ func (s *visibilityStore) parseESDoc(hit *elastic.SearchHit, saTypeMap searchatt s.metricsClient.IncCounter(metrics.ElasticsearchVisibility, metrics.ElasticsearchDocumentParseFailuresCount) return nil, serviceerror.NewInternal(fmt.Sprintf("Unable to encode custom search attributes of Elasticsearch document(%s): %v", hit.Id, err)) } - err = searchattribute.ApplyAliases(s.searchAttributesMapper, record.SearchAttributes, namespace.String()) + aliasedSas, err := searchattribute.AliasFields(s.searchAttributesMapper, record.SearchAttributes, namespace.String()) if err != nil { return nil, err } + + if aliasedSas != nil { + record.SearchAttributes = aliasedSas + } } if memoEncoding != "" { diff --git a/common/searchattribute/mapper.go b/common/searchattribute/mapper.go index 53b1615ee9b..d77058d0357 100644 --- a/common/searchattribute/mapper.go +++ b/common/searchattribute/mapper.go @@ -41,13 +41,15 @@ type ( } ) -// ApplyAliases replaces field names with alias names for custom search attributes. -func ApplyAliases(mapper Mapper, searchAttributes *commonpb.SearchAttributes, namespace string) error { +// AliasFields returns SearchAttributes struct where each search attribute name is replaced with alias. +// If no replacement where made, it returns nil which means that original SearchAttributes struct should be used. +func AliasFields(mapper Mapper, searchAttributes *commonpb.SearchAttributes, namespace string) (*commonpb.SearchAttributes, error) { if len(searchAttributes.GetIndexedFields()) == 0 || mapper == nil { - return nil + return nil, nil } newIndexedFields := make(map[string]*commonpb.Payload, len(searchAttributes.GetIndexedFields())) + mapped := false for saName, saPayload := range searchAttributes.GetIndexedFields() { if !IsMappable(saName) { newIndexedFields[saName] = saPayload @@ -58,26 +60,34 @@ func ApplyAliases(mapper Mapper, searchAttributes *commonpb.SearchAttributes, na if err != nil { if _, isInvalidArgument := err.(*serviceerror.InvalidArgument); isInvalidArgument { // Silently ignore serviceerror.InvalidArgument because it indicates unmapped field (alias was deleted, for example). - // IMPORTANT: ApplyAliases should never return serviceerror.InvalidArgument because it is used by Poll API and the error + // IMPORTANT: AliasFields should never return serviceerror.InvalidArgument because it is used by Poll API and the error // goes through up to SDK, which shutdowns worker when it receives serviceerror.InvalidArgument as poll response. continue } - return err + return nil, err + } + if aliasName != saName { + mapped = true } newIndexedFields[aliasName] = saPayload } - searchAttributes.IndexedFields = newIndexedFields - return nil + // If no field name was mapped, return nil to save on clone operation on caller side. + if !mapped { + return nil, nil + } + return &commonpb.SearchAttributes{IndexedFields: newIndexedFields}, nil } -// SubstituteAliases replaces aliases with actual field names for custom search attributes. -func SubstituteAliases(mapper Mapper, searchAttributes *commonpb.SearchAttributes, namespace string) error { +// UnaliasFields returns SearchAttributes struct where each search attribute alias is replaced with field name. +// If no replacement where made, it returns nil which means that original SearchAttributes struct should be used. +func UnaliasFields(mapper Mapper, searchAttributes *commonpb.SearchAttributes, namespace string) (*commonpb.SearchAttributes, error) { if len(searchAttributes.GetIndexedFields()) == 0 || mapper == nil { - return nil + return nil, nil } newIndexedFields := make(map[string]*commonpb.Payload, len(searchAttributes.GetIndexedFields())) + mapped := false for saName, saPayload := range searchAttributes.GetIndexedFields() { if !IsMappable(saName) { newIndexedFields[saName] = saPayload @@ -86,11 +96,18 @@ func SubstituteAliases(mapper Mapper, searchAttributes *commonpb.SearchAttribute fieldName, err := mapper.GetFieldName(saName, namespace) if err != nil { - return err + return nil, err + } + if fieldName != saName { + mapped = true } newIndexedFields[fieldName] = saPayload } - searchAttributes.IndexedFields = newIndexedFields - return nil + // If no alias was mapped, return nil to save on clone operation on caller side. + if !mapped { + return nil, nil + } + + return &commonpb.SearchAttributes{IndexedFields: newIndexedFields}, nil } diff --git a/common/searchattribute/mapper_test.go b/common/searchattribute/mapper_test.go index 50b93824d30..519e9e11206 100644 --- a/common/searchattribute/mapper_test.go +++ b/common/searchattribute/mapper_test.go @@ -46,6 +46,10 @@ func (t *TestMapper) GetAlias(fieldName string, namespace string) (string, error if namespace == "error-namespace" { return "", serviceerror.NewInternal("mapper error") } else if namespace == "test-namespace" { + if fieldName == "pass-through" { + return fieldName, nil + } + return "alias_of_" + fieldName, nil } @@ -61,19 +65,22 @@ func (t *TestMapper) GetFieldName(alias string, namespace string) (string, error if namespace == "error-namespace" { return "", serviceerror.NewInternal("mapper error") } else if namespace == "test-namespace" { + if alias == "pass-through" { + return alias, nil + } return strings.TrimPrefix(alias, "alias_of_"), nil } return "", serviceerror.NewInvalidArgument("unknown namespace") } -func Test_ApplyAliases(t *testing.T) { +func Test_AliasFields(t *testing.T) { sa := &commonpb.SearchAttributes{ IndexedFields: map[string]*commonpb.Payload{ "field1": {Data: []byte("data1")}, "wrong_field": {Data: []byte("data23")}, // Wrong unknown name must be ignored. }, } - err := ApplyAliases(&TestMapper{}, sa, "error-namespace") + _, err := AliasFields(&TestMapper{}, sa, "error-namespace") assert.Error(t, err) var internalErr *serviceerror.Internal assert.ErrorAs(t, err, &internalErr) @@ -84,9 +91,9 @@ func Test_ApplyAliases(t *testing.T) { "wrong_field": {Data: []byte("data23")}, // Wrong unknown name must be ignored. }, } - err = ApplyAliases(&TestMapper{}, sa, "unknown-namespace") + sa, err = AliasFields(&TestMapper{}, sa, "unknown-namespace") assert.NoError(t, err) - assert.Len(t, sa.GetIndexedFields(), 0) + assert.Nil(t, sa) sa = &commonpb.SearchAttributes{ IndexedFields: map[string]*commonpb.Payload{ @@ -95,8 +102,9 @@ func Test_ApplyAliases(t *testing.T) { "wrong_field": {Data: []byte("data23")}, // Wrong unknown name must be ignored. }, } - err = ApplyAliases(&TestMapper{}, sa, "test-namespace") + sa, err = AliasFields(&TestMapper{}, sa, "test-namespace") assert.NoError(t, err) + assert.NotNil(t, sa) assert.Len(t, sa.GetIndexedFields(), 2) assert.EqualValues(t, "data1", sa.GetIndexedFields()["alias_of_field1"].GetData()) assert.EqualValues(t, "data2", sa.GetIndexedFields()["alias_of_field2"].GetData()) @@ -105,19 +113,31 @@ func Test_ApplyAliases(t *testing.T) { sa = &commonpb.SearchAttributes{ IndexedFields: nil, } - err = ApplyAliases(&TestMapper{}, sa, "error-namespace") + sa, err = AliasFields(&TestMapper{}, sa, "error-namespace") + assert.NoError(t, err) + assert.Nil(t, sa) + sa, err = AliasFields(&TestMapper{}, sa, "unknown-namespace") assert.NoError(t, err) - err = ApplyAliases(&TestMapper{}, sa, "unknown-namespace") + assert.Nil(t, sa) + + // Pass through search attributes are not mapped. + sa = &commonpb.SearchAttributes{ + IndexedFields: map[string]*commonpb.Payload{ + "pass-through": {Data: []byte("data1")}, + }, + } + sa, err = AliasFields(&TestMapper{}, sa, "test-namespace") assert.NoError(t, err) + assert.Nil(t, sa) } -func Test_SubstituteAliases(t *testing.T) { +func Test_UnaliasFields(t *testing.T) { sa := &commonpb.SearchAttributes{ IndexedFields: map[string]*commonpb.Payload{ "alias_of_field1": {Data: []byte("data1")}, }, } - err := SubstituteAliases(&TestMapper{}, sa, "error-namespace") + _, err := UnaliasFields(&TestMapper{}, sa, "error-namespace") assert.Error(t, err) var internalErr *serviceerror.Internal assert.ErrorAs(t, err, &internalErr) @@ -128,7 +148,7 @@ func Test_SubstituteAliases(t *testing.T) { "alias_of_field2": {Data: []byte("data2")}, }, } - err = SubstituteAliases(&TestMapper{}, sa, "unknown-namespace") + _, err = UnaliasFields(&TestMapper{}, sa, "unknown-namespace") assert.Error(t, err) var invalidArgumentErr *serviceerror.InvalidArgument assert.ErrorAs(t, err, &invalidArgumentErr) @@ -139,8 +159,9 @@ func Test_SubstituteAliases(t *testing.T) { "alias_of_field2": {Data: []byte("data2")}, }, } - err = SubstituteAliases(&TestMapper{}, sa, "test-namespace") + sa, err = UnaliasFields(&TestMapper{}, sa, "test-namespace") assert.NoError(t, err) + assert.NotNil(t, sa) assert.Len(t, sa.GetIndexedFields(), 2) assert.EqualValues(t, "data1", sa.GetIndexedFields()["field1"].GetData()) assert.EqualValues(t, "data2", sa.GetIndexedFields()["field2"].GetData()) @@ -152,7 +173,7 @@ func Test_SubstituteAliases(t *testing.T) { "wrong_alias": {Data: []byte("data3")}, }, } - err = SubstituteAliases(&TestMapper{}, sa, "test-namespace") + _, err = UnaliasFields(&TestMapper{}, sa, "test-namespace") assert.Error(t, err) assert.ErrorAs(t, err, &invalidArgumentErr) @@ -160,8 +181,20 @@ func Test_SubstituteAliases(t *testing.T) { sa = &commonpb.SearchAttributes{ IndexedFields: nil, } - err = SubstituteAliases(&TestMapper{}, sa, "error-namespace") + sa, err = UnaliasFields(&TestMapper{}, sa, "error-namespace") assert.NoError(t, err) - err = SubstituteAliases(&TestMapper{}, sa, "unknown-namespace") + assert.Nil(t, sa) + sa, err = UnaliasFields(&TestMapper{}, sa, "unknown-namespace") + assert.NoError(t, err) + assert.Nil(t, sa) + + // Pass through aliases are not substituted. + sa = &commonpb.SearchAttributes{ + IndexedFields: map[string]*commonpb.Payload{ + "pass-through": {Data: []byte("data1")}, + }, + } + sa, err = UnaliasFields(&TestMapper{}, sa, "test-namespace") assert.NoError(t, err) + assert.Nil(t, sa) } diff --git a/service/frontend/workflow_handler.go b/service/frontend/workflow_handler.go index 1676e59886b..4c4a62332a3 100644 --- a/service/frontend/workflow_handler.go +++ b/service/frontend/workflow_handler.go @@ -377,6 +377,10 @@ func (wh *WorkflowHandler) StartWorkflowExecution(ctx context.Context, request * return nil, errRequestIDTooLong } + if err := wh.validateSearchAttributes(request.GetSearchAttributes(), namespaceName); err != nil { + return nil, err + } + enums.SetDefaultWorkflowIdReusePolicy(&request.WorkflowIdReusePolicy) wh.logger.Debug("Start workflow execution request namespace.", tag.WorkflowNamespace(namespaceName.String())) @@ -386,7 +390,7 @@ func (wh *WorkflowHandler) StartWorkflowExecution(ctx context.Context, request * } wh.logger.Debug("Start workflow execution request namespaceID.", tag.WorkflowNamespaceID(namespaceID.String())) - err = wh.processIncomingSearchAttributes(request.GetSearchAttributes(), namespaceName) + request, err = wh.unaliasStartWorkflowExecutionRequestSearchAttributes(request, namespaceName) if err != nil { return nil, err } @@ -1997,6 +2001,10 @@ func (wh *WorkflowHandler) SignalWithStartWorkflowExecution(ctx context.Context, return nil, err } + if err := wh.validateSearchAttributes(request.GetSearchAttributes(), namespaceName); err != nil { + return nil, err + } + enums.SetDefaultWorkflowIdReusePolicy(&request.WorkflowIdReusePolicy) namespaceID, err := wh.namespaceRegistry.GetNamespaceID(namespaceName) @@ -2004,7 +2012,7 @@ func (wh *WorkflowHandler) SignalWithStartWorkflowExecution(ctx context.Context, return nil, err } - err = wh.processIncomingSearchAttributes(request.GetSearchAttributes(), namespaceName) + request, err = wh.unaliasSignalWithStartWorkflowExecutionRequestSearchAttributes(request, namespaceName) if err != nil { return nil, err } @@ -2749,10 +2757,13 @@ func (wh *WorkflowHandler) DescribeWorkflowExecution(ctx context.Context, reques return nil, serviceerror.NewUnavailable(fmt.Sprintf(errUnableToGetSearchAttributesMessage, err)) } searchattribute.ApplyTypeMap(response.GetWorkflowExecutionInfo().GetSearchAttributes(), saTypeMap) - err = searchattribute.ApplyAliases(wh.saMapper, response.GetWorkflowExecutionInfo().GetSearchAttributes(), request.GetNamespace()) + aliasedSas, err := searchattribute.AliasFields(wh.saMapper, response.GetWorkflowExecutionInfo().GetSearchAttributes(), request.GetNamespace()) if err != nil { return nil, err } + if aliasedSas != nil { + response.GetWorkflowExecutionInfo().SearchAttributes = aliasedSas + } } return &workflowservice.DescribeWorkflowExecutionResponse{ @@ -2924,7 +2935,7 @@ func (wh *WorkflowHandler) CreateSchedule(ctx context.Context, request *workflow return nil, err } - err = wh.processIncomingSearchAttributes(request.GetSearchAttributes(), namespaceName) + err = wh.validateSearchAttributes(request.GetSearchAttributes(), namespaceName) if err != nil { return nil, err } @@ -2933,6 +2944,11 @@ func (wh *WorkflowHandler) CreateSchedule(ctx context.Context, request *workflow return nil, err } + request, err = wh.unaliasCreateScheduleRequestSearchAttributes(request, namespaceName) + if err != nil { + return nil, err + } + // size limits will be validated on history. note that the start workflow request is // embedded in the schedule, which is in the scheduler input. so if the scheduler itself // doesn't exceed the limit, the started workflows should be safe as well. @@ -3021,9 +3037,7 @@ func (wh *WorkflowHandler) validateStartWorkflowArgsForSchedule( return errIDReusePolicyNotAllowed } - // map search attributes to aliases here, since we don't go through the frontend when starting later - err := wh.processIncomingSearchAttributes(startWorkflow.GetSearchAttributes(), namespaceName) - if err != nil { + if err := wh.validateSearchAttributes(startWorkflow.GetSearchAttributes(), namespaceName); err != nil { return err } @@ -3074,15 +3088,19 @@ func (wh *WorkflowHandler) DescribeSchedule(ctx context.Context, request *workfl } // map search attributes - if sa := executionInfo.GetSearchAttributes(); sa != nil { + if sas := executionInfo.GetSearchAttributes(); sas != nil { saTypeMap, err := wh.saProvider.GetSearchAttributes(wh.config.ESIndexName, false) if err != nil { return nil, serviceerror.NewUnavailable(fmt.Sprintf(errUnableToGetSearchAttributesMessage, err)) } - searchattribute.ApplyTypeMap(sa, saTypeMap) - if err = searchattribute.ApplyAliases(wh.saMapper, sa, request.GetNamespace()); err != nil { + searchattribute.ApplyTypeMap(sas, saTypeMap) + aliasedSas, err := searchattribute.AliasFields(wh.saMapper, sas, request.GetNamespace()) + if err != nil { return nil, err } + if aliasedSas != nil { + executionInfo.SearchAttributes = aliasedSas + } } // then query to get current state from the workflow itself @@ -3117,9 +3135,13 @@ func (wh *WorkflowHandler) DescribeSchedule(ctx context.Context, request *workfl return serviceerror.NewUnavailable(fmt.Sprintf(errUnableToGetSearchAttributesMessage, err)) } searchattribute.ApplyTypeMap(sa, saTypeMap) - if err = searchattribute.ApplyAliases(wh.saMapper, sa, request.Namespace); err != nil { + aliasedSas, err := searchattribute.AliasFields(wh.saMapper, sa, request.Namespace) + if err != nil { return err } + if aliasedSas != nil { + response.Schedule.Action.GetStartWorkflow().SearchAttributes = aliasedSas + } } // for all running workflows started by the schedule, we should check that they're @@ -3237,6 +3259,11 @@ func (wh *WorkflowHandler) UpdateSchedule(ctx context.Context, request *workflow return nil, err } + request, err = wh.unaliasUpdateScheduleRequestStartWorkflowSearchAttributes(request, namespaceName) + if err != nil { + return nil, err + } + input := &schedspb.FullUpdateRequest{ Schedule: request.Schedule, } @@ -4144,17 +4171,20 @@ func (wh *WorkflowHandler) processOutgoingSearchAttributes(events []*historypb.H } if searchAttributes != nil { searchattribute.ApplyTypeMap(searchAttributes, saTypeMap) - err = searchattribute.ApplyAliases(wh.saMapper, searchAttributes, namespace.String()) + aliasedSas, err := searchattribute.AliasFields(wh.saMapper, searchAttributes, namespace.String()) if err != nil { return err } + if aliasedSas != nil { + searchAttributes.IndexedFields = aliasedSas.IndexedFields + } } } return nil } -func (wh *WorkflowHandler) processIncomingSearchAttributes(searchAttributes *commonpb.SearchAttributes, namespaceName namespace.Name) error { +func (wh *WorkflowHandler) validateSearchAttributes(searchAttributes *commonpb.SearchAttributes, namespaceName namespace.Name) error { // Validate search attributes before substitution because in case of error, error message should contain alias but not field name. if err := wh.saValidator.Validate(searchAttributes, namespaceName.String(), wh.config.ESIndexName); err != nil { return err @@ -4162,9 +4192,6 @@ func (wh *WorkflowHandler) processIncomingSearchAttributes(searchAttributes *com if err := wh.saValidator.ValidateSize(searchAttributes, namespaceName.String()); err != nil { return err } - if err := searchattribute.SubstituteAliases(wh.saMapper, searchAttributes, namespaceName.String()); err != nil { - return err - } return nil } @@ -4740,3 +4767,95 @@ func getBatchOperationState(workflowState enumspb.WorkflowExecutionStatus) enums } return operationState } + +func (wh *WorkflowHandler) unaliasStartWorkflowExecutionRequestSearchAttributes(request *workflowservice.StartWorkflowExecutionRequest, namespaceName namespace.Name) (*workflowservice.StartWorkflowExecutionRequest, error) { + unaliasedSas, err := searchattribute.UnaliasFields(wh.saMapper, request.GetSearchAttributes(), namespaceName.String()) + if err != nil { + return nil, err + } + if unaliasedSas == nil { + return request, nil + } + + // Shallow copy request and replace SearchAttributes fields only. + newRequest := *request + newRequest.SearchAttributes = unaliasedSas + return &newRequest, nil +} + +func (wh *WorkflowHandler) unaliasSignalWithStartWorkflowExecutionRequestSearchAttributes(request *workflowservice.SignalWithStartWorkflowExecutionRequest, namespaceName namespace.Name) (*workflowservice.SignalWithStartWorkflowExecutionRequest, error) { + unaliasedSas, err := searchattribute.UnaliasFields(wh.saMapper, request.GetSearchAttributes(), namespaceName.String()) + if err != nil { + return nil, err + } + if unaliasedSas == nil { + return request, nil + } + + // Shallow copy request and replace SearchAttributes fields only. + newRequest := *request + newRequest.SearchAttributes = unaliasedSas + return &newRequest, nil +} + +func (wh *WorkflowHandler) unaliasCreateScheduleRequestSearchAttributes(request *workflowservice.CreateScheduleRequest, namespaceName namespace.Name) (*workflowservice.CreateScheduleRequest, error) { + unaliasedSas, err := searchattribute.UnaliasFields(wh.saMapper, request.GetSearchAttributes(), namespaceName.String()) + if err != nil { + return nil, err + } + + startWorkflow := request.GetSchedule().GetAction().GetStartWorkflow() + unaliasedStartWorkflowSas, err := searchattribute.UnaliasFields(wh.saMapper, startWorkflow.GetSearchAttributes(), namespaceName.String()) + if err != nil { + return nil, err + } + + if unaliasedSas == nil && unaliasedStartWorkflowSas == nil { + return request, nil + } + + // Shallow copy request and replace SearchAttributes fields only. + newRequest := *request + + if unaliasedSas != nil { + newRequest.SearchAttributes = unaliasedSas + } + + if unaliasedStartWorkflowSas != nil && startWorkflow != nil { + newStartWorkflow := *startWorkflow + newStartWorkflow.SearchAttributes = unaliasedStartWorkflowSas + newSchedule := *request.GetSchedule() + newSchedule.Action = &schedpb.ScheduleAction{ + Action: &schedpb.ScheduleAction_StartWorkflow{ + StartWorkflow: &newStartWorkflow, + }} + newRequest.Schedule = &newSchedule + } + + return &newRequest, nil +} + +func (wh *WorkflowHandler) unaliasUpdateScheduleRequestStartWorkflowSearchAttributes(request *workflowservice.UpdateScheduleRequest, namespaceName namespace.Name) (*workflowservice.UpdateScheduleRequest, error) { + startWorkflow := request.GetSchedule().GetAction().GetStartWorkflow() + if startWorkflow == nil { + return request, nil + } + + unaliasedSas, err := searchattribute.UnaliasFields(wh.saMapper, startWorkflow.GetSearchAttributes(), namespaceName.String()) + if err != nil { + return nil, err + } + if unaliasedSas == nil { + return request, nil + } + newStartWorkflow := *startWorkflow + newStartWorkflow.SearchAttributes = unaliasedSas + newSchedule := *request.GetSchedule() + newSchedule.Action = &schedpb.ScheduleAction{ + Action: &schedpb.ScheduleAction_StartWorkflow{ + StartWorkflow: &newStartWorkflow, + }} + newRequest := *request + newRequest.Schedule = &newSchedule + return &newRequest, nil +} diff --git a/service/history/workflowTaskHandler.go b/service/history/workflowTaskHandler.go index b3e6a5d9fa4..7354485cb8d 100644 --- a/service/history/workflowTaskHandler.go +++ b/service/history/workflowTaskHandler.go @@ -813,10 +813,16 @@ func (handler *workflowTaskHandlerImpl) handleCommandContinueAsNewWorkflow( return err } - if err := searchattribute.SubstituteAliases(handler.searchAttributesMapper, attr.GetSearchAttributes(), namespaceName.String()); err != nil { + unaliasedSas, err := searchattribute.UnaliasFields(handler.searchAttributesMapper, attr.GetSearchAttributes(), namespaceName.String()) + if err != nil { handler.stopProcessing = true return err } + if unaliasedSas != nil { + newAttr := *attr + newAttr.SearchAttributes = unaliasedSas + attr = &newAttr + } // If the workflow task has more than one completion event than just pick the first one if !handler.mutableState.IsWorkflowExecutionRunning() { @@ -928,10 +934,16 @@ func (handler *workflowTaskHandlerImpl) handleCommandStartChildWorkflow( return err } - if err := searchattribute.SubstituteAliases(handler.searchAttributesMapper, attr.GetSearchAttributes(), targetNamespace.String()); err != nil { + unaliasedSas, err := searchattribute.UnaliasFields(handler.searchAttributesMapper, attr.GetSearchAttributes(), targetNamespace.String()) + if err != nil { handler.stopProcessing = true return err } + if unaliasedSas != nil { + newAttr := *attr + newAttr.SearchAttributes = unaliasedSas + attr = &newAttr + } enabled := handler.config.EnableParentClosePolicy(parentNamespace.String()) if enabled { @@ -1056,10 +1068,16 @@ func (handler *workflowTaskHandlerImpl) handleCommandUpsertWorkflowSearchAttribu return err } - if err := searchattribute.SubstituteAliases(handler.searchAttributesMapper, attr.GetSearchAttributes(), namespace.String()); err != nil { + unaliasedSas, err := searchattribute.UnaliasFields(handler.searchAttributesMapper, attr.GetSearchAttributes(), namespace.String()) + if err != nil { handler.stopProcessing = true return err } + if unaliasedSas != nil { + newAttr := *attr + newAttr.SearchAttributes = unaliasedSas + attr = &newAttr + } _, err = handler.mutableState.AddUpsertWorkflowSearchAttributesEvent( handler.workflowTaskCompletedID, attr,