github.com/micro/go-micro/v2@v2.9.1/registry/cache/cache.go (about) 1 // Package cache provides a registry cache 2 package cache 3 4 import ( 5 "math" 6 "math/rand" 7 "sync" 8 "time" 9 10 "github.com/micro/go-micro/v2/logger" 11 "github.com/micro/go-micro/v2/registry" 12 util "github.com/micro/go-micro/v2/util/registry" 13 ) 14 15 // Cache is the registry cache interface 16 type Cache interface { 17 // embed the registry interface 18 registry.Registry 19 // stop the cache watcher 20 Stop() 21 } 22 23 type Options struct { 24 // TTL is the cache TTL 25 TTL time.Duration 26 } 27 28 type Option func(o *Options) 29 30 type cache struct { 31 registry.Registry 32 opts Options 33 34 // registry cache 35 sync.RWMutex 36 cache map[string][]*registry.Service 37 ttls map[string]time.Time 38 watched map[string]bool 39 40 // used to stop the cache 41 exit chan bool 42 43 // indicate whether its running 44 running bool 45 // status of the registry 46 // used to hold onto the cache 47 // in failure state 48 status error 49 } 50 51 var ( 52 DefaultTTL = time.Minute 53 ) 54 55 func backoff(attempts int) time.Duration { 56 if attempts == 0 { 57 return time.Duration(0) 58 } 59 return time.Duration(math.Pow(10, float64(attempts))) * time.Millisecond 60 } 61 62 func (c *cache) getStatus() error { 63 c.RLock() 64 defer c.RUnlock() 65 return c.status 66 } 67 68 func (c *cache) setStatus(err error) { 69 c.Lock() 70 c.status = err 71 c.Unlock() 72 } 73 74 // isValid checks if the service is valid 75 func (c *cache) isValid(services []*registry.Service, ttl time.Time) bool { 76 // no services exist 77 if len(services) == 0 { 78 return false 79 } 80 81 // ttl is invalid 82 if ttl.IsZero() { 83 return false 84 } 85 86 // time since ttl is longer than timeout 87 if time.Since(ttl) > 0 { 88 return false 89 } 90 91 // ok 92 return true 93 } 94 95 func (c *cache) quit() bool { 96 select { 97 case <-c.exit: 98 return true 99 default: 100 return false 101 } 102 } 103 104 func (c *cache) del(service string) { 105 // don't blow away cache in error state 106 if err := c.status; err != nil { 107 return 108 } 109 // otherwise delete entries 110 delete(c.cache, service) 111 delete(c.ttls, service) 112 } 113 114 func (c *cache) get(service string) ([]*registry.Service, error) { 115 // read lock 116 c.RLock() 117 118 // check the cache first 119 services := c.cache[service] 120 // get cache ttl 121 ttl := c.ttls[service] 122 // make a copy 123 cp := util.Copy(services) 124 125 // got services && within ttl so return cache 126 if c.isValid(cp, ttl) { 127 c.RUnlock() 128 // return services 129 return cp, nil 130 } 131 132 // get does the actual request for a service and cache it 133 get := func(service string, cached []*registry.Service) ([]*registry.Service, error) { 134 // ask the registry 135 services, err := c.Registry.GetService(service) 136 if err != nil { 137 // check the cache 138 if len(cached) > 0 { 139 // set the error status 140 c.setStatus(err) 141 142 // return the stale cache 143 return cached, nil 144 } 145 // otherwise return error 146 return nil, err 147 } 148 149 // reset the status 150 if err := c.getStatus(); err != nil { 151 c.setStatus(nil) 152 } 153 154 // cache results 155 c.Lock() 156 c.set(service, util.Copy(services)) 157 c.Unlock() 158 159 return services, nil 160 } 161 162 // watch service if not watched 163 _, ok := c.watched[service] 164 165 // unlock the read lock 166 c.RUnlock() 167 168 // check if its being watched 169 if !ok { 170 c.Lock() 171 172 // set to watched 173 c.watched[service] = true 174 175 // only kick it off if not running 176 if !c.running { 177 go c.run() 178 } 179 180 c.Unlock() 181 } 182 183 // get and return services 184 return get(service, cp) 185 } 186 187 func (c *cache) set(service string, services []*registry.Service) { 188 c.cache[service] = services 189 c.ttls[service] = time.Now().Add(c.opts.TTL) 190 } 191 192 func (c *cache) update(res *registry.Result) { 193 if res == nil || res.Service == nil { 194 return 195 } 196 197 c.Lock() 198 defer c.Unlock() 199 200 // only save watched services 201 if _, ok := c.watched[res.Service.Name]; !ok { 202 return 203 } 204 205 services, ok := c.cache[res.Service.Name] 206 if !ok { 207 // we're not going to cache anything 208 // unless there was already a lookup 209 return 210 } 211 212 if len(res.Service.Nodes) == 0 { 213 switch res.Action { 214 case "delete": 215 c.del(res.Service.Name) 216 } 217 return 218 } 219 220 // existing service found 221 var service *registry.Service 222 var index int 223 for i, s := range services { 224 if s.Version == res.Service.Version { 225 service = s 226 index = i 227 } 228 } 229 230 switch res.Action { 231 case "create", "update": 232 if service == nil { 233 c.set(res.Service.Name, append(services, res.Service)) 234 return 235 } 236 237 // append old nodes to new service 238 for _, cur := range service.Nodes { 239 var seen bool 240 for _, node := range res.Service.Nodes { 241 if cur.Id == node.Id { 242 seen = true 243 break 244 } 245 } 246 if !seen { 247 res.Service.Nodes = append(res.Service.Nodes, cur) 248 } 249 } 250 251 services[index] = res.Service 252 c.set(res.Service.Name, services) 253 case "delete": 254 if service == nil { 255 return 256 } 257 258 var nodes []*registry.Node 259 260 // filter cur nodes to remove the dead one 261 for _, cur := range service.Nodes { 262 var seen bool 263 for _, del := range res.Service.Nodes { 264 if del.Id == cur.Id { 265 seen = true 266 break 267 } 268 } 269 if !seen { 270 nodes = append(nodes, cur) 271 } 272 } 273 274 // still got nodes, save and return 275 if len(nodes) > 0 { 276 service.Nodes = nodes 277 services[index] = service 278 c.set(service.Name, services) 279 return 280 } 281 282 // zero nodes left 283 284 // only have one thing to delete 285 // nuke the thing 286 if len(services) == 1 { 287 c.del(service.Name) 288 return 289 } 290 291 // still have more than 1 service 292 // check the version and keep what we know 293 var srvs []*registry.Service 294 for _, s := range services { 295 if s.Version != service.Version { 296 srvs = append(srvs, s) 297 } 298 } 299 300 // save 301 c.set(service.Name, srvs) 302 } 303 } 304 305 // run starts the cache watcher loop 306 // it creates a new watcher if there's a problem 307 func (c *cache) run() { 308 c.Lock() 309 c.running = true 310 c.Unlock() 311 312 // reset watcher on exit 313 defer func() { 314 c.Lock() 315 c.watched = make(map[string]bool) 316 c.running = false 317 c.Unlock() 318 }() 319 320 var a, b int 321 322 for { 323 // exit early if already dead 324 if c.quit() { 325 return 326 } 327 328 // jitter before starting 329 j := rand.Int63n(100) 330 time.Sleep(time.Duration(j) * time.Millisecond) 331 332 // create new watcher 333 w, err := c.Registry.Watch() 334 if err != nil { 335 if c.quit() { 336 return 337 } 338 339 d := backoff(a) 340 c.setStatus(err) 341 342 if a > 3 { 343 if logger.V(logger.DebugLevel, logger.DefaultLogger) { 344 logger.Debug("rcache: ", err, " backing off ", d) 345 } 346 a = 0 347 } 348 349 time.Sleep(d) 350 a++ 351 352 continue 353 } 354 355 // reset a 356 a = 0 357 358 // watch for events 359 if err := c.watch(w); err != nil { 360 if c.quit() { 361 return 362 } 363 364 d := backoff(b) 365 c.setStatus(err) 366 367 if b > 3 { 368 if logger.V(logger.DebugLevel, logger.DefaultLogger) { 369 logger.Debug("rcache: ", err, " backing off ", d) 370 } 371 b = 0 372 } 373 374 time.Sleep(d) 375 b++ 376 377 continue 378 } 379 380 // reset b 381 b = 0 382 } 383 } 384 385 // watch loops the next event and calls update 386 // it returns if there's an error 387 func (c *cache) watch(w registry.Watcher) error { 388 // used to stop the watch 389 stop := make(chan bool) 390 391 // manage this loop 392 go func() { 393 defer w.Stop() 394 395 select { 396 // wait for exit 397 case <-c.exit: 398 return 399 // we've been stopped 400 case <-stop: 401 return 402 } 403 }() 404 405 for { 406 res, err := w.Next() 407 if err != nil { 408 close(stop) 409 return err 410 } 411 412 // reset the error status since we succeeded 413 if err := c.getStatus(); err != nil { 414 // reset status 415 c.setStatus(nil) 416 } 417 418 c.update(res) 419 } 420 } 421 422 func (c *cache) GetService(service string, opts ...registry.GetOption) ([]*registry.Service, error) { 423 // get the service 424 services, err := c.get(service) 425 if err != nil { 426 return nil, err 427 } 428 429 // if there's nothing return err 430 if len(services) == 0 { 431 return nil, registry.ErrNotFound 432 } 433 434 // return services 435 return services, nil 436 } 437 438 func (c *cache) Stop() { 439 c.Lock() 440 defer c.Unlock() 441 442 select { 443 case <-c.exit: 444 return 445 default: 446 close(c.exit) 447 } 448 } 449 450 func (c *cache) String() string { 451 return "cache" 452 } 453 454 // New returns a new cache 455 func New(r registry.Registry, opts ...Option) Cache { 456 rand.Seed(time.Now().UnixNano()) 457 options := Options{ 458 TTL: DefaultTTL, 459 } 460 461 for _, o := range opts { 462 o(&options) 463 } 464 465 return &cache{ 466 Registry: r, 467 opts: options, 468 watched: make(map[string]bool), 469 cache: make(map[string][]*registry.Service), 470 ttls: make(map[string]time.Time), 471 exit: make(chan bool), 472 } 473 }