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  }