ronantakizawa/codereview-bench huggingface dataset:
Repository: nsqio/nsq
File: apps/nsq_to_nsq/nsq_to_nsq.go (Go)

Code before:
			}
		}
	}

	return pass, backoff
}

func filterMessage(jsonMsg *simplejson.Json, rawMsg []byte) []byte {
	if len(whitelistJsonFields) == 0 {
		// no change
		return rawMsg
	}

	msg, err := jsonMsg.Map()
	if err != nil {
		log.Printf("ERROR: json is not an object: %s", rawMsg)
		return []byte("{}") // cheezy failure mode
	}

	newMsg := make(map[string]interface{}, len(whitelistJsonFields))

	for _, key := range whitelistJsonFields {
		value, ok := msg[key]
		if ok {
			// avoid printing int as float (go 1.0)
			switch tvalue := value.(type) {
			case float64:
				ivalue := int64(tvalue)
				if float64(ivalue) == tvalue {
					newMsg[key] = ivalue
				} else {
					newMsg[key] = tvalue
				}
			default:
				newMsg[key] = value
			}
		}
	}

	newRawMsg, err := json.Marshal(newMsg)
	if err != nil {
		log.Printf("ERROR: Unable to marshal filtered message: %s", err)
		return []byte("{}") // cheezy failure mode
	}
	return newRawMsg
}

func (ph *PublishHandler) HandleMessage(m *nsq.Message, respChan chan *nsq.FinishedMessage) {
	var err error
	msgBody := m.Body


Review comment (suggestion):
this method should include an error return value and just propagate these errors, the caller should decide what to do with them (in this case it should probably requeue with backoff?)


Code after:
			if strVal != *requireJsonValue {
				pass = false
			}
		} else if ph.requireJsonValueIsNumber {
			floatVal, err := jsonVal.Float64()
			if err != nil || ph.requireJsonNumber != floatVal {
				pass = false
			}
		} else {
			// json value wasn't a plain string, and argument wasn't a number
			// give up on comparisons of other types
			pass = false
		}
	}

	return pass, backoff
}

