-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Add implementation of CHASM List/Count Runs #8662
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
common/persistence/visibility/store/elasticsearch/visibility_store.go
Outdated
Show resolved
Hide resolved
common/persistence/visibility/store/elasticsearch/visibility_store.go
Outdated
Show resolved
Hide resolved
457d6b9 to
e63973b
Compare
1b7b1d1 to
db42bca
Compare
c71cd57 to
6d0b6fa
Compare
chasm/visibility.go
Outdated
| UserMemoPrefix = "__user__" | ||
| ChasmMemoPrefix = "__chasm__" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| UserMemoPrefix = "__user__" | |
| ChasmMemoPrefix = "__chasm__" | |
| UserMemoKey = "__user__" | |
| ChasmMemoKey = "__chasm__" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please change the var names.
74fae89 to
23c1374
Compare
bergundy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will let others review the implementation but the API LGTM.
Just please document all of the exported functions. CHASM will be used by a lot of developers and we want to help people out as much as possible.
| NamespaceID string | ||
| NamespaceName string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why we need to take in both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NamespaceID is what we store in persistence, and NamespaceName is needed for dynamic config and custom search attributes mapper.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interesting that matching needs visibility manager as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scheduler has some constraints in their query, so it needs the visibility manager to build the query converter IIRC.
| return fqn, ok | ||
| } | ||
|
|
||
| // ComponentByID returns the registrable component for a given archetype ID. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
plz also comment that those methods should not be used by chasm library developer. and also for RegistrableComponent.SearchAttributesMapper()
we probably need to create a separate interface for developer facing registry later.
| if ok { | ||
| newMemo := memoProvider.Memo(immutableContext) | ||
| if !maps.EqualFunc(n.currentMemo, newMemo, isVisibilityValueEqual) { | ||
| if !proto.Equal(n.currentMemo, newMemo) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm I think now that we are using proto.Message we no longer have guarantee that there's no error upon encoding in task executor, but guess that should never happen.
Something for later, since the currentMemo/SA here are always consistent with CHASM tree state, in visibility task executor we actually don't have to invoke SA/Memo provider again, we can just serialize & buffer the value here (and now we can also return error upon closeTransaction if it can't be encoded), and use it in the vis task executor.
| Count int64 | ||
| } | ||
|
|
||
| ChasmExecutionInfo struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that persistence can depend on chasm package, for those new struct definitions, is it possible to reuse the corresponding definitions in chasm package and thus avoiding the conversion between the different struct definition of essentially the same thing in chasm_engine.go?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can definitely reuse the return struct between the vis manager and ListExecutions in chasm_engine.go, i'm currently keeping separate request types since there is a difference when calling vis manager (archetypeType in engine.go vs archetypeID in vis manager)
| InitialFailoverVersion: resp.GetInitialFailoverVersion(), | ||
| IsGlobalNamespaceEnabled: resp.GetIsGlobalNamespaceEnabled(), | ||
| IsConnectionEnabled: request.GetEnableRemoteClusterConnection(), | ||
| IsReplicationEnabled: request.GetIsReplicationEnabled(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, thanks for catching this
| var userMemo *commonpb.Memo | ||
| var chasmMemoPayload *commonpb.Payload | ||
|
|
||
| // Check if archetype matches to decide how to split memo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm why not just check if __chasm__ is a key in the combinedMemo?
if we are worrying about potential conflict (though I feel that chance is very low...), I'd probably just check if TemporalNamespaceDivision string is a number or not (just the first char should be enough), since we are going to use this method to query across archetypes, e.g. in namespace migration case, I just want all visibility records.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I'm concerned about potential conflict (although very low).
I'm ok with checking if TemporalNamespaceDivision is a number.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved to just checking if number. We can check the first char if we think that optimization is needed, just thought checking the entire string might be more clear.
chasm/visibility.go
Outdated
| UserMemoPrefix = "__user__" | ||
| ChasmMemoPrefix = "__chasm__" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please change the var names.
chasm/search_attribute.go
Outdated
| } | ||
|
|
||
| // GetBool returns the boolean value for a given SearchAttributeBool. If not found or map is nil, second parameter is false. | ||
| func (m SearchAttributesMap) GetBool(sa SearchAttributeBool) (bool, bool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can probably use generics here and have just a single Get method.
It would require defining another TypedSearchAttribute[T] interface with a single typeMarker(T) method and making sure that SearchAttributeBool and friend implement that interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea, i like this approach, making a change for this
chasm/search_attribute.go
Outdated
| } | ||
|
|
||
| alias := sa.definition().alias | ||
| boolValue, ok := m.values[alias].(VisibilityValueBool) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should document this behavior. I'm also on the fence on whether we should error out here or panic or just return false.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Current behavior silently returns not found if any type checks fail. Added documentation for the runtime check before casting, returning a zero val and false if unable to cast successfully.
For the first cast/check to a VisibilityValue, the case this fails is when a Developer doesn't register search attributes and has duplicate aliases.
Second cast from VisibilityValue to underlying value would fail if search attribute author (me) did incorrect mappings between VisibilityValue and its type.
Unit tests are able to catch cases for the Second cast. Ideally, functional tests should be able to cover the first case? I'm not sure if there's any benefit to panicking, but I can see if we threw errors, we could also get different handling of cases, etc.
Let me know your thoughts.
cda223e to
33a8e9c
Compare
9ba9870 to
197cbf8
Compare
197cbf8 to
65b4f4b
Compare
|
|
||
| typedSearchAttribute[T any] interface { | ||
| SearchAttribute | ||
| typeMarker(T) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes the mapping function more concise? Otherwise we need to switch on each visibility type to its underlying primitive right
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also makes it possible to specify the correct type in the method instead of needing to return any
chasm/search_attribute.go
Outdated
| if reflect.TypeOf(visibilityValue) != sa.markedVisibilityType() { | ||
| return zero, false | ||
| } | ||
|
|
||
| reflectVal := reflect.ValueOf(visibilityValue) | ||
| targetType := reflect.TypeFor[T]() | ||
|
|
||
| if !reflectVal.Type().ConvertibleTo(targetType) { | ||
| return zero, false | ||
| } | ||
|
|
||
| result, ok := reflectVal.Convert(targetType).Interface().(T) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think lines L465-474 are unnecessary. You could simply do result, ok := visibilityValue.(T). If ok is true, then it good, otherwise, it's not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea good point lol
| if sadefs.IsChasmSearchAttribute(fieldName) { | ||
| fieldType, err = vi.chasmMapper.ValueType(fieldName) | ||
| if err != nil { | ||
| return nil, query.NewConverterError("invalid search attribute: %s", name) | ||
| } | ||
| } else { | ||
| fieldType, err = vi.searchAttributesTypeMap.GetType(fieldName) | ||
| if err != nil { | ||
| return nil, query.NewConverterError("invalid search attribute: %s", name) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if sadefs.IsChasmSearchAttribute(fieldName) { | |
| fieldType, err = vi.chasmMapper.ValueType(fieldName) | |
| if err != nil { | |
| return nil, query.NewConverterError("invalid search attribute: %s", name) | |
| } | |
| } else { | |
| fieldType, err = vi.searchAttributesTypeMap.GetType(fieldName) | |
| if err != nil { | |
| return nil, query.NewConverterError("invalid search attribute: %s", name) | |
| } | |
| } | |
| if sadefs.IsChasmSearchAttribute(fieldName) { | |
| fieldType, err = vi.chasmMapper.ValueType(fieldName) | |
| } else { | |
| fieldType, err = vi.searchAttributesTypeMap.GetType(fieldName) | |
| } | |
| if err != nil { | |
| return nil, query.NewConverterError("invalid search attribute: %s", name) | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
original suggestion was supposed to combine type maps, but got overridden while going through merge conflicts, changed to
combinedTypeMap := make(map[string]enumspb.IndexedValueType)
maps.Copy(combinedTypeMap, vi.searchAttributesTypeMap.Custom())
maps.Copy(combinedTypeMap, vi.chasmMapper.SATypeMap())
finalTypeMap := searchattribute.NewNameTypeMap(combinedTypeMap)
fyi keeping the combined type map initialization on separate line since there were potential panics if either custom or chasm search attribute type map is nil.
common/persistence/visibility/store/elasticsearch/visibility_store.go
Outdated
Show resolved
Hide resolved
| t.logger.Warn("Failed to get field name for alias, ignoring search attribute", tag.NewStringTag("alias", alias), tag.Error(err)) | ||
| continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should still return an error. Why are you changing it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Original thread: #8558 (comment).
The original context was about deregistering search attributes while the task is getting processed, which would lead to task failure.
After looking at this a bit more, any upsert SA task execution would still fail when generating the ES Doc and attempting to decode the value, if the search attribute has been deregistered.
In the SQL implementation though, since these fields are constant and always attached to the NameTypeMap/ClusterMetadata, Upserting these fields would succeed, so there is a discrepancy there.
I think we should not be throwing an error here though. Assuming we want parity with Upserting search attributes for workflows, once we have parity between ES and SQL for preallocated columns, we should not throw an error if a search attribute gets deregistered when executing the CHASM vis task, and just do a best effort upsert call. Otherwise, this task would get retried and sent to DLQ if I understand correctly.
What changed?
Add implementation of CHASM List/Count Runs. Given a CHASM archetype and supported MemoType, a Visibility Query can be ran against both custom and CHASM search attributes. Both operations wrap around the Visibility Manager methods to List/Count Workflows.
In the initial handler of the query in visibility_store.go, the query converter is passed the chasm VisibilitySearchAttributesMapper and the archetypeID. When resolving query parameters, the order of resolving aliases to field names is Custom -> CHASM -> System/Predefined attribute.
Removes chasm transient dependency on persistence package
Fixes visibility task executor to emit Warning Log instead of returning error when custom search attribute is not found. (Edge case when search attributes get deregistered between task creation and execution)
Why?
Required to support CHASM Visibility and Component authors.
How did you test it?