github.com/evdatsion/aphelion-dpos-bft@v0.32.1/types/event_bus_test.go (about) 1 package types 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 12 abci "github.com/evdatsion/aphelion-dpos-bft/abci/types" 13 cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common" 14 tmpubsub "github.com/evdatsion/aphelion-dpos-bft/libs/pubsub" 15 tmquery "github.com/evdatsion/aphelion-dpos-bft/libs/pubsub/query" 16 ) 17 18 func TestEventBusPublishEventTx(t *testing.T) { 19 eventBus := NewEventBus() 20 err := eventBus.Start() 21 require.NoError(t, err) 22 defer eventBus.Stop() 23 24 tx := Tx("foo") 25 result := abci.ResponseDeliverTx{ 26 Data: []byte("bar"), 27 Events: []abci.Event{ 28 {Type: "testType", Attributes: []cmn.KVPair{{Key: []byte("baz"), Value: []byte("1")}}}, 29 }, 30 } 31 32 // PublishEventTx adds all these 3 tags, so the query below should work 33 query := fmt.Sprintf("tm.event='Tx' AND tx.height=1 AND tx.hash='%X' AND testType.baz=1", tx.Hash()) 34 txsSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query)) 35 require.NoError(t, err) 36 37 done := make(chan struct{}) 38 go func() { 39 msg := <-txsSub.Out() 40 edt := msg.Data().(EventDataTx) 41 assert.Equal(t, int64(1), edt.Height) 42 assert.Equal(t, uint32(0), edt.Index) 43 assert.Equal(t, tx, edt.Tx) 44 assert.Equal(t, result, edt.Result) 45 close(done) 46 }() 47 48 err = eventBus.PublishEventTx(EventDataTx{TxResult{ 49 Height: 1, 50 Index: 0, 51 Tx: tx, 52 Result: result, 53 }}) 54 assert.NoError(t, err) 55 56 select { 57 case <-done: 58 case <-time.After(1 * time.Second): 59 t.Fatal("did not receive a transaction after 1 sec.") 60 } 61 } 62 63 func TestEventBusPublishEventNewBlock(t *testing.T) { 64 eventBus := NewEventBus() 65 err := eventBus.Start() 66 require.NoError(t, err) 67 defer eventBus.Stop() 68 69 block := MakeBlock(0, []Tx{}, nil, []Evidence{}) 70 resultBeginBlock := abci.ResponseBeginBlock{ 71 Events: []abci.Event{ 72 {Type: "testType", Attributes: []cmn.KVPair{{Key: []byte("baz"), Value: []byte("1")}}}, 73 }, 74 } 75 resultEndBlock := abci.ResponseEndBlock{ 76 Events: []abci.Event{ 77 {Type: "testType", Attributes: []cmn.KVPair{{Key: []byte("foz"), Value: []byte("2")}}}, 78 }, 79 } 80 81 // PublishEventNewBlock adds the tm.event tag, so the query below should work 82 query := "tm.event='NewBlock' AND testType.baz=1 AND testType.foz=2" 83 blocksSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query)) 84 require.NoError(t, err) 85 86 done := make(chan struct{}) 87 go func() { 88 msg := <-blocksSub.Out() 89 edt := msg.Data().(EventDataNewBlock) 90 assert.Equal(t, block, edt.Block) 91 assert.Equal(t, resultBeginBlock, edt.ResultBeginBlock) 92 assert.Equal(t, resultEndBlock, edt.ResultEndBlock) 93 close(done) 94 }() 95 96 err = eventBus.PublishEventNewBlock(EventDataNewBlock{ 97 Block: block, 98 ResultBeginBlock: resultBeginBlock, 99 ResultEndBlock: resultEndBlock, 100 }) 101 assert.NoError(t, err) 102 103 select { 104 case <-done: 105 case <-time.After(1 * time.Second): 106 t.Fatal("did not receive a block after 1 sec.") 107 } 108 } 109 110 func TestEventBusPublishEventTxDuplicateKeys(t *testing.T) { 111 eventBus := NewEventBus() 112 err := eventBus.Start() 113 require.NoError(t, err) 114 defer eventBus.Stop() 115 116 tx := Tx("foo") 117 result := abci.ResponseDeliverTx{ 118 Data: []byte("bar"), 119 Events: []abci.Event{ 120 { 121 Type: "transfer", 122 Attributes: []cmn.KVPair{ 123 {Key: []byte("sender"), Value: []byte("foo")}, 124 {Key: []byte("recipient"), Value: []byte("bar")}, 125 {Key: []byte("amount"), Value: []byte("5")}, 126 }, 127 }, 128 { 129 Type: "transfer", 130 Attributes: []cmn.KVPair{ 131 {Key: []byte("sender"), Value: []byte("baz")}, 132 {Key: []byte("recipient"), Value: []byte("cat")}, 133 {Key: []byte("amount"), Value: []byte("13")}, 134 }, 135 }, 136 { 137 Type: "withdraw.rewards", 138 Attributes: []cmn.KVPair{ 139 {Key: []byte("address"), Value: []byte("bar")}, 140 {Key: []byte("source"), Value: []byte("iceman")}, 141 {Key: []byte("amount"), Value: []byte("33")}, 142 }, 143 }, 144 }, 145 } 146 147 testCases := []struct { 148 query string 149 expectResults bool 150 }{ 151 { 152 "tm.event='Tx' AND tx.height=1 AND transfer.sender='DoesNotExist'", 153 false, 154 }, 155 { 156 "tm.event='Tx' AND tx.height=1 AND transfer.sender='foo'", 157 true, 158 }, 159 { 160 "tm.event='Tx' AND tx.height=1 AND transfer.sender='baz'", 161 true, 162 }, 163 { 164 "tm.event='Tx' AND tx.height=1 AND transfer.sender='foo' AND transfer.sender='baz'", 165 true, 166 }, 167 { 168 "tm.event='Tx' AND tx.height=1 AND transfer.sender='foo' AND transfer.sender='DoesNotExist'", 169 false, 170 }, 171 } 172 173 for i, tc := range testCases { 174 sub, err := eventBus.Subscribe(context.Background(), fmt.Sprintf("client-%d", i), tmquery.MustParse(tc.query)) 175 require.NoError(t, err) 176 177 done := make(chan struct{}) 178 179 go func() { 180 msg := <-sub.Out() 181 data := msg.Data().(EventDataTx) 182 assert.Equal(t, int64(1), data.Height) 183 assert.Equal(t, uint32(0), data.Index) 184 assert.Equal(t, tx, data.Tx) 185 assert.Equal(t, result, data.Result) 186 close(done) 187 }() 188 189 err = eventBus.PublishEventTx(EventDataTx{TxResult{ 190 Height: 1, 191 Index: 0, 192 Tx: tx, 193 Result: result, 194 }}) 195 assert.NoError(t, err) 196 197 select { 198 case <-done: 199 if !tc.expectResults { 200 require.Fail(t, "unexpected transaction result(s) from subscription") 201 } 202 case <-time.After(1 * time.Second): 203 if tc.expectResults { 204 require.Fail(t, "failed to receive a transaction after 1 second") 205 } 206 } 207 } 208 } 209 210 func TestEventBusPublishEventNewBlockHeader(t *testing.T) { 211 eventBus := NewEventBus() 212 err := eventBus.Start() 213 require.NoError(t, err) 214 defer eventBus.Stop() 215 216 block := MakeBlock(0, []Tx{}, nil, []Evidence{}) 217 resultBeginBlock := abci.ResponseBeginBlock{ 218 Events: []abci.Event{ 219 {Type: "testType", Attributes: []cmn.KVPair{{Key: []byte("baz"), Value: []byte("1")}}}, 220 }, 221 } 222 resultEndBlock := abci.ResponseEndBlock{ 223 Events: []abci.Event{ 224 {Type: "testType", Attributes: []cmn.KVPair{{Key: []byte("foz"), Value: []byte("2")}}}, 225 }, 226 } 227 228 // PublishEventNewBlockHeader adds the tm.event tag, so the query below should work 229 query := "tm.event='NewBlockHeader' AND testType.baz=1 AND testType.foz=2" 230 headersSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query)) 231 require.NoError(t, err) 232 233 done := make(chan struct{}) 234 go func() { 235 msg := <-headersSub.Out() 236 edt := msg.Data().(EventDataNewBlockHeader) 237 assert.Equal(t, block.Header, edt.Header) 238 assert.Equal(t, resultBeginBlock, edt.ResultBeginBlock) 239 assert.Equal(t, resultEndBlock, edt.ResultEndBlock) 240 close(done) 241 }() 242 243 err = eventBus.PublishEventNewBlockHeader(EventDataNewBlockHeader{ 244 Header: block.Header, 245 ResultBeginBlock: resultBeginBlock, 246 ResultEndBlock: resultEndBlock, 247 }) 248 assert.NoError(t, err) 249 250 select { 251 case <-done: 252 case <-time.After(1 * time.Second): 253 t.Fatal("did not receive a block header after 1 sec.") 254 } 255 } 256 257 func TestEventBusPublish(t *testing.T) { 258 eventBus := NewEventBus() 259 err := eventBus.Start() 260 require.NoError(t, err) 261 defer eventBus.Stop() 262 263 const numEventsExpected = 14 264 265 sub, err := eventBus.Subscribe(context.Background(), "test", tmquery.Empty{}, numEventsExpected) 266 require.NoError(t, err) 267 268 done := make(chan struct{}) 269 go func() { 270 numEvents := 0 271 for range sub.Out() { 272 numEvents++ 273 if numEvents >= numEventsExpected { 274 close(done) 275 return 276 } 277 } 278 }() 279 280 err = eventBus.Publish(EventNewBlockHeader, EventDataNewBlockHeader{}) 281 require.NoError(t, err) 282 err = eventBus.PublishEventNewBlock(EventDataNewBlock{}) 283 require.NoError(t, err) 284 err = eventBus.PublishEventNewBlockHeader(EventDataNewBlockHeader{}) 285 require.NoError(t, err) 286 err = eventBus.PublishEventVote(EventDataVote{}) 287 require.NoError(t, err) 288 err = eventBus.PublishEventNewRoundStep(EventDataRoundState{}) 289 require.NoError(t, err) 290 err = eventBus.PublishEventTimeoutPropose(EventDataRoundState{}) 291 require.NoError(t, err) 292 err = eventBus.PublishEventTimeoutWait(EventDataRoundState{}) 293 require.NoError(t, err) 294 err = eventBus.PublishEventNewRound(EventDataNewRound{}) 295 require.NoError(t, err) 296 err = eventBus.PublishEventCompleteProposal(EventDataCompleteProposal{}) 297 require.NoError(t, err) 298 err = eventBus.PublishEventPolka(EventDataRoundState{}) 299 require.NoError(t, err) 300 err = eventBus.PublishEventUnlock(EventDataRoundState{}) 301 require.NoError(t, err) 302 err = eventBus.PublishEventRelock(EventDataRoundState{}) 303 require.NoError(t, err) 304 err = eventBus.PublishEventLock(EventDataRoundState{}) 305 require.NoError(t, err) 306 err = eventBus.PublishEventValidatorSetUpdates(EventDataValidatorSetUpdates{}) 307 require.NoError(t, err) 308 309 select { 310 case <-done: 311 case <-time.After(1 * time.Second): 312 t.Fatalf("expected to receive %d events after 1 sec.", numEventsExpected) 313 } 314 } 315 316 func BenchmarkEventBus(b *testing.B) { 317 benchmarks := []struct { 318 name string 319 numClients int 320 randQueries bool 321 randEvents bool 322 }{ 323 {"10Clients1Query1Event", 10, false, false}, 324 {"100Clients", 100, false, false}, 325 {"1000Clients", 1000, false, false}, 326 327 {"10ClientsRandQueries1Event", 10, true, false}, 328 {"100Clients", 100, true, false}, 329 {"1000Clients", 1000, true, false}, 330 331 {"10ClientsRandQueriesRandEvents", 10, true, true}, 332 {"100Clients", 100, true, true}, 333 {"1000Clients", 1000, true, true}, 334 335 {"10Clients1QueryRandEvents", 10, false, true}, 336 {"100Clients", 100, false, true}, 337 {"1000Clients", 1000, false, true}, 338 } 339 340 for _, bm := range benchmarks { 341 b.Run(bm.name, func(b *testing.B) { 342 benchmarkEventBus(bm.numClients, bm.randQueries, bm.randEvents, b) 343 }) 344 } 345 } 346 347 func benchmarkEventBus(numClients int, randQueries bool, randEvents bool, b *testing.B) { 348 // for random* functions 349 cmn.Seed(time.Now().Unix()) 350 351 eventBus := NewEventBusWithBufferCapacity(0) // set buffer capacity to 0 so we are not testing cache 352 eventBus.Start() 353 defer eventBus.Stop() 354 355 ctx := context.Background() 356 q := EventQueryNewBlock 357 358 for i := 0; i < numClients; i++ { 359 if randQueries { 360 q = randQuery() 361 } 362 sub, err := eventBus.Subscribe(ctx, fmt.Sprintf("client-%d", i), q) 363 if err != nil { 364 b.Fatal(err) 365 } 366 go func() { 367 for { 368 select { 369 case <-sub.Out(): 370 case <-sub.Cancelled(): 371 return 372 } 373 } 374 }() 375 } 376 377 eventType := EventNewBlock 378 379 b.ReportAllocs() 380 b.ResetTimer() 381 for i := 0; i < b.N; i++ { 382 if randEvents { 383 eventType = randEvent() 384 } 385 386 eventBus.Publish(eventType, EventDataString("Gamora")) 387 } 388 } 389 390 var events = []string{ 391 EventNewBlock, 392 EventNewBlockHeader, 393 EventNewRound, 394 EventNewRoundStep, 395 EventTimeoutPropose, 396 EventCompleteProposal, 397 EventPolka, 398 EventUnlock, 399 EventLock, 400 EventRelock, 401 EventTimeoutWait, 402 EventVote} 403 404 func randEvent() string { 405 return events[cmn.RandIntn(len(events))] 406 } 407 408 var queries = []tmpubsub.Query{ 409 EventQueryNewBlock, 410 EventQueryNewBlockHeader, 411 EventQueryNewRound, 412 EventQueryNewRoundStep, 413 EventQueryTimeoutPropose, 414 EventQueryCompleteProposal, 415 EventQueryPolka, 416 EventQueryUnlock, 417 EventQueryLock, 418 EventQueryRelock, 419 EventQueryTimeoutWait, 420 EventQueryVote} 421 422 func randQuery() tmpubsub.Query { 423 return queries[cmn.RandIntn(len(queries))] 424 }