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 }