code.vegaprotocol.io/vega@v0.79.0/datanode/broker/fan_out_event_source.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  
    23  	"code.vegaprotocol.io/vega/core/events"
    24  )
    25  
    26  // fanOutEventSource: an event source to fan out an event stream, it is told in advance the number of subscribers to
    27  // expect and only starts publishing events once that number of subscriptions has been received.
    28  type fanOutEventSource struct {
    29  	source                 EventReceiver
    30  	sendChannelBufferSize  int
    31  	expectedNumSubscribers int
    32  	numSubscribers         int
    33  	eventChannels          []chan events.Event
    34  	errorChannels          []chan error
    35  	listening              bool
    36  	mutex                  sync.Mutex
    37  }
    38  
    39  //revive:disable:unexported-return
    40  func NewFanOutEventSource(source EventReceiver, sendChannelBufferSize int, expectedNumSubscribers int) *fanOutEventSource {
    41  	return &fanOutEventSource{
    42  		source:                 source,
    43  		sendChannelBufferSize:  sendChannelBufferSize,
    44  		expectedNumSubscribers: expectedNumSubscribers,
    45  	}
    46  }
    47  
    48  func (e *fanOutEventSource) Listen() error {
    49  	e.mutex.Lock()
    50  	defer e.mutex.Unlock()
    51  
    52  	if !e.listening {
    53  		err := e.source.Listen()
    54  		if err != nil {
    55  			return err
    56  		}
    57  		e.listening = true
    58  	}
    59  
    60  	return nil
    61  }
    62  
    63  func (e *fanOutEventSource) Receive(ctx context.Context) (<-chan events.Event, <-chan error) {
    64  	e.mutex.Lock()
    65  	defer e.mutex.Unlock()
    66  
    67  	eventsCh := make(chan events.Event, e.sendChannelBufferSize)
    68  	errorCh := make(chan error, 1)
    69  
    70  	e.eventChannels = append(e.eventChannels, eventsCh)
    71  	e.errorChannels = append(e.errorChannels, errorCh)
    72  
    73  	e.numSubscribers++
    74  
    75  	if e.numSubscribers > e.expectedNumSubscribers {
    76  		panic("number of subscribers exceeded expected subscriber number")
    77  	}
    78  
    79  	// Once the number of subscribers equals the expected number start forwarding events
    80  	if e.numSubscribers == e.expectedNumSubscribers {
    81  		go e.sendEvents(ctx)
    82  	}
    83  
    84  	return eventsCh, errorCh
    85  }
    86  
    87  func (e *fanOutEventSource) sendEvents(ctx context.Context) {
    88  	srcEventCh, srcErrorCh := e.source.Receive(ctx)
    89  
    90  	defer func() {
    91  		for _, evtCh := range e.eventChannels {
    92  			close(evtCh)
    93  		}
    94  
    95  		for _, errorCh := range e.errorChannels {
    96  			close(errorCh)
    97  		}
    98  	}()
    99  
   100  	var firstBlock int64
   101  	var prevBlock int64
   102  	var prevSeq uint64
   103  	first := true
   104  
   105  	for {
   106  		select {
   107  		case <-ctx.Done():
   108  			return
   109  		case event, ok := <-srcEventCh:
   110  			if !ok {
   111  				return
   112  			}
   113  			// Check that the sequence of events is sane:
   114  			//  - First event can be at any height; but event must have sequence number 1
   115  			//  - Subsequent events must either increase sequence number by 1 OR
   116  			//    Go back to 1 and increase block number by 1
   117  			//  Exception:
   118  			//   - We get two blocks with the first block number; presumably because we have genesis
   119  			//     or checkpoint; and then the first real block.
   120  			if first {
   121  				firstBlock = event.BlockNr()
   122  				first = false
   123  			}
   124  
   125  			firstBlockFirstEvent := event.BlockNr() == firstBlock && event.Sequence() == 1
   126  			sameBlockNextEvent := event.BlockNr() == prevBlock && event.Sequence() == prevSeq+1
   127  			nextBlockFirstEvent := event.BlockNr() == prevBlock+1 && event.Sequence() == 1
   128  			firstBlockAgain := event.BlockNr() == firstBlock && prevBlock == firstBlock && event.Sequence() == 1
   129  			ok = firstBlockFirstEvent || sameBlockNextEvent || nextBlockFirstEvent || firstBlockAgain
   130  
   131  			if !ok {
   132  				panic(fmt.Sprintf("non-contiguous event stream: last: %d-%d, received: %d-%d",
   133  					prevBlock, prevSeq, event.BlockNr(), event.Sequence()))
   134  			}
   135  
   136  			// if this is the first event, then this gives 0 + 1 for normal events, next will be 2
   137  			// composite event of 5 gives 0 + 5, the next sequence will be 6
   138  			prevSeq += event.CompositeCount()
   139  			if firstBlockAgain || (nextBlockFirstEvent && !firstBlockFirstEvent) {
   140  				prevSeq = event.CompositeCount()
   141  			}
   142  			prevBlock = event.BlockNr()
   143  
   144  			// Now get on with sending event to listeners
   145  			for _, evtCh := range e.eventChannels {
   146  				// Listen for context cancels, even if we're blocked sending events
   147  				select {
   148  				case evtCh <- event:
   149  				case <-ctx.Done():
   150  					return
   151  				}
   152  			}
   153  
   154  		case err, ok := <-srcErrorCh:
   155  			if !ok {
   156  				return
   157  			}
   158  			for _, errorCh := range e.errorChannels {
   159  				errorCh <- err
   160  			}
   161  		}
   162  	}
   163  }