func filterMessage(jsonMsg *simplejson.Json, rawMsg []byte) ([]byte, error) {
	if len(whitelistJsonFields) == 0 {
		// no change
		return rawMsg, nil
	}

	msg, err := jsonMsg.Map()
	if err != nil {
		return nil, errors.New("json is not an object")
	}

	newMsg := make(map[string]interface{}, len(whitelistJsonFields))

	for _, key := range whitelistJsonFields {
		value, ok := msg[key]
		if ok {
			// avoid printing int as float (go 1.0)
			switch tvalue := value.(type) {
			case float64:
				ivalue := int64(tvalue)
				if float64(ivalue) == tvalue {
					newMsg[key] = ivalue
				} else {
					newMsg[key] = tvalue
				}
			default:
				newMsg[key] = value
			}
		}
	}

	newRawMsg, err := json.Marshal(newMsg)
	if err != nil {
Repository: nsqio/nsq
File: nsqd/protocol_v2_test.go (Go)

Code before:
	for i := 0; i < num; i++ {
		msg := nsq.NewMessage(<-nsqd.idChan, []byte("test body"))
		topic.PutMessage(msg)
	}
	channel := topic.GetChannel("ch")

	// let the topic drain into the channel
	time.Sleep(50 * time.Millisecond)

	sub(t, conn, topicName, "ch")
	err = nsq.Ready(num).Write(conn)
	assert.Equal(t, err, nil)

	doneChan := make(chan int)
	go func() {
		for {
			if channel.Depth() == 0 {
				close(doneChan)
				return
			}
			time.Sleep(5 * time.Millisecond)
		}
	}()
	<-doneChan
	// within 3%
	assert.Equal(t, len(channel.inFlightMessages) <= int(float64(num)*0.45), true)
	assert.Equal(t, len(channel.inFlightMessages) >= int(float64(num)*0.39), true)
}

func TestTLSSnappy(t *testing.T) {
	log.SetOutput(ioutil.Discard)
	defer log.SetOutput(os.Stdout)

	*verbose = true
	options := NewNSQDOptions()
	options.SnappyEnabled = true
	options.TLSCert = "./test/cert.pem"
	options.TLSKey = "./test/key.pem"
	tcpAddr, _, nsqd := mustStartNSQD(options)
	defer nsqd.Exit()

	conn, err := mustConnectNSQD(tcpAddr)
	assert.Equal(t, err, nil)

	data := identifyFeatureNegotiation(t, conn, map[string]interface{}{"tls_v1": true, "snappy": true})
	r := struct {
		TLSv1  bool `json:"tls_v1"`
		Snappy bool `json:"snappy"`
	}{}
	err = json.Unmarshal(data, &r)
	assert.Equal(t, err, nil)

Review comment (suggestion):
more specifically, the channel's lock currently protects `inFlightMessages`, but it probably should be `channel.inFlightMutex` - I don't want to change that in this PR though.


Code after:
	for i := 0; i < num; i++ {
		msg := nsq.NewMessage(<-nsqd.idChan, []byte("test body"))
		topic.PutMessage(msg)
	}
	channel := topic.GetChannel("ch")

	// let the topic drain into the channel
	time.Sleep(50 * time.Millisecond)

	sub(t, conn, topicName, "ch")
	err = nsq.Ready(num).Write(conn)
	assert.Equal(t, err, nil)

	doneChan := make(chan int)
	go func() {
		for {
			if channel.Depth() == 0 {
				close(doneChan)
				return
			}
			time.Sleep(5 * time.Millisecond)
		}
	}()
	<-doneChan

	channel.Lock()
	numInFlight := len(channel.inFlightMessages)
	channel.Unlock()

	// within 3%
	assert.Equal(t, numInFlight <= int(float64(num)*0.45), true)
	assert.Equal(t, numInFlight >= int(float64(num)*0.39), true)
}

func TestTLSSnappy(t *testing.T) {
	log.SetOutput(ioutil.Discard)
	defer log.SetOutput(os.Stdout)

	*verbose = true
	options := NewNSQDOptions()
	options.SnappyEnabled = true
	options.TLSCert = "./test/cert.pem"
	options.TLSKey = "./test/key.pem"
	tcpAddr, _, nsqd := mustStartNSQD(options)
	defer nsqd.Exit()

	conn, err := mustConnectNSQD(tcpAddr)
	assert.Equal(t, err, nil)

	data := identifyFeatureNegotiation(t, conn, map[string]interface{}{"tls_v1": true, "snappy": true})
	r := struct {
Repository: nsqio/nsq
File: nsqd/dqname_windows.go (Go)

Code before:
// +build windows

package nsqd

// On Windows, file names cannot contain colons.
func getBackendName(topicName, channelName string) string {
	// backend names, for uniqueness, automatically include the topic... <topic>__<channel>
	backendName := topicName + "__" + channelName
	return backendName
}


Review comment (suggestion):
Actually, I don't think `__` is sufficient - ideally we'd find a character that isn't a valid NSQ topic/channel name but is a valid filename on windows (similar to `:` on linux) such that it is impossible for a topic to ever collide with a topic/channel combination...


Code after:
// +build windows

package nsqd

// On Windows, file names cannot contain colons.
func getBackendName(topicName, channelName string) string {
	// backend names, for uniqueness, automatically include the topic... <topic>;<channel>
	backendName := topicName + ";" + channelName
	return backendName
}
Repository: nsqio/nsq
File: util/lookupd/types.go (Go)

Code before:
		h = c.HostAddress
	}
	return h
}

type ClientStats struct {
	HostAddress       string
	RemoteAddress     string
	Version           string
	ClientID          string
	Hostname          string
	UserAgent         string
	ConnectedDuration time.Duration
	InFlightCount     int
	ReadyCount        int
	FinishCount       int64
	RequeueCount      int64
	MessageCount      int64
	SampleRate        int32
	TLS               bool
	Deflate           bool
	Snappy            bool
	Authed            bool
	AuthIdentity      string
	AuthIdentityUrl   string
}

func (c *ClientStats) HasUserAgent() bool {
	return c.UserAgent != ""
}

func (c *ClientStats) HasSampleRate() bool {
	return c.SampleRate > 0
}

type ChannelStatsList []*ChannelStats
type ChannelStatsByHost struct {
	ChannelStatsList
}

type ClientStatsList []*ClientStats
type ClientsByHost struct {
	ClientStatsList
}
type TopicStatsList []*TopicStats
type TopicStatsByHost struct {
	TopicStatsList
}
type ProducerList []*Producer
type ProducersByHost struct {
	ProducerList

Review comment (suggestion):
the struct tags aren't used here, they were only for reference since they match what is actually json parsed. (this is handled through simple-json not a straight unmarshal).

tls didn't have one since it just moved down a few lines.

I'm ok adding a tag for that, leaving as is, or killing the other tags.


Code after:
		h = c.HostAddress
	}
	return h
}

