github.com/nsqio/nsq@v1.3.0/nsqd/nsqd_test.go (about) 1 package nsqd 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "io/fs" 8 "net" 9 "os" 10 "strconv" 11 "sync/atomic" 12 "testing" 13 "time" 14 15 "github.com/nsqio/nsq/internal/http_api" 16 "github.com/nsqio/nsq/internal/test" 17 "github.com/nsqio/nsq/nsqlookupd" 18 ) 19 20 const ( 21 ConnectTimeout = 2 * time.Second 22 RequestTimeout = 5 * time.Second 23 ) 24 25 func getMetadata(n *NSQD) (*Metadata, error) { 26 fn := newMetadataFile(n.getOpts()) 27 data, err := os.ReadFile(fn) 28 if err != nil { 29 return nil, err 30 } 31 32 var m Metadata 33 err = json.Unmarshal(data, &m) 34 if err != nil { 35 return nil, err 36 } 37 return &m, nil 38 } 39 40 func TestStartup(t *testing.T) { 41 var msg *Message 42 43 iterations := 300 44 doneExitChan := make(chan int) 45 46 opts := NewOptions() 47 opts.Logger = test.NewTestLogger(t) 48 opts.MemQueueSize = 100 49 opts.MaxBytesPerFile = 10240 50 _, _, nsqd := mustStartNSQD(opts) 51 defer os.RemoveAll(opts.DataPath) 52 53 origDataPath := opts.DataPath 54 55 topicName := "nsqd_test" + strconv.Itoa(int(time.Now().Unix())) 56 57 exitChan := make(chan int) 58 go func() { 59 <-exitChan 60 nsqd.Exit() 61 doneExitChan <- 1 62 }() 63 64 // verify nsqd metadata shows no topics 65 err := nsqd.PersistMetadata() 66 test.Nil(t, err) 67 atomic.StoreInt32(&nsqd.isLoading, 1) 68 nsqd.GetTopic(topicName) // will not persist if `flagLoading` 69 m, err := getMetadata(nsqd) 70 test.Nil(t, err) 71 test.Equal(t, 0, len(m.Topics)) 72 nsqd.DeleteExistingTopic(topicName) 73 atomic.StoreInt32(&nsqd.isLoading, 0) 74 75 body := make([]byte, 256) 76 topic := nsqd.GetTopic(topicName) 77 for i := 0; i < iterations; i++ { 78 msg := NewMessage(topic.GenerateID(), body) 79 topic.PutMessage(msg) 80 } 81 82 t.Logf("pulling from channel") 83 channel1 := topic.GetChannel("ch1") 84 85 t.Logf("read %d msgs", iterations/2) 86 for i := 0; i < iterations/2; i++ { 87 select { 88 case msg = <-channel1.memoryMsgChan: 89 case b := <-channel1.backend.ReadChan(): 90 msg, _ = decodeMessage(b) 91 } 92 t.Logf("read message %d", i+1) 93 test.Equal(t, body, msg.Body) 94 } 95 96 for { 97 if channel1.Depth() == int64(iterations/2) { 98 break 99 } 100 time.Sleep(50 * time.Millisecond) 101 } 102 103 // make sure metadata shows the topic 104 m, err = getMetadata(nsqd) 105 test.Nil(t, err) 106 test.Equal(t, 1, len(m.Topics)) 107 test.Equal(t, topicName, m.Topics[0].Name) 108 109 exitChan <- 1 110 <-doneExitChan 111 112 // start up a new nsqd w/ the same folder 113 114 opts = NewOptions() 115 opts.Logger = test.NewTestLogger(t) 116 opts.MemQueueSize = 100 117 opts.MaxBytesPerFile = 10240 118 opts.DataPath = origDataPath 119 _, _, nsqd = mustStartNSQD(opts) 120 121 go func() { 122 <-exitChan 123 nsqd.Exit() 124 doneExitChan <- 1 125 }() 126 127 topic = nsqd.GetTopic(topicName) 128 // should be empty; channel should have drained everything 129 count := topic.Depth() 130 test.Equal(t, int64(0), count) 131 132 channel1 = topic.GetChannel("ch1") 133 134 for { 135 if channel1.Depth() == int64(iterations/2) { 136 break 137 } 138 time.Sleep(50 * time.Millisecond) 139 } 140 141 // read the other half of the messages 142 for i := 0; i < iterations/2; i++ { 143 select { 144 case msg = <-channel1.memoryMsgChan: 145 case b := <-channel1.backend.ReadChan(): 146 msg, _ = decodeMessage(b) 147 } 148 t.Logf("read message %d", i+1) 149 test.Equal(t, body, msg.Body) 150 } 151 152 // verify we drained things 153 test.Equal(t, 0, len(topic.memoryMsgChan)) 154 test.Equal(t, int64(0), topic.backend.Depth()) 155 156 exitChan <- 1 157 <-doneExitChan 158 } 159 160 func TestEphemeralTopicsAndChannels(t *testing.T) { 161 // ephemeral topics/channels are lazily removed after the last channel/client is removed 162 opts := NewOptions() 163 opts.Logger = test.NewTestLogger(t) 164 opts.MemQueueSize = 100 165 _, _, nsqd := mustStartNSQD(opts) 166 defer os.RemoveAll(opts.DataPath) 167 168 topicName := "ephemeral_topic" + strconv.Itoa(int(time.Now().Unix())) + "#ephemeral" 169 doneExitChan := make(chan int) 170 171 exitChan := make(chan int) 172 go func() { 173 <-exitChan 174 nsqd.Exit() 175 doneExitChan <- 1 176 }() 177 178 body := []byte("an_ephemeral_message") 179 topic := nsqd.GetTopic(topicName) 180 ephemeralChannel := topic.GetChannel("ch1#ephemeral") 181 client := newClientV2(0, nil, nsqd) 182 err := ephemeralChannel.AddClient(client.ID, client) 183 test.Equal(t, err, nil) 184 185 msg := NewMessage(topic.GenerateID(), body) 186 topic.PutMessage(msg) 187 msg = <-ephemeralChannel.memoryMsgChan 188 test.Equal(t, body, msg.Body) 189 190 ephemeralChannel.RemoveClient(client.ID) 191 192 time.Sleep(100 * time.Millisecond) 193 194 topic.Lock() 195 numChannels := len(topic.channelMap) 196 topic.Unlock() 197 test.Equal(t, 0, numChannels) 198 199 nsqd.Lock() 200 numTopics := len(nsqd.topicMap) 201 nsqd.Unlock() 202 test.Equal(t, 0, numTopics) 203 204 exitChan <- 1 205 <-doneExitChan 206 } 207 208 func TestPauseMetadata(t *testing.T) { 209 opts := NewOptions() 210 opts.Logger = test.NewTestLogger(t) 211 _, _, nsqd := mustStartNSQD(opts) 212 defer os.RemoveAll(opts.DataPath) 213 defer nsqd.Exit() 214 215 // avoid concurrency issue of async PersistMetadata() calls 216 atomic.StoreInt32(&nsqd.isLoading, 1) 217 topicName := "pause_metadata" + strconv.Itoa(int(time.Now().Unix())) 218 topic := nsqd.GetTopic(topicName) 219 channel := topic.GetChannel("ch") 220 atomic.StoreInt32(&nsqd.isLoading, 0) 221 nsqd.PersistMetadata() 222 223 var isPaused = func(n *NSQD, topicIndex int, channelIndex int) bool { 224 m, _ := getMetadata(n) 225 return m.Topics[topicIndex].Channels[channelIndex].Paused 226 } 227 228 test.Equal(t, false, isPaused(nsqd, 0, 0)) 229 230 channel.Pause() 231 test.Equal(t, false, isPaused(nsqd, 0, 0)) 232 233 nsqd.PersistMetadata() 234 test.Equal(t, true, isPaused(nsqd, 0, 0)) 235 236 channel.UnPause() 237 test.Equal(t, true, isPaused(nsqd, 0, 0)) 238 239 nsqd.PersistMetadata() 240 test.Equal(t, false, isPaused(nsqd, 0, 0)) 241 } 242 243 func mustStartNSQLookupd(opts *nsqlookupd.Options) (net.Addr, net.Addr, *nsqlookupd.NSQLookupd) { 244 opts.TCPAddress = "127.0.0.1:0" 245 opts.HTTPAddress = "127.0.0.1:0" 246 lookupd, err := nsqlookupd.New(opts) 247 if err != nil { 248 panic(err) 249 } 250 go func() { 251 err := lookupd.Main() 252 if err != nil { 253 panic(err) 254 } 255 }() 256 return lookupd.RealTCPAddr(), lookupd.RealHTTPAddr(), lookupd 257 } 258 259 func TestReconfigure(t *testing.T) { 260 lopts := nsqlookupd.NewOptions() 261 lopts.Logger = test.NewTestLogger(t) 262 263 lopts1 := *lopts 264 _, _, lookupd1 := mustStartNSQLookupd(&lopts1) 265 defer lookupd1.Exit() 266 lopts2 := *lopts 267 _, _, lookupd2 := mustStartNSQLookupd(&lopts2) 268 defer lookupd2.Exit() 269 lopts3 := *lopts 270 _, _, lookupd3 := mustStartNSQLookupd(&lopts3) 271 defer lookupd3.Exit() 272 273 opts := NewOptions() 274 opts.Logger = test.NewTestLogger(t) 275 _, _, nsqd := mustStartNSQD(opts) 276 defer os.RemoveAll(opts.DataPath) 277 defer nsqd.Exit() 278 279 newOpts := NewOptions() 280 newOpts.Logger = opts.Logger 281 newOpts.NSQLookupdTCPAddresses = []string{lookupd1.RealTCPAddr().String()} 282 nsqd.swapOpts(newOpts) 283 nsqd.triggerOptsNotification() 284 test.Equal(t, 1, len(nsqd.getOpts().NSQLookupdTCPAddresses)) 285 286 var numLookupPeers int 287 for i := 0; i < 100; i++ { 288 numLookupPeers = len(nsqd.lookupPeers.Load().([]*lookupPeer)) 289 if numLookupPeers == 1 { 290 break 291 } 292 time.Sleep(10 * time.Millisecond) 293 } 294 test.Equal(t, 1, numLookupPeers) 295 296 newOpts = NewOptions() 297 newOpts.Logger = opts.Logger 298 newOpts.NSQLookupdTCPAddresses = []string{lookupd2.RealTCPAddr().String(), lookupd3.RealTCPAddr().String()} 299 nsqd.swapOpts(newOpts) 300 nsqd.triggerOptsNotification() 301 test.Equal(t, 2, len(nsqd.getOpts().NSQLookupdTCPAddresses)) 302 303 for i := 0; i < 100; i++ { 304 numLookupPeers = len(nsqd.lookupPeers.Load().([]*lookupPeer)) 305 if numLookupPeers == 2 { 306 break 307 } 308 time.Sleep(10 * time.Millisecond) 309 } 310 test.Equal(t, 2, numLookupPeers) 311 312 var lookupPeers []string 313 for _, lp := range nsqd.lookupPeers.Load().([]*lookupPeer) { 314 lookupPeers = append(lookupPeers, lp.addr) 315 } 316 test.Equal(t, newOpts.NSQLookupdTCPAddresses, lookupPeers) 317 } 318 319 func TestCluster(t *testing.T) { 320 lopts := nsqlookupd.NewOptions() 321 lopts.Logger = test.NewTestLogger(t) 322 lopts.BroadcastAddress = "127.0.0.1" 323 _, _, lookupd := mustStartNSQLookupd(lopts) 324 325 opts := NewOptions() 326 opts.Logger = test.NewTestLogger(t) 327 opts.NSQLookupdTCPAddresses = []string{lookupd.RealTCPAddr().String()} 328 opts.BroadcastAddress = "127.0.0.1" 329 _, _, nsqd := mustStartNSQD(opts) 330 defer os.RemoveAll(opts.DataPath) 331 defer nsqd.Exit() 332 333 topicName := "cluster_test" + strconv.Itoa(int(time.Now().Unix())) 334 335 hostname, err := os.Hostname() 336 test.Nil(t, err) 337 338 url := fmt.Sprintf("http://%s/topic/create?topic=%s", nsqd.RealHTTPAddr(), topicName) 339 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).POSTV1(url) 340 test.Nil(t, err) 341 342 url = fmt.Sprintf("http://%s/channel/create?topic=%s&channel=ch", nsqd.RealHTTPAddr(), topicName) 343 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).POSTV1(url) 344 test.Nil(t, err) 345 346 // allow some time for nsqd to push info to nsqlookupd 347 time.Sleep(350 * time.Millisecond) 348 349 var d map[string][]struct { 350 Hostname string `json:"hostname"` 351 BroadcastAddress string `json:"broadcast_address"` 352 TCPPort int `json:"tcp_port"` 353 Tombstoned bool `json:"tombstoned"` 354 } 355 356 endpoint := fmt.Sprintf("http://%s/debug", lookupd.RealHTTPAddr()) 357 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).GETV1(endpoint, &d) 358 test.Nil(t, err) 359 360 topicData := d["topic:"+topicName+":"] 361 test.Equal(t, 1, len(topicData)) 362 363 test.Equal(t, hostname, topicData[0].Hostname) 364 test.Equal(t, "127.0.0.1", topicData[0].BroadcastAddress) 365 test.Equal(t, nsqd.RealTCPAddr().(*net.TCPAddr).Port, topicData[0].TCPPort) 366 test.Equal(t, false, topicData[0].Tombstoned) 367 368 channelData := d["channel:"+topicName+":ch"] 369 test.Equal(t, 1, len(channelData)) 370 371 test.Equal(t, hostname, channelData[0].Hostname) 372 test.Equal(t, "127.0.0.1", channelData[0].BroadcastAddress) 373 test.Equal(t, nsqd.RealTCPAddr().(*net.TCPAddr).Port, channelData[0].TCPPort) 374 test.Equal(t, false, channelData[0].Tombstoned) 375 376 var lr struct { 377 Producers []struct { 378 Hostname string `json:"hostname"` 379 BroadcastAddress string `json:"broadcast_address"` 380 TCPPort int `json:"tcp_port"` 381 } `json:"producers"` 382 Channels []string `json:"channels"` 383 } 384 385 endpoint = fmt.Sprintf("http://%s/lookup?topic=%s", lookupd.RealHTTPAddr(), topicName) 386 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).GETV1(endpoint, &lr) 387 test.Nil(t, err) 388 389 test.Equal(t, 1, len(lr.Producers)) 390 test.Equal(t, hostname, lr.Producers[0].Hostname) 391 test.Equal(t, "127.0.0.1", lr.Producers[0].BroadcastAddress) 392 test.Equal(t, nsqd.RealTCPAddr().(*net.TCPAddr).Port, lr.Producers[0].TCPPort) 393 test.Equal(t, 1, len(lr.Channels)) 394 test.Equal(t, "ch", lr.Channels[0]) 395 396 url = fmt.Sprintf("http://%s/topic/delete?topic=%s", nsqd.RealHTTPAddr(), topicName) 397 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).POSTV1(url) 398 test.Nil(t, err) 399 400 // allow some time for nsqd to push info to nsqlookupd 401 time.Sleep(350 * time.Millisecond) 402 403 endpoint = fmt.Sprintf("http://%s/lookup?topic=%s", lookupd.RealHTTPAddr(), topicName) 404 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).GETV1(endpoint, &lr) 405 test.Nil(t, err) 406 407 test.Equal(t, 0, len(lr.Producers)) 408 409 var dd map[string][]interface{} 410 endpoint = fmt.Sprintf("http://%s/debug", lookupd.RealHTTPAddr()) 411 err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).GETV1(endpoint, &dd) 412 test.Nil(t, err) 413 414 test.Equal(t, 0, len(dd["topic:"+topicName+":"])) 415 test.Equal(t, 0, len(dd["channel:"+topicName+":ch"])) 416 } 417 418 func TestSetHealth(t *testing.T) { 419 opts := NewOptions() 420 opts.Logger = test.NewTestLogger(t) 421 nsqd, err := New(opts) 422 test.Nil(t, err) 423 defer nsqd.Exit() 424 425 test.Nil(t, nsqd.GetError()) 426 test.Equal(t, true, nsqd.IsHealthy()) 427 428 nsqd.SetHealth(nil) 429 test.Nil(t, nsqd.GetError()) 430 test.Equal(t, true, nsqd.IsHealthy()) 431 432 nsqd.SetHealth(errors.New("health error")) 433 test.NotNil(t, nsqd.GetError()) 434 test.Equal(t, "NOK - health error", nsqd.GetHealth()) 435 test.Equal(t, false, nsqd.IsHealthy()) 436 437 nsqd.SetHealth(nil) 438 test.Nil(t, nsqd.GetError()) 439 test.Equal(t, "OK", nsqd.GetHealth()) 440 test.Equal(t, true, nsqd.IsHealthy()) 441 } 442 443 func TestUnixSocketStartup(t *testing.T) { 444 isSocket := func(path string) bool { 445 fileInfo, err := os.Stat(path) 446 if err != nil { 447 return false 448 } 449 return fileInfo.Mode().Type() == fs.ModeSocket 450 } 451 452 opts := NewOptions() 453 opts.Logger = test.NewTestLogger(t) 454 455 _, _, nsqd := mustUnixSocketStartNSQD(opts) 456 defer os.RemoveAll(opts.DataPath) 457 defer nsqd.Exit() 458 459 test.Equal(t, isSocket(opts.TCPAddress), true) 460 test.Equal(t, isSocket(opts.HTTPAddress), true) 461 }