code.vegaprotocol.io/vega@v0.79.0/core/broker/socket_client.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package broker
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"net"
    22  	"strings"
    23  	"sync"
    24  	"time"
    25  
    26  	"code.vegaprotocol.io/vega/core/events"
    27  	"code.vegaprotocol.io/vega/libs/proto"
    28  	"code.vegaprotocol.io/vega/logging"
    29  	eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1"
    30  
    31  	"go.nanomsg.org/mangos/v3"
    32  	mangosErr "go.nanomsg.org/mangos/v3/errors"
    33  	"go.nanomsg.org/mangos/v3/protocol"
    34  	"go.nanomsg.org/mangos/v3/protocol/pair"
    35  	"golang.org/x/sync/errgroup"
    36  
    37  	_ "go.nanomsg.org/mangos/v3/transport/inproc" // Does some nanomsg magic presumably
    38  	_ "go.nanomsg.org/mangos/v3/transport/tcp"    // Does some nanomsg magic presumably
    39  )
    40  
    41  // SocketClient stream events sent to this broker over a socket to a remote broker.
    42  // This is used to send events from a non-validating core node to a data node.
    43  type socketClient struct {
    44  	log *logging.Logger
    45  
    46  	config *SocketConfig
    47  	sock   protocol.Socket
    48  
    49  	eventsCh chan events.Event
    50  
    51  	closed bool
    52  
    53  	mut sync.RWMutex
    54  }
    55  
    56  func pipeEventToString(pe mangos.PipeEvent) string {
    57  	switch pe {
    58  	case mangos.PipeEventAttached:
    59  		return "Attached"
    60  	case mangos.PipeEventDetached:
    61  		return "Detached"
    62  	default:
    63  		return "Attaching"
    64  	}
    65  }
    66  
    67  const namedSocketClientLogger = "socket-client"
    68  
    69  func newSocketClient(ctx context.Context, log *logging.Logger, config *SocketConfig) (*socketClient, error) {
    70  	log = log.Named(namedSocketClientLogger)
    71  	sock, err := pair.NewSocket()
    72  	if err != nil {
    73  		return nil, fmt.Errorf("failed to create new push socket: %w", err)
    74  	}
    75  
    76  	socOpts := map[string]interface{}{
    77  		mangos.OptionWriteQLen:    config.SocketChannelBufferSize,
    78  		mangos.OptionSendDeadline: config.SocketQueueTimeout.Duration,
    79  	}
    80  
    81  	for name, value := range socOpts {
    82  		if err := sock.SetOption(name, value); err != nil {
    83  			return nil, fmt.Errorf("failed to set option: %w", err)
    84  		}
    85  	}
    86  
    87  	sock.SetPipeEventHook(func(pe mangos.PipeEvent, p mangos.Pipe) {
    88  		log.Info(
    89  			"New broker connection event",
    90  			logging.String("eventType", pipeEventToString(pe)),
    91  			logging.Uint32("id", p.ID()),
    92  			logging.String("address", p.Address()),
    93  		)
    94  	})
    95  
    96  	s := &socketClient{
    97  		log: log,
    98  
    99  		config: config,
   100  		sock:   sock,
   101  
   102  		eventsCh: make(chan events.Event, config.EventChannelBufferSize),
   103  	}
   104  
   105  	if err := s.connect(ctx); err != nil {
   106  		return nil, fmt.Errorf("failed to connect: %w", err)
   107  	}
   108  
   109  	go func() {
   110  		if err := s.stream(ctx); err != nil {
   111  			s.log.Fatal("socket streaming has failed", logging.Error(err))
   112  		}
   113  	}()
   114  
   115  	return s, nil
   116  }
   117  
   118  func (s *socketClient) SendBatch(evts []events.Event) error {
   119  	for _, evt := range evts {
   120  		if err := s.Send(evt); err != nil {
   121  			return err
   122  		}
   123  	}
   124  
   125  	return nil
   126  }
   127  
   128  // Send sends events on the events queue.
   129  // Panics if socket is closed or is not streaming.
   130  // Returns an error if events queue is full.
   131  func (s *socketClient) Send(evt events.Event) error {
   132  	s.mut.RLock()
   133  	defer s.mut.RUnlock()
   134  
   135  	if s.closed {
   136  		s.log.Panic("Failed to send event - socket is closed closed socket")
   137  	}
   138  
   139  	select {
   140  	case s.eventsCh <- evt:
   141  		break
   142  	case <-time.After(2 * time.Second):
   143  	default:
   144  		return fmt.Errorf("event queue is full")
   145  	}
   146  
   147  	return nil
   148  }
   149  
   150  func (s *socketClient) close() {
   151  	s.mut.Lock()
   152  	defer s.mut.Unlock()
   153  
   154  	s.closed = true
   155  	close(s.eventsCh)
   156  
   157  	if err := s.sock.Close(); err != nil {
   158  		s.log.Error("failed to close socket", logging.Error(err))
   159  	}
   160  }
   161  
   162  func (s *socketClient) getDialAddr() string {
   163  	return fmt.Sprintf(
   164  		"%s://%s",
   165  		strings.ToLower(s.config.Transport),
   166  		net.JoinHostPort(s.config.Address, fmt.Sprintf("%d", s.config.Port)),
   167  	)
   168  }
   169  
   170  func (s *socketClient) connect(ctx context.Context) error {
   171  	ticker := time.NewTicker(s.config.DialRetryInterval.Get())
   172  	defer ticker.Stop()
   173  	ctx, cancel := context.WithTimeout(ctx, s.config.DialTimeout.Get())
   174  	defer cancel()
   175  
   176  	addr := s.getDialAddr()
   177  
   178  	for {
   179  		err := s.sock.Dial(addr)
   180  		if err == nil {
   181  			return nil
   182  		}
   183  
   184  		s.log.Error("failed to connect, retrying", logging.Error(err), logging.String("peer", addr))
   185  
   186  		select {
   187  		case <-ctx.Done():
   188  			return ctx.Err()
   189  		case <-ticker.C:
   190  		}
   191  	}
   192  }
   193  
   194  func (s *socketClient) stream(ctx context.Context) error {
   195  	s.mut.RLock()
   196  	if s.closed {
   197  		s.mut.RUnlock()
   198  		return fmt.Errorf("socket is closed")
   199  	}
   200  	s.mut.RUnlock()
   201  
   202  	defer s.close()
   203  
   204  	for {
   205  		select {
   206  		case <-ctx.Done():
   207  			return nil
   208  		case evt := <-s.eventsCh:
   209  			msg, err := proto.Marshal(evt.StreamMessage())
   210  			if err != nil {
   211  				s.log.Error("Failed to marshal event", logging.Error(err))
   212  
   213  				continue
   214  			}
   215  
   216  			err = s.sock.Send(msg)
   217  			if err != nil {
   218  				switch err {
   219  				case protocol.ErrClosed:
   220  					return fmt.Errorf("socket is closed: %w", err)
   221  				case protocol.ErrSendTimeout:
   222  					return fmt.Errorf("failed to queue message on socket: %w", err)
   223  				default:
   224  					s.log.Error("Failed to send to socket", logging.Error(err))
   225  				}
   226  
   227  				continue
   228  			}
   229  		}
   230  	}
   231  }
   232  
   233  func (s *socketClient) Receive(ctx context.Context) (<-chan events.Event, <-chan error) {
   234  	// channel onto which we push the raw messages from the queue
   235  	inboundCh := make(chan []byte, 10)
   236  	stopCh := make(chan struct{}, 1)
   237  
   238  	outboundCh := make(chan events.Event, 10)
   239  	errCh := make(chan error, 1)
   240  
   241  	eg, ctx := errgroup.WithContext(ctx)
   242  
   243  	eg.Go(func() error {
   244  		<-ctx.Done()
   245  
   246  		return nil
   247  	})
   248  
   249  	eg.Go(func() error {
   250  		defer close(outboundCh)
   251  
   252  		for msg := range inboundCh {
   253  			var be eventspb.BusEvent
   254  			if err := proto.Unmarshal(msg, &be); err != nil {
   255  				// surely we should stop if this happens?
   256  				s.log.Error("Failed to unmarshal received event", logging.Error(err))
   257  				continue
   258  			}
   259  			if be.Version != eventspb.Version {
   260  				return fmt.Errorf("mismatched BusEvent version received: %d, want %d", be.Version, eventspb.Version)
   261  			}
   262  
   263  			evt := toEvent(ctx, &be)
   264  			if evt == nil {
   265  				s.log.Error("Cannot convert proto event to internal event", logging.String("event_type", be.GetType().String()))
   266  				continue
   267  			}
   268  
   269  			// Listen for context cancels, even if we're blocked sending events
   270  			select {
   271  			case outboundCh <- evt:
   272  			case <-ctx.Done():
   273  				return ctx.Err()
   274  			}
   275  		}
   276  
   277  		return nil
   278  	})
   279  
   280  	eg.Go(func() error {
   281  		defer close(inboundCh)
   282  
   283  		s.sock.SetOption(mangos.OptionRecvDeadline, 1*time.Second)
   284  		for {
   285  			msg, err := s.sock.Recv()
   286  			if err != nil {
   287  				switch err {
   288  				case mangosErr.ErrRecvTimeout:
   289  					select {
   290  					case <-stopCh:
   291  						return nil
   292  					default:
   293  					}
   294  				case mangosErr.ErrBadVersion:
   295  					return fmt.Errorf("failed with bad protocol version: %w", err)
   296  				case mangosErr.ErrClosed:
   297  					return nil
   298  				default:
   299  					s.log.Error("Failed to Receive message", logging.Error(err))
   300  					continue
   301  				}
   302  			}
   303  
   304  			if len(msg) == 0 {
   305  				continue
   306  			}
   307  
   308  			inboundCh <- msg
   309  		}
   310  	})
   311  
   312  	go func() {
   313  		defer func() {
   314  			close(errCh)
   315  		}()
   316  
   317  		if err := eg.Wait(); err != nil {
   318  			errCh <- err
   319  		}
   320  	}()
   321  
   322  	return outboundCh, errCh
   323  }