type ClientStats struct {
	HostAddress       string
	RemoteAddress     string
	Version           string
	ClientID          string
	Hostname          string
	UserAgent         string
	ConnectedDuration time.Duration
	InFlightCount     int
	ReadyCount        int
	FinishCount       int64
	RequeueCount      int64
	MessageCount      int64
	SampleRate        int32
	Deflate           bool
	Snappy            bool
	Authed            bool
	AuthIdentity      string
	AuthIdentityUrl   string

	TLS                           bool
	CipherSuite                   string `json:"tls_cipher_suite"`
	TLSVersion                    string `json:"tls_version"`
	TLSNegotiatedProtocol         string `json:"tls_negotiated_protocol"`
	TLSNegotiatedProtocolIsMutual bool   `json:"tls_negotiated_protocol_is_mutual"`
}

func (c *ClientStats) HasUserAgent() bool {
	return c.UserAgent != ""
}

func (c *ClientStats) HasSampleRate() bool {
	return c.SampleRate > 0
}

type ChannelStatsList []*ChannelStats
type ChannelStatsByHost struct {
	ChannelStatsList
}

type ClientStatsList []*ClientStats
type ClientsByHost struct {
	ClientStatsList
}
type TopicStatsList []*TopicStats
type TopicStatsByHost struct {
Repository: nsqio/nsq
File: apps/nsq_to_file/nsq_to_file.go (Go)

Code before:
				if err != nil {
					log.Fatalf("ERROR: failed syncing messages - %s", err)
				}
				for pos > 0 {
					pos--
					m := output[pos]
					m.Finish()
					output[pos] = nil
				}
			}
			sync = false
		}

		if closeFile {
			f.Close()
			closeFile = false
		}
		if exit {
			close(f.ExitChan)
			break
		}
	}
}

func (f *FileLogger) Close() {
	if f.out != nil {
		f.out.Sync()
		if f.gzipWriter != nil {
			f.gzipWriter.Close()
		}
		f.out.Close()
		f.out = nil
	}
}

func (f *FileLogger) Write(p []byte) (n int, err error) {
	return f.writer.Write(p)
}

func (f *FileLogger) Sync() error {
	var err error
	if f.gzipWriter != nil {
		f.gzipWriter.Close()
		err = f.out.Sync()
		w := &AccountingWriter{f: f}
		f.gzipWriter, _ = gzip.NewWriterLevel(w, f.compressionLevel)
		f.writer = f.gzipWriter
	} else {
		err = f.out.Sync()
	}
	return err

Review comment (suggestion):
aha, do you mean:

``` go
func (f *FileLogger) Write(p []byte) (n int, err error) {
    f.filesize += int64(len(p))
    return f.out.Write(p)
}
```

``` go
if f.gzipEnabled {
    f.gzipWriter, _ = gzip.NewWriterLevel(f, f.compressionLevel)
    f.writer = f.gzipWriter
} else {
    f.writer = f 
}
```

``` go
_, err := f.writer.Write(m.Body)
```


Code after:
			sync = false
		}

		if closeFile {
			f.Close()
			closeFile = false
		}
		if exit {
			close(f.ExitChan)
			break
		}
	}
}

