code.vegaprotocol.io/vega@v0.79.0/core/broker/broker.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 "sync" 22 "time" 23 24 "code.vegaprotocol.io/vega/core/events" 25 "code.vegaprotocol.io/vega/libs/broker" 26 "code.vegaprotocol.io/vega/logging" 27 ) 28 29 type Stats interface { 30 IncrementEventCount(count uint64) 31 } 32 33 // Interface interface (horribly named) is declared here to provide a drop-in replacement for broker mocks used throughout 34 // in addition to providing the classical mockgen functionality, this mock can be used to check the actual events that will be generated 35 // so we don't have to rely on test-only helper functions 36 // 37 //go:generate go run github.com/golang/mock/mockgen -destination mocks/broker_mock.go -package mocks code.vegaprotocol.io/vega/core/broker Interface 38 type Interface interface { 39 Stage(event events.Event) 40 Send(event events.Event) 41 SendBatch(events []events.Event) 42 Subscribe(s broker.Subscriber) int 43 SubscribeBatch(subs ...broker.Subscriber) 44 Unsubscribe(k int) 45 SetStreaming(on bool) bool 46 } 47 48 // SocketClient is an interface to send serialized events over a socket. 49 // 50 //go:generate go run github.com/golang/mock/mockgen -destination mocks/socket_client_mock.go -package mocks code.vegaprotocol.io/vega/core/broker SocketClient 51 type SocketClient interface { 52 SendBatch(events []events.Event) error 53 Receive(ctx context.Context) (<-chan events.Event, <-chan error) 54 } 55 56 type FileClientSend interface { 57 SendBatch(events []events.Event) error 58 Replace(*FileClient) 59 Close() error 60 } 61 62 type subscription struct { 63 broker.Subscriber 64 required bool 65 } 66 67 // Broker - the base broker type 68 // perhaps we can extend this to embed into type-specific brokers. 69 type Broker struct { 70 ctx context.Context 71 72 mu sync.Mutex 73 tSubs map[events.Type]map[int]*subscription 74 // these fields ensure a unique ID for all subscribers, regardless of what event types they subscribe to 75 // once the broker context is cancelled, this map will be used to notify all subscribers, who can then 76 // close their internal channels. We can then cleanly shut down (not having unclosed channels) 77 subs map[int]subscription 78 keys []int 79 eChans map[events.Type]chan []events.Event 80 81 seqGen *gen 82 83 log *logging.Logger 84 85 config Config 86 socketClient SocketClient 87 fileClient FileClientSend 88 canStream bool // whether not we should send events to the socketClient 89 stats Stats 90 91 staged []events.Event 92 } 93 94 // New creates a new base broker. 95 func New(ctx context.Context, log *logging.Logger, config Config, stats Stats) (*Broker, error) { 96 log = log.Named(namedLogger) 97 log.SetLevel(config.Level.Get()) 98 99 b := &Broker{ 100 ctx: ctx, 101 log: log, 102 tSubs: map[events.Type]map[int]*subscription{}, 103 subs: map[int]subscription{}, 104 keys: []int{}, 105 eChans: map[events.Type]chan []events.Event{}, 106 seqGen: newGen(), 107 config: config, 108 canStream: bool(config.Socket.Enabled), 109 stats: stats, 110 staged: []events.Event{}, 111 } 112 113 if config.Socket.Enabled { 114 sc, err := newSocketClient(ctx, log, &config.Socket) 115 if err != nil { 116 return nil, fmt.Errorf("failed to initialize socket client: %w", err) 117 } 118 119 b.socketClient = sc 120 } 121 122 if config.File.Enabled { 123 fc, err := NewFileClient(log, &config.File) 124 if err != nil { 125 return nil, fmt.Errorf("failed to initialize file client: %w", err) 126 } 127 b.fileClient = fc 128 } 129 130 return b, nil 131 } 132 133 func (b *Broker) ReloadConf(config Config) { 134 // check if we need to update the log level. 135 if newLvl := config.Level.Get(); newLvl != b.config.Level.Get() { 136 b.log.SetLevel(newLvl) 137 b.config.Level = config.Level 138 } 139 // if the config has enabled the file client, check if the config is different, and replace or activate the file client. 140 if config.File.Enabled && config.File != b.config.File { 141 fc, err := NewFileClient(b.log, &config.File) 142 if err != nil { 143 // we can't instantiate the file client, though the config is updated - treat this as a fatal error? 144 b.log.Fatal("Could not instantiate file client", logging.Error(err)) 145 } 146 if b.fileClient != nil { 147 b.fileClient.Replace(fc) 148 } else { 149 b.fileClient = fc 150 } 151 // update the conifg 152 b.config.File = config.File 153 } else if !config.File.Enabled && b.fileClient != nil { 154 // we were streaming to a file, not anymore, so close the file and set the field to nil. 155 oldFC := b.fileClient 156 b.fileClient = nil 157 if err := oldFC.Close(); err != nil { 158 b.log.Error("Disabled file streaming, but file was not closed properly", logging.Error(err)) 159 } 160 b.config.File = config.File 161 } 162 // we only allow switching socket config on if it isn't already. Switching sockets is a bit of a pain. 163 if config.Socket.Enabled && !b.config.Socket.Enabled { 164 sc, err := newSocketClient(b.ctx, b.log, &config.Socket) 165 if err != nil { 166 b.log.Fatal("Could not instantiate new socket client", logging.Error(err)) 167 } 168 b.socketClient = sc 169 b.config.Socket = config.Socket 170 } 171 } 172 173 func (b *Broker) OnTick(ctx context.Context, _ time.Time) { 174 if len(b.staged) == 0 { 175 return 176 } 177 for _, evt := range b.staged { 178 evt.Replace(ctx) 179 b.Send(evt) 180 } 181 b.staged = []events.Event{} 182 } 183 184 func (b *Broker) sendChannel(sub broker.Subscriber, evts []events.Event) { 185 // wait for a max of 1 second 186 timeout := time.NewTimer(time.Second) 187 defer func() { 188 // drain the channel if we managed to leave the function before the timer expired 189 if !timeout.Stop() { 190 <-timeout.C 191 } 192 }() 193 select { 194 case <-b.ctx.Done(): 195 return 196 case <-sub.Closed(): 197 return 198 case sub.C() <- evts: 199 return 200 case <-timeout.C: 201 return 202 } 203 } 204 205 func (b *Broker) sendChannelSync(sub broker.Subscriber, evts []events.Event) bool { 206 select { 207 case <-b.ctx.Done(): 208 return false 209 case <-sub.Skip(): 210 return false 211 case <-sub.Closed(): 212 return true 213 case sub.C() <- evts: 214 return false 215 default: 216 // @TODO perhaps log that we've encountered the channel buffer of a subscriber 217 // this could help us find out what combination of event types + batch sizes are 218 // problematic 219 go b.sendChannel(sub, evts) 220 return false 221 } 222 } 223 224 func (b *Broker) startSending(t events.Type, evts []events.Event) { 225 if b.StreamingEnabled() { 226 if err := b.socketClient.SendBatch(evts); err != nil { 227 b.log.Fatal("Failed to send to socket client", logging.Error(err)) 228 } 229 } 230 231 if b.fileStreamEnabled() { 232 if err := b.fileClient.SendBatch(evts); err != nil { 233 b.log.Fatal("Failed to send to file client", logging.Error(err)) 234 } 235 } 236 237 b.mu.Lock() 238 ch, ok := b.eChans[t] 239 if !ok { 240 subs := b.getSubsByType(t) 241 ln := len(subs) + 1 // at least buffer 1 242 ch = make(chan []events.Event, ln*20+20) // create a channel with buffer, min 40 243 b.eChans[t] = ch // assign the newly created channel 244 } 245 b.mu.Unlock() 246 select { 247 case <-b.ctx.Done(): 248 return 249 default: 250 ch <- evts 251 } 252 if ok { 253 // we already started the routine to consume the channel 254 // we can return here 255 return 256 } 257 go func(ch chan []events.Event, t events.Type) { 258 defer func() { 259 b.mu.Lock() 260 delete(b.eChans, t) 261 close(ch) 262 b.mu.Unlock() 263 }() 264 for { 265 select { 266 case <-b.ctx.Done(): 267 return 268 case evts := <-ch: 269 b.mu.Lock() 270 subs := b.getSubsByType(t) 271 b.mu.Unlock() 272 unsub := make([]int, 0, len(subs)) 273 for k, sub := range subs { 274 select { 275 case <-b.ctx.Done(): 276 return 277 case <-sub.Skip(): 278 continue 279 case <-sub.Closed(): 280 unsub = append(unsub, k) 281 default: 282 if sub.required { 283 sub.Push(evts...) 284 } else if shouldUnsubscribe := b.sendChannelSync(sub, evts); shouldUnsubscribe { 285 unsub = append(unsub, k) 286 } 287 } 288 } 289 if len(unsub) != 0 { 290 b.mu.Lock() 291 b.rmSubs(unsub...) 292 b.mu.Unlock() 293 } 294 } 295 } 296 }(ch, t) 297 } 298 299 // SendBatch sends a slice of events to subscribers that can handle the events in the slice 300 // the events don't have to be of the same type, and most subscribers will ignore unknown events 301 // but this will slow down those subscribers, so avoid doing silly things. 302 func (b *Broker) SendBatch(events []events.Event) { 303 if len(events) == 0 { 304 return 305 } 306 b.stats.IncrementEventCount(uint64(len(events))) 307 evts := b.seqGen.setSequence(events...) 308 b.startSending(events[0].Type(), evts) 309 } 310 311 // Send sends an event to all subscribers. 312 func (b *Broker) Send(event events.Event) { 313 b.stats.IncrementEventCount(1) 314 b.startSending(event.Type(), b.seqGen.setSequence(event)) 315 } 316 317 // Stages an event to be sent at the start of the next block. 318 func (b *Broker) Stage(event events.Event) { 319 b.staged = append(b.staged, event) 320 } 321 322 // simplified version for better performance - unfortunately, we'll still need to copy the map. 323 func (b *Broker) getSubsByType(t events.Type) map[int]*subscription { 324 // we add the entire ALL map to type-specific maps, so if set, we can return this map directly 325 326 subs, ok := b.tSubs[t] 327 if !ok { 328 // if a typed map isn't set (yet), and it's not the error event, we can return 329 // ALL subscribers directly instead 330 subs = b.tSubs[events.All] 331 } 332 333 // we still need to create a copy to keep the race detector happy 334 cpy := make(map[int]*subscription, len(subs)) 335 for k, v := range subs { 336 cpy[k] = v 337 } 338 return cpy 339 } 340 341 // Subscribe registers a new subscriber, returning the key. 342 func (b *Broker) Subscribe(s broker.Subscriber) int { 343 b.mu.Lock() 344 k := b.subscribe(s) 345 s.SetID(k) 346 b.mu.Unlock() 347 return k 348 } 349 350 func (b *Broker) SubscribeBatch(subs ...broker.Subscriber) { 351 b.mu.Lock() 352 for _, s := range subs { 353 k := b.subscribe(s) 354 s.SetID(k) 355 } 356 b.mu.Unlock() 357 } 358 359 func (b *Broker) subscribe(s broker.Subscriber) int { 360 k := b.getKey() 361 sub := subscription{ 362 Subscriber: s, 363 required: s.Ack(), 364 } 365 b.subs[k] = sub 366 types := sub.Types() 367 // filter out weird types values like []events.Type{events.PartyEvent, events.All,} 368 // those subscribers subscribe to all events no matter what, so treat them accordingly 369 isAll := false 370 if len(types) == 0 { 371 isAll = true 372 types = []events.Type{events.All} 373 } else { 374 for _, t := range types { 375 if t == events.All { 376 types = []events.Type{events.All} 377 isAll = true 378 break 379 } 380 } 381 } 382 for _, t := range types { 383 if _, ok := b.tSubs[t]; !ok { 384 b.tSubs[t] = map[int]*subscription{} 385 if !isAll { 386 // not the ALL event, so can be added to the map, and as the "all" subscribers should be 387 for ak, as := range b.tSubs[events.All] { 388 b.tSubs[t][ak] = as 389 } 390 } 391 } 392 b.tSubs[t][k] = &sub 393 } 394 if isAll { 395 for t := range b.tSubs { 396 // Don't add ALL subs to the map they're already in, and don't add it to the 397 // special TxErrEvent map, but we should add them to all other maps 398 if t != events.All { 399 b.tSubs[t][k] = &sub 400 } 401 } 402 } 403 return k 404 } 405 406 // Unsubscribe removes subscriber from broker 407 // this does not change the state of the subscriber. 408 func (b *Broker) Unsubscribe(k int) { 409 b.mu.Lock() 410 b.rmSubs(k) 411 b.mu.Unlock() 412 } 413 414 func (b *Broker) getKey() int { 415 if len(b.keys) > 0 { 416 k := b.keys[0] 417 b.keys = b.keys[1:] // pop first element 418 return k 419 } 420 return len(b.subs) + 1 // add 1 to avoid zero value 421 } 422 423 func (b *Broker) rmSubs(keys ...int) { 424 for _, k := range keys { 425 // if the sub doesn't exist, this could be a duplicate call 426 // we do not want the keys slice to contain duplicate values 427 // and so we have to check this first 428 s, ok := b.subs[k] 429 if !ok { 430 return 431 } 432 types := s.Types() 433 for _, t := range types { 434 if t == events.All { 435 types = nil 436 break 437 } 438 } 439 if len(types) == 0 { 440 // remove in all subscribers then 441 for _, v := range b.tSubs { 442 delete(v, k) 443 } 444 } else { 445 for _, t := range types { 446 delete(b.tSubs[t], k) // remove key from typed subs map 447 } 448 } 449 delete(b.subs, k) 450 b.keys = append(b.keys, k) 451 } 452 } 453 454 // SetStreaming allows the ability to toggle on and off sending events to the socketClient. 455 func (b *Broker) SetStreaming(on bool) bool { 456 old := b.canStream 457 b.canStream = on 458 return old 459 } 460 461 func (b *Broker) StreamingEnabled() bool { 462 return b.canStream && b.socketClient != nil 463 } 464 465 func (b *Broker) SocketClient() SocketClient { 466 return b.socketClient 467 } 468 469 func (b *Broker) fileStreamEnabled() bool { 470 return b.fileClient != nil 471 }