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(®istry.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: ®istry.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(®istry.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(®istry.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 }