func (f *FileLogger) Close() {
	if f.out != nil {
		f.out.Sync()
		if f.gzipWriter != nil {
			f.gzipWriter.Close()
		}
		f.out.Close()
		f.out = nil
	}
}

func (f *FileLogger) Write(p []byte) (n int, err error) {
	f.filesize += int64(len(p))
	return f.out.Write(p)
}

func (f *FileLogger) Sync() error {
	var err error
	if f.gzipWriter != nil {
		f.gzipWriter.Close()
		err = f.out.Sync()
		f.gzipWriter, _ = gzip.NewWriterLevel(f, f.compressionLevel)
		f.writer = f.gzipWriter
	} else {
		err = f.out.Sync()
	}
	return err
}

func (f *FileLogger) calculateCurrentFilename() string {
	t := time.Now()
	datetime := strftime(*datetimeFormat, t)
	return strings.Replace(f.filenameFormat, "<DATETIME>", datetime, -1)
}

func (f *FileLogger) needsFileRotate() bool {
	if f.out == nil {
Repository: nsqio/nsq
File: apps/nsq_to_file/nsq_to_file.go (Go)

Code before:
				if err != nil {
					log.Fatalf("ERROR: failed syncing messages - %s", err)
				}
				for pos > 0 {
					pos--
					m := output[pos]
					m.Finish()
					output[pos] = nil
				}
			}
			sync = false
		}

		if closeFile {
			f.Close()
			closeFile = false
		}
		if exit {
			close(f.ExitChan)
			break
		}
	}
}

func (f *FileLogger) Close() {
	if f.out != nil {
		f.out.Sync()
		if f.gzipWriter != nil {
			f.gzipWriter.Close()
		}
		f.out.Close()
		f.out = nil
	}
}

func (f *FileLogger) Write(p []byte) (n int, err error) {
	return f.writer.Write(p)
}

func (f *FileLogger) Sync() error {
	var err error
	if f.gzipWriter != nil {
		f.gzipWriter.Close()
		err = f.out.Sync()
		w := &AccountingWriter{f: f}
		f.gzipWriter, _ = gzip.NewWriterLevel(w, f.compressionLevel)
		f.writer = f.gzipWriter
	} else {
		err = f.out.Sync()
	}
	return err

Review comment (suggestion):
this method here becomes:

``` go
func (f *FileLogger) Write(p []byte) (n int, err error) {
    f.filesize += int64(len(p))
    if f.gzipWriter != nil {
        return f.gzipWriter.Write(p)
    }
    return f.out.Write(p)
}
```

and we remove all the wrapping on lines 221 - 223.


Code after:
			sync = false
		}

		if closeFile {
			f.Close()
			closeFile = false
		}
		if exit {
			close(f.ExitChan)
			break
		}
	}
}

func (f *FileLogger) Close() {
	if f.out != nil {
		f.out.Sync()
		if f.gzipWriter != nil {
			f.gzipWriter.Close()
		}
		f.out.Close()
		f.out = nil
	}
}

func (f *FileLogger) Write(p []byte) (n int, err error) {
	f.filesize += int64(len(p))
	return f.out.Write(p)
}

func (f *FileLogger) Sync() error {
	var err error
	if f.gzipWriter != nil {
		f.gzipWriter.Close()
		err = f.out.Sync()
		f.gzipWriter, _ = gzip.NewWriterLevel(f, f.compressionLevel)
		f.writer = f.gzipWriter
	} else {
		err = f.out.Sync()
	}
	return err
}

func (f *FileLogger) calculateCurrentFilename() string {
	t := time.Now()
	datetime := strftime(*datetimeFormat, t)
	return strings.Replace(f.filenameFormat, "<DATETIME>", datetime, -1)
}

