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 }