github.com/braveheart12/insolar-09-08-19@v0.8.7/network/controller/rpc_controller.go (about) 1 /* 2 * The Clear BSD License 3 * 4 * Copyright (c) 2019 Insolar Technologies 5 * 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met: 9 * 10 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 11 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 12 * Neither the name of Insolar Technologies nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 13 * 14 * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 * 16 */ 17 18 package controller 19 20 import ( 21 "context" 22 "encoding/gob" 23 "fmt" 24 "strings" 25 "time" 26 27 "github.com/pkg/errors" 28 "go.opencensus.io/stats" 29 "go.opencensus.io/trace" 30 31 "github.com/insolar/insolar/component" 32 "github.com/insolar/insolar/core" 33 "github.com/insolar/insolar/core/message" 34 "github.com/insolar/insolar/instrumentation/inslogger" 35 "github.com/insolar/insolar/instrumentation/insmetrics" 36 "github.com/insolar/insolar/instrumentation/instracer" 37 "github.com/insolar/insolar/network" 38 "github.com/insolar/insolar/network/cascade" 39 "github.com/insolar/insolar/network/controller/common" 40 "github.com/insolar/insolar/network/transport/packet/types" 41 ) 42 43 type RPCController interface { 44 component.Initer 45 46 // hack for DI, else we receive ServiceNetwork injection in RPCController instead of rpcController that leads to stack overflow 47 IAmRPCController() 48 49 SendMessage(nodeID core.RecordRef, name string, msg core.Parcel) ([]byte, error) 50 SendCascadeMessage(data core.Cascade, method string, msg core.Parcel) error 51 RemoteProcedureRegister(name string, method core.RemoteProcedure) 52 } 53 54 type rpcController struct { 55 Scheme core.PlatformCryptographyScheme `inject:""` 56 57 options *common.Options 58 hostNetwork network.HostNetwork 59 methodTable map[string]core.RemoteProcedure 60 } 61 62 type RequestRPC struct { 63 Method string 64 Data [][]byte 65 } 66 67 type ResponseRPC struct { 68 Success bool 69 Result []byte 70 Error string 71 } 72 73 type RequestCascade struct { 74 TraceID string 75 RPC RequestRPC 76 Cascade core.Cascade 77 } 78 79 type ResponseCascade struct { 80 Success bool 81 Error string 82 } 83 84 func init() { 85 gob.Register(&RequestRPC{}) 86 gob.Register(&ResponseRPC{}) 87 gob.Register(&RequestCascade{}) 88 gob.Register(&ResponseCascade{}) 89 } 90 91 func (rpc *rpcController) IAmRPCController() { 92 // hack for DI, else we receive ServiceNetwork injection in RPCController instead of rpcController that leads to stack overflow 93 } 94 95 func (rpc *rpcController) RemoteProcedureRegister(name string, method core.RemoteProcedure) { 96 _, span := instracer.StartSpan(context.Background(), "RPCController.RemoteProcedureRegister") 97 span.AddAttributes( 98 trace.StringAttribute("method", name), 99 ) 100 defer span.End() 101 rpc.methodTable[name] = method 102 } 103 104 func (rpc *rpcController) invoke(ctx context.Context, name string, data [][]byte) ([]byte, error) { 105 method, exists := rpc.methodTable[name] 106 if !exists { 107 return nil, errors.New(fmt.Sprintf("RPC with name %s is not registered", name)) 108 } 109 return method(ctx, data) 110 } 111 112 func (rpc *rpcController) SendCascadeMessage(data core.Cascade, method string, msg core.Parcel) error { 113 if msg == nil { 114 return errors.New("message is nil") 115 } 116 ctx, span := instracer.StartSpan(context.Background(), "RPCController.SendCascadeMessage") 117 span.AddAttributes( 118 trace.StringAttribute("method", method), 119 trace.StringAttribute("msg.Type", msg.Type().String()), 120 trace.StringAttribute("msg.DefaultTarget", msg.DefaultTarget().String()), 121 ) 122 defer span.End() 123 ctx = msg.Context(ctx) 124 return rpc.initCascadeSendMessage(ctx, data, false, method, [][]byte{message.ParcelToBytes(msg)}) 125 } 126 127 func (rpc *rpcController) initCascadeSendMessage(ctx context.Context, data core.Cascade, 128 findCurrentNode bool, method string, args [][]byte) error { 129 130 _, span := instracer.StartSpan(context.Background(), "RPCController.initCascadeSendMessage") 131 span.AddAttributes( 132 trace.StringAttribute("method", method), 133 ) 134 defer span.End() 135 if len(data.NodeIds) == 0 { 136 return errors.New("node IDs list should not be empty") 137 } 138 if data.ReplicationFactor == 0 { 139 return errors.New("replication factor should not be zero") 140 } 141 142 var nextNodes []core.RecordRef 143 var err error 144 145 if findCurrentNode { 146 nodeID := rpc.hostNetwork.GetNodeID() 147 nextNodes, err = cascade.CalculateNextNodes(rpc.Scheme, data, &nodeID) 148 } else { 149 nextNodes, err = cascade.CalculateNextNodes(rpc.Scheme, data, nil) 150 } 151 if err != nil { 152 return errors.Wrap(err, "Failed to CalculateNextNodes") 153 } 154 if len(nextNodes) == 0 { 155 return nil 156 } 157 158 var failedNodes []string 159 for _, nextNode := range nextNodes { 160 err = rpc.requestCascadeSendMessage(ctx, data, nextNode, method, args) 161 if err != nil { 162 inslogger.FromContext(ctx).Warnf("Failed to send cascade message to node %s: %s", nextNode, err.Error()) 163 failedNodes = append(failedNodes, nextNode.String()) 164 } 165 } 166 167 if len(failedNodes) > 0 { 168 return errors.New("Failed to send cascade message to nodes: " + strings.Join(failedNodes, ", ")) 169 } 170 inslogger.FromContext(ctx).Debug("Cascade message successfully sent to all nodes of the next layer") 171 return nil 172 } 173 174 func (rpc *rpcController) requestCascadeSendMessage(ctx context.Context, data core.Cascade, nodeID core.RecordRef, 175 method string, args [][]byte) error { 176 177 _, span := instracer.StartSpan(context.Background(), "RPCController.requestCascadeSendMessage") 178 defer span.End() 179 request := rpc.hostNetwork.NewRequestBuilder().Type(types.Cascade).Data(&RequestCascade{ 180 TraceID: inslogger.TraceID(ctx), 181 RPC: RequestRPC{ 182 Method: method, 183 Data: args, 184 }, 185 Cascade: data, 186 }).Build() 187 188 future, err := rpc.hostNetwork.SendRequest(ctx, request, nodeID) 189 if err != nil { 190 return err 191 } 192 193 go func(ctx context.Context, f network.Future, duration time.Duration) { 194 response, err := f.GetResponse(duration) 195 if err != nil { 196 inslogger.FromContext(ctx).Warnf("Failed to get response to cascade message request from node %s: %s", 197 future.GetRequest().GetSender(), err.Error()) 198 return 199 } 200 data := response.GetData().(*ResponseCascade) 201 if !data.Success { 202 inslogger.FromContext(ctx).Warnf("Error response to cascade message request from node %s: %s", 203 response.GetSender(), data.Error) 204 return 205 } 206 }(ctx, future, rpc.options.PacketTimeout) 207 208 return nil 209 } 210 211 func (rpc *rpcController) SendMessage(nodeID core.RecordRef, name string, msg core.Parcel) ([]byte, error) { 212 msgBytes := message.ParcelToBytes(msg) 213 ctx := context.Background() // TODO: ctx as argument 214 ctx = insmetrics.InsertTag(ctx, tagMessageType, msg.Type().String()) 215 stats.Record(ctx, statParcelsSentSizeBytes.M(int64(len(msgBytes)))) 216 request := rpc.hostNetwork.NewRequestBuilder().Type(types.RPC).Data(&RequestRPC{ 217 Method: name, 218 Data: [][]byte{msgBytes}, 219 }).Build() 220 221 start := time.Now() 222 ctx = msg.Context(ctx) 223 logger := inslogger.FromContext(ctx) 224 logger.Debugf("SendParcel with nodeID = %s method = %s, message reference = %s, RequestID = %d", nodeID.String(), 225 name, msg.DefaultTarget().String(), request.GetRequestID()) 226 future, err := rpc.hostNetwork.SendRequest(ctx, request, nodeID) 227 if err != nil { 228 return nil, errors.Wrapf(err, "Error sending RPC request to node %s", nodeID.String()) 229 } 230 response, err := future.GetResponse(rpc.options.PacketTimeout) 231 if err != nil { 232 return nil, errors.Wrapf(err, "Error getting RPC response from node %s", nodeID.String()) 233 } 234 data := response.GetData().(*ResponseRPC) 235 logger.Debugf("Inside SendParcel: type - '%s', target - %s, caller - %s, targetRole - %s, time - %s", 236 msg.Type(), msg.DefaultTarget(), msg.GetCaller(), msg.DefaultRole(), time.Since(start)) 237 if !data.Success { 238 return nil, errors.New("RPC call returned error: " + data.Error) 239 } 240 stats.Record(ctx, statParcelsReplySizeBytes.M(int64(len(data.Result)))) 241 return data.Result, nil 242 } 243 244 func (rpc *rpcController) processMessage(ctx context.Context, request network.Request) (network.Response, error) { 245 ctx = insmetrics.InsertTag(ctx, tagPacketType, request.GetType().String()) 246 stats.Record(ctx, statPacketsReceived.M(1)) 247 248 payload := request.GetData().(*RequestRPC) 249 result, err := rpc.invoke(ctx, payload.Method, payload.Data) 250 if err != nil { 251 return rpc.hostNetwork.BuildResponse(ctx, request, &ResponseRPC{Success: false, Error: err.Error()}), nil 252 } 253 return rpc.hostNetwork.BuildResponse(ctx, request, &ResponseRPC{Success: true, Result: result}), nil 254 } 255 256 func (rpc *rpcController) processCascade(ctx context.Context, request network.Request) (network.Response, error) { 257 payload := request.GetData().(*RequestCascade) 258 ctx, logger := inslogger.WithTraceField(ctx, payload.TraceID) 259 260 generalError := "" 261 _, invokeErr := rpc.invoke(ctx, payload.RPC.Method, payload.RPC.Data) 262 if invokeErr != nil { 263 logger.Debugf("failed to invoke RPC: %s", invokeErr.Error()) 264 generalError += invokeErr.Error() + "; " 265 } 266 sendErr := rpc.initCascadeSendMessage(ctx, payload.Cascade, true, payload.RPC.Method, payload.RPC.Data) 267 if sendErr != nil { 268 logger.Debugf("failed to send message to next cascade layer: %s", sendErr.Error()) 269 generalError += sendErr.Error() 270 } 271 272 if generalError != "" { 273 return rpc.hostNetwork.BuildResponse(ctx, request, &ResponseCascade{Success: false, Error: generalError}), nil 274 } 275 return rpc.hostNetwork.BuildResponse(ctx, request, &ResponseCascade{Success: true}), nil 276 } 277 278 func (rpc *rpcController) Init(ctx context.Context) error { 279 rpc.hostNetwork.RegisterRequestHandler(types.RPC, rpc.processMessage) 280 rpc.hostNetwork.RegisterRequestHandler(types.Cascade, rpc.processCascade) 281 return nil 282 } 283 284 func NewRPCController(options *common.Options, hostNetwork network.HostNetwork) RPCController { 285 return &rpcController{options: options, 286 hostNetwork: hostNetwork, 287 methodTable: make(map[string]core.RemoteProcedure), 288 } 289 }