go.temporal.io/server/chasm/registrable_component.go

233 LOC · 91 covered · 142 uncovered · 38 ranges · 22043 concepts · 24 introducers · 10883 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 package chasm
2
3 import (
4 "fmt"
5 "maps"
6 "reflect"
7
8 "github.com/dgryski/go-farm"
9 enumspb "go.temporal.io/api/enums/v1"
10 "go.temporal.io/server/common/searchattribute/sadefs"
11 )
12
13 type (
14 RegistrableComponent struct {
15 componentType string
16 goType reflect.Type
17
18 // Following three fields are initialized when the component is registered to a library.
19 library namer
20 componentID uint32
21 fqn string
22
23 ephemeral bool
24 singleCluster bool
25 detached bool
26
27 searchAttributesMapper *VisibilitySearchAttributesMapper
28
29 contextValues map[any]any
30 }
31
32 RegistrableComponentOption func(*RegistrableComponent)
33 )
34
35 func NewRegistrableComponent[C Component](
36 componentType string,
37 opts ...RegistrableComponentOption,
38 > ) *RegistrableComponent { registrable_component.go ×1
39 > rc := &RegistrableComponent{
40 > componentType: componentType,
41 > goType: reflect.TypeFor[C](),
42 > }
43 > for _, opt := range opts {
45 > }
47 }
48
49 func WithEphemeral() RegistrableComponentOption {
50 return func(rc *RegistrableComponent) {
51 rc.ephemeral = true
52 }
53 }
54
55 // Is there any use case where we don't want to replicate certain instances of a archetype?
56 func WithSingleCluster() RegistrableComponentOption {
57 return func(rc *RegistrableComponent) {
58 rc.singleCluster = true
59 }
60 }
61
62 // WithDetached marks the registrable component as detached. Detached components ignore
63 // parent lifecycle validation, allowing them to continue operating when their
64 // parent is closed/terminated.
65 // If a registrable component is not detached by default, a component definition
66 // can specify its child as detached via ComponentFieldDetached() option.
67 > func WithDetached() RegistrableComponentOption { registrable_component.go ×1
68 > return func(rc *RegistrableComponent) {
69 > rc.detached = true
70 > }
71 }
72
73 // IsDetached returns true if the component type is registered as detached.
74 > func (rc *RegistrableComponent) IsDetached() bool { registrable_component.go ×1
75 > return rc.detached
76 > }
77
78 // WithBusinessIDAlias allows specifying the business ID alias of the component.
79 // This option must be specified if the archetype uses the Visibility component.
80 func WithBusinessIDAlias(
81 alias string,
82 > ) RegistrableComponentOption { registrable_component.go ×5
83 > return func(rc *RegistrableComponent) {
84 > if rc.searchAttributesMapper == nil {
85 > rc.searchAttributesMapper = newVisibilitySearchAttributesMapper() registrable_component.go ×1
86 > }
87 > if _, ok := rc.searchAttributesMapper.aliasToField[alias]; ok { registrable_component.go ×5
88 //nolint:forbidigo
89 panic(fmt.Sprintf("registrable component validation error: business ID alias %q is already defined as a search attribute", alias))
90 }
91 > if _, ok := rc.searchAttributesMapper.systemAliasToField[alias]; ok { registrable_component.go ×5
92 //nolint:forbidigo
93 panic(fmt.Sprintf("registrable component validation error: business ID alias %q is already defined as a system search attribute", alias))
94 }
95 > rc.searchAttributesMapper.systemAliasToField[alias] = sadefs.WorkflowID registrable_component.go ×5
96 > rc.searchAttributesMapper.fieldToAlias[sadefs.WorkflowID] = alias
97 > rc.searchAttributesMapper.saTypeMap[sadefs.WorkflowID] = enumspb.INDEXED_VALUE_TYPE_KEYWORD
98 }
99 }
100
101 func WithSearchAttributes(
102 searchAttributes ...SearchAttribute,
103 > ) RegistrableComponentOption { registrable_component.go ×3
104 > return func(rc *RegistrableComponent) {
105 > if len(searchAttributes) == 0 {
106 return
107 }
108
109 > if rc.searchAttributesMapper == nil { registrable_component.go ×3
110 > rc.searchAttributesMapper = newVisibilitySearchAttributesMapper() registrable_component.go ×1
111 > }
112
113 > for _, sa := range searchAttributes { registrable_component.go ×3
114 > alias := sa.definition().alias
115 > field := sa.definition().field
116 > valueType := sa.definition().valueType
117 >
118 > // An identity-mapped system search attribute (alias == field, e.g. TaskQueue,
119 > // ExecutionTime) overrides that system column directly, so it is recorded only in
120 > // overriddenSystemFields; queries resolve via the system column.
121 > if field == alias && sadefs.IsSystem(field) {
122 > if !sadefs.IsChasmOverridableSystem(field) { registrable_component.go ×3
123 //nolint:forbidigo
124 panic(fmt.Sprintf("registrable component validation error: system search attribute %q cannot be overridden by a CHASM component", field))
125 }
126 > if _, ok := rc.searchAttributesMapper.overriddenSystemFields[field]; ok { registrable_component.go ×3
127 //nolint:forbidigo
128 panic(fmt.Sprintf("registrable component validation error: system search attribute override %q is already defined", field))
129 }
130 > rc.searchAttributesMapper.overriddenSystemFields[field] = valueType registrable_component.go ×3
131 > continue
132 }
133
134 > if sadefs.IsChasmSystem(alias) { registrable_component.go ×1
135 > //nolint:forbidigo registrable_component.go ×1
136 > panic(fmt.Sprintf("registrable component validation error: CHASM search attribute alias %q is a CHASM system search attribute", alias))
137 }
138 > if !sadefs.IsSystem(alias) && sadefs.IsReserved(alias) { registrable_component.go ×5
139 //nolint:forbidigo
140 panic(fmt.Sprintf("registrable component validation error: CHASM search attribute alias %q is a reserved search attribute", alias))
141 }
142
143 > if _, ok := rc.searchAttributesMapper.systemAliasToField[alias]; ok { registrable_component.go ×5
144 //nolint:forbidigo
145 panic(fmt.Sprintf("registrable component validation error: CHASM search attribute alias %q is already defined as a system search attribute alias", alias))
146 }
147 > if _, ok := rc.searchAttributesMapper.aliasToField[alias]; ok { registrable_component.go ×5
148 > //nolint:forbidigo registrable_component.go ×1
149 > panic(fmt.Sprintf("registrable component validation error: search attribute alias %q is already defined", alias))
150 }
151 > if _, ok := rc.searchAttributesMapper.fieldToAlias[field]; ok { registrable_component.go ×5
152 > //nolint:forbidigo registrable_component.go ×1
153 > panic(fmt.Sprintf("registrable component validation error: search attribute field %q is already defined", field))
154 }
155
156 > rc.searchAttributesMapper.aliasToField[alias] = field registrable_component.go ×5
157 > rc.searchAttributesMapper.fieldToAlias[field] = alias
158 > rc.searchAttributesMapper.saTypeMap[field] = valueType
159 }
160 }
161 }
162
163 // WithContextValues allows specifying key-value pairs that will be available in the Context
164 // via the Value() method whenever the chasm framework starts, updates, reads, polls, executes or
165 // validates tasks on a component.
166 //
167 // This is useful for propagating values needed for those processing logic but are not avaiable via the
168 // component's struct definition, such as configurations.
169 //
170 // Keys need to be globally unique across components. Conflicting keys across will cause component registration to fail.
171 //
172 // Manually added key-value pairs via ContextWithValue() will take precedence over registered context values.
173 func WithContextValues(
174 keyVals map[any]any,
175 > ) RegistrableComponentOption { registry.go ×2
176 > return func(rc *RegistrableComponent) {
177 > if rc.contextValues == nil {
178 > rc.contextValues = make(map[any]any, len(keyVals))
179 > }
180 > maps.Copy(rc.contextValues, keyVals)
181 }
182 }
183
184 func (rc *RegistrableComponent) registerToLibrary(
185 library namer,
186 > ) (string, uint32, error) { registry.go ×6
187 > if rc.library != nil {
188 > return "", 0, fmt.Errorf("component %s is already registered in library %s", rc.componentType, rc.library.Name()) registrable_component.go ×1
189 > }
190
191 > rc.library = library registry.go ×6
192 > rc.fqn = FullyQualifiedName(rc.library.Name(), rc.componentType)
193 > rc.componentID = GenerateTypeID(rc.fqn)
194 > return rc.fqn, rc.componentID, nil
195 }
196
197 // SearchAttributesMapper returns the search attributes mapper for this component.
198 > func (rc *RegistrableComponent) SearchAttributesMapper() *VisibilitySearchAttributesMapper { registrable_component.go ×1
199 > return rc.searchAttributesMapper
200 > }
201
202 // GenerateTypeID generates a unique 32-bit identifier from a fully qualified name (FQN).
203 // The generated ID is used to uniquely identify components and tasks within the CHASM framework. The same FQN will
204 // always produce the same ID.
205 > func GenerateTypeID(fqn string) uint32 { search_attribute.go ×12
206 > return farm.Fingerprint32([]byte(fqn))
207 > }
208
209 // hasBusinessIDAlias returns true if the component has a businessID alias configured
210 // via WithBusinessIDAlias option.
211 > func (rc *RegistrableComponent) hasBusinessIDAlias() bool { registrable_component.go ×1
212 > if rc.searchAttributesMapper == nil {
213 > return false registrable_component.go ×1
214 > }
215 > _, ok := rc.searchAttributesMapper.fieldToAlias[sadefs.WorkflowID] registrable_component.go ×5
216 > return ok
217 }
218
219 // GoType returns the reflect.Type of the component's Go struct.
220 > func (rc *RegistrableComponent) GoType() reflect.Type { chasm_decoder.go ×13
221 > return rc.goType
222 > }
223
224 // fqType returns the fully qualified name of the component, which is a combination of
225 // the library name and the component type. This is used to uniquely identify
226 // the component in the registry.
227 > func (rc *RegistrableComponent) fqType() string { registrable_component.go ×2
228 > if rc.fqn == "" {
229 // this should never happen because the component is only accessible from the library.
230 panic("component is not registered to a library")
231 }
232 > return rc.fqn registrable_component.go ×2
233 }