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  }