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, } } }