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  }