// PutResponse writes a response and an error associated with it to the // cache for the specified cmdID. func (rc *ResponseCache) PutResponse(e engine.Engine, cmdID roachpb.ClientCmdID, replyWithErr roachpb.ResponseWithError) error { // Do nothing if command ID is empty. if cmdID.IsEmpty() { return nil } // Write the response value to the engine. if rc.shouldCacheResponse(replyWithErr) { // Write the error into the reply before caching. replyWithErr.Reply.SetGoError(replyWithErr.Err) // Be sure to clear it when you're done! defer func() { replyWithErr.Reply.BatchResponse_Header.Error = nil }() key := keys.ResponseCacheKey(rc.rangeID, &cmdID) return engine.MVCCPutProto(e, nil, key, roachpb.ZeroTimestamp, nil, replyWithErr.Reply) } return nil }
// GetResponse looks up a response matching the specified cmdID. If the // response is found, it is returned along with its associated error. // If the response is not found, nil is returned for both the response // and its error. In all cases, the third return value is the error // returned from the engine when reading the on-disk cache. func (rc *ResponseCache) GetResponse(e engine.Engine, cmdID roachpb.ClientCmdID) (roachpb.ResponseWithError, error) { // Do nothing if command ID is empty. if cmdID.IsEmpty() { return roachpb.ResponseWithError{}, nil } // Pull response from the cache and read into reply if available. br := &roachpb.BatchResponse{} key := keys.ResponseCacheKey(rc.rangeID, &cmdID) ok, err := engine.MVCCGetProto(e, key, roachpb.ZeroTimestamp, true, nil, br) if err != nil { return roachpb.ResponseWithError{}, err } if ok { header := br.Header() defer func() { header.Error = nil }() return roachpb.ResponseWithError{Reply: br, Err: header.Error.GoError()}, nil } return roachpb.ResponseWithError{}, nil }
func (rc *ResponseCache) decodeResponseCacheKey(encKey roachpb.EncodedKey) (roachpb.ClientCmdID, error) { ret := roachpb.ClientCmdID{} key, _, isValue, err := engine.MVCCDecodeKey(encKey) if err != nil { return ret, err } if isValue { return ret, util.Errorf("key %s is not a raw MVCC value", encKey) } if !bytes.HasPrefix(key, keys.LocalRangeIDPrefix) { return ret, util.Errorf("key %s does not have %s prefix", key, keys.LocalRangeIDPrefix) } // Cut the prefix and the Range ID. b := key[len(keys.LocalRangeIDPrefix):] b, _, err = encoding.DecodeUvarint(b) if err != nil { return ret, err } if !bytes.HasPrefix(b, keys.LocalResponseCacheSuffix) { return ret, util.Errorf("key %s does not contain the response cache suffix %s", key, keys.LocalResponseCacheSuffix) } // Cut the response cache suffix. b = b[len(keys.LocalResponseCacheSuffix):] // Now, decode the command ID. b, wt, err := encoding.DecodeUvarint(b) if err != nil { return ret, err } b, rd, err := encoding.DecodeUint64(b) if err != nil { return ret, err } if len(b) > 0 { return ret, util.Errorf("key %s has leftover bytes after decode: %s; indicates corrupt key", encKey, b) } ret.WallTime = int64(wt) ret.Random = int64(rd) return ret, nil }