예제 #1
0
func simpleCmdLocal(rw *bufio.ReadWriter) error {
	if err := rw.Flush(); err != nil {
		return err
	}

	resHeader, err := binprot.ReadResponseHeader(rw)
	if err != nil {
		return err
	}
	defer binprot.PutResponseHeader(resHeader)

	err = binprot.DecodeError(resHeader)
	if err != nil {
		n, ioerr := rw.Discard(int(resHeader.TotalBodyLength))
		metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n))
		if ioerr != nil {
			return ioerr
		}
		return err
	}

	// Read in the message bytes from the body
	n, err := rw.Discard(int(resHeader.TotalBodyLength))
	metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n))

	return err
}
예제 #2
0
파일: respond.go 프로젝트: Netflix/rend
func (t TextResponder) Get(response common.GetResponse) error {
	if response.Miss {
		// A miss is a no-op in the text world
		return nil
	}

	// Write data out to client
	// [VALUE <key> <flags> <bytes>\r\n
	// <data block>\r\n]*
	// END\r\n
	n, err := fmt.Fprintf(t.writer, "VALUE %s %d %d\r\n", response.Key, response.Flags, len(response.Data))
	metrics.IncCounterBy(common.MetricBytesWrittenRemote, uint64(n))
	if err != nil {
		return err
	}

	n, err = t.writer.Write(response.Data)
	metrics.IncCounterBy(common.MetricBytesWrittenRemote, uint64(n))
	if err != nil {
		return err
	}

	n, err = t.writer.WriteString("\r\n")
	metrics.IncCounterBy(common.MetricBytesWrittenRemote, uint64(n))
	if err != nil {
		return err
	}

	t.writer.Flush()
	return nil
}
예제 #3
0
파일: handler.go 프로젝트: Netflix/rend
func (h Handler) handleSetCommon(cmd common.SetRequest) error {
	// TODO: should there be a unique flags value for regular data?

	// Write value
	h.rw.Write(cmd.Data)
	metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(len(cmd.Data)))

	if err := h.rw.Flush(); err != nil {
		return err
	}

	// Read server's response
	resHeader, err := readResponseHeader(h.rw.Reader)
	if err != nil {
		// Discard response body
		n, ioerr := h.rw.Discard(int(resHeader.TotalBodyLength))
		metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n))
		if ioerr != nil {
			return ioerr
		}

		// For Add and Replace, the error here will be common.ErrKeyExists or common.ErrKeyNotFound
		// respectively. For each, this is the right response to send to the requestor. The error
		// here is overloaded because it would signal a true error for sets, but a normal "error"
		// response for Add and Replace.
		return err
	}

	return nil
}
예제 #4
0
파일: l1only.go 프로젝트: RSellathurai/rend
func (l *L1OnlyOrca) Get(req common.GetRequest) error {
	metrics.IncCounter(MetricCmdGet)
	metrics.IncCounterBy(MetricCmdGetKeys, uint64(len(req.Keys)))
	//debugString := "get"
	//for _, k := range req.Keys {
	//	debugString += " "
	//	debugString += string(k)
	//}
	//println(debugString)

	metrics.IncCounter(MetricCmdGetL1)
	metrics.IncCounterBy(MetricCmdGetKeysL1, uint64(len(req.Keys)))
	resChan, errChan := l.l1.Get(req)

	var err error

	// Read all the responses back from l.l1.
	// The contract is that the resChan will have GetResponse's for get hits and misses,
	// and the errChan will have any other errors, such as an out of memory error from
	// memcached. If any receive happens from errChan, there will be no more responses
	// from resChan.
	for {
		select {
		case res, ok := <-resChan:
			if !ok {
				resChan = nil
			} else {
				if res.Miss {
					metrics.IncCounter(MetricCmdGetMissesL1)
					metrics.IncCounter(MetricCmdGetMisses)
				} else {
					metrics.IncCounter(MetricCmdGetHits)
					metrics.IncCounter(MetricCmdGetHitsL1)
				}
				l.res.Get(res)
			}

		case getErr, ok := <-errChan:
			if !ok {
				errChan = nil
			} else {
				metrics.IncCounter(MetricCmdGetErrors)
				metrics.IncCounter(MetricCmdGetErrorsL1)
				err = getErr
			}
		}

		if resChan == nil && errChan == nil {
			break
		}
	}

	if err == nil {
		l.res.GetEnd(req.NoopOpaque, req.NoopEnd)
	}

	return err
}
예제 #5
0
func (h Handler) realHandleSet(cmd common.SetRequest, reqType common.RequestType) error {
	// TODO: should there be a unique flags value for regular data?
	// Write command header
	switch reqType {
	case common.RequestSet:
		if err := binprot.WriteSetCmd(h.rw.Writer, cmd.Key, cmd.Flags, cmd.Exptime, uint32(len(cmd.Data))); err != nil {
			return err
		}
	case common.RequestAdd:
		if err := binprot.WriteAddCmd(h.rw.Writer, cmd.Key, cmd.Flags, cmd.Exptime, uint32(len(cmd.Data))); err != nil {
			return err
		}
	case common.RequestReplace:
		if err := binprot.WriteReplaceCmd(h.rw.Writer, cmd.Key, cmd.Flags, cmd.Exptime, uint32(len(cmd.Data))); err != nil {
			return err
		}
	default:
		// I know. It's all wrong. By rights we shouldn't even be here. But we are.
		panic("Unrecognized request type in realHandleSet!")
	}

	// Write value
	h.rw.Write(cmd.Data)
	metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(len(cmd.Data)))

	if err := h.rw.Flush(); err != nil {
		return err
	}

	// Read server's response
	resHeader, err := readResponseHeader(h.rw.Reader)
	if err != nil {
		// Discard response body
		n, ioerr := h.rw.Discard(int(resHeader.TotalBodyLength))
		metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n))
		if ioerr != nil {
			return ioerr
		}

		// For Add and Replace, the error here will be common.ErrKeyExists or common.ErrKeyNotFound
		// respectively. For each, this is the right response to send to the requestor. The error
		// here is overloaded because it would signal a true error for sets, but a normal "error"
		// response for Add and Replace.
		return err
	}

	return nil
}
예제 #6
0
파일: respond.go 프로젝트: Netflix/rend
func writeSuccessResponseHeader(w *bufio.Writer, opcode uint8, keyLength, extraLength,
	totalBodyLength int, opaque uint32, flush bool) error {

	header := resHeadPool.Get().(ResponseHeader)

	header.Magic = MagicResponse
	header.Opcode = opcode
	header.KeyLength = uint16(keyLength)
	header.ExtraLength = uint8(extraLength)
	header.DataType = uint8(0)
	header.Status = StatusSuccess
	header.TotalBodyLength = uint32(totalBodyLength)
	header.OpaqueToken = opaque
	header.CASToken = uint64(0)

	if err := writeResponseHeader(w, header); err != nil {
		resHeadPool.Put(header)
		return err
	}

	if flush {
		if err := w.Flush(); err != nil {
			resHeadPool.Put(header)
			return err
		}
	}

	metrics.IncCounterBy(common.MetricBytesWrittenRemote, resHeaderLen)
	resHeadPool.Put(header)

	return nil
}
예제 #7
0
파일: respond.go 프로젝트: Netflix/rend
func writeErrorResponseHeader(w *bufio.Writer, opcode uint8, status uint16, opaque uint32) error {
	header := resHeadPool.Get().(ResponseHeader)

	header.Magic = MagicResponse
	header.Opcode = opcode
	header.KeyLength = uint16(0)
	header.ExtraLength = uint8(0)
	header.DataType = uint8(0)
	header.Status = status
	header.TotalBodyLength = uint32(0)
	header.OpaqueToken = opaque
	header.CASToken = uint64(0)

	if err := writeResponseHeader(w, header); err != nil {
		resHeadPool.Put(header)
		return err
	}

	if err := w.Flush(); err != nil {
		resHeadPool.Put(header)
		return err
	}

	metrics.IncCounterBy(common.MetricBytesWrittenRemote, resHeaderLen)
	resHeadPool.Put(header)

	return nil
}
예제 #8
0
파일: parser.go 프로젝트: Netflix/rend
func appendPrependRequest(r io.Reader, reqHeader RequestHeader, reqType common.RequestType, quiet bool, start uint64) (common.SetRequest, common.RequestType, uint64, error) {
	// key, value
	key, err := readString(r, reqHeader.KeyLength)
	if err != nil {
		log.Println("Error reading key")
		return common.SetRequest{}, reqType, start, err
	}

	realLength := reqHeader.TotalBodyLength - uint32(reqHeader.KeyLength)

	// Read in the body of the set request
	dataBuf := make([]byte, realLength)
	n, err := io.ReadAtLeast(r, dataBuf, int(realLength))
	metrics.IncCounterBy(common.MetricBytesReadRemote, uint64(n))
	if err != nil {
		return common.SetRequest{}, reqType, start, err
	}

	return common.SetRequest{
		Quiet:   quiet,
		Key:     key,
		Flags:   0,
		Exptime: 0,
		Opaque:  reqHeader.OpaqueToken,
		Data:    dataBuf,
	}, reqType, start, nil
}
예제 #9
0
파일: respond.go 프로젝트: Netflix/rend
func (b BinaryResponder) Version(opaque uint32) error {
	if err := writeSuccessResponseHeader(b.writer, OpcodeVersion, 0, 0, len(common.VersionString), opaque, false); err != nil {
		return err
	}
	n, _ := b.writer.WriteString(common.VersionString)
	metrics.IncCounterBy(common.MetricBytesWrittenRemote, uint64(n))
	return b.writer.Flush()
}
예제 #10
0
파일: parser.go 프로젝트: Netflix/rend
func setRequest(r *bufio.Reader, clParts []string, reqType common.RequestType, start uint64) (common.SetRequest, common.RequestType, uint64, error) {
	// sanity check
	if len(clParts) != 5 {
		return common.SetRequest{}, reqType, start, common.ErrBadRequest
	}

	key := []byte(clParts[1])

	flags, err := strconv.ParseUint(strings.TrimSpace(clParts[2]), 10, 32)
	if err != nil {
		log.Printf("Error parsing flags for set/add/replace command: %s\n", err.Error())
		return common.SetRequest{}, reqType, start, common.ErrBadFlags
	}

	exptime, err := strconv.ParseUint(strings.TrimSpace(clParts[3]), 10, 32)
	if err != nil {
		log.Printf("Error parsing ttl for set/add/replace command: %s\n", err.Error())
		return common.SetRequest{}, reqType, start, common.ErrBadExptime
	}

	length, err := strconv.ParseUint(strings.TrimSpace(clParts[4]), 10, 32)
	if err != nil {
		log.Printf("Error parsing length for set/add/replace command: %s\n", err.Error())
		return common.SetRequest{}, reqType, start, common.ErrBadLength
	}

	// Read in data
	dataBuf := make([]byte, length)
	n, err := io.ReadAtLeast(r, dataBuf, int(length))
	metrics.IncCounterBy(common.MetricBytesReadRemote, uint64(n))
	if err != nil {
		return common.SetRequest{}, reqType, start, common.ErrInternal
	}

	// Consume the last two bytes "\r\n"
	r.ReadString(byte('\n'))
	metrics.IncCounterBy(common.MetricBytesReadRemote, 2)

	return common.SetRequest{
		Key:     key,
		Flags:   uint32(flags),
		Exptime: uint32(exptime),
		Opaque:  uint32(0),
		Data:    dataBuf,
	}, reqType, start, nil
}
예제 #11
0
파일: types.go 프로젝트: RSellathurai/rend
func writeMetadata(w io.Writer, md metadata) error {
	buf := make([]byte, metadataSize-tokenSize)

	binary.BigEndian.PutUint32(buf[0:4], md.Length)
	binary.BigEndian.PutUint32(buf[4:8], md.OrigFlags)
	binary.BigEndian.PutUint32(buf[8:12], md.NumChunks)
	binary.BigEndian.PutUint32(buf[12:16], md.ChunkSize)

	n, err := w.Write(buf)
	metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(n))
	if err != nil {
		return err
	}

	n, err = w.Write(md.Token[:])
	metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(n))
	return err
}
예제 #12
0
파일: respond.go 프로젝트: Netflix/rend
func (t TextResponder) resp(s string) error {
	n, err := fmt.Fprintf(t.writer, s+"\r\n")
	metrics.IncCounterBy(common.MetricBytesWrittenRemote, uint64(n))
	if err != nil {
		return err
	}

	return t.writer.Flush()
}
예제 #13
0
func getLocal(rw *bufio.ReadWriter, readExp bool) (data []byte, flags, exp uint32, err error) {
	if err := rw.Flush(); err != nil {
		return nil, 0, 0, err
	}

	resHeader, err := binprot.ReadResponseHeader(rw)
	if err != nil {
		return nil, 0, 0, err
	}
	defer binprot.PutResponseHeader(resHeader)

	err = binprot.DecodeError(resHeader)
	if err != nil {
		n, ioerr := rw.Discard(int(resHeader.TotalBodyLength))
		metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n))
		if ioerr != nil {
			return nil, 0, 0, ioerr
		}
		return nil, 0, 0, err
	}

	var serverFlags uint32
	binary.Read(rw, binary.BigEndian, &serverFlags)
	metrics.IncCounterBy(common.MetricBytesReadLocal, 4)

	var serverExp uint32
	if readExp {
		binary.Read(rw, binary.BigEndian, &serverExp)
		metrics.IncCounterBy(common.MetricBytesReadLocal, 4)
	}

	// total body - key - extra
	dataLen := resHeader.TotalBodyLength - uint32(resHeader.KeyLength) - uint32(resHeader.ExtraLength)
	buf := make([]byte, dataLen)

	// Read in value
	n, err := io.ReadAtLeast(rw, buf, int(dataLen))
	metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n))
	if err != nil {
		return nil, 0, 0, err
	}

	return buf, serverFlags, serverExp, nil
}
예제 #14
0
파일: parser.go 프로젝트: Netflix/rend
func readString(r io.Reader, l uint16) ([]byte, error) {
	buf := make([]byte, l)
	n, err := io.ReadAtLeast(r, buf, int(l))
	metrics.IncCounterBy(common.MetricBytesReadRemote, uint64(n))
	if err != nil {
		return nil, err
	}

	return buf, nil
}
예제 #15
0
파일: localComm.go 프로젝트: Netflix/rend
func getMetadataCommon(rw *bufio.ReadWriter) (metadata, error) {
	if err := rw.Flush(); err != nil {
		return emptyMeta, err
	}

	resHeader, err := binprot.ReadResponseHeader(rw)
	if err != nil {
		return emptyMeta, err
	}
	defer binprot.PutResponseHeader(resHeader)

	err = binprot.DecodeError(resHeader)
	if err != nil {
		// read in the message "Not found" after a miss
		n, ioerr := rw.Discard(int(resHeader.TotalBodyLength))
		metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n))
		if ioerr != nil {
			return emptyMeta, ioerr
		}
		return emptyMeta, err
	}

	// we currently do nothing with the flags
	//buf := make([]byte, 4)
	//n, err := io.ReadAtLeast(rw, buf, 4)
	//metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n))
	//if err != nil {
	//	return emptyMeta, err
	//}
	//serverFlags := binary.BigEndian.Uint32(buf)

	// instead of reading and parsing flags, just discard
	rw.Discard(4)
	metrics.IncCounterBy(common.MetricBytesReadLocal, 4)

	metaData, err := readMetadata(rw)
	if err != nil {
		return emptyMeta, err
	}

	return metaData, nil
}
예제 #16
0
파일: parser.go 프로젝트: Netflix/rend
func readUInt32(r io.Reader) (uint32, error) {
	buf := make([]byte, 4)

	n, err := io.ReadAtLeast(r, buf, 4)
	metrics.IncCounterBy(common.MetricBytesReadRemote, uint64(n))
	if err != nil {
		return uint32(0), err
	}

	return binary.BigEndian.Uint32(buf), nil
}
예제 #17
0
파일: commands.go 프로젝트: Netflix/rend
// Key commands send the header and key only
func writeKeyCmd(w io.Writer, opcode uint8, key []byte) error {
	// opcode, keyLength, extraLength, totalBodyLength
	header := makeRequestHeader(opcode, len(key), 0, len(key))
	writeRequestHeader(w, header)

	n, err := w.Write(key)

	metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(ReqHeaderLen+n))
	reqHeadPool.Put(header)

	return err
}
예제 #18
0
파일: commands.go 프로젝트: Netflix/rend
// And the noop command is just a header
func WriteNoopCmd(w io.Writer) error {
	// opcode, keyLength, extraLength, totalBodyLength
	header := makeRequestHeader(OpcodeNoop, 0, 0, 0)
	//fmt.Printf("Delete: key: %v | totalBodyLength: %v\n", string(key), len(key))

	err := writeRequestHeader(w, header)

	metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(ReqHeaderLen))

	reqHeadPool.Put(header)

	return err
}
예제 #19
0
파일: respond.go 프로젝트: Netflix/rend
func getCommon(w *bufio.Writer, response common.GetResponse, opcode uint8) error {
	// total body length = extras (flags, 4 bytes) + data length
	totalBodyLength := len(response.Data) + 4
	writeSuccessResponseHeader(w, opcode, 0, 4, totalBodyLength, response.Opaque, false)
	buf := make([]byte, 4)
	binary.BigEndian.PutUint32(buf, response.Flags)
	w.Write(buf)
	w.Write(response.Data)
	if err := w.Flush(); err != nil {
		return err
	}
	metrics.IncCounterBy(common.MetricBytesWrittenRemote, uint64(totalBodyLength))
	return nil
}
예제 #20
0
파일: commands.go 프로젝트: Netflix/rend
func writeAppendPrependCmdCommon(w io.Writer, opcode uint8, key []byte, flags, exptime, dataSize uint32) error {
	// opcode, keyLength, extraLength, totalBodyLength
	// key + body
	totalBodyLength := len(key) + int(dataSize)
	header := makeRequestHeader(opcode, len(key), 0, totalBodyLength)

	writeRequestHeader(w, header)

	n, err := w.Write(key)
	metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(n))

	reqHeadPool.Put(header)

	return err
}
예제 #21
0
파일: types.go 프로젝트: RSellathurai/rend
func readMetadata(r io.Reader) (metadata, error) {
	buf := make([]byte, metadataSize)

	n, err := io.ReadAtLeast(r, buf, metadataSize)
	metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n))
	if err != nil {
		return emptyMeta, nil
	}

	m := metadata{}
	m.Length = binary.BigEndian.Uint32(buf[0:4])
	m.OrigFlags = binary.BigEndian.Uint32(buf[4:8])
	m.NumChunks = binary.BigEndian.Uint32(buf[8:12])
	m.ChunkSize = binary.BigEndian.Uint32(buf[12:16])
	copy(m.Token[:], buf[16:])

	return m, nil
}
예제 #22
0
파일: commands.go 프로젝트: Netflix/rend
// Key Exptime commands send the header, key, and an exptime
func writeKeyExptimeCmd(w io.Writer, opcode uint8, key []byte, exptime uint32) error {
	// opcode, keyLength, extraLength, totalBodyLength
	// key + extras + body
	extrasLen := 4
	totalBodyLength := len(key) + extrasLen
	header := makeRequestHeader(opcode, len(key), extrasLen, totalBodyLength)

	writeRequestHeader(w, header)

	buf := make([]byte, len(key)+4)
	binary.BigEndian.PutUint32(buf[0:4], exptime)
	copy(buf[4:], key)

	n, err := w.Write(buf)
	metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(n))

	reqHeadPool.Put(header)

	return err
}
예제 #23
0
파일: parser.go 프로젝트: RSellathurai/rend
func setRequest(r io.Reader, reqHeader RequestHeader, reqType common.RequestType, quiet bool) (common.SetRequest, common.RequestType, error) {
	// flags, exptime, key, value
	flags, err := readUInt32(r)
	if err != nil {
		log.Println("Error reading flags")
		return common.SetRequest{}, reqType, err
	}

	exptime, err := readUInt32(r)
	if err != nil {
		log.Println("Error reading exptime")
		return common.SetRequest{}, reqType, err
	}

	key, err := readString(r, reqHeader.KeyLength)
	if err != nil {
		log.Println("Error reading key")
		return common.SetRequest{}, reqType, err
	}

	realLength := reqHeader.TotalBodyLength -
		uint32(reqHeader.ExtraLength) -
		uint32(reqHeader.KeyLength)

	// Read in the body of the set request
	dataBuf := make([]byte, realLength)
	n, err := io.ReadAtLeast(r, dataBuf, int(realLength))
	metrics.IncCounterBy(common.MetricBytesReadRemote, uint64(n))
	if err != nil {
		return common.SetRequest{}, reqType, err
	}

	return common.SetRequest{
		Quiet:   quiet,
		Key:     key,
		Flags:   flags,
		Exptime: exptime,
		Opaque:  reqHeader.OpaqueToken,
		Data:    dataBuf,
	}, reqType, nil
}
예제 #24
0
파일: commands.go 프로젝트: Netflix/rend
// Data commands are those that send a header, key, exptime, and data
func writeDataCmdCommon(w io.Writer, opcode uint8, key []byte, flags, exptime, dataSize uint32) error {
	// opcode, keyLength, extraLength, totalBodyLength
	// key + extras + body
	extrasLen := 8
	totalBodyLength := len(key) + extrasLen + int(dataSize)
	header := makeRequestHeader(opcode, len(key), extrasLen, totalBodyLength)

	writeRequestHeader(w, header)

	buf := make([]byte, len(key)+8)
	binary.BigEndian.PutUint32(buf[0:4], flags)
	binary.BigEndian.PutUint32(buf[4:8], exptime)
	copy(buf[8:], key)

	n, err := w.Write(buf)
	metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(n))

	reqHeadPool.Put(header)

	return err
}
예제 #25
0
파일: respond.go 프로젝트: Netflix/rend
func (b BinaryResponder) GetE(response common.GetEResponse) error {
	if response.Miss {
		if !response.Quiet {
			return b.Error(response.Opaque, common.RequestGetE, common.ErrKeyNotFound, false)
		}
		return nil
	}

	// total body length = extras (flags & exptime, 8 bytes) + data length
	totalBodyLength := len(response.Data) + 8
	writeSuccessResponseHeader(b.writer, OpcodeGetE, 0, 8, totalBodyLength, response.Opaque, false)
	binary.Write(b.writer, binary.BigEndian, response.Flags)
	binary.Write(b.writer, binary.BigEndian, response.Exptime)
	b.writer.Write(response.Data)

	if err := b.writer.Flush(); err != nil {
		return err
	}
	metrics.IncCounterBy(common.MetricBytesWrittenRemote, uint64(totalBodyLength))
	return nil
}
예제 #26
0
파일: localComm.go 프로젝트: Netflix/rend
func simpleCmdLocal(rw *bufio.ReadWriter, flush bool) error {
	if flush {
		if err := rw.Flush(); err != nil {
			return err
		}
	}

	resHeader, err := binprot.ReadResponseHeader(rw)
	if err != nil {
		return err
	}

	n, ioerr := rw.Discard(int(resHeader.TotalBodyLength))
	metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n))
	if ioerr != nil {
		binprot.PutResponseHeader(resHeader)
		return ioerr
	}

	binprot.PutResponseHeader(resHeader)
	return binprot.DecodeError(resHeader)
}
예제 #27
0
func readRequestHeader(r io.Reader) (RequestHeader, error) {
	buf := bufPool.Get().([]byte)

	br, err := io.ReadAtLeast(r, buf, ReqHeaderLen)
	metrics.IncCounterBy(common.MetricBytesReadRemote, uint64(br))
	if err != nil {
		bufPool.Put(buf)
		return emptyReqHeader, err
	}

	if buf[0] != MagicRequest {
		bufPool.Put(buf)
		metrics.IncCounter(MetricBinaryRequestHeadersBadMagic)
		return emptyReqHeader, ErrBadMagic
	}

	rh := reqHeadPool.Get().(RequestHeader)
	rh.Magic = buf[0]
	rh.Opcode = buf[1]
	rh.KeyLength = binary.BigEndian.Uint16(buf[2:4])
	rh.ExtraLength = buf[4]
	// ignore DataType, unused
	//rh.DataType = buf[5]
	rh.DataType = 0
	// ignore VBucket, unused
	//rh.VBucket = binary.BigEndian.Uint16(buf[6:8])
	rh.VBucket = 0
	rh.TotalBodyLength = binary.BigEndian.Uint32(buf[8:12])
	rh.OpaqueToken = binary.BigEndian.Uint32(buf[12:16])
	// ignore CAS, unused in rend
	//rh.CASToken = binary.BigEndian.Uint64(buf[16:24])
	rh.CASToken = 0

	bufPool.Put(buf)
	metrics.IncCounter(MetricBinaryRequestHeadersParsed)

	return rh, nil
}
예제 #28
0
func writeResponseHeader(w io.Writer, rh ResponseHeader) error {
	buf := bufPool.Get().([]byte)

	buf[0] = rh.Magic
	buf[1] = rh.Opcode
	binary.BigEndian.PutUint16(buf[2:4], rh.KeyLength)
	buf[4] = rh.ExtraLength
	// DataType is unused
	buf[5] = 0
	binary.BigEndian.PutUint16(buf[6:8], rh.Status)
	binary.BigEndian.PutUint32(buf[8:12], rh.TotalBodyLength)
	binary.BigEndian.PutUint32(buf[12:16], rh.OpaqueToken)

	// zero CAS region
	for i := 16; i < 24; i++ {
		buf[i] = 0
	}

	n, err := w.Write(buf)
	metrics.IncCounterBy(common.MetricBytesWrittenLocal, uint64(n))
	bufPool.Put(buf)
	return err
}
예제 #29
0
파일: localComm.go 프로젝트: Netflix/rend
func getLocalIntoBuf(rw *bufio.Reader, metaData metadata, tokenBuf, dataBuf []byte, chunkNum, totalDataLength int) (opcodeNoop bool, err error) {
	resHeader, err := binprot.ReadResponseHeader(rw)
	if err != nil {
		return false, err
	}
	defer binprot.PutResponseHeader(resHeader)

	// it feels a bit dirty knowing about batch gets here, but it's the most logical place to put
	// a check for an opcode that signals the end of a batch get or GAT. This code is a bit too big
	// to copy-paste in multiple places.
	if resHeader.Opcode == binprot.OpcodeNoop {
		return true, nil
	}

	err = binprot.DecodeError(resHeader)
	if err != nil {
		// read in the message "Not found" after a miss
		n, ioerr := rw.Discard(int(resHeader.TotalBodyLength))
		metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n))
		if ioerr != nil {
			return false, ioerr
		}
		return false, err
	}

	// we currently do nothing with the flags
	//buf := make([]byte, 4)
	//n, err := io.ReadAtLeast(rw, buf, 4)
	//metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n))
	//if err != nil {
	//	return emptyMeta, err
	//}
	//serverFlags := binary.BigEndian.Uint32(buf)

	// instead of reading and parsing flags, just discard
	rw.Discard(4)
	metrics.IncCounterBy(common.MetricBytesReadLocal, 4)

	// Read in token if requested
	if tokenBuf != nil {
		n, err := io.ReadAtLeast(rw, tokenBuf, tokenSize)
		metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n))
		if err != nil {
			return false, err
		}
	}

	// indices for slicing, end exclusive
	start, end := chunkSliceIndices(int(metaData.ChunkSize), chunkNum, int(metaData.Length))
	// read data directly into buf
	chunkBuf := dataBuf[start:end]

	// Read in value
	n, err := io.ReadAtLeast(rw, chunkBuf, len(chunkBuf))
	metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n))
	if err != nil {
		return false, err
	}

	// consume padding at end of chunk if needed
	if len(chunkBuf) < totalDataLength {
		n, ioerr := rw.Discard(totalDataLength - len(chunkBuf))
		metrics.IncCounterBy(common.MetricBytesReadLocal, uint64(n))
		if ioerr != nil {
			return false, ioerr
		}
	}

	return false, nil
}
예제 #30
0
파일: parser.go 프로젝트: Netflix/rend
func (t TextParser) Parse() (common.Request, common.RequestType, uint64, error) {
	data, err := t.reader.ReadString('\n')
	start := timer.Now()
	metrics.IncCounterBy(common.MetricBytesReadRemote, uint64(len(data)))

	if err != nil {
		if err == io.EOF {
			log.Println("Connection closed")
		} else {
			log.Printf("Error while reading text command line: %s\n", err.Error())
		}
		return nil, common.RequestUnknown, start, err
	}

	clParts := strings.Split(strings.TrimSpace(data), " ")

	switch clParts[0] {
	case "set":
		return setRequest(t.reader, clParts, common.RequestSet, start)

	case "add":
		return setRequest(t.reader, clParts, common.RequestAdd, start)

	case "replace":
		return setRequest(t.reader, clParts, common.RequestReplace, start)

	case "append":
		return setRequest(t.reader, clParts, common.RequestAppend, start)

	case "prepend":
		return setRequest(t.reader, clParts, common.RequestPrepend, start)

	case "get":
		if len(clParts) < 2 {
			return nil, common.RequestGet, start, common.ErrBadRequest
		}

		var keys [][]byte
		for _, key := range clParts[1:] {
			keys = append(keys, []byte(key))
		}

		opaques := make([]uint32, len(keys))
		quiet := make([]bool, len(keys))

		return common.GetRequest{
			Keys:    keys,
			Opaques: opaques,
			Quiet:   quiet,
			NoopEnd: false,
		}, common.RequestGet, start, nil

	case "delete":
		if len(clParts) != 2 {
			return nil, common.RequestDelete, start, common.ErrBadRequest
		}

		return common.DeleteRequest{
			Key:    []byte(clParts[1]),
			Opaque: uint32(0),
		}, common.RequestDelete, start, nil

	// TODO: Error handling for invalid cmd line
	case "touch":
		if len(clParts) != 3 {
			return nil, common.RequestTouch, start, common.ErrBadRequest
		}

		key := []byte(clParts[1])

		exptime, err := strconv.ParseUint(strings.TrimSpace(clParts[2]), 10, 32)
		if err != nil {
			log.Printf("Error parsing ttl for touch command: %s\n", err.Error())
			return nil, common.RequestSet, start, common.ErrBadRequest
		}

		return common.TouchRequest{
			Key:     key,
			Exptime: uint32(exptime),
			Opaque:  uint32(0),
		}, common.RequestTouch, start, nil
	case "noop":
		if len(clParts) != 1 {
			return nil, common.RequestNoop, start, common.ErrBadRequest
		}
		return common.NoopRequest{
			Opaque: 0,
		}, common.RequestNoop, start, nil

	case "quit":
		if len(clParts) != 1 {
			return nil, common.RequestQuit, start, common.ErrBadRequest
		}
		return common.QuitRequest{
			Opaque: 0,
			Quiet:  false,
		}, common.RequestQuit, start, nil

	case "version":
		if len(clParts) != 1 {
			return nil, common.RequestQuit, start, common.ErrBadRequest
		}
		return common.VersionRequest{
			Opaque: 0,
		}, common.RequestVersion, start, nil

	default:
		return nil, common.RequestUnknown, start, nil
	}
}