func (f *FileLogger) needsFileRotate() bool {
	if f.out == nil {
Repository: nsqio/nsq
File: apps/nsq_to_file/nsq_to_file.go (Go)

Code before:
				if err != nil {
					log.Fatalf("ERROR: failed syncing messages - %s", err)
				}
				for pos > 0 {
					pos--
					m := output[pos]
					m.Finish()
					output[pos] = nil
				}
			}
			sync = false
		}

		if closeFile {
			f.Close()
			closeFile = false
		}
		if exit {
			close(f.ExitChan)
			break
		}
	}
}

func (f *FileLogger) Close() {
	if f.out != nil {
		f.out.Sync()
		if f.gzipWriter != nil {
			f.gzipWriter.Close()
		}
		f.out.Close()
		f.out = nil
	}
}

func (f *FileLogger) Write(p []byte) (n int, err error) {
	return f.writer.Write(p)
}

func (f *FileLogger) Sync() error {
	var err error
	if f.gzipWriter != nil {
		f.gzipWriter.Close()
		err = f.out.Sync()
		w := &AccountingWriter{f: f}
		f.gzipWriter, _ = gzip.NewWriterLevel(w, f.compressionLevel)
		f.writer = f.gzipWriter
	} else {
		err = f.out.Sync()
	}
	return err

Review comment (suggestion):
but the `f.filesize` you accounting is NOT equal to the size written by gzipWriter.
In most cases,

``` go
f.filesize += int64(len(p))
```

is greater than the file size written by

``` go
f.gzipWriter.Write(p)
```


Code after:
			sync = false
		}

		if closeFile {
			f.Close()
			closeFile = false
		}
		if exit {
			close(f.ExitChan)
			break
		}
	}
}

func (f *FileLogger) Close() {
	if f.out != nil {
		f.out.Sync()
		if f.gzipWriter != nil {
			f.gzipWriter.Close()
		}
		f.out.Close()
		f.out = nil
	}
}

func (f *FileLogger) Write(p []byte) (n int, err error) {
	f.filesize += int64(len(p))
	return f.out.Write(p)
}

func (f *FileLogger) Sync() error {
	var err error
	if f.gzipWriter != nil {
		f.gzipWriter.Close()
		err = f.out.Sync()
		f.gzipWriter, _ = gzip.NewWriterLevel(f, f.compressionLevel)
		f.writer = f.gzipWriter
	} else {
		err = f.out.Sync()
	}
	return err
}

func (f *FileLogger) calculateCurrentFilename() string {
	t := time.Now()
	datetime := strftime(*datetimeFormat, t)
	return strings.Replace(f.filenameFormat, "<DATETIME>", datetime, -1)
}

func (f *FileLogger) needsFileRotate() bool {
	if f.out == nil {
Repository: nsqio/nsq
File: apps/nsq_to_file/nsq_to_file.go (Go)

Code before:
		if exit {
			close(f.ExitChan)
			break
		}
	}
}

func (f *FileLogger) Close() {
	if f.out != nil {
		f.out.Sync()
		if f.gzipWriter != nil {
			f.gzipWriter.Close()
		}
		f.out.Close()
		f.out = nil
	}
}

func (f *FileLogger) Write(p []byte) (n int, err error) {
	f.filesize += int64(len(p))
	return f.out.Write(p)
}

func (f *FileLogger) Sync() error {
	var err error
	if f.gzipWriter != nil {
		f.gzipWriter.Close()
		err = f.out.Sync()
		f.gzipWriter, _ = gzip.NewWriterLevel(f, f.compressionLevel)
		f.writer = f.gzipWriter
	} else {
		err = f.out.Sync()
	}
	return err
}

func (f *FileLogger) calculateCurrentFilename() string {
	t := time.Now()
	datetime := strftime(*datetimeFormat, t)
	return strings.Replace(f.filenameFormat, "<DATETIME>", datetime, -1)
}

