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 }