code.vegaprotocol.io/vega@v0.79.0/datanode/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/datanode/entities" 26 "code.vegaprotocol.io/vega/libs/broker" 27 "code.vegaprotocol.io/vega/logging" 28 "code.vegaprotocol.io/vega/protos/vega" 29 ) 30 31 // TestInterface interface (horribly named) is declared here to provide a drop-in replacement for broker mocks used throughout 32 // in addition to providing the classical mockgen functionality, this mock can be used to check the actual events that will be generated 33 // so we don't have to rely on test-only helper functions. 34 type TestInterface interface { 35 Send(event events.Event) 36 Subscribe(s broker.Subscriber) int 37 SubscribeBatch(subs ...broker.Subscriber) 38 Unsubscribe(k int) 39 Receive(ctx context.Context) error 40 } 41 42 type EventReceiver interface { 43 Listen() error 44 Receive(ctx context.Context) (<-chan events.Event, <-chan error) 45 } 46 47 type RawEventReceiver interface { 48 Listen() error 49 Receive(ctx context.Context) (<-chan []byte, <-chan error) 50 } 51 52 type rawEventReceiverSender interface { 53 RawEventReceiver 54 Send(events.Event) error 55 } 56 57 type subscription struct { 58 broker.Subscriber 59 required bool 60 } 61 62 type ChainInfoI interface { 63 SetChainID(string) error 64 GetChainID() (string, error) 65 } 66 67 type OrderEventWithVegaTime struct { 68 events.Order 69 vegaTime time.Time 70 } 71 72 func (oe *OrderEventWithVegaTime) VegaTime() time.Time { 73 return oe.vegaTime 74 } 75 76 func (oe *OrderEventWithVegaTime) GetOrder() *vega.Order { 77 return oe.Order.Order() 78 } 79 80 // Broker - the base broker type 81 // perhaps we can extend this to embed into type-specific brokers. 82 type Broker struct { 83 ctx context.Context 84 mu sync.RWMutex 85 tSubs map[events.Type]map[int]*subscription 86 // these fields ensure a unique ID for all subscribers, regardless of what event types they subscribe to 87 // once the broker context is cancelled, this map will be used to notify all subscribers, who can then 88 // close their internal channels. We can then cleanly shut down (not having unclosed channels) 89 subs map[int]subscription 90 keys []int 91 eChans map[events.Type]chan []events.Event 92 smVer int // version of subscriber map 93 94 eventSource EventReceiver 95 quit chan struct{} 96 chainID string 97 vegaTime time.Time 98 } 99 100 // New creates a new base broker. 101 func New(ctx context.Context, log *logging.Logger, config Config, chainID string, 102 eventsource EventReceiver, 103 ) (*Broker, error) { 104 log = log.Named(namedLogger) 105 log.SetLevel(config.Level.Get()) 106 107 b := &Broker{ 108 ctx: ctx, 109 tSubs: map[events.Type]map[int]*subscription{}, 110 subs: map[int]subscription{}, 111 keys: []int{}, 112 eChans: map[events.Type]chan []events.Event{}, 113 eventSource: eventsource, 114 quit: make(chan struct{}), 115 chainID: chainID, 116 } 117 118 return b, nil 119 } 120 121 func (b *Broker) sendChannel(sub broker.Subscriber, evts []events.Event) { 122 // wait for a max of 1 second 123 timeout := time.NewTimer(time.Second) 124 defer func() { 125 // drain the channel if we managed to leave the function before the timer expired 126 if !timeout.Stop() { 127 <-timeout.C 128 } 129 }() 130 select { 131 case <-b.ctx.Done(): 132 return 133 case <-sub.Closed(): 134 return 135 case sub.C() <- evts: 136 return 137 case <-timeout.C: 138 return 139 } 140 } 141 142 func (b *Broker) sendChannelSync(sub broker.Subscriber, evts []events.Event) bool { 143 select { 144 case <-b.ctx.Done(): 145 return false 146 case <-sub.Skip(): 147 return false 148 case <-sub.Closed(): 149 return true 150 case sub.C() <- evts: 151 return false 152 default: 153 // @TODO perhaps log that we've encountered the channel buffer of a subscriber 154 // this could help us find out what combination of event types + batch sizes are 155 // problematic 156 go b.sendChannel(sub, evts) 157 return false 158 } 159 } 160 161 func (b *Broker) startSending(t events.Type, evt events.Event) { 162 var ( 163 subs map[int]*subscription 164 ver int 165 ) 166 b.mu.Lock() 167 ch, ok := b.eChans[t] 168 if !ok { 169 subs, ver = b.getSubsByType(t, 0) 170 ln := len(subs) + 1 // at least buffer 1 171 ch = make(chan []events.Event, ln*20+20) // create a channel with buffer, min 40 172 b.eChans[t] = ch // assign the newly created channel 173 } 174 b.mu.Unlock() 175 176 if t == events.TimeUpdate { 177 timeUpdate := evt.(entities.TimeUpdateEvent) 178 b.vegaTime = timeUpdate.Time().Truncate(time.Microsecond) 179 } 180 181 if t == events.OrderEvent { 182 orderEvent := evt.(*events.Order) 183 evt = &OrderEventWithVegaTime{*orderEvent, b.vegaTime} 184 } 185 186 ch <- []events.Event{evt} 187 if ok { 188 // we already started the routine to consume the channel 189 // we can return here 190 return 191 } 192 go func(ch chan []events.Event, t events.Type) { 193 defer func() { 194 b.mu.Lock() 195 delete(b.eChans, t) 196 close(ch) 197 b.mu.Unlock() 198 }() 199 for { 200 select { 201 case <-b.ctx.Done(): 202 return 203 case events := <-ch: 204 // we're only reading here, so allow multiple routines to do traverse the map simultaneously 205 b.mu.RLock() 206 ns, nv := b.getSubsByType(t, ver) 207 b.mu.RUnlock() 208 // if nv == ver, the subs haven't changed 209 if nv != ver { 210 ver = nv 211 subs = ns 212 } 213 unsub := make([]int, 0, len(subs)) 214 for k, sub := range subs { 215 select { 216 case <-sub.Skip(): 217 continue 218 case <-sub.Closed(): 219 unsub = append(unsub, k) 220 default: 221 if sub.required { 222 sub.Push(events...) 223 } else if rm := b.sendChannelSync(sub, events); rm { 224 unsub = append(unsub, k) 225 } 226 } 227 } 228 if len(unsub) != 0 { 229 b.mu.Lock() 230 b.rmSubs(unsub...) 231 // we could update the sub map here, because we know subscribers have been removed 232 // but that would hold a write lock for a longer time. 233 b.mu.Unlock() 234 } 235 } 236 } 237 }(ch, t) 238 } 239 240 // Send sends an event to all subscribers. 241 func (b *Broker) Send(event events.Event) { 242 b.startSending(event.Type(), event) 243 } 244 245 // simplified version for better performance - unfortunately, we'll still need to copy the map. 246 func (b *Broker) getSubsByType(t events.Type, sv int) (map[int]*subscription, int) { 247 // we add the entire ALL map to type-specific maps, so if set, we can return this map directly 248 if sv != 0 && sv == b.smVer { 249 return nil, sv 250 } 251 subs, ok := b.tSubs[t] 252 if !ok && t != events.TxErrEvent { 253 // if a typed map isn't set (yet), and it's not the error event, we can return 254 // ALL subscribers directly instead 255 subs = b.tSubs[events.All] 256 } 257 // we still need to create a copy to keep the race detector happy 258 cpy := make(map[int]*subscription, len(subs)) 259 for k, v := range subs { 260 cpy[k] = v 261 } 262 return cpy, b.smVer 263 } 264 265 // Subscribe registers a new subscriber, returning the key. 266 func (b *Broker) Subscribe(s broker.Subscriber) int { 267 b.mu.Lock() 268 k := b.subscribe(s) 269 b.mu.Unlock() 270 return k 271 } 272 273 func (b *Broker) SubscribeBatch(subs ...broker.Subscriber) { 274 b.mu.Lock() 275 for _, s := range subs { 276 k := b.subscribe(s) 277 s.SetID(k) 278 } 279 b.mu.Unlock() 280 } 281 282 func (b *Broker) subscribe(s broker.Subscriber) int { 283 k := b.getKey() 284 sub := subscription{ 285 Subscriber: s, 286 required: s.Ack(), 287 } 288 b.subs[k] = sub 289 types := sub.Types() 290 // filter out weird types values like []events.Type{events.PartyEvent, events.All,} 291 // those subscribers subscribe to all events no matter what, so treat them accordingly 292 isAll := false 293 if len(types) == 0 { 294 isAll = true 295 types = []events.Type{events.All} 296 } else { 297 for _, t := range types { 298 if t == events.All { 299 types = []events.Type{events.All} 300 isAll = true 301 break 302 } 303 } 304 } 305 for _, t := range types { 306 if _, ok := b.tSubs[t]; !ok { 307 b.tSubs[t] = map[int]*subscription{} 308 if !isAll { 309 // not the ALL event, so can be added to the map, and as the "all" subscribers should be 310 for ak, as := range b.tSubs[events.All] { 311 b.tSubs[t][ak] = as 312 } 313 } 314 } 315 b.tSubs[t][k] = &sub 316 } 317 if isAll { 318 for t := range b.tSubs { 319 // Don't add ALL subs to the map they're already in, and don't add it to the 320 // special TxErrEvent map, but we should add them to all other maps 321 if t != events.All && t != events.TxErrEvent { 322 b.tSubs[t][k] = &sub 323 } 324 } 325 } 326 b.smVer++ 327 return k 328 } 329 330 // Unsubscribe removes subscriber from broker 331 // this does not change the state of the subscriber. 332 func (b *Broker) Unsubscribe(k int) { 333 b.mu.Lock() 334 b.rmSubs(k) 335 b.mu.Unlock() 336 } 337 338 func (b *Broker) getKey() int { 339 if len(b.keys) > 0 { 340 k := b.keys[0] 341 b.keys = b.keys[1:] // pop first element 342 return k 343 } 344 return len(b.subs) + 1 // add 1 to avoid zero value 345 } 346 347 func (b *Broker) rmSubs(keys ...int) { 348 if len(keys) == 0 { 349 return 350 } 351 for _, k := range keys { 352 // if the sub doesn't exist, this could be a duplicate call 353 // we do not want the keys slice to contain duplicate values 354 // and so we have to check this first 355 s, ok := b.subs[k] 356 if !ok { 357 return 358 } 359 types := s.Types() 360 for _, t := range types { 361 if t == events.All { 362 types = nil 363 break 364 } 365 } 366 if len(types) == 0 { 367 // remove in all subscribers then 368 for _, v := range b.tSubs { 369 delete(v, k) 370 } 371 } else { 372 for _, t := range types { 373 delete(b.tSubs[t], k) // remove key from typed subs map 374 } 375 } 376 delete(b.subs, k) 377 b.keys = append(b.keys, k) 378 } 379 b.smVer++ 380 } 381 382 func (b *Broker) Receive(ctx context.Context) error { 383 if err := b.eventSource.Listen(); err != nil { 384 return err 385 } 386 387 receiveCh, errCh := b.eventSource.Receive(ctx) 388 389 for e := range receiveCh { 390 if err := checkChainID(b.chainID, e.ChainID()); err != nil { 391 return err 392 } 393 b.Send(e) 394 } 395 396 select { 397 case err := <-errCh: 398 return err 399 default: 400 return nil 401 } 402 } 403 404 func checkChainID(expectedChainID string, chainID string) error { 405 if chainID != expectedChainID { 406 return fmt.Errorf("mismatched chain id received: %s, want %s", chainID, expectedChainID) 407 } 408 return nil 409 }