func (f *FileLogger) needsFileRotate() bool {
	if f.out == nil {
		return true
	}

	filename := f.calculateCurrentFilename()
	if filename != f.lastFilename {
		log.Printf("INFO: new filename %s, need rotate", filename)
		return true // rotate by filename

Review comment (suggestion):
I'm not sure why we originally used `Close()` here but I think we can rip this out and just use http://golang.org/pkg/compress/gzip/#Writer.Flush (keep the underlying file sync though).

``` go
func (f *FileLogger) Sync() error {
    if f.gzipWriter != nil {
        err := f.gzipWriter.Flush()
        if err != nil {
            return err
        }
    }
    return f.out.Sync()
}
```


Code after:
			close(f.ExitChan)
			break
		}
	}
}

func (f *FileLogger) Close() {
	if f.out != nil {
		f.out.Sync()
		if f.gzipWriter != nil {
			f.gzipWriter.Close()
		}
		f.out.Close()
		f.out = nil
	}
}

func (f *FileLogger) Write(p []byte) (n int, err error) {
	f.filesize += int64(len(p))
	return f.out.Write(p)
}

func (f *FileLogger) Sync() error {
	var err error
	if f.gzipWriter != nil {
		f.gzipWriter.Close()
		err = f.out.Sync()
		f.gzipWriter, _ = gzip.NewWriterLevel(f, f.compressionLevel)
		f.writer = f.gzipWriter
	} else {
		err = f.out.Sync()
	}
	return err
}

func (f *FileLogger) calculateCurrentFilename() string {
	t := time.Now()
	datetime := strftime(*datetimeFormat, t)
	return strings.Replace(f.filenameFormat, "<DATETIME>", datetime, -1)
}

func (f *FileLogger) needsFileRotate() bool {
	if f.out == nil {
		return true
	}

	filename := f.calculateCurrentFilename()
	if filename != f.lastFilename {
		log.Printf("INFO: new filename %s, need rotate", filename)
		return true // rotate by filename
	}
Repository: nsqio/nsq
File: apps/nsq_to_file/nsq_to_file.go (Go)

Code before:
		if exit {
			close(f.ExitChan)
			break
		}
	}
}

func (f *FileLogger) Close() {
	if f.out != nil {
		f.out.Sync()
		if f.gzipWriter != nil {
			f.gzipWriter.Close()
		}
		f.out.Close()
		f.out = nil
	}
}

func (f *FileLogger) Write(p []byte) (n int, err error) {
	f.filesize += int64(len(p))
	return f.out.Write(p)
}

func (f *FileLogger) Sync() error {
	var err error
	if f.gzipWriter != nil {
		f.gzipWriter.Close()
		err = f.out.Sync()
		f.gzipWriter, _ = gzip.NewWriterLevel(f, f.compressionLevel)
		f.writer = f.gzipWriter
	} else {
		err = f.out.Sync()
	}
	return err
}

func (f *FileLogger) calculateCurrentFilename() string {
	t := time.Now()
	datetime := strftime(*datetimeFormat, t)
	return strings.Replace(f.filenameFormat, "<DATETIME>", datetime, -1)
}

func (f *FileLogger) needsFileRotate() bool {
	if f.out == nil {
		return true
	}

	filename := f.calculateCurrentFilename()
	if filename != f.lastFilename {
		log.Printf("INFO: new filename %s, need rotate", filename)
		return true // rotate by filename

Review comment (bug):
interesting.
the `Flush` will NOT append 'EOF' to the gzip file.

``` go
package main

import "compress/gzip"
import "os"

func main() {
    f, _ := os.OpenFile("xiaost.log.gz", os.O_WRONLY | os.O_CREATE, 0666)
    w, _ := gzip.NewWriterLevel(f, gzip.DefaultCompression)
    w.Write([]byte("xiaost\n"))
    w.Flush()
    f.Sync()
    f.Close()
}
```

``` shell
$ gzcat xiaost.log.gz
gzcat: xiaost.log.gz: unexpected end of file
gzcat: xiaost.log.gz: uncompress failed
```

but `Close` do.

what's more, I found that we use `os.O_EXCL` to avoid appending data to gzip file.
I don't know why. 
It works:

``` shell
$ echo xiaost | gzip > test.gz
$ echo xiao | gzip >> test.gz
$ gzcat test
xiaost
xiao
```


Code after:
			close(f.ExitChan)
			break
		}
	}
}

