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