go.temporal.io/server@v1.23.0/common/tqname/tqname.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 tqname 26 27 import ( 28 "errors" 29 "fmt" 30 "strconv" 31 "strings" 32 ) 33 34 const ( 35 // mangledTaskQueuePrefix is the prefix for all mangled task queue names. 36 mangledTaskQueuePrefix = "/_sys/" 37 38 suffixDelimiter = "/" 39 versionSetDelimiter = ":" 40 ) 41 42 type ( 43 // Name encapsulates all the name mangling we do for task queues. 44 // 45 // Users work with "high-level task queues" and can give them arbitrary names (except for 46 // our prefix). 47 // 48 // Each high-level task queue corresponds to one or more "low-level task queues", each of 49 // which has a distinct task queue manager in memory in matching service, as well as a 50 // distinct identity in persistence. 51 // 52 // There are two pieces of identifying information for low-level task queues: a partition 53 // index, and a version set identifier. All low-level task queues have a partition index, 54 // which may be 0. Partition 0 is called the "root". The version set identifier is 55 // optional: task queues with it are called "versioned" and task queues without it are 56 // called "unversioned". 57 // 58 // All versioned low-level task queues use mangled names. All unversioned low-level task 59 // queues with non-zero partition also use mangled names. 60 // 61 // Mangled names look like this: 62 // 63 // /_sys/<base name>/[<version set id>:]<partition id> 64 // 65 // The partition id is required, and the version set id is optional. If present, it is 66 // separated from the partition id by a colon. This scheme lets users use anything they 67 // like for a base name, except for strings starting with "/_sys/", without ambiguity. 68 // 69 // For backward compatibility, unversioned low-level task queues with partition 0 do not 70 // use mangled names, they use the bare base name. 71 Name struct { 72 baseName string // base name of the task queue as specified by user 73 partition int // partition of task queue 74 versionSet string // version set id 75 } 76 ) 77 78 var ( 79 ErrNoParent = errors.New("root task queue partition has no parent") 80 ErrInvalidDegree = errors.New("invalid task queue partition branching degree") 81 ) 82 83 // Parse takes a mangled low-level task queue name and returns a Name. Returns an error if the 84 // given name is not a valid mangled name. 85 func Parse(name string) (Name, error) { 86 baseName := name 87 partition := 0 88 versionSet := "" 89 90 if strings.HasPrefix(name, mangledTaskQueuePrefix) { 91 suffixOff := strings.LastIndex(name, suffixDelimiter) 92 if suffixOff <= len(mangledTaskQueuePrefix) { 93 return Name{}, fmt.Errorf("invalid task queue name %q", name) 94 } 95 baseName = name[len(mangledTaskQueuePrefix):suffixOff] 96 97 suffix := name[suffixOff+1:] 98 if partitionOff := strings.LastIndex(suffix, versionSetDelimiter); partitionOff == 0 { 99 return Name{}, fmt.Errorf("invalid task queue name %q", name) 100 } else if partitionOff > 0 { 101 // pull out version set 102 versionSet, suffix = suffix[:partitionOff], suffix[partitionOff+1:] 103 } 104 105 var err error 106 partition, err = strconv.Atoi(suffix) 107 if err != nil || partition < 0 || partition == 0 && len(versionSet) == 0 { 108 return Name{}, fmt.Errorf("invalid task queue name %q", name) 109 } 110 } 111 112 return Name{ 113 baseName: baseName, 114 partition: partition, 115 versionSet: versionSet, 116 }, nil 117 } 118 119 // FromBaseName takes a base name and returns a Name. Returns an error if name looks like a 120 // mangled name. 121 func FromBaseName(name string) (Name, error) { 122 if strings.HasPrefix(name, mangledTaskQueuePrefix) { 123 return Name{}, fmt.Errorf("base name %q must not have prefix %q", name, mangledTaskQueuePrefix) 124 } 125 return Name{baseName: name}, nil 126 } 127 128 // IsRoot returns true if this task queue is a root partition. 129 func (tn Name) IsRoot() bool { 130 return tn.partition == 0 131 } 132 133 // WithPartition returns a new Name with the same base and version set but a different partition. 134 func (tn Name) WithPartition(partition int) Name { 135 nn := tn 136 nn.partition = partition 137 return nn 138 } 139 140 // Root is shorthand for WithPartition(0). 141 func (tn Name) Root() Name { 142 return tn.WithPartition(0) 143 } 144 145 // WithVersionSet returns a new Name with the same base and partition but a different version set. 146 func (tn Name) WithVersionSet(versionSet string) Name { 147 nn := tn 148 nn.versionSet = versionSet 149 return nn 150 } 151 152 // BaseNameString returns the base name for a task queue. This should be used when looking up 153 // settings in dynamic config, and pretty much nowhere else. To get the name of the root 154 // partition, use tn.Root().FullName(). 155 func (tn Name) BaseNameString() string { 156 return tn.baseName 157 } 158 159 // Partition returns the partition number for a task queue. 160 func (tn Name) Partition() int { 161 return tn.partition 162 } 163 164 // VersionSet returns the version set for a task queue. 165 func (tn Name) VersionSet() string { 166 return tn.versionSet 167 } 168 169 // Parent returns a Name for the parent partition, using the given branching degree. 170 func (tn Name) Parent(degree int) (Name, error) { 171 if tn.IsRoot() { 172 return Name{}, ErrNoParent 173 } else if degree < 1 { 174 return Name{}, ErrInvalidDegree 175 } 176 parent := (tn.partition+degree-1)/degree - 1 177 return tn.WithPartition(parent), nil 178 } 179 180 // FullName returns the mangled name of the task queue, to be used in RPCs and persistence. 181 func (tn Name) FullName() string { 182 if len(tn.versionSet) == 0 { 183 if tn.partition == 0 { 184 return tn.baseName 185 } 186 return fmt.Sprintf("%s%s%s%d", mangledTaskQueuePrefix, tn.baseName, suffixDelimiter, tn.partition) 187 } 188 // versioned always use prefix 189 return fmt.Sprintf("%s%s%s%s%s%d", mangledTaskQueuePrefix, tn.baseName, suffixDelimiter, tn.versionSet, versionSetDelimiter, tn.partition) 190 }