func (f *FileLogger) Close() {
	if f.out != nil {
		f.out.Sync()
		if f.gzipWriter != nil {
			f.gzipWriter.Close()
		}
		f.out.Close()
		f.out = nil
	}
}

func (f *FileLogger) Write(p []byte) (n int, err error) {
	f.filesize += int64(len(p))
	return f.out.Write(p)
}

func (f *FileLogger) Sync() error {
	var err error
	if f.gzipWriter != nil {
		f.gzipWriter.Close()
		err = f.out.Sync()
		f.gzipWriter, _ = gzip.NewWriterLevel(f, f.compressionLevel)
		f.writer = f.gzipWriter
	} else {
		err = f.out.Sync()
	}
	return err
}

func (f *FileLogger) calculateCurrentFilename() string {
	t := time.Now()
	datetime := strftime(*datetimeFormat, t)
	return strings.Replace(f.filenameFormat, "<DATETIME>", datetime, -1)
}

func (f *FileLogger) needsFileRotate() bool {
	if f.out == nil {
		return true
	}

	filename := f.calculateCurrentFilename()
	if filename != f.lastFilename {
		log.Printf("INFO: new filename %s, need rotate", filename)
		return true // rotate by filename
	}
Repository: nsqio/nsq
File: nsqd/nsqd.go (Go)

Code before:
	n.resizePool(len(channels), workCh, responseCh, closeCh)

	for {
		select {
		case <-workTicker.C:
			if len(channels) == 0 {
				continue
			}
		case <-refreshTicker.C:
			channels = n.channels()
			n.resizePool(len(channels), workCh, responseCh, closeCh)
			continue
		case <-n.exitChan:
			goto exit
		}

	loop:
		num := n.opts.QueueGCSelectionCount
		if num > len(channels) {
			num = len(channels)
		}

		for _, i := range uniqRands(num, len(channels)) {
			workCh <- channels[i]
		}

		numDirty := 0
		for i := 0; i < num; i++ {
			if <-responseCh {
				numDirty++
			}
		}

		if float64(numDirty)/float64(num) > n.opts.QueueGCDirtyPercent {
			goto loop
		}
	}

exit:
	n.logf("QUEUEGC: closing")
	workTicker.Stop()
	refreshTicker.Stop()
}

func buildTLSConfig(opts *nsqdOptions) (*tls.Config, error) {
	var tlsConfig *tls.Config

	if opts.TLSCert == "" && opts.TLSKey == "" {
		return nil, nil
	}


Review comment (bug):
making a note for myself:

This is insufficient in the case where we asynchronously get a signal from `n.exitChan` to close... there's definitely a case where this would deadlock.

We should probably select on `n.exitChan` here, too.


Code after:
		select {
		case <-workTicker.C:
			if len(channels) == 0 {
				continue
			}
		case <-refreshTicker.C:
			channels = n.channels()
			n.resizePool(len(channels), workCh, responseCh, closeCh)
			continue
		case <-n.exitChan:
			goto exit
		}

		num := n.opts.QueueScanSelectionCount
		if num > len(channels) {
			num = len(channels)
		}

	loop:
		for _, i := range util.UniqRands(num, len(channels)) {
			workCh <- channels[i]
		}

		numDirty := 0
		for i := 0; i < num; i++ {
			if <-responseCh {
				numDirty++
			}
		}

		if float64(numDirty)/float64(num) > n.opts.QueueScanDirtyPercent {
			goto loop
		}
	}

exit:
	n.logf("QUEUESCAN: closing")
	close(closeCh)
	workTicker.Stop()
	refreshTicker.Stop()
}

func buildTLSConfig(opts *nsqdOptions) (*tls.Config, error) {
	var tlsConfig *tls.Config

	if opts.TLSCert == "" && opts.TLSKey == "" {
		return nil, nil
	}

	tlsClientAuthPolicy := tls.VerifyClientCertIfGiven