github.com/number571/tendermint@v0.34.11-gost/libs/pubsub/pubsub_test.go (about) 1 package pubsub_test 2 3 import ( 4 "context" 5 "fmt" 6 "runtime/debug" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 abci "github.com/number571/tendermint/abci/types" 14 "github.com/number571/tendermint/libs/log" 15 16 "github.com/number571/tendermint/libs/pubsub" 17 "github.com/number571/tendermint/libs/pubsub/query" 18 ) 19 20 const ( 21 clientID = "test-client" 22 ) 23 24 func TestSubscribe(t *testing.T) { 25 s := pubsub.NewServer() 26 s.SetLogger(log.TestingLogger()) 27 err := s.Start() 28 require.NoError(t, err) 29 t.Cleanup(func() { 30 if err := s.Stop(); err != nil { 31 t.Error(err) 32 } 33 }) 34 35 ctx := context.Background() 36 subscription, err := s.Subscribe(ctx, clientID, query.Empty{}) 37 require.NoError(t, err) 38 39 require.Equal(t, 1, s.NumClients()) 40 require.Equal(t, 1, s.NumClientSubscriptions(clientID)) 41 42 err = s.Publish(ctx, "Ka-Zar") 43 require.NoError(t, err) 44 assertReceive(t, "Ka-Zar", subscription.Out()) 45 46 published := make(chan struct{}) 47 go func() { 48 defer close(published) 49 50 err := s.Publish(ctx, "Quicksilver") 51 require.NoError(t, err) 52 53 err = s.Publish(ctx, "Asylum") 54 require.NoError(t, err) 55 56 err = s.Publish(ctx, "Ivan") 57 require.NoError(t, err) 58 }() 59 60 select { 61 case <-published: 62 assertReceive(t, "Quicksilver", subscription.Out()) 63 assertCanceled(t, subscription, pubsub.ErrOutOfCapacity) 64 case <-time.After(3 * time.Second): 65 t.Fatal("Expected Publish(Asylum) not to block") 66 } 67 } 68 69 func TestSubscribeWithCapacity(t *testing.T) { 70 s := pubsub.NewServer() 71 s.SetLogger(log.TestingLogger()) 72 err := s.Start() 73 require.NoError(t, err) 74 t.Cleanup(func() { 75 if err := s.Stop(); err != nil { 76 t.Error(err) 77 } 78 }) 79 80 ctx := context.Background() 81 require.Panics(t, func() { 82 _, err = s.Subscribe(ctx, clientID, query.Empty{}, -1) 83 require.NoError(t, err) 84 }) 85 require.Panics(t, func() { 86 _, err = s.Subscribe(ctx, clientID, query.Empty{}, 0) 87 require.NoError(t, err) 88 }) 89 subscription, err := s.Subscribe(ctx, clientID, query.Empty{}, 1) 90 require.NoError(t, err) 91 err = s.Publish(ctx, "Aggamon") 92 require.NoError(t, err) 93 assertReceive(t, "Aggamon", subscription.Out()) 94 } 95 96 func TestSubscribeUnbuffered(t *testing.T) { 97 s := pubsub.NewServer() 98 s.SetLogger(log.TestingLogger()) 99 err := s.Start() 100 require.NoError(t, err) 101 t.Cleanup(func() { 102 if err := s.Stop(); err != nil { 103 t.Error(err) 104 } 105 }) 106 107 ctx := context.Background() 108 subscription, err := s.SubscribeUnbuffered(ctx, clientID, query.Empty{}) 109 require.NoError(t, err) 110 111 published := make(chan struct{}) 112 go func() { 113 defer close(published) 114 115 err := s.Publish(ctx, "Ultron") 116 require.NoError(t, err) 117 118 err = s.Publish(ctx, "Darkhawk") 119 require.NoError(t, err) 120 }() 121 122 select { 123 case <-published: 124 t.Fatal("Expected Publish(Darkhawk) to block") 125 case <-time.After(3 * time.Second): 126 assertReceive(t, "Ultron", subscription.Out()) 127 assertReceive(t, "Darkhawk", subscription.Out()) 128 } 129 } 130 131 func TestSlowClientIsRemovedWithErrOutOfCapacity(t *testing.T) { 132 s := pubsub.NewServer() 133 s.SetLogger(log.TestingLogger()) 134 err := s.Start() 135 require.NoError(t, err) 136 t.Cleanup(func() { 137 if err := s.Stop(); err != nil { 138 t.Error(err) 139 } 140 }) 141 142 ctx := context.Background() 143 subscription, err := s.Subscribe(ctx, clientID, query.Empty{}) 144 require.NoError(t, err) 145 err = s.Publish(ctx, "Fat Cobra") 146 require.NoError(t, err) 147 err = s.Publish(ctx, "Viper") 148 require.NoError(t, err) 149 150 assertCanceled(t, subscription, pubsub.ErrOutOfCapacity) 151 } 152 153 func TestDifferentClients(t *testing.T) { 154 s := pubsub.NewServer() 155 s.SetLogger(log.TestingLogger()) 156 157 require.NoError(t, s.Start()) 158 t.Cleanup(func() { 159 if err := s.Stop(); err != nil { 160 t.Error(err) 161 } 162 }) 163 164 ctx := context.Background() 165 166 subscription1, err := s.Subscribe(ctx, "client-1", query.MustParse("tm.events.type='NewBlock'")) 167 require.NoError(t, err) 168 169 events := []abci.Event{ 170 { 171 Type: "tm.events", 172 Attributes: []abci.EventAttribute{{Key: "type", Value: "NewBlock"}}, 173 }, 174 } 175 176 require.NoError(t, s.PublishWithEvents(ctx, "Iceman", events)) 177 assertReceive(t, "Iceman", subscription1.Out()) 178 179 subscription2, err := s.Subscribe( 180 ctx, 181 "client-2", 182 query.MustParse("tm.events.type='NewBlock' AND abci.account.name='Igor'"), 183 ) 184 require.NoError(t, err) 185 186 events = []abci.Event{ 187 { 188 Type: "tm.events", 189 Attributes: []abci.EventAttribute{{Key: "type", Value: "NewBlock"}}, 190 }, 191 { 192 Type: "abci.account", 193 Attributes: []abci.EventAttribute{{Key: "name", Value: "Igor"}}, 194 }, 195 } 196 197 require.NoError(t, s.PublishWithEvents(ctx, "Ultimo", events)) 198 assertReceive(t, "Ultimo", subscription1.Out()) 199 assertReceive(t, "Ultimo", subscription2.Out()) 200 201 subscription3, err := s.Subscribe( 202 ctx, 203 "client-3", 204 query.MustParse("tm.events.type='NewRoundStep' AND abci.account.name='Igor' AND abci.invoice.number = 10"), 205 ) 206 require.NoError(t, err) 207 208 events = []abci.Event{ 209 { 210 Type: "tm.events", 211 Attributes: []abci.EventAttribute{{Key: "type", Value: "NewRoundStep"}}, 212 }, 213 } 214 215 require.NoError(t, s.PublishWithEvents(ctx, "Valeria Richards", events)) 216 require.Zero(t, len(subscription3.Out())) 217 } 218 219 func TestSubscribeDuplicateKeys(t *testing.T) { 220 ctx := context.Background() 221 s := pubsub.NewServer() 222 s.SetLogger(log.TestingLogger()) 223 224 require.NoError(t, s.Start()) 225 226 t.Cleanup(func() { 227 if err := s.Stop(); err != nil { 228 t.Error(err) 229 } 230 }) 231 232 testCases := []struct { 233 query string 234 expected interface{} 235 }{ 236 { 237 "withdraw.rewards='17'", 238 "Iceman", 239 }, 240 { 241 "withdraw.rewards='22'", 242 "Iceman", 243 }, 244 { 245 "withdraw.rewards='1' AND withdraw.rewards='22'", 246 "Iceman", 247 }, 248 { 249 "withdraw.rewards='100'", 250 nil, 251 }, 252 } 253 254 for i, tc := range testCases { 255 sub, err := s.Subscribe(ctx, fmt.Sprintf("client-%d", i), query.MustParse(tc.query)) 256 require.NoError(t, err) 257 258 events := []abci.Event{ 259 { 260 Type: "transfer", 261 Attributes: []abci.EventAttribute{ 262 {Key: "sender", Value: "foo"}, 263 {Key: "sender", Value: "bar"}, 264 {Key: "sender", Value: "baz"}, 265 }, 266 }, 267 { 268 Type: "withdraw", 269 Attributes: []abci.EventAttribute{ 270 {Key: "rewards", Value: "1"}, 271 {Key: "rewards", Value: "17"}, 272 {Key: "rewards", Value: "22"}, 273 }, 274 }, 275 } 276 277 require.NoError(t, s.PublishWithEvents(ctx, "Iceman", events)) 278 279 if tc.expected != nil { 280 assertReceive(t, tc.expected, sub.Out()) 281 } else { 282 require.Zero(t, len(sub.Out())) 283 } 284 } 285 } 286 287 func TestClientSubscribesTwice(t *testing.T) { 288 s := pubsub.NewServer() 289 s.SetLogger(log.TestingLogger()) 290 err := s.Start() 291 require.NoError(t, err) 292 t.Cleanup(func() { 293 if err := s.Stop(); err != nil { 294 t.Error(err) 295 } 296 }) 297 298 ctx := context.Background() 299 q := query.MustParse("tm.events.type='NewBlock'") 300 301 subscription1, err := s.Subscribe(ctx, clientID, q) 302 require.NoError(t, err) 303 304 events := []abci.Event{ 305 { 306 Type: "tm.events", 307 Attributes: []abci.EventAttribute{{Key: "type", Value: "NewBlock"}}, 308 }, 309 } 310 311 require.NoError(t, s.PublishWithEvents(ctx, "Goblin Queen", events)) 312 assertReceive(t, "Goblin Queen", subscription1.Out()) 313 314 subscription2, err := s.Subscribe(ctx, clientID, q) 315 require.Error(t, err) 316 require.Nil(t, subscription2) 317 318 require.NoError(t, s.PublishWithEvents(ctx, "Spider-Man", events)) 319 assertReceive(t, "Spider-Man", subscription1.Out()) 320 } 321 322 func TestUnsubscribe(t *testing.T) { 323 s := pubsub.NewServer() 324 s.SetLogger(log.TestingLogger()) 325 err := s.Start() 326 require.NoError(t, err) 327 t.Cleanup(func() { 328 if err := s.Stop(); err != nil { 329 t.Error(err) 330 } 331 }) 332 333 ctx := context.Background() 334 subscription, err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) 335 require.NoError(t, err) 336 err = s.Unsubscribe(ctx, pubsub.UnsubscribeArgs{ 337 Subscriber: clientID, 338 Query: query.MustParse("tm.events.type='NewBlock'")}) 339 require.NoError(t, err) 340 341 err = s.Publish(ctx, "Nick Fury") 342 require.NoError(t, err) 343 require.Zero(t, len(subscription.Out()), "Should not receive anything after Unsubscribe") 344 345 assertCanceled(t, subscription, pubsub.ErrUnsubscribed) 346 } 347 348 func TestClientUnsubscribesTwice(t *testing.T) { 349 s := pubsub.NewServer() 350 s.SetLogger(log.TestingLogger()) 351 err := s.Start() 352 require.NoError(t, err) 353 t.Cleanup(func() { 354 if err := s.Stop(); err != nil { 355 t.Error(err) 356 } 357 }) 358 359 ctx := context.Background() 360 _, err = s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) 361 require.NoError(t, err) 362 err = s.Unsubscribe(ctx, pubsub.UnsubscribeArgs{ 363 Subscriber: clientID, 364 Query: query.MustParse("tm.events.type='NewBlock'")}) 365 require.NoError(t, err) 366 367 err = s.Unsubscribe(ctx, pubsub.UnsubscribeArgs{ 368 Subscriber: clientID, 369 Query: query.MustParse("tm.events.type='NewBlock'")}) 370 require.Equal(t, pubsub.ErrSubscriptionNotFound, err) 371 err = s.UnsubscribeAll(ctx, clientID) 372 require.Equal(t, pubsub.ErrSubscriptionNotFound, err) 373 } 374 375 func TestResubscribe(t *testing.T) { 376 s := pubsub.NewServer() 377 s.SetLogger(log.TestingLogger()) 378 err := s.Start() 379 require.NoError(t, err) 380 t.Cleanup(func() { 381 if err := s.Stop(); err != nil { 382 t.Error(err) 383 } 384 }) 385 386 ctx := context.Background() 387 _, err = s.Subscribe(ctx, clientID, query.Empty{}) 388 require.NoError(t, err) 389 err = s.Unsubscribe(ctx, pubsub.UnsubscribeArgs{Subscriber: clientID, Query: query.Empty{}}) 390 require.NoError(t, err) 391 subscription, err := s.Subscribe(ctx, clientID, query.Empty{}) 392 require.NoError(t, err) 393 394 err = s.Publish(ctx, "Cable") 395 require.NoError(t, err) 396 assertReceive(t, "Cable", subscription.Out()) 397 } 398 399 func TestUnsubscribeAll(t *testing.T) { 400 s := pubsub.NewServer() 401 s.SetLogger(log.TestingLogger()) 402 err := s.Start() 403 require.NoError(t, err) 404 t.Cleanup(func() { 405 if err := s.Stop(); err != nil { 406 t.Error(err) 407 } 408 }) 409 410 ctx := context.Background() 411 subscription1, err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) 412 require.NoError(t, err) 413 subscription2, err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlockHeader'")) 414 require.NoError(t, err) 415 416 err = s.UnsubscribeAll(ctx, clientID) 417 require.NoError(t, err) 418 419 err = s.Publish(ctx, "Nick Fury") 420 require.NoError(t, err) 421 require.Zero(t, len(subscription1.Out()), "Should not receive anything after UnsubscribeAll") 422 require.Zero(t, len(subscription2.Out()), "Should not receive anything after UnsubscribeAll") 423 424 assertCanceled(t, subscription1, pubsub.ErrUnsubscribed) 425 assertCanceled(t, subscription2, pubsub.ErrUnsubscribed) 426 } 427 428 func TestBufferCapacity(t *testing.T) { 429 s := pubsub.NewServer(pubsub.BufferCapacity(2)) 430 s.SetLogger(log.TestingLogger()) 431 432 require.Equal(t, 2, s.BufferCapacity()) 433 434 ctx := context.Background() 435 err := s.Publish(ctx, "Nighthawk") 436 require.NoError(t, err) 437 err = s.Publish(ctx, "Sage") 438 require.NoError(t, err) 439 440 ctx, cancel := context.WithTimeout(ctx, 10*time.Millisecond) 441 defer cancel() 442 443 err = s.Publish(ctx, "Ironclad") 444 if assert.Error(t, err) { 445 require.Equal(t, context.DeadlineExceeded, err) 446 } 447 } 448 449 func Benchmark10Clients(b *testing.B) { benchmarkNClients(10, b) } 450 func Benchmark100Clients(b *testing.B) { benchmarkNClients(100, b) } 451 func Benchmark1000Clients(b *testing.B) { benchmarkNClients(1000, b) } 452 453 func Benchmark10ClientsOneQuery(b *testing.B) { benchmarkNClientsOneQuery(10, b) } 454 func Benchmark100ClientsOneQuery(b *testing.B) { benchmarkNClientsOneQuery(100, b) } 455 func Benchmark1000ClientsOneQuery(b *testing.B) { benchmarkNClientsOneQuery(1000, b) } 456 457 func benchmarkNClients(n int, b *testing.B) { 458 s := pubsub.NewServer() 459 err := s.Start() 460 require.NoError(b, err) 461 462 b.Cleanup(func() { 463 if err := s.Stop(); err != nil { 464 b.Error(err) 465 } 466 }) 467 468 ctx := context.Background() 469 for i := 0; i < n; i++ { 470 subscription, err := s.Subscribe( 471 ctx, 472 clientID, 473 query.MustParse(fmt.Sprintf("abci.Account.Owner = 'Ivan' AND abci.Invoices.Number = %d", i)), 474 ) 475 if err != nil { 476 b.Fatal(err) 477 } 478 go func() { 479 for { 480 select { 481 case <-subscription.Out(): 482 continue 483 case <-subscription.Canceled(): 484 return 485 } 486 } 487 }() 488 } 489 490 b.ReportAllocs() 491 b.ResetTimer() 492 for i := 0; i < b.N; i++ { 493 events := []abci.Event{ 494 { 495 Type: "abci.Account", 496 Attributes: []abci.EventAttribute{{Key: "Owner", Value: "Ivan"}}, 497 }, 498 { 499 Type: "abci.Invoices", 500 Attributes: []abci.EventAttribute{{Key: "Number", Value: string(rune(i))}}, 501 }, 502 } 503 504 require.NoError(b, s.PublishWithEvents(ctx, "Gamora", events)) 505 } 506 } 507 508 func benchmarkNClientsOneQuery(n int, b *testing.B) { 509 s := pubsub.NewServer() 510 err := s.Start() 511 require.NoError(b, err) 512 b.Cleanup(func() { 513 if err := s.Stop(); err != nil { 514 b.Error(err) 515 } 516 }) 517 518 ctx := context.Background() 519 q := query.MustParse("abci.Account.Owner = 'Ivan' AND abci.Invoices.Number = 1") 520 for i := 0; i < n; i++ { 521 subscription, err := s.Subscribe(ctx, clientID, q) 522 if err != nil { 523 b.Fatal(err) 524 } 525 go func() { 526 for { 527 select { 528 case <-subscription.Out(): 529 continue 530 case <-subscription.Canceled(): 531 return 532 } 533 } 534 }() 535 } 536 537 b.ReportAllocs() 538 b.ResetTimer() 539 540 for i := 0; i < b.N; i++ { 541 events := []abci.Event{ 542 { 543 Type: "abci.Account", 544 Attributes: []abci.EventAttribute{{Key: "Owner", Value: "Ivan"}}, 545 }, 546 { 547 Type: "abci.Invoices", 548 Attributes: []abci.EventAttribute{{Key: "Number", Value: "1"}}, 549 }, 550 } 551 552 require.NoError(b, s.PublishWithEvents(ctx, "Gamora", events)) 553 } 554 } 555 556 // HELPERS 557 558 func assertReceive(t *testing.T, expected interface{}, ch <-chan pubsub.Message, msgAndArgs ...interface{}) { 559 select { 560 case actual := <-ch: 561 require.Equal(t, expected, actual.Data(), msgAndArgs...) 562 case <-time.After(1 * time.Second): 563 t.Errorf("expected to receive %v from the channel, got nothing after 1s", expected) 564 debug.PrintStack() 565 } 566 } 567 568 func assertCanceled(t *testing.T, subscription *pubsub.Subscription, err error) { 569 _, ok := <-subscription.Canceled() 570 require.False(t, ok) 571 require.Equal(t, err, subscription.Err()) 572 }