gitlab.com/gpdionisio/tendermint@v0.34.19-dev2/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  }