예제 #1
0
func realHandleGet(cmd common.GetRequest, dataOut chan common.GetResponse, errorOut chan error, rw *bufio.ReadWriter) {
	// read index
	// make buf
	// for numChunks do
	//   read chunk directly into buffer
	// send response

	defer close(errorOut)
	defer close(dataOut)

outer:
	for idx, key := range cmd.Keys {
		missResponse := common.GetResponse{
			Miss:   true,
			Quiet:  cmd.Quiet[idx],
			Opaque: cmd.Opaques[idx],
			Flags:  0,
			Key:    key,
			Data:   nil,
		}

		_, metaData, err := getMetadata(rw, key)
		if err != nil {
			if err == common.ErrKeyNotFound {
				metrics.IncCounter(MetricCmdGetMissesMeta)
				dataOut <- missResponse
				continue outer
			}

			errorOut <- err
			return
		}

		missResponse.Flags = metaData.OrigFlags

		cmdSize := int(metaData.NumChunks)*(len(key)+4 /* key suffix */ +binprot.ReqHeaderLen) + binprot.ReqHeaderLen /* for the noop */
		cmdbuf := bytes.NewBuffer(make([]byte, 0, cmdSize))
		// Write all the get commands before reading
		for i := 0; i < int(metaData.NumChunks); i++ {
			chunkKey := chunkKey(key, i)
			// bytes.Buffer doesn't error
			binprot.WriteGetQCmd(cmdbuf, chunkKey)
		}

		// The final command must be Get or Noop to guarantee a response
		// We use Noop to make coding easier, but it's (very) slightly less efficient
		// since we send 24 extra bytes in each direction
		// bytes.Buffer doesn't error
		binprot.WriteNoopCmd(cmdbuf)

		// bufio's ReadFrom will end up doing an io.Copy(cmdbuf, socket), which is more
		// efficient than writing directly into the bufio or using cmdbuf.WriteTo(rw)
		if _, err := rw.ReadFrom(cmdbuf); err != nil {
			errorOut <- err
			return
		}

		// Flush to make sure all the get commands are sent to the server.
		if err := rw.Flush(); err != nil {
			errorOut <- err
			return
		}

		dataBuf := make([]byte, metaData.Length)
		tokenBuf := make([]byte, tokenSize)

		// Now that all the headers are sent, start reading in the data chunks. We read until the
		// header for the Noop command comes back, keeping track of how many chunks are read. This
		// means that there is no fast fail when a chunk is missing, but at least all the data is
		// read in so there's no problem with unread, buffered data that should have been discarded.
		// If the number of chunks doesn't match, we throw away the data and call it a miss.
		chunk := 0
		miss := false
		var lastErr error

		for {
			opcodeNoop, err := getLocalIntoBuf(rw.Reader, metaData, tokenBuf, dataBuf, chunk, int(metaData.ChunkSize))
			if err != nil {
				if err == common.ErrKeyNotFound {
					if !miss {
						metrics.IncCounter(MetricCmdGetMissesChunk)
						miss = true
					}
					continue
				} else {
					lastErr = err
				}
			}

			if opcodeNoop {
				break
			}

			if !bytes.Equal(metaData.Token[:], tokenBuf) {
				//fmt.Println(id, "Get miss because of invalid chunk token. Cmd:", cmd)
				//fmt.Printf("Expected: %v\n", metaData.Token)
				//fmt.Printf("Got:      %v\n", tokenBuf)
				if !miss {
					metrics.IncCounter(MetricCmdGetMissesToken)
					miss = true
				}
			}

			chunk++
		}

		if lastErr != nil {
			errorOut <- lastErr
			return
		}
		if miss {
			//fmt.Println("Get miss because of missing chunk")
			dataOut <- missResponse
			continue outer
		}

		dataOut <- common.GetResponse{
			Miss:   false,
			Quiet:  cmd.Quiet[idx],
			Opaque: cmd.Opaques[idx],
			Flags:  metaData.OrigFlags,
			Key:    key,
			Data:   dataBuf,
		}
	}
}
예제 #2
0
func (h Handler) GAT(cmd common.GATRequest) (common.GetResponse, error) {
	missResponse := common.GetResponse{
		Miss:   true,
		Quiet:  false,
		Opaque: cmd.Opaque,
		Flags:  0,
		Key:    cmd.Key,
		Data:   nil,
	}

	_, metaData, err := getAndTouchMetadata(h.rw, cmd.Key, cmd.Exptime)
	if err != nil {
		if err == common.ErrKeyNotFound {
			metrics.IncCounter(MetricCmdGatMissesMeta)
			return missResponse, nil
		}

		return common.GetResponse{}, err
	}

	missResponse.Flags = metaData.OrigFlags

	// Write all the GAT commands before reading
	for i := 0; i < int(metaData.NumChunks); i++ {
		chunkKey := chunkKey(cmd.Key, i)
		if err := binprot.WriteGATQCmd(h.rw.Writer, chunkKey, cmd.Exptime); err != nil {
			return common.GetResponse{}, err
		}
	}

	// The final command must be GAT or Noop to guarantee a response
	// We use Noop to make coding easier, but it's (very) slightly less efficient
	// since we send 24 extra bytes in each direction
	if err := binprot.WriteNoopCmd(h.rw.Writer); err != nil {
		return common.GetResponse{}, err
	}

	// Flush to make sure all the GAT commands are sent to the server.
	if err := h.rw.Flush(); err != nil {
		return common.GetResponse{}, err
	}

	dataBuf := make([]byte, metaData.Length)
	tokenBuf := make([]byte, 16)

	// Now that all the headers are sent, start reading in the data chunks. We read until the
	// header for the Noop command comes back, keeping track of how many chunks are read. This
	// means that there is no fast fail when a chunk is missing, but at least all the data is
	// read in so there's no problem with unread, buffered data that should have been discarded.
	// If the number of chunks doesn't match, we throw away the data and call it a miss.
	chunk := 0
	miss := false
	var lastErr error

	for {
		opcodeNoop, err := getLocalIntoBuf(h.rw.Reader, metaData, tokenBuf, dataBuf, chunk, int(metaData.ChunkSize))
		if err != nil {
			if err == common.ErrKeyNotFound {
				if !miss {
					metrics.IncCounter(MetricCmdGatMissesChunk)
					miss = true
				}
				continue
			} else {
				lastErr = err
			}
		}

		if opcodeNoop {
			break
		}

		if !bytes.Equal(metaData.Token[:], tokenBuf) {
			//fmt.Println(id, "GAT miss because of invalid chunk token. Cmd:", cmd)
			//fmt.Printf("Expected: %v\n", metaData.Token)
			//fmt.Printf("Got:      %v\n", tokenBuf)
			if !miss {
				metrics.IncCounter(MetricCmdGatMissesToken)
				miss = true
			}
		}

		chunk++
	}

	if lastErr != nil {
		return common.GetResponse{}, lastErr
	}
	if miss {
		//fmt.Println("GAT miss because of missing chunk")
		return missResponse, nil
	}

	return common.GetResponse{
		Miss:   false,
		Quiet:  false,
		Opaque: cmd.Opaque,
		Flags:  metaData.OrigFlags,
		Key:    cmd.Key,
		Data:   dataBuf,
	}, nil
}
예제 #3
0
파일: handler.go 프로젝트: hardiku/rend
func realHandleGet(cmd common.GetRequest, dataOut chan common.GetResponse, errorOut chan error, rw *bufio.ReadWriter) {
	// read index
	// make buf
	// for numChunks do
	//   read chunk directly into buffer
	// send response

	defer close(errorOut)
	defer close(dataOut)

outer:
	for idx, key := range cmd.Keys {
		missResponse := common.GetResponse{
			Miss:   true,
			Quiet:  cmd.Quiet[idx],
			Opaque: cmd.Opaques[idx],
			Flags:  0,
			Key:    key,
			Data:   nil,
		}

		_, metaData, err := getMetadata(rw, key)
		if err != nil {
			if err == common.ErrKeyNotFound {
				//fmt.Println("Get miss because of missing metadata. Key:", key)
				dataOut <- missResponse
				continue outer
			}

			errorOut <- err
			return
		}

		missResponse.Flags = metaData.OrigFlags
		// Write all the get commands before reading
		for i := 0; i < int(metaData.NumChunks); i++ {
			chunkKey := chunkKey(key, i)
			if err := binprot.WriteGetQCmd(rw.Writer, chunkKey); err != nil {
				errorOut <- err
				return
			}
		}

		// The final command must be Get or Noop to guarantee a response
		// We use Noop to make coding easier, but it's (very) slightly less efficient
		// since we send 24 extra bytes in each direction
		if err := binprot.WriteNoopCmd(rw.Writer); err != nil {
			errorOut <- err
			return
		}

		// Flush to make sure all the get commands are sent to the server.
		if err := rw.Flush(); err != nil {
			errorOut <- err
			return
		}

		dataBuf := make([]byte, metaData.Length)
		tokenBuf := make([]byte, tokenSize)

		// Now that all the headers are sent, start reading in the data chunks. We read until the
		// header for the Noop command comes back, keeping track of how many chunks are read. This
		// means that there is no fast fail when a chunk is missing, but at least all the data is
		// read in so there's no problem with unread, buffered data that should have been discarded.
		// If the number of chunks doesn't match, we throw away the data and call it a miss.
		opcodeNoop := false
		chunk := 0
		var lastErr error
		missed := false

		for !opcodeNoop {
			opcodeNoop, lastErr = getLocalIntoBuf(rw.Reader, metaData, tokenBuf, dataBuf, chunk, int(metaData.ChunkSize))
			if lastErr != nil {
				if lastErr == common.ErrKeyNotFound {
					lastErr = nil
					missed = true
					continue
				}
				lastErr = err
			}

			if !bytes.Equal(metaData.Token[:], tokenBuf) {
				//fmt.Println(id, "Get miss because of invalid chunk token. Cmd:", cmd)
				//fmt.Printf("Expected: %v\n", metaData.Token)
				//fmt.Printf("Got:      %v\n", tokenBuf)
				missed = true
			}

			chunk++
		}

		if lastErr != nil {
			errorOut <- lastErr
			return
		}
		if missed {
			//fmt.Println("Get miss because of missing chunk")
			dataOut <- missResponse
			continue outer
		}

		dataOut <- common.GetResponse{
			Miss:   false,
			Quiet:  cmd.Quiet[idx],
			Opaque: cmd.Opaques[idx],
			Flags:  metaData.OrigFlags,
			Key:    key,
			Data:   dataBuf,
		}
	}
}