github.com/nsqio/nsq@v1.3.0/nsqlookupd/nsqlookupd_test.go (about) 1 package nsqlookupd 2 3 import ( 4 "fmt" 5 "net" 6 "testing" 7 "time" 8 9 "github.com/nsqio/go-nsq" 10 "github.com/nsqio/nsq/internal/clusterinfo" 11 "github.com/nsqio/nsq/internal/http_api" 12 "github.com/nsqio/nsq/internal/test" 13 ) 14 15 const ( 16 ConnectTimeout = 2 * time.Second 17 RequestTimeout = 5 * time.Second 18 TCPPort = 5000 19 HTTPPort = 5555 20 HostAddr = "ip.address" 21 NSQDVersion = "fake-version" 22 ) 23 24 type ProducersDoc struct { 25 Producers []interface{} `json:"producers"` 26 } 27 28 type TopicsDoc struct { 29 Topics []interface{} `json:"topics"` 30 } 31 32 type LookupDoc struct { 33 Channels []interface{} `json:"channels"` 34 Producers []*PeerInfo `json:"producers"` 35 } 36 37 func mustStartLookupd(opts *Options) (*net.TCPAddr, *net.TCPAddr, *NSQLookupd) { 38 opts.TCPAddress = "127.0.0.1:0" 39 opts.HTTPAddress = "127.0.0.1:0" 40 nsqlookupd, err := New(opts) 41 if err != nil { 42 panic(err) 43 } 44 go func() { 45 err := nsqlookupd.Main() 46 if err != nil { 47 panic(err) 48 } 49 }() 50 return nsqlookupd.RealTCPAddr(), nsqlookupd.RealHTTPAddr(), nsqlookupd 51 } 52 53 func mustConnectLookupd(t *testing.T, tcpAddr *net.TCPAddr) net.Conn { 54 conn, err := net.DialTimeout("tcp", tcpAddr.String(), time.Second) 55 if err != nil { 56 t.Fatal("failed to connect to lookupd") 57 } 58 conn.Write(nsq.MagicV1) 59 return conn 60 } 61 62 func identify(t *testing.T, conn net.Conn) { 63 ci := make(map[string]interface{}) 64 ci["tcp_port"] = TCPPort 65 ci["http_port"] = HTTPPort 66 ci["broadcast_address"] = HostAddr 67 ci["hostname"] = HostAddr 68 ci["version"] = NSQDVersion 69 cmd, _ := nsq.Identify(ci) 70 _, err := cmd.WriteTo(conn) 71 test.Nil(t, err) 72 _, err = nsq.ReadResponse(conn) 73 test.Nil(t, err) 74 } 75 76 func TestBasicLookupd(t *testing.T) { 77 opts := NewOptions() 78 opts.Logger = test.NewTestLogger(t) 79 tcpAddr, httpAddr, nsqlookupd := mustStartLookupd(opts) 80 defer nsqlookupd.Exit() 81 82 topics := nsqlookupd.DB.FindRegistrations("topic", "*", "*") 83 test.Equal(t, 0, len(topics)) 84 85 topicName := "connectmsg" 86 87 conn := mustConnectLookupd(t, tcpAddr) 88 89 identify(t, conn) 90 91 nsq.Register(topicName, "channel1").WriteTo(conn) 92 v, err := nsq.ReadResponse(conn) 93 test.Nil(t, err) 94 test.Equal(t, []byte("OK"), v) 95 96 pr := ProducersDoc{} 97 endpoint := fmt.Sprintf("http://%s/nodes", httpAddr) 98 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).GETV1(endpoint, &pr) 99 test.Nil(t, err) 100 101 t.Logf("got %v", pr) 102 test.Equal(t, 1, len(pr.Producers)) 103 104 topics = nsqlookupd.DB.FindRegistrations("topic", topicName, "") 105 test.Equal(t, 1, len(topics)) 106 107 producers := nsqlookupd.DB.FindProducers("topic", topicName, "") 108 test.Equal(t, 1, len(producers)) 109 producer := producers[0] 110 111 test.Equal(t, HostAddr, producer.peerInfo.BroadcastAddress) 112 test.Equal(t, HostAddr, producer.peerInfo.Hostname) 113 test.Equal(t, TCPPort, producer.peerInfo.TCPPort) 114 test.Equal(t, HTTPPort, producer.peerInfo.HTTPPort) 115 116 tr := TopicsDoc{} 117 endpoint = fmt.Sprintf("http://%s/topics", httpAddr) 118 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).GETV1(endpoint, &tr) 119 test.Nil(t, err) 120 121 t.Logf("got %v", tr) 122 test.Equal(t, 1, len(tr.Topics)) 123 124 lr := LookupDoc{} 125 endpoint = fmt.Sprintf("http://%s/lookup?topic=%s", httpAddr, topicName) 126 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).GETV1(endpoint, &lr) 127 test.Nil(t, err) 128 129 t.Logf("got %v", lr) 130 test.Equal(t, 1, len(lr.Channels)) 131 test.Equal(t, 1, len(lr.Producers)) 132 for _, p := range lr.Producers { 133 test.Equal(t, TCPPort, p.TCPPort) 134 test.Equal(t, HTTPPort, p.HTTPPort) 135 test.Equal(t, HostAddr, p.BroadcastAddress) 136 test.Equal(t, NSQDVersion, p.Version) 137 } 138 139 conn.Close() 140 time.Sleep(10 * time.Millisecond) 141 142 // now there should be no producers, but still topic/channel entries 143 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).GETV1(endpoint, &lr) 144 test.Nil(t, err) 145 146 test.Equal(t, 1, len(lr.Channels)) 147 test.Equal(t, 0, len(lr.Producers)) 148 } 149 150 func TestChannelUnregister(t *testing.T) { 151 opts := NewOptions() 152 opts.Logger = test.NewTestLogger(t) 153 tcpAddr, httpAddr, nsqlookupd := mustStartLookupd(opts) 154 defer nsqlookupd.Exit() 155 156 topics := nsqlookupd.DB.FindRegistrations("topic", "*", "*") 157 test.Equal(t, 0, len(topics)) 158 159 topicName := "channel_unregister" 160 161 conn := mustConnectLookupd(t, tcpAddr) 162 defer conn.Close() 163 164 identify(t, conn) 165 166 nsq.Register(topicName, "ch1").WriteTo(conn) 167 v, err := nsq.ReadResponse(conn) 168 test.Nil(t, err) 169 test.Equal(t, []byte("OK"), v) 170 171 topics = nsqlookupd.DB.FindRegistrations("topic", topicName, "") 172 test.Equal(t, 1, len(topics)) 173 174 channels := nsqlookupd.DB.FindRegistrations("channel", topicName, "*") 175 test.Equal(t, 1, len(channels)) 176 177 nsq.UnRegister(topicName, "ch1").WriteTo(conn) 178 v, err = nsq.ReadResponse(conn) 179 test.Nil(t, err) 180 test.Equal(t, []byte("OK"), v) 181 182 topics = nsqlookupd.DB.FindRegistrations("topic", topicName, "") 183 test.Equal(t, 1, len(topics)) 184 185 // we should still have mention of the topic even though there is no producer 186 // (ie. we haven't *deleted* the channel, just unregistered as a producer) 187 channels = nsqlookupd.DB.FindRegistrations("channel", topicName, "*") 188 test.Equal(t, 1, len(channels)) 189 190 pr := ProducersDoc{} 191 endpoint := fmt.Sprintf("http://%s/lookup?topic=%s", httpAddr, topicName) 192 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).GETV1(endpoint, &pr) 193 test.Nil(t, err) 194 t.Logf("got %v", pr) 195 test.Equal(t, 1, len(pr.Producers)) 196 } 197 198 func TestTombstoneRecover(t *testing.T) { 199 opts := NewOptions() 200 opts.Logger = test.NewTestLogger(t) 201 opts.TombstoneLifetime = 50 * time.Millisecond 202 tcpAddr, httpAddr, nsqlookupd := mustStartLookupd(opts) 203 defer nsqlookupd.Exit() 204 205 topicName := "tombstone_recover" 206 topicName2 := topicName + "2" 207 208 conn := mustConnectLookupd(t, tcpAddr) 209 defer conn.Close() 210 211 identify(t, conn) 212 213 nsq.Register(topicName, "channel1").WriteTo(conn) 214 _, err := nsq.ReadResponse(conn) 215 test.Nil(t, err) 216 217 nsq.Register(topicName2, "channel2").WriteTo(conn) 218 _, err = nsq.ReadResponse(conn) 219 test.Nil(t, err) 220 221 endpoint := fmt.Sprintf("http://%s/topic/tombstone?topic=%s&node=%s:%d", 222 httpAddr, topicName, HostAddr, HTTPPort) 223 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).POSTV1(endpoint) 224 test.Nil(t, err) 225 226 pr := ProducersDoc{} 227 228 endpoint = fmt.Sprintf("http://%s/lookup?topic=%s", httpAddr, topicName) 229 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).GETV1(endpoint, &pr) 230 test.Nil(t, err) 231 test.Equal(t, 0, len(pr.Producers)) 232 233 endpoint = fmt.Sprintf("http://%s/lookup?topic=%s", httpAddr, topicName2) 234 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).GETV1(endpoint, &pr) 235 test.Nil(t, err) 236 test.Equal(t, 1, len(pr.Producers)) 237 238 time.Sleep(75 * time.Millisecond) 239 240 endpoint = fmt.Sprintf("http://%s/lookup?topic=%s", httpAddr, topicName) 241 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).GETV1(endpoint, &pr) 242 test.Nil(t, err) 243 test.Equal(t, 1, len(pr.Producers)) 244 } 245 246 func TestTombstoneUnregister(t *testing.T) { 247 opts := NewOptions() 248 opts.Logger = test.NewTestLogger(t) 249 opts.TombstoneLifetime = 50 * time.Millisecond 250 tcpAddr, httpAddr, nsqlookupd := mustStartLookupd(opts) 251 defer nsqlookupd.Exit() 252 253 topicName := "tombstone_unregister" 254 255 conn := mustConnectLookupd(t, tcpAddr) 256 defer conn.Close() 257 258 identify(t, conn) 259 260 nsq.Register(topicName, "channel1").WriteTo(conn) 261 _, err := nsq.ReadResponse(conn) 262 test.Nil(t, err) 263 264 endpoint := fmt.Sprintf("http://%s/topic/tombstone?topic=%s&node=%s:%d", 265 httpAddr, topicName, HostAddr, HTTPPort) 266 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).POSTV1(endpoint) 267 test.Nil(t, err) 268 269 pr := ProducersDoc{} 270 271 endpoint = fmt.Sprintf("http://%s/lookup?topic=%s", httpAddr, topicName) 272 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).GETV1(endpoint, &pr) 273 test.Nil(t, err) 274 test.Equal(t, 0, len(pr.Producers)) 275 276 nsq.UnRegister(topicName, "").WriteTo(conn) 277 _, err = nsq.ReadResponse(conn) 278 test.Nil(t, err) 279 280 time.Sleep(55 * time.Millisecond) 281 282 endpoint = fmt.Sprintf("http://%s/lookup?topic=%s", httpAddr, topicName) 283 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).GETV1(endpoint, &pr) 284 test.Nil(t, err) 285 test.Equal(t, 0, len(pr.Producers)) 286 } 287 288 func TestInactiveNodes(t *testing.T) { 289 opts := NewOptions() 290 opts.Logger = test.NewTestLogger(t) 291 opts.InactiveProducerTimeout = 200 * time.Millisecond 292 tcpAddr, httpAddr, nsqlookupd := mustStartLookupd(opts) 293 defer nsqlookupd.Exit() 294 295 lookupdHTTPAddrs := []string{httpAddr.String()} 296 297 topicName := "inactive_nodes" 298 299 conn := mustConnectLookupd(t, tcpAddr) 300 defer conn.Close() 301 302 identify(t, conn) 303 304 nsq.Register(topicName, "channel1").WriteTo(conn) 305 _, err := nsq.ReadResponse(conn) 306 test.Nil(t, err) 307 308 ci := clusterinfo.New(nil, http_api.NewClient(nil, ConnectTimeout, RequestTimeout)) 309 310 producers, _ := ci.GetLookupdProducers(lookupdHTTPAddrs) 311 test.Equal(t, 1, len(producers)) 312 test.Equal(t, 1, len(producers[0].Topics)) 313 test.Equal(t, topicName, producers[0].Topics[0].Topic) 314 test.Equal(t, false, producers[0].Topics[0].Tombstoned) 315 316 time.Sleep(250 * time.Millisecond) 317 318 producers, _ = ci.GetLookupdProducers(lookupdHTTPAddrs) 319 test.Equal(t, 0, len(producers)) 320 } 321 322 func TestTombstonedNodes(t *testing.T) { 323 opts := NewOptions() 324 opts.Logger = test.NewTestLogger(t) 325 tcpAddr, httpAddr, nsqlookupd := mustStartLookupd(opts) 326 defer nsqlookupd.Exit() 327 328 lookupdHTTPAddrs := []string{httpAddr.String()} 329 330 topicName := "inactive_nodes" 331 332 conn := mustConnectLookupd(t, tcpAddr) 333 defer conn.Close() 334 335 identify(t, conn) 336 337 nsq.Register(topicName, "channel1").WriteTo(conn) 338 _, err := nsq.ReadResponse(conn) 339 test.Nil(t, err) 340 341 ci := clusterinfo.New(nil, http_api.NewClient(nil, ConnectTimeout, RequestTimeout)) 342 343 producers, _ := ci.GetLookupdProducers(lookupdHTTPAddrs) 344 test.Equal(t, 1, len(producers)) 345 test.Equal(t, 1, len(producers[0].Topics)) 346 test.Equal(t, topicName, producers[0].Topics[0].Topic) 347 test.Equal(t, false, producers[0].Topics[0].Tombstoned) 348 349 endpoint := fmt.Sprintf("http://%s/topic/tombstone?topic=%s&node=%s:%d", 350 httpAddr, topicName, HostAddr, HTTPPort) 351 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).POSTV1(endpoint) 352 test.Nil(t, err) 353 354 producers, _ = ci.GetLookupdProducers(lookupdHTTPAddrs) 355 test.Equal(t, 1, len(producers)) 356 test.Equal(t, 1, len(producers[0].Topics)) 357 test.Equal(t, topicName, producers[0].Topics[0].Topic) 358 test.Equal(t, true, producers[0].Topics[0].Tombstoned) 359 }