go.temporal.io/server@v1.23.0/common/namespace/namespace.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package namespace 26 27 import ( 28 "fmt" 29 "time" 30 31 "github.com/google/uuid" 32 33 "golang.org/x/exp/maps" 34 35 enumspb "go.temporal.io/api/enums/v1" 36 namespacepb "go.temporal.io/api/namespace/v1" 37 "go.temporal.io/api/serviceerror" 38 "go.temporal.io/server/api/adminservice/v1" 39 persistencespb "go.temporal.io/server/api/persistence/v1" 40 "go.temporal.io/server/common" 41 "go.temporal.io/server/common/persistence" 42 "go.temporal.io/server/common/util" 43 ) 44 45 type ( 46 // Mutation changes a Namespace "in-flight" during a Clone operation. 47 Mutation interface { 48 apply(*persistence.GetNamespaceResponse) 49 } 50 51 // BadBinaryError is an error type carrying additional information about 52 // when/why/who configured a given checksum as being bad. 53 BadBinaryError struct { 54 cksum string 55 info *namespacepb.BadBinaryInfo 56 } 57 58 // ID is the unique identifier type for a Namespace. 59 ID string 60 61 // Name is a user-supplied nickname for a Namespace. 62 Name string 63 64 // Namespaces is a *Namespace slice 65 Namespaces []*Namespace 66 67 // Namespace contains the info and config for a namespace 68 Namespace struct { 69 info *persistencespb.NamespaceInfo 70 config *persistencespb.NamespaceConfig 71 replicationConfig *persistencespb.NamespaceReplicationConfig 72 configVersion int64 73 failoverVersion int64 74 isGlobalNamespace bool 75 failoverNotificationVersion int64 76 notificationVersion int64 77 78 customSearchAttributesMapper CustomSearchAttributesMapper 79 } 80 81 CustomSearchAttributesMapper struct { 82 fieldToAlias map[string]string 83 aliasToField map[string]string 84 } 85 ) 86 87 const ( 88 EmptyName Name = "" 89 EmptyID ID = "" 90 ) 91 92 func NewID() ID { 93 return ID(uuid.NewString()) 94 } 95 96 func FromPersistentState(record *persistence.GetNamespaceResponse) *Namespace { 97 return &Namespace{ 98 info: record.Namespace.Info, 99 config: record.Namespace.Config, 100 replicationConfig: record.Namespace.ReplicationConfig, 101 configVersion: record.Namespace.ConfigVersion, 102 failoverVersion: record.Namespace.FailoverVersion, 103 isGlobalNamespace: record.IsGlobalNamespace, 104 failoverNotificationVersion: record.Namespace.FailoverNotificationVersion, 105 notificationVersion: record.NotificationVersion, 106 customSearchAttributesMapper: CustomSearchAttributesMapper{ 107 fieldToAlias: record.Namespace.Config.CustomSearchAttributeAliases, 108 aliasToField: util.InverseMap(record.Namespace.Config.CustomSearchAttributeAliases), 109 }, 110 } 111 } 112 113 func FromAdminClientApiResponse(response *adminservice.GetNamespaceResponse) *Namespace { 114 info := &persistencespb.NamespaceInfo{ 115 Id: response.GetInfo().GetId(), 116 Name: response.GetInfo().GetName(), 117 State: response.GetInfo().GetState(), 118 Description: response.GetInfo().GetDescription(), 119 Owner: response.GetInfo().GetOwnerEmail(), 120 Data: response.GetInfo().GetData(), 121 } 122 config := &persistencespb.NamespaceConfig{ 123 Retention: response.GetConfig().GetWorkflowExecutionRetentionTtl(), 124 HistoryArchivalState: response.GetConfig().GetHistoryArchivalState(), 125 HistoryArchivalUri: response.GetConfig().GetHistoryArchivalUri(), 126 VisibilityArchivalState: response.GetConfig().GetVisibilityArchivalState(), 127 VisibilityArchivalUri: response.GetConfig().GetVisibilityArchivalUri(), 128 CustomSearchAttributeAliases: response.GetConfig().GetCustomSearchAttributeAliases(), 129 } 130 replicationConfig := &persistencespb.NamespaceReplicationConfig{ 131 ActiveClusterName: response.GetReplicationConfig().GetActiveClusterName(), 132 State: response.GetReplicationConfig().GetState(), 133 Clusters: ConvertClusterReplicationConfigFromProto(response.GetReplicationConfig().GetClusters()), 134 FailoverHistory: convertFailoverHistoryToPersistenceProto(response.GetFailoverHistory()), 135 } 136 return &Namespace{ 137 info: info, 138 config: config, 139 replicationConfig: replicationConfig, 140 configVersion: response.GetConfigVersion(), 141 failoverVersion: response.GetFailoverVersion(), 142 isGlobalNamespace: response.GetIsGlobalNamespace(), 143 } 144 } 145 146 func (ns *Namespace) Clone(ms ...Mutation) *Namespace { 147 newns := *ns 148 r := persistence.GetNamespaceResponse{ 149 Namespace: &persistencespb.NamespaceDetail{ 150 Info: common.CloneProto(newns.info), 151 Config: common.CloneProto(newns.config), 152 ReplicationConfig: common.CloneProto(newns.replicationConfig), 153 ConfigVersion: newns.configVersion, 154 FailoverNotificationVersion: newns.failoverNotificationVersion, 155 FailoverVersion: newns.failoverVersion, 156 }, 157 IsGlobalNamespace: newns.isGlobalNamespace, 158 NotificationVersion: newns.notificationVersion, 159 } 160 for _, m := range ms { 161 m.apply(&r) 162 } 163 return FromPersistentState(&r) 164 } 165 166 // VisibilityArchivalState observes the visibility archive configuration (state 167 // and URI) for this namespace. 168 func (ns *Namespace) VisibilityArchivalState() ArchivalConfigState { 169 return ArchivalConfigState{ 170 State: ns.config.VisibilityArchivalState, 171 URI: ns.config.VisibilityArchivalUri, 172 } 173 } 174 175 // HistoryArchivalState observes the history archive configuration (state and 176 // URI) for this namespace. 177 func (ns *Namespace) HistoryArchivalState() ArchivalConfigState { 178 return ArchivalConfigState{ 179 State: ns.config.HistoryArchivalState, 180 URI: ns.config.HistoryArchivalUri, 181 } 182 } 183 184 // VerifyBinaryChecksum returns an error if the provided checksum is one of this 185 // namespace's configured bad binary checksums. The returned error (if any) will 186 // be unwrappable as BadBinaryError. 187 func (ns *Namespace) VerifyBinaryChecksum(cksum string) error { 188 badBinMap := ns.config.GetBadBinaries().GetBinaries() 189 if badBinMap == nil { 190 return nil 191 } 192 if info, ok := badBinMap[cksum]; ok { 193 return BadBinaryError{cksum: cksum, info: info} 194 } 195 return nil 196 } 197 198 // ID observes this namespace's permanent unique identifier in string form. 199 func (ns *Namespace) ID() ID { 200 if ns.info == nil { 201 return ID("") 202 } 203 return ID(ns.info.Id) 204 } 205 206 // Name observes this namespace's configured name. 207 func (ns *Namespace) Name() Name { 208 if ns.info == nil { 209 return Name("") 210 } 211 return Name(ns.info.Name) 212 } 213 214 func (ns *Namespace) State() enumspb.NamespaceState { 215 if ns.replicationConfig == nil { 216 return enumspb.NAMESPACE_STATE_UNSPECIFIED 217 } 218 return ns.info.State 219 } 220 221 func (ns *Namespace) ReplicationState() enumspb.ReplicationState { 222 if ns.replicationConfig == nil { 223 return enumspb.REPLICATION_STATE_UNSPECIFIED 224 } 225 return ns.replicationConfig.State 226 } 227 228 // ActiveClusterName observes the name of the cluster that is currently active 229 // for this namspace. 230 func (ns *Namespace) ActiveClusterName() string { 231 if ns.replicationConfig == nil { 232 return "" 233 } 234 return ns.replicationConfig.ActiveClusterName 235 } 236 237 // ClusterNames observes the names of the clusters to which this namespace is 238 // replicated. 239 func (ns *Namespace) ClusterNames() []string { 240 // copy slice to preserve immutability 241 out := make([]string, len(ns.replicationConfig.Clusters)) 242 copy(out, ns.replicationConfig.Clusters) 243 return out 244 } 245 246 // IsOnCluster returns true is namespace is registered on cluster otherwise false. 247 func (ns *Namespace) IsOnCluster(clusterName string) bool { 248 for _, namespaceCluster := range ns.replicationConfig.Clusters { 249 if namespaceCluster == clusterName { 250 return true 251 } 252 } 253 return false 254 } 255 256 // ConfigVersion return the namespace config version 257 func (ns *Namespace) ConfigVersion() int64 { 258 return ns.configVersion 259 } 260 261 // FailoverVersion return the namespace failover version 262 func (ns *Namespace) FailoverVersion() int64 { 263 return ns.failoverVersion 264 } 265 266 // IsGlobalNamespace returns whether the namespace is a global namespace. 267 // Being a global namespace doesn't necessarily mean that there are multiple registered clusters for it, only that it 268 // has a failover version. To determine whether operations should be replicated for a namespace, see ReplicationPolicy. 269 func (ns *Namespace) IsGlobalNamespace() bool { 270 return ns.isGlobalNamespace 271 } 272 273 // FailoverNotificationVersion return the global notification version of when failover happened 274 func (ns *Namespace) FailoverNotificationVersion() int64 { 275 return ns.failoverNotificationVersion 276 } 277 278 // NotificationVersion return the global notification version of when namespace changed 279 func (ns *Namespace) NotificationVersion() int64 { 280 return ns.notificationVersion 281 } 282 283 // ActiveInCluster returns whether the namespace is active, i.e. non global 284 // namespace or global namespace which active cluster is the provided cluster 285 func (ns *Namespace) ActiveInCluster(clusterName string) bool { 286 if !ns.isGlobalNamespace { 287 // namespace is not a global namespace, meaning namespace is always 288 // "active" within each cluster 289 return true 290 } 291 return clusterName == ns.ActiveClusterName() 292 } 293 294 // ReplicationPolicy return the derived workflow replication policy 295 func (ns *Namespace) ReplicationPolicy() ReplicationPolicy { 296 // frontend guarantee that the clusters always contains the active 297 // namespace, so if the # of clusters is 1 then we do not need to send out 298 // any events for replication 299 if ns.isGlobalNamespace && len(ns.replicationConfig.Clusters) > 1 { 300 return ReplicationPolicyMultiCluster 301 } 302 return ReplicationPolicyOneCluster 303 } 304 305 func (ns *Namespace) GetCustomData(key string) string { 306 if ns.info.Data == nil { 307 return "" 308 } 309 return ns.info.Data[key] 310 } 311 312 // Retention returns retention duration for this namespace. 313 func (ns *Namespace) Retention() time.Duration { 314 if ns.config.Retention == nil { 315 return 0 316 } 317 318 return ns.config.Retention.AsDuration() 319 } 320 321 func (ns *Namespace) CustomSearchAttributesMapper() CustomSearchAttributesMapper { 322 return ns.customSearchAttributesMapper 323 } 324 325 // Error returns the reason associated with this bad binary. 326 func (e BadBinaryError) Error() string { 327 return e.info.Reason 328 } 329 330 // Reason returns the reason associated with this bad binary. 331 func (e BadBinaryError) Reason() string { 332 return e.info.Reason 333 } 334 335 // Operator returns the operator associated with this bad binary. 336 func (e BadBinaryError) Operator() string { 337 return e.info.Operator 338 } 339 340 // Created returns the time at which this bad binary was declared to be bad. 341 func (e BadBinaryError) Created() time.Time { 342 return e.info.CreateTime.AsTime() 343 } 344 345 // Checksum observes the binary checksum that caused this error. 346 func (e BadBinaryError) Checksum() string { 347 return e.cksum 348 } 349 350 func (id ID) String() string { 351 return string(id) 352 } 353 354 func (id ID) IsEmpty() bool { 355 return id == EmptyID 356 } 357 358 func (n Name) String() string { 359 return string(n) 360 } 361 362 func (n Name) IsEmpty() bool { 363 return n == EmptyName 364 } 365 366 func (m *CustomSearchAttributesMapper) GetAlias(fieldName string, namespace string) (string, error) { 367 alias, ok := m.fieldToAlias[fieldName] 368 if !ok { 369 return "", serviceerror.NewInvalidArgument( 370 fmt.Sprintf("Namespace %s has no mapping defined for field name %s", namespace, fieldName), 371 ) 372 } 373 return alias, nil 374 } 375 376 func (m *CustomSearchAttributesMapper) GetFieldName(alias string, namespace string) (string, error) { 377 fieldName, ok := m.aliasToField[alias] 378 if !ok { 379 return "", serviceerror.NewInvalidArgument( 380 fmt.Sprintf("Namespace %s has no mapping defined for search attribute %s", namespace, alias), 381 ) 382 } 383 return fieldName, nil 384 } 385 386 func (m *CustomSearchAttributesMapper) FieldToAliasMap() map[string]string { 387 return maps.Clone(m.fieldToAlias) 388 }