func doTap(b Bucket, req *gomemcached.MCRequest, r io.Reader, chpkt chan<- transmissible, cherr <-chan error) *gomemcached.MCResponse { tc, err := req.ParseTapCommands() if err != nil { return &gomemcached.MCResponse{ Status: gomemcached.EINVAL, Body: []byte(fmt.Sprintf("ParseTapCommands err: %v", err)), } } // TODO: TAP of a vbucket list. res, yesDump := tapFlagBool(&tc, gomemcached.DUMP) if res != nil { return res } if yesDump || tapFlagExists(&tc, gomemcached.BACKFILL) { res := doTapBackFill(b, req, r, chpkt, cherr, tc) if res != nil { return res } if yesDump { close(chpkt) return &gomemcached.MCResponse{Fatal: true} } } // TODO: There's probably a mutation gap between backfill and tap-forward. return doTapForward(b, req, r, chpkt, cherr, tc) }
func sendGet(key string) { req := gomemcached.MCRequest{ Opcode: gomemcached.GET, Cas: 938424885, Opaque: 7242, VBucket: 824, Extras: []byte{}, Key: []byte(key), Body: []byte{}, } conn, _ := net.Dial("tcp", "localhost:9955") conn.Write(req.Bytes()) res := gomemcached.MCResponse{} _, err := res.Receive(bufio.NewReader(conn), nil) if err != nil { fmt.Println("Error: ", err.Error()) } fmt.Println(res.String()) value := string(res.Body[:len(res.Body)]) fmt.Println(value) conn.Close() }
func doReceive( uprconn *uprConnection, host string, msgch chan msgT, killSwitch chan bool) { var hdr [mcd.HDR_LEN]byte var msg msgT var pkt mcd.MCRequest var err error mcconn := uprconn.conn.Hijack() loop: for { if _, err = pkt.Receive(mcconn, hdr[:]); err != nil { msg = msgT{uprconn: uprconn, err: err} } else { msg = msgT{uprconn: uprconn, pkt: pkt} } select { case msgch <- msg: case <-killSwitch: break loop } } return }
// UPR_FAILOVER_LOG, synchronous call. func (client *Client) UprFailOverLog( req *mc.MCRequest) ([][2]uint64, error) { req.Opcode = UPR_FAILOVER_LOG // #OpCode req.Opaque = 0xDEADBEEF // #Opaque req.Key = []byte{} // #Key req.Extras = []byte{} // #Extras // Trasmit the request if err := client.conn.Transmit(req); err != nil { return nil, err } res := <-client.response // Wait for response from doRecieve() if res.Opcode != UPR_FAILOVER_LOG { err := fmt.Errorf("UprFailOverLog: unexpected #opcode", res.Opcode) return nil, err } else if req.Opaque != res.Opaque { err := fmt.Errorf( "UprFailOverLog: #opaque mismatch", req.Opaque, res.Opaque) return nil, err } else if len(res.Body)%16 != 0 { err := fmt.Errorf( "UprFailOverLog: Invalide body of length", len(res.Body)) return nil, err } else if res.Status != mc.SUCCESS { return nil, fmt.Errorf("UprOpen: Status", res.Status) } // Return the log return parseFailoverLog(res.Body), nil }
// UPR_OPEN, synchronous call. func (client *Client) UprOpen( req *mc.MCRequest, name string, seqNo, flags uint32) error { if len(name) > 65535 { log.Panicln("UprOpen: name cannot exceed 65535") } req.Opcode = UPR_OPEN // #OpCode req.Key = []byte(name) // #Key req.Extras = make([]byte, 8) binary.BigEndian.PutUint32(req.Extras[:4], seqNo) // #Extras.sequenceNo // while consumer is opening the connection Type flag needs to be cleared. binary.BigEndian.PutUint32(req.Extras[4:], flags) // #Extras.flags // Trasmit the request if err := client.conn.Transmit(req); err != nil { return err } client.name = name res := <-client.response // Wait for response from doRecieve() if res == nil { return fmt.Errorf("UprOpen: closed") } else if res.Opcode != UPR_OPEN { return fmt.Errorf("UprOpen: unexpected #opcode", res.Opcode) } else if req.Opaque != res.Opaque { return fmt.Errorf("UprOpen: #opaque mismatch", req.Opaque, res.Opaque) } else if res.Status != mc.SUCCESS { return fmt.Errorf("UprOpen: Status", res.Status) } return nil }
// 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 [gomemcached.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 gomemcached.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 } //log.Printf("** TapFeed received %#v : %q", pkt, pkt.Body) if pkt.Opcode == gomemcached.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&gomemcached.TAP_ACK != 0 { if _, err := mc.sendAck(&pkt); err != nil { feed.Error = err break loop } } } } if err := mc.Close(); err != nil { log.Printf("Error closing memcached client: %v", err) } }
func transmitRequest(o io.Writer, req *gomemcached.MCRequest) (int, error) { if o == nil { return 0, errNoConn } n, err := req.Transmit(o) if TransmitHook != nil { TransmitHook(req, n, err) } return n, err }
func transmitRequest(o io.Writer, req *gomemcached.MCRequest) (err error) { if len(req.Body) < 128 { _, err = o.Write(req.Bytes()) } else { _, err = o.Write(req.HeaderBytes()) if err == nil && len(req.Body) > 0 { _, err = o.Write(req.Body) } } return }
func TestTransmitReq(t *testing.T) { b := bytes.NewBuffer([]byte{}) buf := bufio.NewWriter(b) req := gomemcached.MCRequest{ Opcode: gomemcached.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{ gomemcached.REQ_MAGIC, byte(gomemcached.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()) } }
func BenchmarkTransmitReqNull(b *testing.B) { req := gomemcached.MCRequest{ Opcode: gomemcached.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 := gomemcached.MCRequest{ Opcode: gomemcached.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) } } }
func sendUnknownCommand() { req := gomemcached.MCRequest{ Opcode: gomemcached.ADD, Cas: 938424885, Opaque: 7242, VBucket: 824, Extras: []byte{}, Key: []byte("key"), Body: []byte("somevalue"), } conn, _ := net.Dial("tcp", "localhost:9955") conn.Write(req.Bytes()) res := gomemcached.MCResponse{} _, err := res.Receive(bufio.NewReader(conn), nil) if err != nil { fmt.Println("Error: ", err.Error()) } fmt.Println(res.String()) conn.Close() }
func transmitRequest(o io.Writer, req *gomemcached.MCRequest) (err error) { if o == nil { return noConn } return req.Transmit(o) }
func transmitRequest(o io.Writer, req *gomemcached.MCRequest) (err error) { _, err = o.Write(req.Bytes()) return }
// UPR_STREAM_REQ, synchronous call. func (client *Client) UprStream( req *mc.MCRequest, flags uint32, startSeqno, endSeqno, vuuid, highSeqno uint64) (*Stream, uint64, error) { req.Opcode = UPR_STREAM_REQ // #OpCode // #Opaque req.Key = []byte{} // #Keys req.Extras = make([]byte, 40) binary.BigEndian.PutUint32(req.Extras[:4], flags) binary.BigEndian.PutUint32(req.Extras[4:8], uint32(0)) binary.BigEndian.PutUint64(req.Extras[8:16], startSeqno) binary.BigEndian.PutUint64(req.Extras[16:24], endSeqno) binary.BigEndian.PutUint64(req.Extras[24:32], vuuid) binary.BigEndian.PutUint64(req.Extras[32:40], highSeqno) // #Extras stream := client.NewStream(req.VBucket, vuuid, req.Opaque) client.addStream(req.Opaque, stream) if client.conn != nil { if err := client.conn.Transmit(req); err != nil { // Transmit request return nil, 0, err } } else { err := fmt.Errorf("Trying to open a stream on a closed connection") return nil, 0, err } res := <-client.response // Wait for response if res == nil { return nil, 0, errors.New("closed") } else if res.Opcode != UPR_STREAM_REQ { err := fmt.Errorf("UprStream: unexpected #opcode", res.Opcode) return nil, 0, err } else if req.Opaque != res.Opaque { err := fmt.Errorf("UprStream: #opaque mismatch", req.Opaque, res.Opaque) return nil, 0, err } // If not success, remove the Stream reference from client connection, // that was optimistically added above. if res.Status != mc.SUCCESS { client.evictStream(req.Opaque) } // Check whether it is rollback var err error switch res.Status { case mc.SUCCESS: stream.Log = parseFailoverLog(res.Body) log.Println("Stream req ", stream.Log) return stream, 0, err case ROLLBACK: if len(res.Extras) != 8 { err = fmt.Errorf("UprStream: Invalid rollback", res.Extras) } rollback := binary.BigEndian.Uint64(res.Extras) return nil, rollback, err default: return nil, 0, fmt.Errorf("UprStream: Status", res.Status) } }