// sendRPC sends an RPC out to the wire. // Returns the response (for now, as the call is synchronous). func (c *Client) sendRPC(rpc hrpc.Call) error { // Header. c.id++ reqheader := &pb.RequestHeader{ CallId: &c.id, MethodName: proto.String(rpc.GetName()), RequestParam: proto.Bool(true), } payload, err := rpc.Serialize() if err != nil { return fmt.Errorf("Failed to serialize RPC: %s", err) } payloadLen := proto.EncodeVarint(uint64(len(payload))) headerData, err := proto.Marshal(reqheader) if err != nil { return fmt.Errorf("Failed to marshal Get request: %s", err) } buf := make([]byte, 5, 4+1+len(headerData)+len(payloadLen)+len(payload)) binary.BigEndian.PutUint32(buf, uint32(cap(buf)-4)) buf[4] = byte(len(headerData)) buf = append(buf, headerData...) buf = append(buf, payloadLen...) buf = append(buf, payload...) c.sentRPCsMutex.Lock() c.sentRPCs[c.id] = rpc c.sentRPCsMutex.Unlock() err = c.write(buf) if err != nil { return UnrecoverableError{err} } return nil }
// sendRPC takes an RPC call, and will send it to the correct region server. If // the correct region server is offline or otherwise unavailable, sendRPC will // continually retry until the deadline set on the RPC's context is exceeded. func (c *Client) sendRPC(rpc hrpc.Call) (proto.Message, error) { log.WithFields(log.Fields{ "Type": rpc.GetName(), "Table": string(rpc.Table()), "Key": string(rpc.Key()), }).Debug("Sending RPC") err := c.queueRPC(rpc) if err == ErrDeadline { return nil, err } else if err != nil { log.WithFields(log.Fields{ "Type": rpc.GetName(), "Table": string(rpc.Table()), "Key": string(rpc.Key()), }).Debug("We hit an error queuing the RPC. Resending.") // There was an error locating the region for the RPC, or the client // for the region encountered an error and has shut down. return c.sendRPC(rpc) } if err == nil { var res hrpc.RPCResult resch := rpc.GetResultChan() select { case res = <-resch: case <-rpc.GetContext().Done(): return nil, ErrDeadline } err := res.Error log.WithFields(log.Fields{ "Type": rpc.GetName(), "Table": string(rpc.Table()), "Key": string(rpc.Key()), "Result": res.Msg, "Error": err, }).Debug("Successfully sent RPC. Returning.") if _, ok := err.(region.RetryableError); ok { return c.sendRPC(rpc) } else if _, ok := err.(region.UnrecoverableError); ok { // Prevents dropping into the else block below, // error handling happens a few lines down } else { return res.Msg, res.Error } } // There was an issue related to the network, so we're going to mark the // region as unavailable, and generate the channel used for announcing // when it's available again region := rpc.GetRegion() log.WithFields(log.Fields{ "Type": rpc.GetName(), "Table": string(rpc.Table()), "Key": string(rpc.Key()), }).Debug("Encountered a network error. Region unavailable?") if region != nil { succ := region.MarkUnavailable() if succ { go c.reestablishRegion(region) } } log.WithFields(log.Fields{ "Type": rpc.GetName(), "Table": string(rpc.Table()), "Key": string(rpc.Key()), }).Debug("Retrying sendRPC") return c.sendRPC(rpc) }