github.com/xmidt-org/webpa-common@v1.11.9/service/accessor.go (about) 1 package service 2 3 import ( 4 "errors" 5 "fmt" 6 "math/rand" 7 "sync" 8 ) 9 10 var ( 11 errNoInstances = errors.New("There are no instances available") 12 errNoFailOvers = errors.New("no failover instances available") 13 errFailOversFailed = errors.New("failovers could not find an instance") 14 ) 15 16 // Accessor holds a hash of server nodes. 17 type Accessor interface { 18 // Get fetches the server node associated with a particular key. 19 Get(key []byte) (string, error) 20 } 21 22 // AccessorFunc is a function type that implements Accessor 23 type AccessorFunc func([]byte) (string, error) 24 25 func (af AccessorFunc) Get(key []byte) (string, error) { 26 return af(key) 27 } 28 29 type emptyAccessor struct{} 30 31 func (ea emptyAccessor) Get([]byte) (string, error) { 32 return "", errNoInstances 33 } 34 35 // EmptyAccessor returns an Accessor that always returns an error from Get. 36 func EmptyAccessor() Accessor { 37 return emptyAccessor{} 38 } 39 40 // MapAccessor is a static Accessor that honors a set of known keys. Any other key 41 // will result in an error. Mostly useful for testing. 42 type MapAccessor map[string]string 43 44 func (ma MapAccessor) Get(key []byte) (string, error) { 45 if v, ok := ma[string(key)]; ok { 46 return v, nil 47 } else { 48 return "", fmt.Errorf("No such key: %s", string(key)) 49 } 50 } 51 52 // UpdatableAccessor is an Accessor whose contents can be mutated safely under concurrency. 53 // The zero value of this struct is a valid Accessor initialized with no instances. Get will 54 // return an error until there is an update with at least (1) instance. 55 type UpdatableAccessor struct { 56 lock sync.RWMutex 57 58 err error 59 current Accessor 60 } 61 62 // Get hashes the key against the current set of instances to select an instance consistently. 63 // This method will return an error if this instance isn't updated yet or has been updated with 64 // no instances. 65 func (ua *UpdatableAccessor) Get(key []byte) (instance string, err error) { 66 ua.lock.RLock() 67 68 switch { 69 case ua.err != nil: 70 err = ua.err 71 72 case ua.current != nil: 73 instance, err = ua.current.Get(key) 74 75 default: 76 err = errNoInstances 77 } 78 79 ua.lock.RUnlock() 80 return 81 } 82 83 // SetError clears the instances being used by this instance and sets the error to be returned 84 // by Get with every call. This error will be returned by Get until an update with one or more instances 85 // occurs. 86 func (ua *UpdatableAccessor) SetError(err error) { 87 ua.lock.Lock() 88 ua.err = err 89 ua.current = nil 90 ua.lock.Unlock() 91 } 92 93 // SetInstances changes the instances used by this UpdateAccessor, clearing any error. Note that Get will 94 // still return an error if a is nil or empty. 95 func (ua *UpdatableAccessor) SetInstances(a Accessor) { 96 ua.lock.Lock() 97 ua.err = nil 98 ua.current = a 99 ua.lock.Unlock() 100 } 101 102 // Update sets both the instances and the Get error in a single, atomic call. 103 func (ua *UpdatableAccessor) Update(a Accessor, err error) { 104 ua.lock.Lock() 105 ua.err = err 106 ua.current = a 107 ua.lock.Unlock() 108 } 109 110 type RouteTraffic interface { 111 Route(instance string) error 112 } 113 114 type emptyRouter struct { 115 } 116 117 func (r emptyRouter) Route(instance string) error { 118 return nil 119 } 120 121 func DefaultTrafficRouter() RouteTraffic { 122 return emptyRouter{} 123 } 124 125 type AccessorQueue interface { 126 Order([]string) []string 127 } 128 129 type emptyQueue struct { 130 r *rand.Rand 131 } 132 133 func DefaultOrder() AccessorQueue { 134 return emptyQueue{} 135 } 136 137 func (r emptyQueue) Order(keys []string) []string { 138 return keys 139 } 140 141 type AccessorValue struct { 142 Accessor Accessor 143 Err error 144 } 145 type LayeredAccessor interface { 146 Accessor 147 148 SetError(err error) 149 SetPrimary(a Accessor) 150 UpdatePrimary(a Accessor, err error) 151 SetFailOver(failover map[string]AccessorValue) 152 UpdateFailOver(key string, a Accessor, err error) 153 } 154 155 type layeredAccessor struct { 156 router RouteTraffic 157 accessorQueue AccessorQueue 158 159 err error 160 primary Accessor 161 failover map[string]AccessorValue 162 163 lock sync.RWMutex 164 } 165 166 func NewLayeredAccesor(router RouteTraffic, chooser AccessorQueue) LayeredAccessor { 167 return &layeredAccessor{ 168 router: router, 169 accessorQueue: chooser, 170 failover: make(map[string]AccessorValue), 171 } 172 } 173 174 // SetError clears the instances being used by this instance and sets the error to be returned 175 // by Get with every call. This error will be returned by Get until an update with one or more instances 176 // occurs. 177 func (la *layeredAccessor) SetError(err error) { 178 la.lock.Lock() 179 la.err = err 180 la.primary = nil 181 la.lock.Unlock() 182 } 183 184 // SetPrimary changes the instances used by this UpdateAccessor, clearing any error. Note that Get will 185 // still return an error if a is nil or empty. 186 func (la *layeredAccessor) SetPrimary(a Accessor) { 187 la.lock.Lock() 188 la.err = nil 189 la.primary = a 190 la.lock.Unlock() 191 } 192 193 // SetPrimary changes the instances used by this UpdateAccessor, clearing any error. Note that Get will 194 // still return an error if a is nil or empty. 195 func (la *layeredAccessor) SetFailOver(failover map[string]AccessorValue) { 196 la.lock.Lock() 197 la.failover = failover 198 la.lock.Unlock() 199 } 200 201 // Update sets both the instances and the Get error in a single, atomic call. 202 func (la *layeredAccessor) UpdatePrimary(a Accessor, err error) { 203 la.lock.Lock() 204 la.err = err 205 la.primary = a 206 la.lock.Unlock() 207 } 208 209 // Update sets the instances, failovers and the Get error in a single, atomic call. 210 func (la *layeredAccessor) UpdateFailOver(key string, a Accessor, err error) { 211 la.lock.Lock() 212 if la.failover == nil { 213 la.failover = make(map[string]AccessorValue) 214 } 215 216 la.failover[key] = AccessorValue{a, err} 217 la.lock.Unlock() 218 } 219 220 // Get hashes the key against the current set of instances to select an instance consistently. 221 // This method will return an error if this instance isn't updated yet or has been updated with 222 // no instances. 223 func (la *layeredAccessor) Get(key []byte) (string, error) { 224 var instance string 225 var err error 226 la.lock.RLock() 227 228 routeErr := RouteError{} 229 230 switch { 231 case la.err != nil: 232 routeErr.addError(la.err) 233 instance, err = la.getFailOverInstance(key) 234 routeErr.addError(err) 235 236 case la.primary != nil: 237 instance, err = la.primary.Get(key) 238 239 if err != nil { 240 routeErr.addError(err) 241 instance, err = la.getFailOverInstance(key) 242 routeErr.addError(err) 243 } 244 245 if err := la.router.Route(instance); err != nil { 246 routeErr.addError(err) 247 tempInstance, err := la.getFailOverInstance(key) 248 if err != nil { 249 routeErr.addError(err) 250 } else { 251 instance = tempInstance 252 } 253 } 254 case la.failover != nil && len(la.failover) > 0: 255 instance, err = la.getFailOverInstance(key) 256 routeErr.addError(err) 257 default: 258 routeErr.addError(errNoInstances) 259 } 260 261 la.lock.RUnlock() 262 if routeErr.ErrChain.Empty() { 263 return instance, nil 264 } 265 routeErr.Instance = instance 266 return instance, routeErr 267 } 268 269 func (la *layeredAccessor) getFailOverInstance(key []byte) (instance string, err error) { 270 if la.failover == nil || len(la.failover) == 0 { 271 return "", errNoFailOvers 272 } 273 274 var order []string 275 dcs := make([]string, len(la.failover)) 276 index := 0 277 for dc := range la.failover { 278 dcs[index] = dc 279 index++ 280 } 281 if la.accessorQueue != nil { 282 order = la.accessorQueue.Order(dcs) 283 } else { 284 order = dcs 285 } 286 287 for _, dc := range order { 288 err = la.failover[dc].Err 289 if err != nil { 290 continue 291 } 292 instance, err = la.failover[dc].Accessor.Get(key) 293 if la.router == nil && err == nil { 294 return 295 } else if la.router != nil { 296 if tempErr := la.router.Route(instance); tempErr == nil { 297 return 298 } 299 } 300 301 } 302 return "", errFailOversFailed 303 } 304 305 type ErrorChain struct { 306 SubError error 307 Err error 308 } 309 310 func (err ErrorChain) Error() string { 311 if err.SubError == nil { 312 return err.Err.Error() 313 } 314 if err.Err == nil { 315 panic("main Err can't be nil") 316 } 317 return fmt.Sprintf("%s(%s)", err.Err, err.SubError) 318 } 319 320 func (err ErrorChain) Empty() bool { 321 return err.Err == nil && err.SubError == nil 322 } 323 324 type RouteError struct { 325 ErrChain ErrorChain 326 Instance string 327 } 328 329 func (err *RouteError) addError(e error) { 330 if e != nil { 331 if err.ErrChain.Empty() { 332 err.ErrChain = ErrorChain{Err: e} 333 } else { 334 err.ErrChain = ErrorChain{Err: e, SubError: err.ErrChain} 335 } 336 } 337 } 338 339 func (err RouteError) Error() string { 340 return fmt.Sprintf("failed to route `%s`. reason: %s", err.Instance, err.ErrChain.Error()) 341 }