github.com/braveheart12/insolar-09-08-19@v0.8.7/network/pulsenetwork/distributor.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 pulsenetwork
    19  
    20  import (
    21  	"context"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/insolar/insolar/configuration"
    26  	"github.com/insolar/insolar/core"
    27  	"github.com/insolar/insolar/instrumentation/inslogger"
    28  	"github.com/insolar/insolar/instrumentation/instracer"
    29  	"github.com/insolar/insolar/network"
    30  	"github.com/insolar/insolar/network/sequence"
    31  	"github.com/insolar/insolar/network/transport"
    32  	"github.com/insolar/insolar/network/transport/host"
    33  	"github.com/insolar/insolar/network/transport/packet"
    34  	"github.com/insolar/insolar/network/transport/packet/types"
    35  	"github.com/pkg/errors"
    36  )
    37  
    38  type distributor struct {
    39  	Transport   transport.Transport `inject:""`
    40  	idGenerator sequence.Generator
    41  
    42  	pingRequestTimeout        time.Duration
    43  	randomHostsRequestTimeout time.Duration
    44  	pulseRequestTimeout       time.Duration
    45  	randomNodesCount          int
    46  
    47  	pulsarHost     *host.Host
    48  	bootstrapHosts []*host.Host
    49  }
    50  
    51  func NewDistributor(conf configuration.PulseDistributor) (core.PulseDistributor, error) {
    52  	bootstrapHosts := make([]*host.Host, 0, len(conf.BootstrapHosts))
    53  
    54  	for _, node := range conf.BootstrapHosts {
    55  		bootstrapHost, err := host.NewHost(node)
    56  		if err != nil {
    57  			return nil, errors.Wrap(err, "[ NewDistributor ] failed to create bootstrap node host")
    58  		}
    59  		bootstrapHosts = append(bootstrapHosts, bootstrapHost)
    60  	}
    61  
    62  	return &distributor{
    63  		idGenerator: sequence.NewGeneratorImpl(),
    64  
    65  		pingRequestTimeout:        time.Duration(conf.PingRequestTimeout) * time.Millisecond,
    66  		randomHostsRequestTimeout: time.Duration(conf.RandomHostsRequestTimeout) * time.Millisecond,
    67  		pulseRequestTimeout:       time.Duration(conf.PulseRequestTimeout) * time.Millisecond,
    68  		randomNodesCount:          conf.RandomNodesCount,
    69  
    70  		bootstrapHosts: bootstrapHosts,
    71  	}, nil
    72  }
    73  
    74  func (d *distributor) Start(ctx context.Context) error {
    75  	pulsarHost, err := host.NewHost(d.Transport.PublicAddress())
    76  	if err != nil {
    77  		return errors.Wrap(err, "[ NewDistributor ] failed to create pulsar host")
    78  	}
    79  	pulsarHost.NodeID = core.RecordRef{}
    80  
    81  	d.pulsarHost = pulsarHost
    82  	return nil
    83  }
    84  
    85  func (d *distributor) Distribute(ctx context.Context, pulse core.Pulse) {
    86  	logger := inslogger.FromContext(ctx)
    87  	defer func() {
    88  		if r := recover(); r != nil {
    89  			logger.Errorf("sendPulseToNetwork failed with panic: %v", r)
    90  		}
    91  	}()
    92  
    93  	ctx, span := instracer.StartSpan(ctx, "distributor.Distribute")
    94  	defer span.End()
    95  	d.resume(ctx)
    96  	defer d.pause(ctx)
    97  
    98  	wg := sync.WaitGroup{}
    99  	wg.Add(len(d.bootstrapHosts))
   100  
   101  	for _, bootstrapHost := range d.bootstrapHosts {
   102  		go func(ctx context.Context, pulse core.Pulse, bootstrapHost *host.Host) {
   103  			defer wg.Done()
   104  
   105  			if bootstrapHost.NodeID.IsEmpty() {
   106  				err := d.pingHost(ctx, bootstrapHost)
   107  				if err != nil {
   108  					logger.Errorf("[ Distribute pulse %d ] Failed to ping and fill node id: %s", pulse.PulseNumber, err)
   109  					return
   110  				}
   111  			}
   112  
   113  			err := d.sendPulseToHost(ctx, &pulse, bootstrapHost)
   114  			if err != nil {
   115  				logger.Errorf("[ Distribute pulse %d ] Failed to send pulse: %s", pulse.PulseNumber, err)
   116  				return
   117  			}
   118  			logger.Infof("[ Distribute pulse %d ] Successfully sent pulse to node %s", pulse.PulseNumber, bootstrapHost)
   119  		}(ctx, pulse, bootstrapHost)
   120  	}
   121  
   122  	wg.Wait()
   123  }
   124  
   125  func (d *distributor) generateID() network.RequestID {
   126  	return network.RequestID(d.idGenerator.Generate())
   127  }
   128  
   129  func (d *distributor) pingHost(ctx context.Context, host *host.Host) error {
   130  	logger := inslogger.FromContext(ctx)
   131  
   132  	ctx, span := instracer.StartSpan(ctx, "distributor.pingHost")
   133  	defer span.End()
   134  	builder := packet.NewBuilder(d.pulsarHost)
   135  	pingPacket := builder.Receiver(host).Type(types.Ping).RequestID(d.generateID()).Build()
   136  	pingCall, err := d.Transport.SendRequest(ctx, pingPacket)
   137  	if err != nil {
   138  		logger.Error(err)
   139  		return errors.Wrap(err, "[ pingHost ] failed to send ping request")
   140  	}
   141  
   142  	logger.Debugf("before ping request")
   143  	result, err := pingCall.GetResult(d.pingRequestTimeout)
   144  	if err != nil {
   145  		logger.Error(err)
   146  		return errors.Wrap(err, "[ pingHost ] failed to get ping result")
   147  	}
   148  
   149  	if result.Error != nil {
   150  		logger.Error(result.Error)
   151  		return errors.Wrap(err, "[ pingHost ] ping result returned error")
   152  	}
   153  
   154  	host.NodeID = result.Sender.NodeID
   155  	logger.Debugf("ping request is done")
   156  
   157  	return nil
   158  }
   159  
   160  func (d *distributor) sendPulseToHost(ctx context.Context, pulse *core.Pulse, host *host.Host) error {
   161  	logger := inslogger.FromContext(ctx)
   162  	defer func() {
   163  		if x := recover(); x != nil {
   164  			logger.Errorf("sendPulseToHost failed with panic: %v", x)
   165  		}
   166  	}()
   167  
   168  	ctx, span := instracer.StartSpan(ctx, "distributor.sendPulseToHosts")
   169  	defer span.End()
   170  	pb := packet.NewBuilder(d.pulsarHost)
   171  	pulseRequest := pb.Receiver(host).Request(&packet.RequestPulse{Pulse: *pulse}).RequestID(d.generateID()).Type(types.Pulse).Build()
   172  	call, err := d.Transport.SendRequest(ctx, pulseRequest)
   173  	if err != nil {
   174  		return err
   175  	}
   176  	result, err := call.GetResult(d.pulseRequestTimeout)
   177  	if err != nil {
   178  		return err
   179  	}
   180  	if result.Error != nil {
   181  		return result.Error
   182  	}
   183  
   184  	return nil
   185  }
   186  
   187  func (d *distributor) pause(ctx context.Context) {
   188  	inslogger.FromContext(ctx).Info("[ Pause ] Pause distribution, stopping transport")
   189  	_, span := instracer.StartSpan(ctx, "distributor.pause")
   190  	defer span.End()
   191  	go d.Transport.Stop()
   192  	<-d.Transport.Stopped()
   193  	d.Transport.Close()
   194  }
   195  
   196  func (d *distributor) resume(ctx context.Context) {
   197  	inslogger.FromContext(ctx).Info("[ Resume ] Resume distribution, starting transport")
   198  	ctx, span := instracer.StartSpan(ctx, "distributor.resume")
   199  	defer span.End()
   200  	if err := d.Transport.Listen(ctx); err != nil {
   201  		inslogger.FromContext(ctx).Error("[ Resume ] Error executing listen:", err)
   202  	}
   203  }