github.com/nsqio/nsq@v1.3.0/nsqlookupd/registration_db.go (about) 1 package nsqlookupd 2 3 import ( 4 "fmt" 5 "sync" 6 "sync/atomic" 7 "time" 8 ) 9 10 type RegistrationDB struct { 11 sync.RWMutex 12 registrationMap map[Registration]ProducerMap 13 } 14 15 type Registration struct { 16 Category string 17 Key string 18 SubKey string 19 } 20 type Registrations []Registration 21 22 type PeerInfo struct { 23 lastUpdate int64 24 id string 25 RemoteAddress string `json:"remote_address"` 26 Hostname string `json:"hostname"` 27 BroadcastAddress string `json:"broadcast_address"` 28 TCPPort int `json:"tcp_port"` 29 HTTPPort int `json:"http_port"` 30 Version string `json:"version"` 31 } 32 33 type Producer struct { 34 peerInfo *PeerInfo 35 tombstoned bool 36 tombstonedAt time.Time 37 } 38 39 type Producers []*Producer 40 type ProducerMap map[string]*Producer 41 42 func (p *Producer) String() string { 43 return fmt.Sprintf("%s [%d, %d]", p.peerInfo.BroadcastAddress, p.peerInfo.TCPPort, p.peerInfo.HTTPPort) 44 } 45 46 func (p *Producer) Tombstone() { 47 p.tombstoned = true 48 p.tombstonedAt = time.Now() 49 } 50 51 func (p *Producer) IsTombstoned(lifetime time.Duration) bool { 52 return p.tombstoned && time.Since(p.tombstonedAt) < lifetime 53 } 54 55 func NewRegistrationDB() *RegistrationDB { 56 return &RegistrationDB{ 57 registrationMap: make(map[Registration]ProducerMap), 58 } 59 } 60 61 // add a registration key 62 func (r *RegistrationDB) AddRegistration(k Registration) { 63 r.Lock() 64 defer r.Unlock() 65 _, ok := r.registrationMap[k] 66 if !ok { 67 r.registrationMap[k] = make(map[string]*Producer) 68 } 69 } 70 71 // add a producer to a registration 72 func (r *RegistrationDB) AddProducer(k Registration, p *Producer) bool { 73 r.Lock() 74 defer r.Unlock() 75 _, ok := r.registrationMap[k] 76 if !ok { 77 r.registrationMap[k] = make(map[string]*Producer) 78 } 79 producers := r.registrationMap[k] 80 _, found := producers[p.peerInfo.id] 81 if !found { 82 producers[p.peerInfo.id] = p 83 } 84 return !found 85 } 86 87 // remove a producer from a registration 88 func (r *RegistrationDB) RemoveProducer(k Registration, id string) (bool, int) { 89 r.Lock() 90 defer r.Unlock() 91 producers, ok := r.registrationMap[k] 92 if !ok { 93 return false, 0 94 } 95 removed := false 96 if _, exists := producers[id]; exists { 97 removed = true 98 } 99 100 // Note: this leaves keys in the DB even if they have empty lists 101 delete(producers, id) 102 return removed, len(producers) 103 } 104 105 // remove a Registration and all it's producers 106 func (r *RegistrationDB) RemoveRegistration(k Registration) { 107 r.Lock() 108 defer r.Unlock() 109 delete(r.registrationMap, k) 110 } 111 112 func (r *RegistrationDB) needFilter(key string, subkey string) bool { 113 return key == "*" || subkey == "*" 114 } 115 116 func (r *RegistrationDB) FindRegistrations(category string, key string, subkey string) Registrations { 117 r.RLock() 118 defer r.RUnlock() 119 if !r.needFilter(key, subkey) { 120 k := Registration{category, key, subkey} 121 if _, ok := r.registrationMap[k]; ok { 122 return Registrations{k} 123 } 124 return Registrations{} 125 } 126 results := Registrations{} 127 for k := range r.registrationMap { 128 if !k.IsMatch(category, key, subkey) { 129 continue 130 } 131 results = append(results, k) 132 } 133 return results 134 } 135 136 func (r *RegistrationDB) FindProducers(category string, key string, subkey string) Producers { 137 r.RLock() 138 defer r.RUnlock() 139 if !r.needFilter(key, subkey) { 140 k := Registration{category, key, subkey} 141 return ProducerMap2Slice(r.registrationMap[k]) 142 } 143 144 results := make(map[string]struct{}) 145 var retProducers Producers 146 for k, producers := range r.registrationMap { 147 if !k.IsMatch(category, key, subkey) { 148 continue 149 } 150 for _, producer := range producers { 151 _, found := results[producer.peerInfo.id] 152 if !found { 153 results[producer.peerInfo.id] = struct{}{} 154 retProducers = append(retProducers, producer) 155 } 156 } 157 } 158 return retProducers 159 } 160 161 func (r *RegistrationDB) LookupRegistrations(id string) Registrations { 162 r.RLock() 163 defer r.RUnlock() 164 results := Registrations{} 165 for k, producers := range r.registrationMap { 166 if _, exists := producers[id]; exists { 167 results = append(results, k) 168 } 169 } 170 return results 171 } 172 173 func (k Registration) IsMatch(category string, key string, subkey string) bool { 174 if category != k.Category { 175 return false 176 } 177 if key != "*" && k.Key != key { 178 return false 179 } 180 if subkey != "*" && k.SubKey != subkey { 181 return false 182 } 183 return true 184 } 185 186 func (rr Registrations) Filter(category string, key string, subkey string) Registrations { 187 output := Registrations{} 188 for _, k := range rr { 189 if k.IsMatch(category, key, subkey) { 190 output = append(output, k) 191 } 192 } 193 return output 194 } 195 196 func (rr Registrations) Keys() []string { 197 keys := make([]string, len(rr)) 198 for i, k := range rr { 199 keys[i] = k.Key 200 } 201 return keys 202 } 203 204 func (rr Registrations) SubKeys() []string { 205 subkeys := make([]string, len(rr)) 206 for i, k := range rr { 207 subkeys[i] = k.SubKey 208 } 209 return subkeys 210 } 211 212 func (pp Producers) FilterByActive(inactivityTimeout time.Duration, tombstoneLifetime time.Duration) Producers { 213 now := time.Now() 214 results := Producers{} 215 for _, p := range pp { 216 cur := time.Unix(0, atomic.LoadInt64(&p.peerInfo.lastUpdate)) 217 if now.Sub(cur) > inactivityTimeout || p.IsTombstoned(tombstoneLifetime) { 218 continue 219 } 220 results = append(results, p) 221 } 222 return results 223 } 224 225 func (pp Producers) PeerInfo() []*PeerInfo { 226 results := []*PeerInfo{} 227 for _, p := range pp { 228 results = append(results, p.peerInfo) 229 } 230 return results 231 } 232 233 func ProducerMap2Slice(pm ProducerMap) Producers { 234 var producers Producers 235 for _, producer := range pm { 236 producers = append(producers, producer) 237 } 238 239 return producers 240 }