// Internal goroutine that reads from the socket and writes events to // the channel func (mc *Client) runFeed(ch chan TapEvent, feed *TapFeed) { defer close(ch) var headerBuf [transport.HDR_LEN]byte loop: for { // Read the next request from the server. // // (Can't call mc.Receive() because it reads a // _response_ not a request.) var pkt transport.MCRequest n, err := pkt.Receive(mc.conn, headerBuf[:]) if TapRecvHook != nil { TapRecvHook(&pkt, n, err) } if err != nil { if err != io.EOF { feed.Error = err } break loop } //logging.Warnf("** TapFeed received %#v : %q", pkt, pkt.Body) if pkt.Opcode == transport.TAP_CONNECT { // This is not an event from the server; it's // an error response to my connect request. feed.Error = fmt.Errorf("tap connection failed: %s", pkt.Body) break loop } event := makeTapEvent(pkt) if event != nil { if event.Opcode == tapEndStream { break loop } select { case ch <- *event: case <-feed.closer: break loop } } if len(pkt.Extras) >= 4 { reqFlags := binary.BigEndian.Uint16(pkt.Extras[2:]) if reqFlags&transport.TAP_ACK != 0 { if _, err := mc.sendAck(&pkt); err != nil { feed.Error = err break loop } } } } if err := mc.Close(); err != nil { logging.Warnf("Error closing memcached client: %v", err) } }
func TestTransmitReq(t *testing.T) { b := bytes.NewBuffer([]byte{}) buf := bufio.NewWriter(b) req := transport.MCRequest{ Opcode: transport.SET, Cas: 938424885, Opaque: 7242, VBucket: 824, Extras: []byte{}, Key: []byte("somekey"), Body: []byte("somevalue"), } // Verify nil transmit is OK _, err := transmitRequest(nil, &req) if err != errNoConn { t.Errorf("Expected errNoConn with no conn, got %v", err) } _, err = transmitRequest(buf, &req) if err != nil { t.Fatalf("Error transmitting request: %v", err) } buf.Flush() expected := []byte{ transport.REQ_MAGIC, byte(transport.SET), 0x0, 0x7, // length of key 0x0, // extra length 0x0, // reserved 0x3, 0x38, // vbucket 0x0, 0x0, 0x0, 0x10, // Length of value 0x0, 0x0, 0x1c, 0x4a, // opaque 0x0, 0x0, 0x0, 0x0, 0x37, 0xef, 0x3a, 0x35, // CAS 's', 'o', 'm', 'e', 'k', 'e', 'y', 's', 'o', 'm', 'e', 'v', 'a', 'l', 'u', 'e'} if len(b.Bytes()) != req.Size() { t.Fatalf("Expected %v bytes, got %v", req.Size(), len(b.Bytes())) } if !reflect.DeepEqual(b.Bytes(), expected) { t.Fatalf("Expected:\n%#v\n -- got -- \n%#v", expected, b.Bytes()) } }
// receive loop func (feed *DcpFeed) doReceive(rcvch chan []interface{}, conn *Client) { defer close(rcvch) var headerBuf [transport.HDR_LEN]byte var duration time.Duration var start time.Time var blocked bool epoc := time.Now() tick := time.Tick(time.Minute * 5) // log every 5 minutes. for { pkt := transport.MCRequest{} // always a new instance. bytes, err := pkt.Receive(conn.conn, headerBuf[:]) if err != nil && err == io.EOF { logging.Infof("%v EOF received\n", feed.logPrefix) break } else if feed.isClosed() { logging.Infof("%v doReceive(): connection closed\n", feed.logPrefix) break } else if err != nil { logging.Errorf("%v doReceive(): %v\n", feed.logPrefix, err) break } logging.Tracef("%v packet received %#v", feed.logPrefix, pkt) if len(rcvch) == cap(rcvch) { start, blocked = time.Now(), true } rcvch <- []interface{}{&pkt, bytes} if blocked { duration += time.Since(start) blocked = false select { case <-tick: percent := float64(duration) / float64(time.Since(epoc)) fmsg := "%v DCP-socket -> projector %f%% blocked" logging.Infof(fmsg, feed.logPrefix, percent) default: } } } }
func BenchmarkTransmitReqNull(b *testing.B) { req := transport.MCRequest{ Opcode: transport.SET, Cas: 938424885, Opaque: 7242, VBucket: 824, Extras: []byte{}, Key: []byte("somekey"), Body: []byte("somevalue"), } b.SetBytes(int64(req.Size())) for i := 0; i < b.N; i++ { _, err := transmitRequest(ioutil.Discard, &req) if err != nil { b.Fatalf("Error transmitting request: %v", err) } } }
func BenchmarkTransmitReqLarge(b *testing.B) { bout := bytes.NewBuffer([]byte{}) req := transport.MCRequest{ Opcode: transport.SET, Cas: 938424885, Opaque: 7242, VBucket: 824, Extras: []byte{}, Key: []byte("somekey"), Body: make([]byte, 24*1024), } b.SetBytes(int64(req.Size())) for i := 0; i < b.N; i++ { bout.Reset() buf := bufio.NewWriterSize(bout, req.Size()*2) _, err := transmitRequest(buf, &req) if err != nil { b.Fatalf("Error transmitting request: %v", err) } } }