func TestChannelEmptyConsumer(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) tcpAddr, _, nsqd := mustStartNSQd(NewNsqdOptions()) defer nsqd.Exit() conn, _ := mustConnectNSQd(tcpAddr) topicName := "test_channel_empty" + strconv.Itoa(int(time.Now().Unix())) topic := nsqd.GetTopic(topicName) channel := topic.GetChannel("channel") client := NewClientV2(0, conn, &Context{nsqd}) client.SetReadyCount(25) channel.AddClient(client.ID, client) for i := 0; i < 25; i++ { msg := nsq.NewMessage(<-nsqd.idChan, []byte("test")) channel.StartInFlightTimeout(msg, 0) client.SendingMessage() } for _, cl := range channel.clients { stats := cl.Stats() assert.Equal(t, stats.InFlightCount, int64(25)) } channel.Empty() for _, cl := range channel.clients { stats := cl.Stats() assert.Equal(t, stats.InFlightCount, int64(0)) } }
// exercise the basic operations of the V2 protocol func TestBasicV2(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) options := NewNsqdOptions() options.clientTimeout = 60 * time.Second tcpAddr, _ := mustStartNSQd(options) defer nsqd.Exit() topicName := "test_v2" + strconv.Itoa(int(time.Now().Unix())) topic := nsqd.GetTopic(topicName) msg := nsq.NewMessage(<-nsqd.idChan, []byte("test body")) topic.PutMessage(msg) conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) identify(t, conn) sub(t, conn, topicName, "ch") err = nsq.Ready(1).Write(conn) assert.Equal(t, err, nil) resp, err := nsq.ReadResponse(conn) assert.Equal(t, err, nil) frameType, data, err := nsq.UnpackResponse(resp) msgOut, _ := nsq.DecodeMessage(data) assert.Equal(t, frameType, nsq.FrameTypeMessage) assert.Equal(t, msgOut.Id, msg.Id) assert.Equal(t, msgOut.Body, msg.Body) assert.Equal(t, msgOut.Attempts, uint16(1)) }
func TestChannelEmpty(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) nsqd := NewNSQd(1, NewNsqdOptions()) defer nsqd.Exit() topicName := "test_channel_empty" + strconv.Itoa(int(time.Now().Unix())) topic := nsqd.GetTopic(topicName) channel := topic.GetChannel("channel") msgs := make([]*nsq.Message, 0, 25) for i := 0; i < 25; i++ { msg := nsq.NewMessage(<-nsqd.idChan, []byte("test")) channel.StartInFlightTimeout(msg, 0) msgs = append(msgs, msg) } channel.RequeueMessage(0, msgs[len(msgs)-1].Id, 100*time.Millisecond) assert.Equal(t, len(channel.inFlightMessages), 24) assert.Equal(t, len(channel.inFlightPQ), 24) assert.Equal(t, len(channel.deferredMessages), 1) assert.Equal(t, len(channel.deferredPQ), 1) channel.Empty() assert.Equal(t, len(channel.inFlightMessages), 0) assert.Equal(t, len(channel.inFlightPQ), 0) assert.Equal(t, len(channel.deferredMessages), 0) assert.Equal(t, len(channel.deferredPQ), 0) assert.Equal(t, channel.Depth(), int64(0)) }
func TestInFlightWorker(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) options := NewNsqdOptions() options.msgTimeout = 200 * time.Millisecond nsqd := NewNSQd(1, options) defer nsqd.Exit() topicName := "test_in_flight_worker" + strconv.Itoa(int(time.Now().Unix())) topic := nsqd.GetTopic(topicName) channel := topic.GetChannel("channel") for i := 0; i < 1000; i++ { msg := nsq.NewMessage(<-nsqd.idChan, []byte("test")) channel.StartInFlightTimeout(msg, 0) } assert.Equal(t, len(channel.inFlightMessages), 1000) assert.Equal(t, len(channel.inFlightPQ), 1000) // the in flight worker has a resolution of 100ms so we need to wait // at least that much longer than our msgTimeout (in worst case) time.Sleep(options.msgTimeout + 100*time.Millisecond) assert.Equal(t, len(channel.inFlightMessages), 0) assert.Equal(t, len(channel.inFlightPQ), 0) }
func putHandler(w http.ResponseWriter, req *http.Request) { reqParams, err := util.NewReqParams(req) if err != nil { log.Printf("ERROR: failed to parse request params - %s", err.Error()) util.ApiResponse(w, 500, "INVALID_REQUEST", nil) return } topicName, err := reqParams.Get("topic") if err != nil { util.ApiResponse(w, 500, "MISSING_ARG_TOPIC", nil) return } if !nsq.IsValidTopicName(topicName) { util.ApiResponse(w, 500, "INVALID_ARG_TOPIC", nil) return } if int64(len(reqParams.Body)) > nsqd.options.maxMessageSize { util.ApiResponse(w, 500, "MSG_TOO_BIG", nil) return } topic := nsqd.GetTopic(topicName) msg := nsq.NewMessage(<-nsqd.idChan, reqParams.Body) err = topic.PutMessage(msg) if err != nil { util.ApiResponse(w, 500, "NOK", nil) return } w.Header().Set("Content-Length", "2") io.WriteString(w, "OK") }
func TestInFlightWorker(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) options := NewNsqdOptions() options.msgTimeout = 300 * time.Millisecond nsqd = NewNSQd(1, options) defer nsqd.Exit() topicName := "test_in_flight_worker" + strconv.Itoa(int(time.Now().Unix())) topic := nsqd.GetTopic(topicName) channel := topic.GetChannel("channel") for i := 0; i < 1000; i++ { msg := nsq.NewMessage(<-nsqd.idChan, []byte("test")) channel.StartInFlightTimeout(msg, NewClientV2(nil)) } assert.Equal(t, len(channel.inFlightMessages), 1000) assert.Equal(t, len(channel.inFlightPQ), 1000) time.Sleep(350 * time.Millisecond) assert.Equal(t, len(channel.inFlightMessages), 0) assert.Equal(t, len(channel.inFlightPQ), 0) }
func BenchmarkTopicToChannelPut(b *testing.B) { b.StopTimer() log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) topicName := "bench_topic_to_channel_put" + strconv.Itoa(b.N) channelName := "bench" options := NewNsqdOptions() options.memQueueSize = int64(b.N) nsqd := NewNSQd(1, options) defer nsqd.Exit() channel := nsqd.GetTopic(topicName).GetChannel(channelName) b.StartTimer() for i := 0; i <= b.N; i++ { topic := nsqd.GetTopic(topicName) msg := nsq.NewMessage(<-nsqd.idChan, []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaa")) topic.PutMessage(msg) } for { if len(channel.memoryMsgChan) == b.N { break } runtime.Gosched() } }
func TestStats(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) options := NewNsqdOptions() tcpAddr, _, nsqd := mustStartNSQd(options) defer nsqd.Exit() topicName := "test_stats" + strconv.Itoa(int(time.Now().Unix())) topic := nsqd.GetTopic(topicName) msg := nsq.NewMessage(<-nsqd.idChan, []byte("test body")) topic.PutMessage(msg) conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) identify(t, conn) sub(t, conn, topicName, "ch") stats := nsqd.getStats() assert.Equal(t, len(stats), 1) assert.Equal(t, len(stats[0].Channels), 1) assert.Equal(t, len(stats[0].Channels[0].Clients), 1) log.Printf("stats: %+v", stats) }
// messagePump selects over the in-memory and backend queue and // writes messages to every channel for this topic func (t *Topic) messagePump() { var msg *nsq.Message var buf []byte var err error for { // do an extra check for exit before we select on all the memory/backend/exitChan // this solves the case where we are closed and something else is writing into // backend. we don't want to reverse that if atomic.LoadInt32(&t.exitFlag) == 1 { goto exit } select { case msg = <-t.memoryMsgChan: case buf = <-t.backend.ReadChan(): msg, err = nsq.DecodeMessage(buf) if err != nil { log.Printf("ERROR: failed to decode message - %s", err.Error()) continue } case <-t.exitChan: goto exit } t.RLock() // check if all the channels have been deleted if len(t.channelMap) == 0 { // put this message back on the queue // we need to background because we currently hold the lock go func() { t.PutMessage(msg) }() // reset the sync.Once t.messagePumpStarter = new(sync.Once) t.RUnlock() goto exit } for _, channel := range t.channelMap { // copy the message because each channel // needs a unique instance chanMsg := nsq.NewMessage(msg.Id, msg.Body) chanMsg.Timestamp = msg.Timestamp err := channel.PutMessage(chanMsg) if err != nil { log.Printf("TOPIC(%s) ERROR: failed to put msg(%s) to channel(%s) - %s", t.name, msg.Id, channel.name, err.Error()) } } t.RUnlock() } exit: log.Printf("TOPIC(%s): closing ... messagePump", t.name) }
func BenchmarkTopicPut(b *testing.B) { b.StopTimer() log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) topicName := "bench_topic_put" + strconv.Itoa(b.N) options := NewNsqdOptions() options.memQueueSize = int64(b.N) nsqd := NewNSQd(1, options) defer nsqd.Exit() b.StartTimer() for i := 0; i <= b.N; i++ { topic := nsqd.GetTopic(topicName) msg := nsq.NewMessage(<-nsqd.idChan, []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaa")) topic.PutMessage(msg) } }
func TestMultipleConsumerV2(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) msgChan := make(chan *nsq.Message) options := NewNsqdOptions() options.clientTimeout = 60 * time.Second tcpAddr, _ := mustStartNSQd(options) defer nsqd.Exit() topicName := "test_multiple_v2" + strconv.Itoa(int(time.Now().Unix())) topic := nsqd.GetTopic(topicName) msg := nsq.NewMessage(<-nsqd.idChan, []byte("test body")) topic.GetChannel("ch1") topic.GetChannel("ch2") topic.PutMessage(msg) for _, i := range []string{"1", "2"} { conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) identify(t, conn) sub(t, conn, topicName, "ch"+i) err = nsq.Ready(1).Write(conn) assert.Equal(t, err, nil) go func(c net.Conn) { resp, _ := nsq.ReadResponse(c) _, data, _ := nsq.UnpackResponse(resp) msg, _ := nsq.DecodeMessage(data) msgChan <- msg }(conn) } msgOut := <-msgChan assert.Equal(t, msgOut.Id, msg.Id) assert.Equal(t, msgOut.Body, msg.Body) assert.Equal(t, msgOut.Attempts, uint16(1)) msgOut = <-msgChan assert.Equal(t, msgOut.Id, msg.Id) assert.Equal(t, msgOut.Body, msg.Body) assert.Equal(t, msgOut.Attempts, uint16(1)) }
func (s *httpServer) mputHandler(w http.ResponseWriter, req *http.Request) { if req.Method != "POST" { util.ApiResponse(w, 500, "INVALID_REQUEST", nil) return } reqParams, err := util.NewReqParams(req) if err != nil { log.Printf("ERROR: failed to parse request params - %s", err.Error()) util.ApiResponse(w, 500, "INVALID_REQUEST", nil) return } topicName, err := reqParams.Get("topic") if err != nil { util.ApiResponse(w, 500, "MISSING_ARG_TOPIC", nil) return } if !nsq.IsValidTopicName(topicName) { util.ApiResponse(w, 500, "INVALID_ARG_TOPIC", nil) return } topic := s.context.nsqd.GetTopic(topicName) for _, block := range bytes.Split(reqParams.Body, []byte("\n")) { if len(block) != 0 { if int64(len(reqParams.Body)) > s.context.nsqd.options.maxMessageSize { util.ApiResponse(w, 500, "MSG_TOO_BIG", nil) return } msg := nsq.NewMessage(<-s.context.nsqd.idChan, block) err := topic.PutMessage(msg) if err != nil { util.ApiResponse(w, 500, "NOK", nil) return } } } w.Header().Set("Content-Length", "2") io.WriteString(w, "OK") }
// ensure that we can push a message through a topic and get it out of a channel func TestPutMessage(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) nsqd := NewNSQd(1, NewNsqdOptions()) defer nsqd.Exit() topicName := "test_put_message" + strconv.Itoa(int(time.Now().Unix())) topic := nsqd.GetTopic(topicName) channel1 := topic.GetChannel("ch") var id nsq.MessageID msg := nsq.NewMessage(id, []byte("test")) topic.PutMessage(msg) outputMsg := <-channel1.clientMsgChan assert.Equal(t, msg.Id, outputMsg.Id) assert.Equal(t, msg.Body, outputMsg.Body) }
func TestEphemeralChannel(t *testing.T) { // a normal channel sticks around after clients disconnect; an ephemeral channel is // lazily removed after the last client disconnects log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) options := NewNsqdOptions() options.memQueueSize = 100 _, _, nsqd := mustStartNSQd(options) topicName := "ephemeral_test" + strconv.Itoa(int(time.Now().Unix())) doneExitChan := make(chan int) exitChan := make(chan int) go func() { <-exitChan nsqd.Exit() doneExitChan <- 1 }() body := []byte("an_ephemeral_message") topic := nsqd.GetTopic(topicName) ephemeralChannel := topic.GetChannel("ch1#ephemeral") client := NewClientV2(0, nil, &Context{nsqd}) ephemeralChannel.AddClient(client.ID, client) msg := nsq.NewMessage(<-nsqd.idChan, body) topic.PutMessage(msg) msg = <-ephemeralChannel.clientMsgChan assert.Equal(t, msg.Body, body) log.Printf("pulling from channel") ephemeralChannel.RemoveClient(client.ID) time.Sleep(50 * time.Millisecond) assert.Equal(t, len(topic.channelMap), 0) exitChan <- 1 <-doneExitChan }
func TestDeleteLast(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) nsqd := NewNSQd(1, NewNsqdOptions()) defer nsqd.Exit() topic := nsqd.GetTopic("test") channel1 := topic.GetChannel("ch1") assert.NotEqual(t, nil, channel1) err := topic.DeleteExistingChannel("ch1") assert.Equal(t, nil, err) assert.Equal(t, 0, len(topic.channelMap)) msg := nsq.NewMessage(<-nsqd.idChan, []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaa")) err = topic.PutMessage(msg) time.Sleep(100 * time.Millisecond) assert.Equal(t, nil, err) assert.Equal(t, topic.Depth(), int64(1)) }
func (p *ProtocolV2) PUB(client *ClientV2, params [][]byte) ([]byte, error) { var err error var bodyLen int32 if len(params) < 2 { return nil, nsq.NewFatalClientErr(nil, "E_INVALID", "PUB insufficient number of parameters") } topicName := string(params[1]) if !nsq.IsValidTopicName(topicName) { return nil, nsq.NewFatalClientErr(nil, "E_BAD_TOPIC", fmt.Sprintf("PUB topic name '%s' is not valid", topicName)) } err = binary.Read(client.Reader, binary.BigEndian, &bodyLen) if err != nil { return nil, nsq.NewFatalClientErr(err, "E_BAD_MESSAGE", "PUB failed to read message body size") } if int64(bodyLen) > nsqd.options.maxMessageSize { return nil, nsq.NewFatalClientErr(nil, "E_BAD_MESSAGE", fmt.Sprintf("PUB message too big %d > %d", bodyLen, nsqd.options.maxMessageSize)) } messageBody := make([]byte, bodyLen) _, err = io.ReadFull(client.Reader, messageBody) if err != nil { return nil, nsq.NewFatalClientErr(err, "E_BAD_MESSAGE", "PUB failed to read message body") } topic := nsqd.GetTopic(topicName) msg := nsq.NewMessage(<-nsqd.idChan, messageBody) err = topic.PutMessage(msg) if err != nil { return nil, nsq.NewFatalClientErr(err, "E_PUB_FAILED", "PUB failed "+err.Error()) } return []byte("OK"), nil }
// messagePump selects over the in-memory and backend queue and // writes messages to every channel for this topic func (t *Topic) messagePump() { var msg *nsq.Message var buf []byte var err error var chans []*Channel var memoryMsgChan chan *nsq.Message var backendChan chan []byte t.RLock() for _, c := range t.channelMap { chans = append(chans, c) } t.RUnlock() if len(chans) > 0 { memoryMsgChan = t.memoryMsgChan backendChan = t.backend.ReadChan() } for { select { case msg = <-memoryMsgChan: case buf = <-backendChan: msg, err = nsq.DecodeMessage(buf) if err != nil { log.Printf("ERROR: failed to decode message - %s", err.Error()) continue } case <-t.channelUpdateChan: chans = make([]*Channel, 0) t.RLock() for _, c := range t.channelMap { chans = append(chans, c) } t.RUnlock() if len(chans) == 0 { memoryMsgChan = nil backendChan = nil } else { memoryMsgChan = t.memoryMsgChan backendChan = t.backend.ReadChan() } continue case <-t.exitChan: goto exit } for i, channel := range chans { chanMsg := msg // copy the message because each channel // needs a unique instance but... // fastpath to avoid copy if its the first channel // (the topic already created the first copy) if i > 0 { chanMsg = nsq.NewMessage(msg.Id, msg.Body) chanMsg.Timestamp = msg.Timestamp } err := channel.PutMessage(chanMsg) if err != nil { log.Printf("TOPIC(%s) ERROR: failed to put msg(%s) to channel(%s) - %s", t.name, msg.Id, channel.name, err.Error()) } } } exit: log.Printf("TOPIC(%s): closing ... messagePump", t.name) }
// messagePump selects over the in-memory and backend queue and // writes messages to every channel for this topic func (t *Topic) messagePump() { var msg *nsq.Message var buf []byte var err error var chans []*Channel t.RLock() for _, c := range t.channelMap { chans = append(chans, c) } t.RUnlock() for { select { case msg = <-t.memoryMsgChan: case buf = <-t.backend.ReadChan(): msg, err = nsq.DecodeMessage(buf) if err != nil { log.Printf("ERROR: failed to decode message - %s", err.Error()) continue } case <-t.channelUpdateChan: chans = chans[:0] t.RLock() for _, c := range t.channelMap { chans = append(chans, c) } t.RUnlock() continue case <-t.exitChan: goto exit } // check if all the channels have been deleted if len(chans) == 0 { // put this message back on the queue t.PutMessage(msg) // reset the sync.Once t.messagePumpStarter = new(sync.Once) goto exit } for i, channel := range chans { chanMsg := msg // copy the message because each channel // needs a unique instance but... // fastpath to avoid copy if its the first channel // (the topic already created the first copy) if i > 0 { chanMsg = nsq.NewMessage(msg.Id, msg.Body) chanMsg.Timestamp = msg.Timestamp } err := channel.PutMessage(chanMsg) if err != nil { log.Printf("TOPIC(%s) ERROR: failed to put msg(%s) to channel(%s) - %s", t.name, msg.Id, channel.name, err.Error()) } } } exit: log.Printf("TOPIC(%s): closing ... messagePump", t.name) }
func (s *httpServer) mputHandler(w http.ResponseWriter, req *http.Request) { var msgs []*nsq.Message var exit bool if req.Method != "POST" { util.ApiResponse(w, 500, "INVALID_REQUEST", nil) return } reqParams, err := url.ParseQuery(req.URL.RawQuery) if err != nil { log.Printf("ERROR: failed to parse request params - %s", err.Error()) util.ApiResponse(w, 500, "INVALID_REQUEST", nil) } topicNames, ok := reqParams["topic"] if !ok { util.ApiResponse(w, 500, "MISSING_ARG_TOPIC", nil) return } topicName := topicNames[0] if !nsq.IsValidTopicName(topicName) { util.ApiResponse(w, 500, "INVALID_ARG_TOPIC", nil) return } topic := s.context.nsqd.GetTopic(topicName) rdr := bufio.NewReader(req.Body) for !exit { block, err := rdr.ReadBytes('\n') if err != nil { if err != io.EOF { util.ApiResponse(w, 500, "INTERNAL_ERROR", nil) return } exit = true } if len(block) > 0 && block[len(block)-1] == '\n' { block = block[:len(block)-1] } if int64(len(block)) > s.context.nsqd.options.maxMessageSize { util.ApiResponse(w, 500, "MSG_TOO_BIG", nil) return } msg := nsq.NewMessage(<-s.context.nsqd.idChan, block) msgs = append(msgs, msg) } err = topic.PutMessages(msgs) if err != nil { util.ApiResponse(w, 500, "NOK", nil) return } w.Header().Set("Content-Length", "2") io.WriteString(w, "OK") }
func TestStartup(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) iterations := 300 doneExitChan := make(chan int) options := NewNsqdOptions() options.memQueueSize = 100 options.maxBytesPerFile = 10240 mustStartNSQd(options) topicName := "nsqd_test" + strconv.Itoa(int(time.Now().Unix())) exitChan := make(chan int) go func() { <-exitChan nsqd.Exit() doneExitChan <- 1 }() body := make([]byte, 256) topic := nsqd.GetTopic(topicName) for i := 0; i < iterations; i++ { msg := nsq.NewMessage(<-nsqd.idChan, body) topic.PutMessage(msg) } log.Printf("pulling from channel") channel1 := topic.GetChannel("ch1") log.Printf("read %d msgs", iterations/2) for i := 0; i < iterations/2; i++ { msg := <-channel1.clientMsgChan log.Printf("read message %d", i+1) assert.Equal(t, msg.Body, body) } for { if channel1.Depth() == int64(iterations/2) { break } time.Sleep(50 * time.Millisecond) } exitChan <- 1 <-doneExitChan // start up a new nsqd w/ the same folder options = NewNsqdOptions() options.memQueueSize = 100 options.maxBytesPerFile = 10240 mustStartNSQd(options) go func() { <-exitChan nsqd.Exit() doneExitChan <- 1 }() topic = nsqd.GetTopic(topicName) // should be empty; channel should have drained everything count := topic.Depth() assert.Equal(t, count, int64(0)) channel1 = topic.GetChannel("ch1") chan_count := channel1.Depth() assert.Equal(t, chan_count, int64(iterations/2)) // read the other half of the messages for i := 0; i < iterations/2; i++ { msg := <-channel1.clientMsgChan log.Printf("read message %d", i+1) assert.Equal(t, msg.Body, body) } // verify we drained things assert.Equal(t, len(topic.memoryMsgChan), 0) assert.Equal(t, topic.backend.Depth(), int64(0)) exitChan <- 1 <-doneExitChan }
func (p *ProtocolV2) MPUB(client *ClientV2, params [][]byte) ([]byte, error) { var err error if len(params) < 2 { return nil, nsq.NewFatalClientErr(nil, "E_INVALID", "MPUB insufficient number of parameters") } topicName := string(params[1]) if !nsq.IsValidTopicName(topicName) { return nil, nsq.NewFatalClientErr(nil, "E_BAD_TOPIC", fmt.Sprintf("E_BAD_TOPIC MPUB topic name '%s' is not valid", topicName)) } bodyLen, err := p.readLen(client) if err != nil { return nil, nsq.NewFatalClientErr(err, "E_BAD_BODY", "MPUB failed to read body size") } if int64(bodyLen) > nsqd.options.maxBodySize { return nil, nsq.NewFatalClientErr(nil, "E_BAD_BODY", fmt.Sprintf("MPUB body too big %d > %d", bodyLen, nsqd.options.maxBodySize)) } numMessages, err := p.readLen(client) if err != nil { return nil, nsq.NewFatalClientErr(err, "E_BAD_BODY", "MPUB failed to read message count") } messages := make([]*nsq.Message, 0, numMessages) for i := int32(0); i < numMessages; i++ { messageSize, err := p.readLen(client) if err != nil { return nil, nsq.NewFatalClientErr(err, "E_BAD_MESSAGE", fmt.Sprintf("MPUB failed to read message(%d) body size", i)) } if int64(messageSize) > nsqd.options.maxMessageSize { return nil, nsq.NewFatalClientErr(nil, "E_BAD_MESSAGE", fmt.Sprintf("MPUB message too big %d > %d", messageSize, nsqd.options.maxMessageSize)) } msgBody := make([]byte, messageSize) _, err = io.ReadFull(client.Reader, msgBody) if err != nil { return nil, nsq.NewFatalClientErr(err, "E_BAD_MESSAGE", "MPUB failed to read message body") } messages = append(messages, nsq.NewMessage(<-nsqd.idChan, msgBody)) } topic := nsqd.GetTopic(topicName) // if we've made it this far we've validated all the input, // the only possible error is that the topic is exiting during // this next call (and no messages will be queued in that case) err = topic.PutMessages(messages) if err != nil { return nil, nsq.NewFatalClientErr(err, "E_MPUB_FAILED", "MPUB failed "+err.Error()) } return okBytes, nil }
func TestClientTimeout(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) topicName := "test_client_timeout_v2" + strconv.Itoa(int(time.Now().Unix())) options := NewNsqdOptions() options.clientTimeout = 50 * time.Millisecond tcpAddr, _ := mustStartNSQd(options) defer nsqd.Exit() conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) identify(t, conn) sub(t, conn, topicName, "ch") time.Sleep(50 * time.Millisecond) // depending on timing there may be 1 or 2 hearbeats sent // just read until we get an error timer := time.After(100 * time.Millisecond) for { select { case <-timer: t.Fatalf("test timed out") default: _, err := nsq.ReadResponse(conn) if err != nil { goto done } } } done: } func TestClientHeartbeat(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) topicName := "test_hb_v2" + strconv.Itoa(int(time.Now().Unix())) options := NewNsqdOptions() options.clientTimeout = 100 * time.Millisecond tcpAddr, _ := mustStartNSQd(options) defer nsqd.Exit() conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) identify(t, conn) sub(t, conn, topicName, "ch") err = nsq.Ready(1).Write(conn) assert.Equal(t, err, nil) resp, _ := nsq.ReadResponse(conn) _, data, _ := nsq.UnpackResponse(resp) assert.Equal(t, data, []byte("_heartbeat_")) time.Sleep(10 * time.Millisecond) err = nsq.Nop().Write(conn) assert.Equal(t, err, nil) // wait long enough that would have timed out (had we not sent the above cmd) time.Sleep(50 * time.Millisecond) err = nsq.Nop().Write(conn) assert.Equal(t, err, nil) } func TestClientHeartbeatDisableSUB(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) topicName := "test_hb_v2" + strconv.Itoa(int(time.Now().Unix())) options := NewNsqdOptions() options.clientTimeout = 200 * time.Millisecond tcpAddr, _ := mustStartNSQd(options) defer nsqd.Exit() conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) identifyHeartbeatInterval(t, conn, -1, nsq.FrameTypeResponse, "OK") subFail(t, conn, topicName, "ch") } func TestClientHeartbeatDisable(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) options := NewNsqdOptions() options.clientTimeout = 100 * time.Millisecond tcpAddr, _ := mustStartNSQd(options) defer nsqd.Exit() conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) identifyHeartbeatInterval(t, conn, -1, nsq.FrameTypeResponse, "OK") time.Sleep(150 * time.Millisecond) err = nsq.Nop().Write(conn) assert.Equal(t, err, nil) } func TestMaxHeartbeatIntervalValid(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) options := NewNsqdOptions() options.maxHeartbeatInterval = 300 * time.Second tcpAddr, _ := mustStartNSQd(options) defer nsqd.Exit() conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) hbi := int(options.maxHeartbeatInterval / time.Millisecond) identifyHeartbeatInterval(t, conn, hbi, nsq.FrameTypeResponse, "OK") } func TestMaxHeartbeatIntervalInvalid(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) options := NewNsqdOptions() options.maxHeartbeatInterval = 300 * time.Second tcpAddr, _ := mustStartNSQd(options) defer nsqd.Exit() conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) hbi := int(options.maxHeartbeatInterval/time.Millisecond + 1) identifyHeartbeatInterval(t, conn, hbi, nsq.FrameTypeError, "E_BAD_BODY IDENTIFY heartbeat interval (300001) is invalid") } func TestPausing(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) topicName := "test_pause_v2" + strconv.Itoa(int(time.Now().Unix())) tcpAddr, _ := mustStartNSQd(NewNsqdOptions()) defer nsqd.Exit() conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) identify(t, conn) sub(t, conn, topicName, "ch") err = nsq.Ready(1).Write(conn) assert.Equal(t, err, nil) topic := nsqd.GetTopic(topicName) msg := nsq.NewMessage(<-nsqd.idChan, []byte("test body")) channel := topic.GetChannel("ch") topic.PutMessage(msg) // receive the first message via the client, finish it, and send new RDY resp, _ := nsq.ReadResponse(conn) _, data, _ := nsq.UnpackResponse(resp) msg, err = nsq.DecodeMessage(data) assert.Equal(t, msg.Body, []byte("test body")) err = nsq.Finish(msg.Id).Write(conn) assert.Equal(t, err, nil) err = nsq.Ready(1).Write(conn) assert.Equal(t, err, nil) // sleep to allow the RDY state to take effect time.Sleep(50 * time.Millisecond) // pause the channel... the client shouldn't receive any more messages channel.Pause() // sleep to allow the paused state to take effect time.Sleep(50 * time.Millisecond) msg = nsq.NewMessage(<-nsqd.idChan, []byte("test body2")) topic.PutMessage(msg) // allow the client to possibly get a message, the test would hang indefinitely // if pausing was not working on the internal clientMsgChan read time.Sleep(50 * time.Millisecond) msg = <-channel.clientMsgChan assert.Equal(t, msg.Body, []byte("test body2")) // unpause the channel... the client should now be pushed a message channel.UnPause() msg = nsq.NewMessage(<-nsqd.idChan, []byte("test body3")) topic.PutMessage(msg) resp, _ = nsq.ReadResponse(conn) _, data, _ = nsq.UnpackResponse(resp) msg, err = nsq.DecodeMessage(data) assert.Equal(t, msg.Body, []byte("test body3")) } func TestEmptyCommand(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) tcpAddr, _ := mustStartNSQd(NewNsqdOptions()) defer nsqd.Exit() conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) _, err = conn.Write([]byte("\n\n")) assert.Equal(t, err, nil) // if we didn't panic here we're good, see issue #120 } func TestSizeLimits(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) options := NewNsqdOptions() *verbose = true options.maxMessageSize = 100 options.maxBodySize = 1000 tcpAddr, _ := mustStartNSQd(options) defer nsqd.Exit() conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) topicName := "test_limits_v2" + strconv.Itoa(int(time.Now().Unix())) identify(t, conn) sub(t, conn, topicName, "ch") nsq.Publish(topicName, make([]byte, 95)).Write(conn) resp, _ := nsq.ReadResponse(conn) frameType, data, _ := nsq.UnpackResponse(resp) log.Printf("frameType: %d, data: %s", frameType, data) assert.Equal(t, frameType, nsq.FrameTypeResponse) assert.Equal(t, data, []byte("OK")) nsq.Publish(topicName, make([]byte, 105)).Write(conn) resp, _ = nsq.ReadResponse(conn) frameType, data, _ = nsq.UnpackResponse(resp) log.Printf("frameType: %d, data: %s", frameType, data) assert.Equal(t, frameType, nsq.FrameTypeError) assert.Equal(t, string(data), fmt.Sprintf("E_BAD_MESSAGE PUB message too big 105 > 100")) // need to reconnect conn, err = mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) // MPUB body that's valid mpub := make([][]byte, 0) for i := 0; i < 5; i++ { mpub = append(mpub, make([]byte, 100)) } cmd, _ := nsq.MultiPublish(topicName, mpub) cmd.Write(conn) resp, _ = nsq.ReadResponse(conn) frameType, data, _ = nsq.UnpackResponse(resp) log.Printf("frameType: %d, data: %s", frameType, data) assert.Equal(t, frameType, nsq.FrameTypeResponse) assert.Equal(t, data, []byte("OK")) // MPUB body that's invalid (body too big) mpub = make([][]byte, 0) for i := 0; i < 11; i++ { mpub = append(mpub, make([]byte, 100)) } cmd, _ = nsq.MultiPublish(topicName, mpub) cmd.Write(conn) resp, _ = nsq.ReadResponse(conn) frameType, data, _ = nsq.UnpackResponse(resp) log.Printf("frameType: %d, data: %s", frameType, data) assert.Equal(t, frameType, nsq.FrameTypeError) assert.Equal(t, string(data), fmt.Sprintf("E_BAD_BODY MPUB body too big 1148 > 1000")) // need to reconnect conn, err = mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) // MPUB body that's invalid (one of the messages is too big) mpub = make([][]byte, 0) for i := 0; i < 5; i++ { mpub = append(mpub, make([]byte, 101)) } cmd, _ = nsq.MultiPublish(topicName, mpub) cmd.Write(conn) resp, _ = nsq.ReadResponse(conn) frameType, data, _ = nsq.UnpackResponse(resp) log.Printf("frameType: %d, data: %s", frameType, data) assert.Equal(t, frameType, nsq.FrameTypeError) assert.Equal(t, string(data), fmt.Sprintf("E_BAD_MESSAGE MPUB message too big 101 > 100")) } func TestTouch(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) *verbose = true options := NewNsqdOptions() options.msgTimeout = 50 * time.Millisecond tcpAddr, _ := mustStartNSQd(options) defer nsqd.Exit() topicName := "test_touch" + strconv.Itoa(int(time.Now().Unix())) conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) identify(t, conn) sub(t, conn, topicName, "ch") topic := nsqd.GetTopic(topicName) channel := topic.GetChannel("ch") msg := nsq.NewMessage(<-nsqd.idChan, []byte("test body")) topic.PutMessage(msg) err = nsq.Ready(1).Write(conn) assert.Equal(t, err, nil) resp, err := nsq.ReadResponse(conn) assert.Equal(t, err, nil) frameType, data, err := nsq.UnpackResponse(resp) msgOut, _ := nsq.DecodeMessage(data) assert.Equal(t, frameType, nsq.FrameTypeMessage) assert.Equal(t, msgOut.Id, msg.Id) time.Sleep(25 * time.Millisecond) err = nsq.Touch(msg.Id).Write(conn) assert.Equal(t, err, nil) time.Sleep(30 * time.Millisecond) err = nsq.Finish(msg.Id).Write(conn) assert.Equal(t, err, nil) assert.Equal(t, channel.timeoutCount, uint64(0)) } func TestMaxRdyCount(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) *verbose = true options := NewNsqdOptions() options.maxRdyCount = 50 tcpAddr, _ := mustStartNSQd(options) defer nsqd.Exit() topicName := "test_max_rdy_count" + strconv.Itoa(int(time.Now().Unix())) conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) topic := nsqd.GetTopic(topicName) msg := nsq.NewMessage(<-nsqd.idChan, []byte("test body")) topic.PutMessage(msg) data := identifyFeatureNegotiation(t, conn) r := struct { MaxRdyCount int64 `json:"max_rdy_count"` }{} err = json.Unmarshal(data, &r) assert.Equal(t, err, nil) assert.Equal(t, r.MaxRdyCount, int64(50)) sub(t, conn, topicName, "ch") err = nsq.Ready(int(options.maxRdyCount)).Write(conn) assert.Equal(t, err, nil) resp, err := nsq.ReadResponse(conn) assert.Equal(t, err, nil) frameType, data, err := nsq.UnpackResponse(resp) msgOut, _ := nsq.DecodeMessage(data) assert.Equal(t, frameType, nsq.FrameTypeMessage) assert.Equal(t, msgOut.Id, msg.Id) err = nsq.Ready(int(options.maxRdyCount) + 1).Write(conn) assert.Equal(t, err, nil) resp, err = nsq.ReadResponse(conn) assert.Equal(t, err, nil) frameType, data, err = nsq.UnpackResponse(resp) assert.Equal(t, frameType, int32(1)) assert.Equal(t, string(data), "E_INVALID RDY count 51 out of range 0-50") } func TestFatalError(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) tcpAddr, _ := mustStartNSQd(NewNsqdOptions()) defer nsqd.Exit() conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) _, err = conn.Write([]byte("ASDF\n")) assert.Equal(t, err, nil) resp, err := nsq.ReadResponse(conn) assert.Equal(t, err, nil) frameType, data, err := nsq.UnpackResponse(resp) assert.Equal(t, frameType, int32(1)) assert.Equal(t, string(data), "E_INVALID invalid command ASDF") _, err = nsq.ReadResponse(conn) assert.NotEqual(t, err, nil) } func TestOutputBuffering(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) *verbose = true options := NewNsqdOptions() options.maxOutputBufferSize = 512 * 1024 options.maxOutputBufferTimeout = time.Second tcpAddr, _ := mustStartNSQd(options) defer nsqd.Exit() topicName := "test_output_buffering" + strconv.Itoa(int(time.Now().Unix())) conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) outputBufferSize := 256 * 1024 outputBufferTimeout := 500 topic := nsqd.GetTopic(topicName) msg := nsq.NewMessage(<-nsqd.idChan, make([]byte, outputBufferSize-1024)) topic.PutMessage(msg) identifyOutputBuffering(t, conn, outputBufferSize, outputBufferTimeout, nsq.FrameTypeResponse, "OK") sub(t, conn, topicName, "ch") err = nsq.Ready(10).Write(conn) assert.Equal(t, err, nil) start := time.Now() resp, err := nsq.ReadResponse(conn) assert.Equal(t, err, nil) end := time.Now() assert.Equal(t, int(end.Sub(start)/time.Millisecond) >= outputBufferTimeout, true) frameType, data, err := nsq.UnpackResponse(resp) msgOut, _ := nsq.DecodeMessage(data) assert.Equal(t, frameType, nsq.FrameTypeMessage) assert.Equal(t, msgOut.Id, msg.Id) } func TestOutputBufferingValidity(t *testing.T) { log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) *verbose = true options := NewNsqdOptions() options.maxOutputBufferSize = 512 * 1024 options.maxOutputBufferTimeout = time.Second tcpAddr, _ := mustStartNSQd(options) defer nsqd.Exit() conn, err := mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) identifyOutputBuffering(t, conn, 512*1024, 1000, nsq.FrameTypeResponse, "OK") identifyOutputBuffering(t, conn, -1, -1, nsq.FrameTypeResponse, "OK") identifyOutputBuffering(t, conn, 0, 0, nsq.FrameTypeResponse, "OK") identifyOutputBuffering(t, conn, 512*1024+1, 0, nsq.FrameTypeError, fmt.Sprintf("E_BAD_BODY IDENTIFY output buffer size (%d) is invalid", 512*1024+1)) conn, err = mustConnectNSQd(tcpAddr) assert.Equal(t, err, nil) identifyOutputBuffering(t, conn, 0, 1001, nsq.FrameTypeError, "E_BAD_BODY IDENTIFY output buffer timeout (1001) is invalid") } func BenchmarkProtocolV2Exec(b *testing.B) { b.StopTimer() log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) p := &ProtocolV2{} c := NewClientV2(nil) params := [][]byte{[]byte("NOP")} b.StartTimer() for i := 0; i < b.N; i++ { p.Exec(c, params) } } func benchmarkProtocolV2Pub(b *testing.B, size int) { var wg sync.WaitGroup b.StopTimer() log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) options := NewNsqdOptions() options.memQueueSize = int64(b.N) tcpAddr, _ := mustStartNSQd(options) msg := make([]byte, size) batchSize := 200 batch := make([][]byte, 0) for i := 0; i < batchSize; i++ { batch = append(batch, msg) } topicName := "bench_v2_pub" + strconv.Itoa(int(time.Now().Unix())) b.SetBytes(int64(len(msg))) b.StartTimer() for j := 0; j < runtime.GOMAXPROCS(0); j++ { wg.Add(1) go func() { conn, err := mustConnectNSQd(tcpAddr) if err != nil { panic(err.Error()) } rw := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) num := b.N / runtime.GOMAXPROCS(0) / batchSize for i := 0; i < num; i += 1 { cmd, _ := nsq.MultiPublish(topicName, batch) err := cmd.Write(rw) if err != nil { panic(err.Error()) } err = rw.Flush() if err != nil { panic(err.Error()) } resp, err := nsq.ReadResponse(rw) if err != nil { panic(err.Error()) } _, data, _ := nsq.UnpackResponse(resp) if !bytes.Equal(data, []byte("OK")) { panic("invalid response") } } wg.Done() }() } wg.Wait() b.StopTimer() nsqd.Exit() } func BenchmarkProtocolV2Pub256(b *testing.B) { benchmarkProtocolV2Pub(b, 256) } func BenchmarkProtocolV2Pub512(b *testing.B) { benchmarkProtocolV2Pub(b, 512) } func BenchmarkProtocolV2Pub1k(b *testing.B) { benchmarkProtocolV2Pub(b, 1024) } func BenchmarkProtocolV2Pub2k(b *testing.B) { benchmarkProtocolV2Pub(b, 2*1024) } func BenchmarkProtocolV2Pub4k(b *testing.B) { benchmarkProtocolV2Pub(b, 4*1024) } func BenchmarkProtocolV2Pub8k(b *testing.B) { benchmarkProtocolV2Pub(b, 8*1024) } func BenchmarkProtocolV2Pub16k(b *testing.B) { benchmarkProtocolV2Pub(b, 16*1024) } func BenchmarkProtocolV2Pub32k(b *testing.B) { benchmarkProtocolV2Pub(b, 32*1024) } func BenchmarkProtocolV2Pub64k(b *testing.B) { benchmarkProtocolV2Pub(b, 64*1024) } func BenchmarkProtocolV2Pub128k(b *testing.B) { benchmarkProtocolV2Pub(b, 128*1024) } func BenchmarkProtocolV2Pub256k(b *testing.B) { benchmarkProtocolV2Pub(b, 256*1024) } func BenchmarkProtocolV2Pub512k(b *testing.B) { benchmarkProtocolV2Pub(b, 512*1024) } func BenchmarkProtocolV2Pub1m(b *testing.B) { benchmarkProtocolV2Pub(b, 1024*1024) } func benchmarkProtocolV2Sub(b *testing.B, size int) { var wg sync.WaitGroup b.StopTimer() log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) options := NewNsqdOptions() options.memQueueSize = int64(b.N) tcpAddr, _ := mustStartNSQd(options) msg := make([]byte, size) topicName := "bench_v2_sub" + strconv.Itoa(b.N) + strconv.Itoa(int(time.Now().Unix())) topic := nsqd.GetTopic(topicName) for i := 0; i < b.N; i++ { msg := nsq.NewMessage(<-nsqd.idChan, msg) topic.PutMessage(msg) } topic.GetChannel("ch") b.SetBytes(int64(len(msg))) goChan := make(chan int) rdyChan := make(chan int) workers := runtime.GOMAXPROCS(0) for j := 0; j < workers; j++ { wg.Add(1) go func() { subWorker(b.N, workers, tcpAddr, topicName, rdyChan, goChan) wg.Done() }() <-rdyChan } b.StartTimer() close(goChan) wg.Wait() b.StopTimer() nsqd.Exit() } func subWorker(n int, workers int, tcpAddr *net.TCPAddr, topicName string, rdyChan chan int, goChan chan int) { conn, err := mustConnectNSQd(tcpAddr) if err != nil { panic(err.Error()) } rw := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) identify(nil, conn) sub(nil, conn, topicName, "ch") rdyCount := int(math.Min(math.Max(float64(n/workers), 1), 2500)) rdyChan <- 1 <-goChan nsq.Ready(rdyCount).Write(rw) rw.Flush() num := n / workers numRdy := num/rdyCount - 1 rdy := rdyCount for i := 0; i < num; i += 1 { resp, err := nsq.ReadResponse(rw) if err != nil { panic(err.Error()) } frameType, data, err := nsq.UnpackResponse(resp) if err != nil { panic(err.Error()) } if frameType != nsq.FrameTypeMessage { panic("got something else") } msg, err := nsq.DecodeMessage(data) if err != nil { panic(err.Error()) } nsq.Finish(msg.Id).Write(rw) rdy-- if rdy == 0 && numRdy > 0 { nsq.Ready(rdyCount).Write(rw) rdy = rdyCount numRdy-- rw.Flush() } } } func BenchmarkProtocolV2Sub256(b *testing.B) { benchmarkProtocolV2Sub(b, 256) } func BenchmarkProtocolV2Sub512(b *testing.B) { benchmarkProtocolV2Sub(b, 512) } func BenchmarkProtocolV2Sub1k(b *testing.B) { benchmarkProtocolV2Sub(b, 1024) } func BenchmarkProtocolV2Sub2k(b *testing.B) { benchmarkProtocolV2Sub(b, 2*1024) } func BenchmarkProtocolV2Sub4k(b *testing.B) { benchmarkProtocolV2Sub(b, 4*1024) } func BenchmarkProtocolV2Sub8k(b *testing.B) { benchmarkProtocolV2Sub(b, 8*1024) } func BenchmarkProtocolV2Sub16k(b *testing.B) { benchmarkProtocolV2Sub(b, 16*1024) } func BenchmarkProtocolV2Sub32k(b *testing.B) { benchmarkProtocolV2Sub(b, 32*1024) } func BenchmarkProtocolV2Sub64k(b *testing.B) { benchmarkProtocolV2Sub(b, 64*1024) } func BenchmarkProtocolV2Sub128k(b *testing.B) { benchmarkProtocolV2Sub(b, 128*1024) } func BenchmarkProtocolV2Sub256k(b *testing.B) { benchmarkProtocolV2Sub(b, 256*1024) } func BenchmarkProtocolV2Sub512k(b *testing.B) { benchmarkProtocolV2Sub(b, 512*1024) } func BenchmarkProtocolV2Sub1m(b *testing.B) { benchmarkProtocolV2Sub(b, 1024*1024) } func benchmarkProtocolV2MultiSub(b *testing.B, num int) { var wg sync.WaitGroup b.StopTimer() log.SetOutput(ioutil.Discard) defer log.SetOutput(os.Stdout) options := NewNsqdOptions() options.memQueueSize = int64(b.N) tcpAddr, _ := mustStartNSQd(options) msg := make([]byte, 256) b.SetBytes(int64(len(msg) * num)) goChan := make(chan int) rdyChan := make(chan int) workers := runtime.GOMAXPROCS(0) for i := 0; i < num; i++ { topicName := "bench_v2" + strconv.Itoa(b.N) + "_" + strconv.Itoa(i) + "_" + strconv.Itoa(int(time.Now().Unix())) topic := nsqd.GetTopic(topicName) for i := 0; i < b.N; i++ { msg := nsq.NewMessage(<-nsqd.idChan, msg) topic.PutMessage(msg) } topic.GetChannel("ch") for j := 0; j < workers; j++ { wg.Add(1) go func() { subWorker(b.N, workers, tcpAddr, topicName, rdyChan, goChan) wg.Done() }() <-rdyChan } } b.StartTimer() close(goChan) wg.Wait() b.StopTimer() nsqd.Exit() } func BenchmarkProtocolV2MultiSub1(b *testing.B) { benchmarkProtocolV2MultiSub(b, 1) } func BenchmarkProtocolV2MultiSub2(b *testing.B) { benchmarkProtocolV2MultiSub(b, 2) } func BenchmarkProtocolV2MultiSub4(b *testing.B) { benchmarkProtocolV2MultiSub(b, 4) } func BenchmarkProtocolV2MultiSub8(b *testing.B) { benchmarkProtocolV2MultiSub(b, 8) } func BenchmarkProtocolV2MultiSub16(b *testing.B) { benchmarkProtocolV2MultiSub(b, 16) }