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  }