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  }