github.com/volts-dev/volts@v0.0.0-20240120094013-5e9c65924106/registry/memory/memory.go (about)

     1  package memory
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	"github.com/google/uuid"
     8  	"github.com/volts-dev/volts/registry"
     9  )
    10  
    11  var log = registry.Logger()
    12  
    13  var (
    14  	sendEventTime = 10 * time.Millisecond
    15  	ttlPruneTime  = time.Second
    16  )
    17  
    18  type (
    19  	node struct {
    20  		LastSeen time.Time
    21  		*registry.Node
    22  		TTL time.Duration
    23  	}
    24  
    25  	record struct {
    26  		Name      string
    27  		Version   string
    28  		Metadata  map[string]string
    29  		Nodes     map[string]*node
    30  		Endpoints []*registry.Endpoint
    31  	}
    32  
    33  	memRegistry struct {
    34  		config *registry.Config
    35  
    36  		records  map[string]map[string]*record
    37  		watchers map[string]*memWatcher
    38  
    39  		sync.RWMutex
    40  	}
    41  )
    42  
    43  func init() {
    44  	registry.Register("memory", New)
    45  }
    46  
    47  func New(opts ...registry.Option) registry.IRegistry {
    48  	var defaultOpts []registry.Option
    49  	defaultOpts = append(defaultOpts,
    50  		registry.WithName("memory"),
    51  		registry.Timeout(time.Millisecond*100),
    52  	)
    53  
    54  	cfg := registry.NewConfig(append(defaultOpts, opts...)...)
    55  
    56  	records := getServiceRecords(cfg.Context)
    57  	if records == nil {
    58  		records = make(map[string]map[string]*record)
    59  	}
    60  
    61  	reg := &memRegistry{
    62  		config:   cfg,
    63  		records:  records,
    64  		watchers: make(map[string]*memWatcher),
    65  	}
    66  
    67  	go reg.ttlPrune()
    68  
    69  	return reg
    70  }
    71  
    72  func (m *memRegistry) ttlPrune() {
    73  	prune := time.NewTicker(ttlPruneTime)
    74  	defer prune.Stop()
    75  
    76  	for {
    77  		select {
    78  		case <-prune.C:
    79  			m.Lock()
    80  			for name, records := range m.records {
    81  				for version, record := range records {
    82  					for id, n := range record.Nodes {
    83  						if n.TTL != 0 && time.Since(n.LastSeen) > n.TTL {
    84  							log.Dbgf("Registry TTL expired for node %s of service %s", n.Id, name)
    85  							delete(m.records[name][version].Nodes, id)
    86  						}
    87  					}
    88  				}
    89  			}
    90  			m.Unlock()
    91  		}
    92  	}
    93  }
    94  
    95  func (m *memRegistry) sendEvent(r *registry.Result) {
    96  	m.RLock()
    97  	watchers := make([]*memWatcher, 0, len(m.watchers))
    98  	for _, w := range m.watchers {
    99  		watchers = append(watchers, w)
   100  	}
   101  	m.RUnlock()
   102  
   103  	for _, w := range watchers {
   104  		select {
   105  		case <-w.exit:
   106  			m.Lock()
   107  			delete(m.watchers, w.id)
   108  			m.Unlock()
   109  		default:
   110  			select {
   111  			case w.res <- r:
   112  			case <-time.After(sendEventTime):
   113  			}
   114  		}
   115  	}
   116  }
   117  
   118  func (m *memRegistry) Init(opts ...registry.Option) error {
   119  	for _, o := range opts {
   120  		o(m.config)
   121  	}
   122  
   123  	// add services
   124  	m.Lock()
   125  	defer m.Unlock()
   126  
   127  	records := getServiceRecords(m.config.Context)
   128  	for name, record := range records {
   129  		// add a whole new service including all of its versions
   130  		if _, ok := m.records[name]; !ok {
   131  			m.records[name] = record
   132  			continue
   133  		}
   134  		// add the versions of the service we dont track yet
   135  		for version, r := range record {
   136  			if _, ok := m.records[name][version]; !ok {
   137  				m.records[name][version] = r
   138  				continue
   139  			}
   140  		}
   141  	}
   142  
   143  	return nil
   144  }
   145  
   146  func (m *memRegistry) Config() *registry.Config {
   147  	return m.config
   148  }
   149  
   150  func (m *memRegistry) Register(s *registry.Service, opts ...registry.Option) error {
   151  	m.Lock()
   152  	defer m.Unlock()
   153  	cfg := m.config
   154  	for _, o := range opts {
   155  		o(cfg)
   156  	}
   157  
   158  	r := serviceToRecord(s, cfg.TTL)
   159  
   160  	if _, ok := m.records[s.Name]; !ok {
   161  		m.records[s.Name] = make(map[string]*record)
   162  	}
   163  
   164  	if _, ok := m.records[s.Name][s.Version]; !ok {
   165  		m.records[s.Name][s.Version] = r
   166  		log.Dbgf("Registry added new service: %s, version: %s", s.Name, s.Version)
   167  		go m.sendEvent(&registry.Result{Action: "update", Service: s})
   168  		return nil
   169  	}
   170  
   171  	addedNodes := false
   172  	for _, n := range s.Nodes {
   173  		if _, ok := m.records[s.Name][s.Version].Nodes[n.Id]; !ok {
   174  			addedNodes = true
   175  			metadata := make(map[string]string)
   176  			for k, v := range n.Metadata {
   177  				metadata[k] = v
   178  			}
   179  			m.records[s.Name][s.Version].Nodes[n.Id] = &node{
   180  				Node: &registry.Node{
   181  					Id:       n.Id,
   182  					Address:  n.Address,
   183  					Metadata: metadata,
   184  				},
   185  				TTL:      cfg.TTL,
   186  				LastSeen: time.Now(),
   187  			}
   188  		}
   189  	}
   190  
   191  	cfg.LocalServices = append(cfg.LocalServices, s)
   192  
   193  	if addedNodes {
   194  		log.Dbgf("Registry added new node to service: %s, version: %s", s.Name, s.Version)
   195  		go m.sendEvent(&registry.Result{Action: "update", Service: s})
   196  		return nil
   197  	}
   198  
   199  	// refresh TTL and timestamp
   200  	for _, n := range s.Nodes {
   201  		log.Dbgf("Updated registration for service: %s, version: %s", s.Name, s.Version)
   202  		m.records[s.Name][s.Version].Nodes[n.Id].TTL = cfg.TTL
   203  		m.records[s.Name][s.Version].Nodes[n.Id].LastSeen = time.Now()
   204  	}
   205  
   206  	return nil
   207  }
   208  
   209  func (m *memRegistry) Deregister(s *registry.Service, opts ...registry.Option) error {
   210  	m.Lock()
   211  	defer m.Unlock()
   212  
   213  	if _, ok := m.records[s.Name]; ok {
   214  		if _, ok := m.records[s.Name][s.Version]; ok {
   215  			for _, n := range s.Nodes {
   216  				if _, ok := m.records[s.Name][s.Version].Nodes[n.Id]; ok {
   217  					log.Dbgf("Registry removed node from service: %s, version: %s", s.Name, s.Version)
   218  					delete(m.records[s.Name][s.Version].Nodes, n.Id)
   219  				}
   220  			}
   221  			if len(m.records[s.Name][s.Version].Nodes) == 0 {
   222  				delete(m.records[s.Name], s.Version)
   223  				log.Dbgf("Registry removed service: %s, version: %s", s.Name, s.Version)
   224  			}
   225  		}
   226  		if len(m.records[s.Name]) == 0 {
   227  			delete(m.records, s.Name)
   228  			log.Dbgf("Registry removed service: %s", s.Name)
   229  		}
   230  		go m.sendEvent(&registry.Result{Action: "delete", Service: s})
   231  	}
   232  
   233  	return nil
   234  }
   235  
   236  func (m *memRegistry) GetService(name string) ([]*registry.Service, error) {
   237  	m.RLock()
   238  	defer m.RUnlock()
   239  
   240  	records, ok := m.records[name]
   241  	if !ok {
   242  		return nil, registry.ErrNotFound
   243  	}
   244  
   245  	services := make([]*registry.Service, len(m.records[name]))
   246  	i := 0
   247  	for _, record := range records {
   248  		services[i] = recordToService(record)
   249  		i++
   250  	}
   251  
   252  	return services, nil
   253  }
   254  
   255  func (m *memRegistry) LocalServices() []*registry.Service {
   256  	return m.config.LocalServices
   257  }
   258  
   259  func (m *memRegistry) ListServices() ([]*registry.Service, error) {
   260  	m.RLock()
   261  	defer m.RUnlock()
   262  
   263  	var services []*registry.Service
   264  	for _, records := range m.records {
   265  		for _, record := range records {
   266  			services = append(services, recordToService(record))
   267  		}
   268  	}
   269  
   270  	return services, nil
   271  }
   272  
   273  func (m *memRegistry) Watcher(opts ...registry.WatchOptions) (registry.Watcher, error) {
   274  	var wo registry.WatchConfig
   275  	for _, o := range opts {
   276  		o(&wo)
   277  	}
   278  
   279  	w := &memWatcher{
   280  		exit: make(chan bool),
   281  		res:  make(chan *registry.Result),
   282  		id:   uuid.New().String(),
   283  		wo:   wo,
   284  	}
   285  
   286  	m.Lock()
   287  	m.watchers[w.id] = w
   288  	m.Unlock()
   289  
   290  	return w, nil
   291  }
   292  
   293  func (m *memRegistry) String() string {
   294  	return m.config.Name
   295  }