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 }