code.vegaprotocol.io/vega@v0.79.0/core/broker/broker_test.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_test
    17  
    18  import (
    19  	"context"
    20  	"errors"
    21  	"fmt"
    22  	"net"
    23  	"os"
    24  	"os/exec"
    25  	"sync"
    26  	"testing"
    27  	"time"
    28  
    29  	"code.vegaprotocol.io/vega/core/broker"
    30  	"code.vegaprotocol.io/vega/core/broker/mocks"
    31  	"code.vegaprotocol.io/vega/core/events"
    32  	"code.vegaprotocol.io/vega/core/stats"
    33  	"code.vegaprotocol.io/vega/core/types"
    34  	vgcontext "code.vegaprotocol.io/vega/libs/context"
    35  	"code.vegaprotocol.io/vega/logging"
    36  	eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1"
    37  
    38  	"github.com/golang/mock/gomock"
    39  	"github.com/golang/protobuf/proto"
    40  	"github.com/stretchr/testify/assert"
    41  	"github.com/stretchr/testify/require"
    42  	"go.nanomsg.org/mangos/v3/protocol/pair"
    43  )
    44  
    45  type brokerTst struct {
    46  	*broker.Broker
    47  	cfunc context.CancelFunc
    48  	ctx   context.Context
    49  	ctrl  *gomock.Controller
    50  	stats *stats.Blockchain
    51  }
    52  
    53  type evt struct {
    54  	t       events.Type
    55  	ctx     context.Context
    56  	sid     uint64
    57  	id      string
    58  	cid     string
    59  	txHash  string
    60  	blockNr int64
    61  }
    62  
    63  func getBroker(t *testing.T) *brokerTst {
    64  	t.Helper()
    65  	ctx, cfunc := context.WithCancel(context.Background())
    66  	ctrl := gomock.NewController(t)
    67  	statistics := stats.NewBlockchain()
    68  	broker, _ := broker.New(ctx, logging.NewTestLogger(), broker.NewDefaultConfig(), statistics)
    69  	return &brokerTst{
    70  		Broker: broker,
    71  		cfunc:  cfunc,
    72  		ctx:    ctx,
    73  		ctrl:   ctrl,
    74  		stats:  statistics,
    75  	}
    76  }
    77  
    78  func (b brokerTst) randomEvt() *evt {
    79  	idString := "generic-id"
    80  	if ctxV, ok := b.ctx.Value("traceID").(string); ok {
    81  		idString = ctxV
    82  	}
    83  	return &evt{
    84  		t:       events.All,
    85  		ctx:     b.ctx,
    86  		id:      idString,
    87  		cid:     "testchain",
    88  		txHash:  "testTxHash",
    89  		blockNr: 0,
    90  	}
    91  }
    92  
    93  func (b *brokerTst) Finish() {
    94  	b.cfunc()
    95  }
    96  
    97  func TestSequenceIDGen(t *testing.T) {
    98  	t.Run("Sequence ID is correctly - events dispatched per block (ordered)", testSequenceIDGenSeveralBlocksOrdered)
    99  	t.Run("Sequence ID is correctly - events dispatched for several blocks at the same time", testSequenceIDGenSeveralBlocksUnordered)
   100  }
   101  
   102  func TestSubscribe(t *testing.T) {
   103  	t.Run("Subscribe and unsubscribe required - success", testSubUnsubSuccess)
   104  	t.Run("Subscribe reuses keys", testSubReuseKey)
   105  	t.Run("Unsubscribe automatically if subscriber is closed", testUnsubscribeAutomaticallyIfSubscriberIsClosed)
   106  }
   107  
   108  func TestStream(t *testing.T) {
   109  	t.Run("Streams events over socket", testStreamsOverSocket)
   110  	t.Run("Stops process if can not send to socket", testStopsProcessOnStreamError)
   111  }
   112  
   113  func TestSendEvent(t *testing.T) {
   114  	t.Run("Skip optional subscribers", testSkipOptional)
   115  	t.Run("Skip optional subscribers in a batch send", testSendBatchChannel)
   116  	t.Run("Send batch to ack subscriber", testSendBatch)
   117  	t.Run("Stop sending if context is cancelled", testStopCtx)
   118  	t.Run("Stop sending if context is cancelled, even new events", testStopCtxSendAgain)
   119  	t.Run("Skip subscriber based on channel state", testSkipSubscriberBasedOnChannelState)
   120  	t.Run("Send only to typed subscriber (also tests TxErrEvents are skipped)", testEventTypeSubscription)
   121  }
   122  
   123  func TestFileStreaming(t *testing.T) {
   124  	tstBroker := getBroker(t)
   125  	defer tstBroker.Finish()
   126  	// create a temp file to use, close it so the opening is definitely handled by the file client, but remove it after the tests.
   127  	tmp, err := os.CreateTemp("", "file_client_tmp")
   128  	require.NoError(t, err)
   129  	tmpName := tmp.Name()
   130  	require.NoError(t, tmp.Close())
   131  	defer os.Remove(tmpName)
   132  	cfg := broker.NewDefaultConfig()
   133  	// current file contents:
   134  	content, err := os.ReadFile(tmpName)
   135  	require.NoError(t, err)
   136  	tmp2, err := os.CreateTemp("", "file_client_tmp_2")
   137  	require.NoError(t, err)
   138  	tmp2Name := tmp2.Name()
   139  	defer os.Remove(tmp2Name)
   140  	require.NoError(t, tmp2.Close())
   141  	content2, err := os.ReadFile(tmp2Name)
   142  	require.NoError(t, err)
   143  	require.Equal(t, content, content2)
   144  	t.Run("Reload config, toggling file streaming", func(t *testing.T) {
   145  		cfg.File.Enabled = true
   146  		cfg.File.File = tmpName
   147  		tstBroker.ReloadConf(cfg)
   148  		batchSendTest(t, tstBroker)
   149  		// now disable file streaming (closes the temp file)
   150  		cfg.File.Enabled = false
   151  		cfg.File.File = ""
   152  		tstBroker.ReloadConf(cfg)
   153  		newContent, err := os.ReadFile(tmpName)
   154  		require.NoError(t, err)
   155  		require.NotEqual(t, newContent, content)
   156  		require.True(t, len(content) < len(newContent))
   157  	})
   158  	t.Run("Reload config, switch file output", func(t *testing.T) {
   159  		cfg.File.Enabled = true
   160  		cfg.File.File = tmpName
   161  		batchSendTest(t, tstBroker)
   162  		cfg.File.File = tmp2Name
   163  		tstBroker.ReloadConf(cfg)
   164  		// turn the file streaming off
   165  		cfg.File.Enabled = false
   166  		cfg.File.File = ""
   167  		tstBroker.ReloadConf(cfg)
   168  		tmpCont, err := os.ReadFile(tmpName)
   169  		require.NoError(t, err)
   170  		tmp2Cont, err := os.ReadFile(tmp2Name)
   171  		require.NoError(t, err)
   172  		require.NotEqual(t, tmpCont, tmp2Cont)
   173  		require.Equal(t, tmp2Cont, content2)
   174  	})
   175  	t.Run("Reload Config, switch file output, but write to the new file", func(t *testing.T) {
   176  		cfg.File.Enabled = true
   177  		cfg.File.File = tmpName
   178  		tstBroker.ReloadConf(cfg)
   179  		// send another batch, this doubles the data in the file because we used tmpName in tests above
   180  		batchSendTest(t, tstBroker)
   181  		cfg.File.File = tmp2Name
   182  		tstBroker.ReloadConf(cfg)
   183  		// send the data once here...
   184  		batchSendTest(t, tstBroker)
   185  		// turn the file streaming off (optional step)
   186  		cfg.File.Enabled = false
   187  		cfg.File.File = ""
   188  		tstBroker.ReloadConf(cfg)
   189  		tmpCont, err := os.ReadFile(tmpName)
   190  		require.NoError(t, err)
   191  		tmp2Cont, err := os.ReadFile(tmp2Name)
   192  		require.NoError(t, err)
   193  		require.NotEqual(t, tmpCont, tmp2Cont)
   194  		require.NotEqual(t, tmp2Cont, content2)
   195  	})
   196  }
   197  
   198  func testSequenceIDGenSeveralBlocksOrdered(t *testing.T) {
   199  	t.Parallel()
   200  	tstBroker := getBroker(t)
   201  	defer tstBroker.Finish()
   202  	ctxH1, ctxH2 := vgcontext.WithTraceID(tstBroker.ctx, "hash-1"), vgcontext.WithTraceID(tstBroker.ctx, "hash-2")
   203  	dataH1 := []events.Event{
   204  		events.NewTime(ctxH1, time.Now()),
   205  		events.NewPartyEvent(ctxH1, types.Party{Id: "test-party-h1"}),
   206  	}
   207  	dataH2 := []events.Event{
   208  		events.NewTime(ctxH2, time.Now()),
   209  		events.NewPartyEvent(ctxH2, types.Party{Id: "test-party-h2"}),
   210  	}
   211  	allData := make([]events.Event, 0, len(dataH1)+len(dataH2))
   212  	done := make(chan struct{})
   213  	mu := sync.Mutex{}
   214  	sub := mocks.NewMockSubscriber(tstBroker.ctrl)
   215  	sub.EXPECT().Types().Times(2).Return(nil)
   216  	sub.EXPECT().Ack().Times(1).Return(true)
   217  	sub.EXPECT().Skip().AnyTimes().Return(tstBroker.ctx.Done())
   218  	sub.EXPECT().Closed().AnyTimes().Return(tstBroker.ctx.Done())
   219  	sub.EXPECT().Push(gomock.Any()).AnyTimes().Do(func(evts ...events.Event) {
   220  		// race detector complains about appending here, because data comes from
   221  		// different go routines, so we'll use a quick & dirty fix: mutex the slice
   222  		mu.Lock()
   223  		defer mu.Unlock()
   224  		allData = append(allData, evts...)
   225  		if len(allData) >= cap(allData) {
   226  			close(done)
   227  		}
   228  	})
   229  	sub.EXPECT().SetID(gomock.Any()).Times(1)
   230  	k := tstBroker.Subscribe(sub)
   231  	// send batches for both events - hash 2 after hash 1
   232  	tstBroker.SendBatch(dataH1)
   233  	tstBroker.SendBatch(dataH2)
   234  	seqH1 := []uint64{}
   235  	seqH2 := []uint64{}
   236  	for i := range dataH1 {
   237  		seqH1 = append(seqH1, dataH1[i].Sequence())
   238  		seqH2 = append(seqH2, dataH2[i].Sequence())
   239  	}
   240  	assert.Equal(t, seqH1, seqH2)
   241  	<-done
   242  	tstBroker.Unsubscribe(k)
   243  	assert.NotEqual(t, seqH1[0], seqH2[1]) // the two are equal, we can compare X-slice
   244  	assert.Equal(t, len(allData), len(dataH1)+len(dataH2))
   245  }
   246  
   247  func testSequenceIDGenSeveralBlocksUnordered(t *testing.T) {
   248  	t.Parallel()
   249  	tstBroker := getBroker(t)
   250  	defer tstBroker.Finish()
   251  	ctxH1, ctxH2 := vgcontext.WithTraceID(tstBroker.ctx, "hash-1"), vgcontext.WithTraceID(tstBroker.ctx, "hash-2")
   252  	dataH1 := []events.Event{
   253  		events.NewTime(ctxH1, time.Now()),
   254  		events.NewPartyEvent(ctxH1, types.Party{Id: "test-party-h1"}),
   255  	}
   256  	dataH2 := []events.Event{
   257  		events.NewTime(ctxH2, time.Now()),
   258  		events.NewPartyEvent(ctxH2, types.Party{Id: "test-party-h2"}),
   259  	}
   260  	allData := make([]events.Event, 0, len(dataH1)+len(dataH2))
   261  	mu := sync.Mutex{}
   262  	done := make(chan struct{})
   263  	sub := mocks.NewMockSubscriber(tstBroker.ctrl)
   264  	sub.EXPECT().Types().Times(2).Return(nil)
   265  	sub.EXPECT().Ack().Times(1).Return(true)
   266  	sub.EXPECT().Skip().AnyTimes().Return(tstBroker.ctx.Done())
   267  	sub.EXPECT().Closed().AnyTimes().Return(tstBroker.ctx.Done())
   268  	sub.EXPECT().Push(gomock.Any()).AnyTimes().Do(func(evts ...events.Event) {
   269  		mu.Lock()
   270  		defer mu.Unlock()
   271  		allData = append(allData, evts...)
   272  		if len(allData) >= cap(allData) {
   273  			close(done)
   274  		}
   275  	})
   276  	sub.EXPECT().SetID(gomock.Any()).Times(1)
   277  	k := tstBroker.Subscribe(sub)
   278  	// We can't use sendBatch here: we use the traceID of the first event in the batch to determine
   279  	// the hash (batch-sending events can only happen within a single block)
   280  	for i := range dataH1 {
   281  		tstBroker.Send(dataH1[i])
   282  		tstBroker.Send(dataH2[i])
   283  	}
   284  	seqH1 := []uint64{}
   285  	seqH2 := []uint64{}
   286  	for i := range dataH1 {
   287  		seqH1 = append(seqH1, dataH1[i].Sequence())
   288  		seqH2 = append(seqH2, dataH2[i].Sequence())
   289  	}
   290  	assert.Equal(t, seqH1, seqH2)
   291  	<-done
   292  	tstBroker.Unsubscribe(k)
   293  	assert.NotEqual(t, seqH1[0], seqH2[1]) // the two are equal, we can compare X-slice
   294  	assert.Equal(t, len(allData), len(dataH1)+len(dataH2))
   295  }
   296  
   297  func testSubUnsubSuccess(t *testing.T) {
   298  	t.Parallel()
   299  	broker := getBroker(t)
   300  	defer broker.Finish()
   301  	sub := mocks.NewMockSubscriber(broker.ctrl)
   302  	reqSub := mocks.NewMockSubscriber(broker.ctrl)
   303  	// subscribe + unsubscribe -> 2 calls
   304  	sub.EXPECT().Types().Times(2).Return(nil)
   305  	sub.EXPECT().Ack().Times(1).Return(false)
   306  	sub.EXPECT().SetID(gomock.Any()).Times(1)
   307  	reqSub.EXPECT().Types().Times(2).Return(nil)
   308  	reqSub.EXPECT().Ack().Times(1).Return(true)
   309  	reqSub.EXPECT().SetID(gomock.Any()).Times(1)
   310  	k1 := broker.Subscribe(sub)    // not required
   311  	k2 := broker.Subscribe(reqSub) // required
   312  	assert.NotZero(t, k1)
   313  	assert.NotZero(t, k2)
   314  	assert.NotEqual(t, k1, k2)
   315  	broker.Unsubscribe(k1)
   316  	broker.Unsubscribe(k2)
   317  	// no calls to subs expected once they are unsubscribed
   318  	broker.Send(broker.randomEvt())
   319  }
   320  
   321  func testSubReuseKey(t *testing.T) {
   322  	t.Parallel()
   323  	broker := getBroker(t)
   324  	defer broker.Finish()
   325  	sub := mocks.NewMockSubscriber(broker.ctrl)
   326  	sub.EXPECT().Types().Times(4).Return(nil)
   327  	sub.EXPECT().Ack().Times(1).Return(false)
   328  	sub.EXPECT().SetID(gomock.Any()).Times(2)
   329  	k1 := broker.Subscribe(sub)
   330  	sub.EXPECT().Ack().Times(1).Return(true)
   331  	assert.NotZero(t, k1)
   332  	broker.Unsubscribe(k1)
   333  	k2 := broker.Subscribe(sub)
   334  	assert.Equal(t, k1, k2)
   335  	broker.Unsubscribe(k2)
   336  	// second unsubscribe is a no-op
   337  	broker.Unsubscribe(k1)
   338  }
   339  
   340  func testUnsubscribeAutomaticallyIfSubscriberIsClosed(t *testing.T) {
   341  	t.Parallel()
   342  
   343  	testedBroker := getBroker(t)
   344  	defer testedBroker.Finish()
   345  
   346  	// The Broker.Send() method launches a go routine. As a result, to control
   347  	// its state, we will use an unbuffered (blocking) channel to wait until
   348  	// we unblock it by sending a signal, or it timeouts.
   349  	waiter := newWaiter()
   350  
   351  	// First, we add the subscriber to the broker.
   352  	sub := mocks.NewMockSubscriber(testedBroker.ctrl)
   353  
   354  	// This tells the broker to treat the subscriber synchronously.
   355  	sub.EXPECT().Ack().Times(1).Return(true)
   356  	// This tells the broker the subscriber is listening to all even types.
   357  	sub.EXPECT().Types().AnyTimes().Return(nil)
   358  	sub.EXPECT().SetID(1).Times(1)
   359  
   360  	subID := testedBroker.Subscribe(sub)
   361  	defer testedBroker.Unsubscribe(subID)
   362  	require.Equal(t, 1, subID)
   363  
   364  	// Then, we send an event that should be pushed since the subscriber is not
   365  	// closed and doesn't want this event to be skipped.
   366  	event1 := testedBroker.randomEvt()
   367  
   368  	notClosedCh := make(chan struct{})
   369  	defer close(notClosedCh)
   370  	dontSkipCh := make(chan struct{})
   371  	defer close(dontSkipCh)
   372  
   373  	// We configure the subscriber to tell the broker to push the event.
   374  	sub.EXPECT().Closed().Times(1).Return(notClosedCh)
   375  	sub.EXPECT().Skip().Times(1).Return(dontSkipCh)
   376  	sub.EXPECT().Push(event1).Times(1).Do(func(_ *evt) {
   377  		// When the event is pushed, we tell the test to continue.
   378  		waiter.Unblock()
   379  	})
   380  
   381  	// We send the first event.
   382  	testedBroker.Send(event1)
   383  
   384  	// Let's wait for a signal.
   385  	if err := waiter.Wait(); err != nil {
   386  		t.Fatalf("Subscriber.Skip() was never called: %v", err)
   387  	}
   388  
   389  	// To finish, we send another event, but, this time, the subscriber is
   390  	// closed, so the broker does not push the event.
   391  	event2 := testedBroker.randomEvt()
   392  
   393  	sub.EXPECT().Closed().Times(1).DoAndReturn(func() <-chan struct{} {
   394  		iAmClosed := make(chan struct{})
   395  		// Returning a closed channel is how the subscriber notifies the broker
   396  		// it is closed.
   397  		close(iAmClosed)
   398  
   399  		// We warn the test the method we are expecting to be called has been
   400  		// called.
   401  		waiter.Unblock()
   402  		return iAmClosed
   403  	})
   404  	sub.EXPECT().Skip().AnyTimes().Return(dontSkipCh)
   405  	sub.EXPECT().Push(gomock.Any()).Times(0)
   406  
   407  	// We send the second event. It should be pushed.
   408  	testedBroker.Send(event2)
   409  
   410  	// Let's wait for a signal.
   411  	if err := waiter.Wait(); err != nil {
   412  		t.Fatalf("Subscriber.Skip() was never called: %v", err)
   413  	}
   414  }
   415  
   416  func testSendBatch(t *testing.T) {
   417  	t.Parallel()
   418  	tstBroker := getBroker(t)
   419  	defer tstBroker.Finish()
   420  	batchSendTest(t, tstBroker)
   421  }
   422  
   423  func batchSendTest(t *testing.T, tstBroker *brokerTst) {
   424  	t.Helper()
   425  	sub := mocks.NewMockSubscriber(tstBroker.ctrl)
   426  	cancelCh := make(chan struct{})
   427  	defer close(cancelCh)
   428  	sub.EXPECT().Types().Times(2).Return(nil)
   429  	sub.EXPECT().Ack().AnyTimes().Return(true)
   430  	sub.EXPECT().SetID(gomock.Any()).Times(1)
   431  	k1 := tstBroker.Subscribe(sub)
   432  	assert.NotZero(t, k1)
   433  	data := []events.Event{
   434  		tstBroker.randomEvt(),
   435  		tstBroker.randomEvt(),
   436  		tstBroker.randomEvt(),
   437  	}
   438  	// ensure all 3 events are being sent (wait for routine to spawn)
   439  	sub.EXPECT().Closed().AnyTimes().Return(cancelCh)
   440  	sub.EXPECT().Skip().AnyTimes().Return(cancelCh)
   441  	wg := sync.WaitGroup{}
   442  	wg.Add(1)
   443  	sub.EXPECT().Push(gomock.Any()).Times(1).Do(func(evts ...events.Event) {
   444  		assert.Equal(t, len(data), len(evts))
   445  		wg.Done()
   446  	})
   447  
   448  	// send events
   449  	tstBroker.SendBatch(data)
   450  	wg.Wait()
   451  	require.Equal(t, uint64(3), tstBroker.stats.CurrentEventsInBatch())
   452  	tstBroker.stats.NewBatch()
   453  	require.Equal(t, uint64(3), tstBroker.stats.TotalEventsLastBatch())
   454  	tstBroker.Unsubscribe(k1)
   455  }
   456  
   457  func testSendBatchChannel(t *testing.T) {
   458  	t.Parallel()
   459  	tstBroker := getBroker(t)
   460  	sub := mocks.NewMockSubscriber(tstBroker.ctrl)
   461  	skipCh, closedCh, cCh := make(chan struct{}), make(chan struct{}), make(chan []events.Event, 1)
   462  	defer func() {
   463  		tstBroker.Finish()
   464  		close(closedCh)
   465  		close(skipCh)
   466  	}()
   467  	twg := sync.WaitGroup{}
   468  	twg.Add(2)
   469  	sub.EXPECT().Types().Times(2).Return(nil).Do(func() {
   470  		twg.Done()
   471  	})
   472  	sub.EXPECT().Ack().AnyTimes().Return(false)
   473  	sub.EXPECT().SetID(gomock.Any()).Times(1)
   474  	k1 := tstBroker.Subscribe(sub)
   475  	assert.NotZero(t, k1)
   476  	batch2 := []events.Event{
   477  		tstBroker.randomEvt(),
   478  		tstBroker.randomEvt(),
   479  	}
   480  	evts := []events.Event{
   481  		tstBroker.randomEvt(),
   482  		tstBroker.randomEvt(),
   483  		tstBroker.randomEvt(),
   484  	}
   485  	// ensure both batches are sent
   486  	wg := sync.WaitGroup{}
   487  	// 3 calls, only the first batch will be sent
   488  	// third call is routine that tries to send the second batch. This will of course fail
   489  	wg.Add(3)
   490  	sub.EXPECT().Closed().AnyTimes().Return(closedCh)
   491  	sub.EXPECT().Skip().AnyTimes().Return(skipCh)
   492  	// we try to get the channel 3 times, only 1 of the attempts will actually publish the events
   493  	sub.EXPECT().C().Times(3).Return(cCh).Do(func() {
   494  		// Done call each time we tried sending an event
   495  		wg.Done()
   496  	})
   497  
   498  	// send events
   499  	tstBroker.SendBatch(evts)
   500  	tstBroker.SendBatch(batch2)
   501  	wg.Wait()
   502  	// we've tried to send 2 batches of events, subscriber could only accept one. Check state of all the things
   503  	// we need to unsubscribe the subscriber, because we're closing the channels and race detector complains
   504  	// because there's a loop calling functions that are returning the channels we're closing here
   505  	tstBroker.Unsubscribe(k1)
   506  	// ensure unsubscribe has returned
   507  	twg.Wait()
   508  
   509  	// get our batches
   510  	batches := [][]events.Event{
   511  		<-cCh, <-cCh,
   512  	}
   513  
   514  	// assert we have all events now.
   515  	batchSizes := map[int]struct{}{}
   516  	evtSeq := map[uint64]struct{}{}
   517  	for _, batch := range batches {
   518  		batchSizes[len(batch)] = struct{}{}
   519  		for _, v := range batch {
   520  			evtSeq[v.Sequence()] = struct{}{}
   521  		}
   522  	}
   523  
   524  	// now ensure we have the batch with right sizes
   525  	_, ok := batchSizes[len(batch2)]
   526  	assert.True(t, ok, "missing batch of size ", len(batch2))
   527  	_, ok = batchSizes[len(evts)]
   528  	assert.True(t, ok, "missing batch of size ", len(evts))
   529  
   530  	// now ensure we got all sequence IDs
   531  	for _, v := range append(evts, batch2...) {
   532  		_, ok := evtSeq[v.Sequence()]
   533  		if !ok {
   534  			t.Fatalf("missing event sequence from batches %v", v.Sequence())
   535  		}
   536  	}
   537  }
   538  
   539  func testSkipOptional(t *testing.T) {
   540  	t.Parallel()
   541  	tstBroker := getBroker(t)
   542  	sub := mocks.NewMockSubscriber(tstBroker.ctrl)
   543  	skipCh, closedCh, cCh := make(chan struct{}), make(chan struct{}), make(chan []events.Event, 1)
   544  	defer func() {
   545  		tstBroker.Finish()
   546  		close(closedCh)
   547  		close(skipCh)
   548  	}()
   549  	twg := sync.WaitGroup{}
   550  	twg.Add(2)
   551  	sub.EXPECT().Types().Times(2).Return(nil).Do(func() {
   552  		twg.Done()
   553  	})
   554  	sub.EXPECT().Ack().AnyTimes().Return(false)
   555  	sub.EXPECT().SetID(gomock.Any()).Times(1)
   556  	k1 := tstBroker.Subscribe(sub)
   557  	assert.NotZero(t, k1)
   558  
   559  	evts := []*evt{
   560  		tstBroker.randomEvt(),
   561  		tstBroker.randomEvt(),
   562  		tstBroker.randomEvt(),
   563  	}
   564  	// ensure all 3 events are being sent (wait for routine to spawn)
   565  	wg := sync.WaitGroup{}
   566  	wg.Add(len(evts)*2 - 1)
   567  	sub.EXPECT().Closed().AnyTimes().Return(closedCh)
   568  	sub.EXPECT().Skip().AnyTimes().Return(skipCh)
   569  	// we try to get the channel 3 times, only 1 of the attempts will actually publish the event
   570  	// the other 2 attempts will run in a routine
   571  	sub.EXPECT().C().Times(len(evts)*2 - 1).Return(cCh).Do(func() {
   572  		// Done call each time we tried sending an event
   573  		wg.Done()
   574  	})
   575  
   576  	// send events
   577  	for _, e := range evts {
   578  		tstBroker.Send(e)
   579  	}
   580  	wg.Wait()
   581  	// we've tried to send 3 events, subscriber could only accept one. Check state of all the things
   582  	// we need to unsubscribe the subscriber, because we're closing the channels and race detector complains
   583  	// because there's a loop calling functions that are returning the channels we're closing here
   584  	tstBroker.Unsubscribe(k1)
   585  	// ensure unsubscribe has returned
   586  	twg.Wait()
   587  	require.Equal(t, uint64(len(evts)), tstBroker.stats.CurrentEventsInBatch())
   588  	tstBroker.stats.NewBatch()
   589  	require.Equal(t, uint64(len(evts)), tstBroker.stats.TotalEventsLastBatch())
   590  
   591  	// make a map to check all sequences
   592  	seq := map[uint64]struct{}{}
   593  	for i := len(evts); i != 0; i-- {
   594  		ev := <-cCh
   595  		assert.NotEmpty(t, ev)
   596  		for _, e := range ev {
   597  			seq[e.Sequence()] = struct{}{}
   598  		}
   599  	}
   600  
   601  	// no verify all ev sequence are received
   602  	for _, ev := range evts {
   603  		_, ok := seq[ev.Sequence()]
   604  		if !ok {
   605  			t.Fatalf("missing event sequence from received events %v", ev.Sequence())
   606  		}
   607  	}
   608  
   609  	// make sure the channel is empty (no writes were pending)
   610  	assert.Equal(t, 0, len(cCh))
   611  }
   612  
   613  func testStopCtx(t *testing.T) {
   614  	t.Parallel()
   615  	broker := getBroker(t)
   616  	defer broker.Finish()
   617  	sub := mocks.NewMockSubscriber(broker.ctrl)
   618  	ch := make(chan struct{})
   619  	sub.EXPECT().Closed().AnyTimes().Return(ch)
   620  	sub.EXPECT().Skip().AnyTimes().Return(ch)
   621  	// no calls sub are expected, we cancelled the context
   622  	broker.cfunc()
   623  	sub.EXPECT().Types().Times(2).Return(nil)
   624  	sub.EXPECT().Ack().AnyTimes().Return(true)
   625  	sub.EXPECT().SetID(gomock.Any()).Times(1)
   626  	k1 := broker.Subscribe(sub) // required sub
   627  	assert.NotZero(t, k1)
   628  	broker.Send(broker.randomEvt())
   629  	// calling unsubscribe acquires lock, so we can ensure the Send call has returned
   630  	broker.Unsubscribe(k1)
   631  	close(ch)
   632  }
   633  
   634  func testStopCtxSendAgain(t *testing.T) {
   635  	t.Parallel()
   636  	broker := getBroker(t)
   637  	defer broker.Finish()
   638  	sub := mocks.NewMockSubscriber(broker.ctrl)
   639  	ch := make(chan struct{})
   640  	sub.EXPECT().Closed().AnyTimes().Return(ch)
   641  	sub.EXPECT().Skip().AnyTimes().Return(ch)
   642  	// no calls sub are expected, we cancelled the context
   643  	broker.cfunc()
   644  	sub.EXPECT().Types().Times(2).Return(nil)
   645  	sub.EXPECT().Ack().AnyTimes().Return(true)
   646  	sub.EXPECT().SetID(gomock.Any()).Times(1)
   647  	k1 := broker.Subscribe(sub) // required sub
   648  	assert.NotZero(t, k1)
   649  	broker.Send(broker.randomEvt())
   650  	broker.cfunc()
   651  	broker.Send(broker.randomEvt())
   652  	// calling unsubscribe acquires lock, so we can ensure the Send call has returned
   653  	broker.Unsubscribe(k1)
   654  	close(ch)
   655  }
   656  
   657  func testSkipSubscriberBasedOnChannelState(t *testing.T) {
   658  	t.Parallel()
   659  
   660  	testedBroker := getBroker(t)
   661  	defer testedBroker.Finish()
   662  
   663  	// The Broker.Send() method launches a go routine. As a result, to control
   664  	// its state, we will use an unbuffered (blocking) channel to wait until
   665  	// we unblock it by sending a signal, or it timeouts.
   666  	waiter := newWaiter()
   667  
   668  	// First, we add the subscriber to the broker.
   669  	sub := mocks.NewMockSubscriber(testedBroker.ctrl)
   670  
   671  	// This tells the broker to treat the subscriber synchronously.
   672  	sub.EXPECT().Ack().Times(1).Return(true)
   673  	// This tells the broker the subscriber is listening to all even types.
   674  	sub.EXPECT().Types().AnyTimes().Return(nil)
   675  	sub.EXPECT().SetID(1).Times(1)
   676  
   677  	subID := testedBroker.Subscribe(sub)
   678  	defer testedBroker.Unsubscribe(subID)
   679  	require.Equal(t, 1, subID)
   680  
   681  	// Then, we send an event that should be skipped since the subscriber tell
   682  	// us to do so.
   683  	event1 := testedBroker.randomEvt()
   684  
   685  	notClosedCh := make(chan struct{})
   686  	defer close(notClosedCh)
   687  
   688  	// We configure the subscriber to tell the broker to skip the sending.
   689  	sub.EXPECT().Closed().AnyTimes().Return(notClosedCh)
   690  	sub.EXPECT().Skip().Times(1).DoAndReturn(func() <-chan struct{} {
   691  		skipMeCh := make(chan struct{})
   692  		// Returning a closed channel is how the subscriber notifies the broker
   693  		// it wants to skip next events.
   694  		close(skipMeCh)
   695  
   696  		// We warn the test the method we are expecting to be called has been
   697  		// called.
   698  		waiter.Unblock()
   699  		return skipMeCh
   700  	})
   701  	sub.EXPECT().Push(event1).Times(0)
   702  
   703  	// We send the first event. It should be ignored.
   704  	testedBroker.Send(event1)
   705  
   706  	// Let's wait for a signal.
   707  	if err := waiter.Wait(); err != nil {
   708  		t.Fatalf("Subscriber.Skip() was never called: %v", err)
   709  	}
   710  
   711  	// To finish, we send another event, but, this time, the subscriber doesn't
   712  	// want to skip it. So, it should be pushed.
   713  	event2 := testedBroker.randomEvt()
   714  
   715  	dontSkipCh := make(chan struct{})
   716  	defer close(dontSkipCh)
   717  
   718  	sub.EXPECT().Closed().AnyTimes().Return(notClosedCh)
   719  	sub.EXPECT().Skip().AnyTimes().Return(dontSkipCh)
   720  	sub.EXPECT().Push(event2).Times(1).Do(func(_ ...events.Event) {
   721  		// We warn the test the method we are expecting to be called has been
   722  		// called.
   723  		waiter.Unblock()
   724  	})
   725  
   726  	// We send the second event. It should be pushed.
   727  	testedBroker.Send(event2)
   728  
   729  	// Let's wait for a signal.
   730  	if err := waiter.Wait(); err != nil {
   731  		t.Fatalf("Subscriber.Skip() was never called: %v", err)
   732  	}
   733  }
   734  
   735  // test making sure that events are sent only to subs that are interested in it.
   736  func testEventTypeSubscription(t *testing.T) {
   737  	t.Parallel()
   738  	broker := getBroker(t)
   739  	defer broker.Finish()
   740  	sub := mocks.NewMockSubscriber(broker.ctrl)
   741  	allSub := mocks.NewMockSubscriber(broker.ctrl)
   742  	diffSub := mocks.NewMockSubscriber(broker.ctrl)
   743  	skipCh, closeCh := make(chan struct{}), make(chan struct{})
   744  	event := broker.randomEvt()
   745  	event.t = events.TimeUpdate
   746  	wg := sync.WaitGroup{}
   747  	wg.Add(2)
   748  	// Closed check
   749  	sub.EXPECT().Closed().AnyTimes().Return(closeCh)
   750  	diffSub.EXPECT().Closed().AnyTimes().Return(closeCh) // can use the same channels, we're not closing them anyway
   751  	allSub.EXPECT().Closed().AnyTimes().Return(closeCh)
   752  	// skip check
   753  	sub.EXPECT().Skip().AnyTimes().Return(skipCh)
   754  	allSub.EXPECT().Skip().AnyTimes().Return(skipCh)
   755  	diffSub.EXPECT().Skip().AnyTimes().Return(skipCh)
   756  	// actually push the event - diffSub expects nothing
   757  	sub.EXPECT().Push(gomock.Any()).Times(1).Do(func(_ interface{}) {
   758  		wg.Done()
   759  	})
   760  	allSub.EXPECT().Push(gomock.Any()).Times(1).Do(func(_ interface{}) {
   761  		wg.Done()
   762  	})
   763  	// the event types this subscriber is interested in
   764  	sub.EXPECT().Types().Times(2).Return([]events.Type{events.TimeUpdate})
   765  	allSub.EXPECT().Types().Times(2).Return(nil) // subscribed to ALL events
   766  	// fake type:
   767  	different := events.Type(int(events.All) + int(events.TimeUpdate) + 1 + int(events.TxErrEvent)) // this value cannot exist as an events.Type value
   768  	diffSub.EXPECT().Types().Times(2).Return([]events.Type{different})
   769  	// subscribe the subscriber
   770  	sub.EXPECT().Ack().AnyTimes().Return(true)
   771  	diffSub.EXPECT().Ack().AnyTimes().Return(true)
   772  	allSub.EXPECT().Ack().AnyTimes().Return(true)
   773  	sub.EXPECT().SetID(gomock.Any()).Times(1)
   774  	diffSub.EXPECT().SetID(gomock.Any()).Times(1)
   775  	allSub.EXPECT().SetID(gomock.Any()).Times(1)
   776  	k1 := broker.Subscribe(sub)     // required sub
   777  	k2 := broker.Subscribe(diffSub) // required sub, but won't be used anyway
   778  	k3 := broker.Subscribe(allSub)
   779  	assert.NotZero(t, k1)
   780  	assert.NotZero(t, k2)
   781  	assert.NotZero(t, k3)
   782  	assert.NotEqual(t, k1, k2)
   783  	// send a time event
   784  	broker.Send(events.NewTime(context.Background(), time.Now()))
   785  	// ensure the event was delivered
   786  	wg.Wait()
   787  	// unsubscribe the subscriber, now we're done
   788  	broker.Unsubscribe(k1)
   789  	broker.Unsubscribe(k2)
   790  	broker.Unsubscribe(k3)
   791  	close(skipCh)
   792  	close(closeCh)
   793  }
   794  
   795  func testStreamsOverSocket(t *testing.T) {
   796  	t.Parallel()
   797  	ctx, cfunc := context.WithCancel(context.Background())
   798  	config := broker.NewDefaultConfig()
   799  	config.Socket.Enabled = true
   800  	config.Socket.Transport = "inproc"
   801  
   802  	sock, err := pair.NewSocket()
   803  	assert.NoError(t, err)
   804  
   805  	addr := fmt.Sprintf(
   806  		"inproc://%s",
   807  		net.JoinHostPort(config.Socket.Address, fmt.Sprintf("%d", config.Socket.Port)),
   808  	)
   809  	err = sock.Listen(addr)
   810  	assert.NoError(t, err)
   811  
   812  	broker, _ := broker.New(ctx, logging.NewTestLogger(), config, stats.NewBlockchain())
   813  
   814  	defer func() {
   815  		cfunc()
   816  		sock.Close()
   817  	}()
   818  
   819  	sentEvent := events.NewTime(ctx, time.Date(2020, time.December, 25, 0o0, 0o1, 0o1, 0, time.UTC))
   820  
   821  	broker.Send(sentEvent)
   822  
   823  	receivedBytes, err := sock.Recv()
   824  	assert.NoError(t, err)
   825  
   826  	var receivedEvent eventspb.BusEvent
   827  	err = proto.Unmarshal(receivedBytes, &receivedEvent)
   828  	assert.NoError(t, err)
   829  	assert.True(t, proto.Equal(sentEvent.StreamMessage(), &receivedEvent))
   830  }
   831  
   832  func testStopsProcessOnStreamError(t *testing.T) {
   833  	t.Parallel()
   834  	if os.Getenv("RUN_TEST") == "1" {
   835  		ctx, cfunc := context.WithCancel(context.Background())
   836  		config := broker.NewDefaultConfig()
   837  		config.Socket.Enabled = true
   838  		config.Socket.Transport = "inproc"
   839  
   840  		// Having such a small buffers will make the process fail
   841  		config.Socket.SocketChannelBufferSize = 0
   842  		config.Socket.EventChannelBufferSize = 0
   843  
   844  		sock, err := pair.NewSocket()
   845  		assert.NoError(t, err)
   846  
   847  		addr := fmt.Sprintf(
   848  			"inproc://%s",
   849  			net.JoinHostPort(config.Socket.Address, fmt.Sprintf("%d", config.Socket.Port)),
   850  		)
   851  		err = sock.Listen(addr)
   852  		assert.NoError(t, err)
   853  
   854  		broker, _ := broker.New(ctx, logging.NewTestLogger(), config, stats.NewBlockchain())
   855  
   856  		defer func() {
   857  			cfunc()
   858  			sock.Close()
   859  		}()
   860  
   861  		sentEvent := events.NewTime(ctx, time.Date(2020, time.December, 25, 0o0, 0o1, 0o1, 0, time.UTC))
   862  
   863  		broker.Send(sentEvent)
   864  		// One of the next call should terminate the process
   865  		broker.Send(sentEvent)
   866  		broker.Send(sentEvent)
   867  		broker.Send(sentEvent)
   868  		return
   869  	}
   870  
   871  	cmd := exec.Command(os.Args[0], "-test.run=TestStream/Stops_process_if_can_not_send_to_socket")
   872  	cmd.Env = append(os.Environ(), "RUN_TEST=1")
   873  	err := cmd.Run()
   874  	if e, ok := err.(*exec.ExitError); ok && !e.Success() {
   875  		return
   876  	}
   877  	t.Fatalf("process ran with err %v, want exit status 1", err)
   878  }
   879  
   880  func (e evt) Type() events.Type {
   881  	return e.t
   882  }
   883  
   884  func (e evt) Context() context.Context {
   885  	return e.ctx
   886  }
   887  
   888  func (e evt) Replace(context.Context) {}
   889  
   890  func (e *evt) SetSequenceID(s uint64) {
   891  	e.sid = s
   892  }
   893  
   894  func (e evt) Sequence() uint64 {
   895  	return e.sid
   896  }
   897  
   898  func (e evt) TraceID() string {
   899  	return e.id
   900  }
   901  
   902  func (e evt) ChainID() string {
   903  	return e.cid
   904  }
   905  
   906  func (e evt) TxHash() string {
   907  	return e.txHash
   908  }
   909  
   910  func (e evt) BlockNr() int64 {
   911  	return e.blockNr
   912  }
   913  
   914  func (e evt) StreamMessage() *eventspb.BusEvent {
   915  	return nil
   916  }
   917  
   918  func (e evt) CompositeCount() uint64 {
   919  	return 1
   920  }
   921  
   922  type waiter struct {
   923  	ch  chan struct{}
   924  	ctx context.Context
   925  }
   926  
   927  func (c *waiter) Unblock() {
   928  	c.ch <- struct{}{}
   929  }
   930  
   931  func (c *waiter) Wait() error {
   932  	for {
   933  		select {
   934  		case <-c.ch:
   935  			return nil
   936  		case <-c.ctx.Done():
   937  			return errors.New("waiter timed out")
   938  		}
   939  	}
   940  }
   941  
   942  // newWaiter waits until it's unblocked or after 30 seconds elapsed, so we
   943  // don't block the tests.
   944  func newWaiter() *waiter {
   945  	ch := make(chan struct{}, 1)
   946  	ctx, cancelFn := context.WithCancel(context.Background())
   947  	ticker := time.NewTicker(WaiterInterval)
   948  
   949  	go func() {
   950  		<-ticker.C
   951  		cancelFn()
   952  		ticker.Stop()
   953  		close(ch)
   954  	}()
   955  
   956  	return &waiter{
   957  		ch:  ch,
   958  		ctx: ctx,
   959  	}
   960  }