github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/p2p/server_test.go (about)

     1  // Copyright 2021 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package p2p
    15  
    16  import (
    17  	"context"
    18  	"encoding/json"
    19  	"fmt"
    20  	"math"
    21  	"net"
    22  	"strings"
    23  	"sync"
    24  	"sync/atomic"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/phayes/freeport"
    29  	"github.com/pingcap/tiflow/proto/p2p"
    30  	"github.com/stretchr/testify/require"
    31  	"google.golang.org/grpc"
    32  )
    33  
    34  const (
    35  	defaultTimeout                = time.Second * 10
    36  	defaultMessageBatchSizeSmall  = 128
    37  	defaultMessageBatchSizeMedium = 512
    38  	defaultMessageBatchSizeLarge  = 1024
    39  	defaultMultiClientCount       = 16
    40  )
    41  
    42  // read only
    43  var defaultServerConfig4Testing = &MessageServerConfig{
    44  	MaxPendingMessageCountPerTopic:       256,
    45  	MaxPendingTaskCount:                  102400,
    46  	SendChannelSize:                      16,
    47  	SendRateLimitPerStream:               1024,
    48  	AckInterval:                          time.Millisecond * 200,
    49  	WorkerPoolSize:                       4,
    50  	MaxPeerCount:                         1024,
    51  	WaitUnregisterHandleTimeoutThreshold: time.Millisecond * 100,
    52  }
    53  
    54  func newServerForTesting(t *testing.T, serverID string) (server *MessageServer, newClient func() (p2p.CDCPeerToPeerClient, func()), cancel func()) {
    55  	port := freeport.GetPort()
    56  	addr := fmt.Sprintf("127.0.0.1:%d", port)
    57  	lis, err := net.Listen("tcp", addr)
    58  	require.NoError(t, err)
    59  
    60  	var opts []grpc.ServerOption
    61  	grpcServer := grpc.NewServer(opts...)
    62  
    63  	config := *defaultServerConfig4Testing
    64  	server = NewMessageServer(serverID, &config)
    65  	p2p.RegisterCDCPeerToPeerServer(grpcServer, server)
    66  
    67  	var wg sync.WaitGroup
    68  	wg.Add(1)
    69  	go func() {
    70  		defer wg.Done()
    71  		_ = grpcServer.Serve(lis)
    72  	}()
    73  
    74  	cancel = func() {
    75  		grpcServer.Stop()
    76  		wg.Wait()
    77  	}
    78  
    79  	newClient = func() (p2p.CDCPeerToPeerClient, func()) {
    80  		conn, err := grpc.Dial(
    81  			addr,
    82  			grpc.WithInsecure(),
    83  			grpc.WithContextDialer(func(_ context.Context, s string) (net.Conn, error) {
    84  				return net.Dial("tcp", addr)
    85  			}))
    86  		require.NoError(t, err)
    87  
    88  		cancel := func() {
    89  			_ = conn.Close()
    90  		}
    91  		return p2p.NewCDCPeerToPeerClient(conn), cancel
    92  	}
    93  	return
    94  }
    95  
    96  func mustAddHandler(ctx context.Context, t *testing.T, server *MessageServer, topic string, tpi interface{}, f func(string, interface{}) error) <-chan error {
    97  	doneCh, errCh, err := server.AddHandler(ctx, topic, tpi, f)
    98  	require.NoError(t, err)
    99  
   100  	select {
   101  	case <-ctx.Done():
   102  		t.Fatalf("AddHandler timed out")
   103  	case <-doneCh:
   104  	}
   105  
   106  	return errCh
   107  }
   108  
   109  type testTopicContent struct {
   110  	Index int64
   111  }
   112  
   113  func TestServerMultiClientSingleTopic(t *testing.T) {
   114  	ctx, cancel := context.WithTimeout(context.TODO(), defaultTimeout)
   115  	defer cancel()
   116  
   117  	serverID := "test-server-1"
   118  	server, newClient, closer := newServerForTesting(t, serverID)
   119  	defer closer()
   120  
   121  	// Avoids server returning error due to congested topic.
   122  	server.config.MaxPendingMessageCountPerTopic = math.MaxInt64
   123  
   124  	localCh := make(chan RawMessageEntry, defaultMessageBatchSizeMedium)
   125  	var wg sync.WaitGroup
   126  	wg.Add(1)
   127  	go func() {
   128  		defer wg.Done()
   129  		err := server.Run(ctx, localCh)
   130  		require.Regexp(t, ".*context canceled.*", err)
   131  	}()
   132  
   133  	var ackWg sync.WaitGroup
   134  	for i := 0; i < defaultMultiClientCount; i++ {
   135  		wg.Add(1)
   136  		ackWg.Add(1)
   137  		i := i
   138  		go func() {
   139  			defer wg.Done()
   140  			client, closeClient := newClient()
   141  			stream, err := client.SendMessage(ctx)
   142  			require.NoError(t, err)
   143  
   144  			err = stream.Send(&p2p.MessagePacket{
   145  				Meta: &p2p.StreamMeta{
   146  					SenderId:   fmt.Sprintf("test-client-%d", i),
   147  					ReceiverId: serverID,
   148  					Epoch:      0,
   149  				},
   150  			})
   151  			require.NoError(t, err)
   152  
   153  			go func() {
   154  				defer closeClient()
   155  				defer ackWg.Done()
   156  				var lastAck int64
   157  				for {
   158  					resp, err := stream.Recv()
   159  					require.NoError(t, err)
   160  					require.Equal(t, p2p.ExitReason_OK, resp.ExitReason)
   161  
   162  					acks := resp.Ack
   163  					require.Len(t, acks, 1)
   164  					require.Equal(t, "test-topic-1", acks[0].Topic)
   165  					require.GreaterOrEqual(t, acks[0].LastSeq, lastAck)
   166  					lastAck = acks[0].LastSeq
   167  					if lastAck == defaultMessageBatchSizeMedium {
   168  						return
   169  					}
   170  				}
   171  			}()
   172  
   173  			for j := 0; j < defaultMessageBatchSizeMedium; j++ {
   174  				content := &testTopicContent{Index: int64(j + 1)}
   175  				bytes, err := json.Marshal(content)
   176  				require.NoError(t, err)
   177  
   178  				err = stream.Send(&p2p.MessagePacket{
   179  					Entries: []*p2p.MessageEntry{
   180  						{
   181  							Topic:    "test-topic-1",
   182  							Content:  bytes,
   183  							Sequence: int64(j + 1),
   184  						},
   185  					},
   186  				})
   187  				require.NoError(t, err)
   188  			}
   189  		}()
   190  	}
   191  
   192  	time.Sleep(1 * time.Second)
   193  
   194  	lastIndices := sync.Map{}
   195  	errCh := mustAddHandler(ctx, t, server, "test-topic-1", &testTopicContent{}, func(senderID string, i interface{}) error {
   196  		if strings.Contains(senderID, "server") {
   197  			require.Equal(t, serverID, senderID)
   198  		} else {
   199  			require.Regexp(t, "test-client-.*", senderID)
   200  		}
   201  		require.IsType(t, &testTopicContent{}, i)
   202  		content := i.(*testTopicContent)
   203  
   204  		v, ok := lastIndices.Load(senderID)
   205  		if !ok {
   206  			require.Equal(t, int64(1), content.Index)
   207  		} else {
   208  			require.Equal(t, content.Index-1, v.(int64))
   209  		}
   210  		lastIndices.Store(senderID, content.Index)
   211  		return nil
   212  	})
   213  
   214  	// test local client
   215  	wg.Add(1)
   216  	ackWg.Add(1)
   217  	go func() {
   218  		defer wg.Done()
   219  		for j := 0; j < defaultMessageBatchSizeLarge; j++ {
   220  			content := &testTopicContent{Index: int64(j + 1)}
   221  			select {
   222  			case <-ctx.Done():
   223  				t.Fail()
   224  			case localCh <- RawMessageEntry{
   225  				topic: "test-topic-1",
   226  				value: content,
   227  			}:
   228  			}
   229  		}
   230  		go func() {
   231  			defer ackWg.Done()
   232  			ticker := time.NewTicker(100 * time.Millisecond)
   233  			defer ticker.Stop()
   234  			for {
   235  				select {
   236  				case <-ctx.Done():
   237  					t.Fail()
   238  				case <-ticker.C:
   239  					v, ok := lastIndices.Load(serverID)
   240  					if !ok {
   241  						continue
   242  					}
   243  					idx := v.(int64)
   244  					if idx == defaultMessageBatchSizeLarge {
   245  						return
   246  					}
   247  				}
   248  			}
   249  		}()
   250  	}()
   251  
   252  	wg.Add(1)
   253  	go func() {
   254  		defer wg.Done()
   255  		select {
   256  		case <-ctx.Done():
   257  		case err := <-errCh:
   258  			require.Error(t, err)
   259  			require.Regexp(t, ".*ErrWorkerPoolHandleCancelled.*", err.Error())
   260  		}
   261  	}()
   262  
   263  	ackWg.Wait()
   264  
   265  	err := server.SyncRemoveHandler(ctx, "test-topic-1")
   266  	require.NoError(t, err)
   267  
   268  	// double remove to test idempotency.
   269  	err = server.SyncRemoveHandler(ctx, "test-topic-1")
   270  	require.NoError(t, err)
   271  
   272  	closer()
   273  	cancel()
   274  
   275  	wg.Wait()
   276  }
   277  
   278  func TestServerDeregisterHandler(t *testing.T) {
   279  	ctx, cancel := context.WithTimeout(context.TODO(), defaultTimeout)
   280  	defer cancel()
   281  
   282  	server, newClient, closer := newServerForTesting(t, "test-server-1")
   283  	defer closer()
   284  
   285  	var wg sync.WaitGroup
   286  	wg.Add(1)
   287  	go func() {
   288  		defer wg.Done()
   289  		err := server.Run(ctx, nil)
   290  		require.Regexp(t, ".*context canceled.*", err.Error())
   291  	}()
   292  
   293  	var (
   294  		lastIndex int64
   295  		removed   int32
   296  	)
   297  	errCh := mustAddHandler(ctx, t, server, "test-topic-1", &testTopicContent{}, func(senderID string, i interface{}) error {
   298  		require.Equal(t, int32(0), atomic.LoadInt32(&removed))
   299  		require.Equal(t, "test-client-1", senderID)
   300  		require.IsType(t, &testTopicContent{}, i)
   301  		content := i.(*testTopicContent)
   302  		swapped := atomic.CompareAndSwapInt64(&lastIndex, content.Index-1, content.Index)
   303  		require.True(t, swapped)
   304  		return nil
   305  	})
   306  
   307  	wg.Add(1)
   308  	go func() {
   309  		defer wg.Done()
   310  		select {
   311  		case <-ctx.Done():
   312  		case err := <-errCh:
   313  			require.Regexp(t, ".*ErrWorkerPoolHandleCancelled.*", err.Error())
   314  		}
   315  	}()
   316  
   317  	client, closeClient := newClient()
   318  	defer closeClient()
   319  
   320  	stream, err := client.SendMessage(ctx)
   321  	require.NoError(t, err)
   322  
   323  	err = stream.Send(&p2p.MessagePacket{
   324  		Meta: &p2p.StreamMeta{
   325  			SenderId:   "test-client-1",
   326  			ReceiverId: "test-server-1",
   327  			Epoch:      0,
   328  		},
   329  	})
   330  	require.NoError(t, err)
   331  
   332  	removeHandler := func() {
   333  		wg.Add(1)
   334  		go func() {
   335  			defer wg.Done()
   336  			doneCh, err := server.RemoveHandler(ctx, "test-topic-1")
   337  			require.NoError(t, err)
   338  			select {
   339  			case <-ctx.Done():
   340  				require.FailNow(t, "RemoveHandler timed out")
   341  			case <-doneCh:
   342  			}
   343  			atomic.StoreInt32(&removed, 1)
   344  		}()
   345  	}
   346  
   347  	wg.Add(1)
   348  	go func() {
   349  		defer wg.Done()
   350  		defer cancel()
   351  		defer closer()
   352  		var lastAck int64
   353  		for {
   354  			resp, err := stream.Recv()
   355  			require.NoError(t, err)
   356  			require.Equal(t, p2p.ExitReason_OK, resp.ExitReason)
   357  			acks := resp.Ack
   358  			require.Len(t, acks, 1)
   359  			require.Equal(t, "test-topic-1", acks[0].Topic)
   360  			require.GreaterOrEqual(t, acks[0].LastSeq, lastAck)
   361  			lastAck = acks[0].LastSeq
   362  			if lastAck >= defaultMessageBatchSizeSmall/3 {
   363  				removeHandler()
   364  				time.Sleep(time.Millisecond * 500)
   365  				require.Equal(t, int32(1), atomic.LoadInt32(&removed))
   366  				time.Sleep(time.Millisecond * 500)
   367  				return
   368  			}
   369  		}
   370  	}()
   371  
   372  	for i := 0; i < defaultMessageBatchSizeSmall; i++ {
   373  		content := &testTopicContent{Index: int64(i + 1)}
   374  		bytes, err := json.Marshal(content)
   375  		require.NoError(t, err)
   376  
   377  		err = stream.Send(&p2p.MessagePacket{
   378  			Entries: []*p2p.MessageEntry{
   379  				{
   380  					Topic:    "test-topic-1",
   381  					Content:  bytes,
   382  					Sequence: int64(i + 1),
   383  				},
   384  			},
   385  		})
   386  		require.NoError(t, err)
   387  	}
   388  
   389  	wg.Wait()
   390  }
   391  
   392  func TestServerClosed(t *testing.T) {
   393  	ctx, cancel := context.WithTimeout(context.TODO(), defaultTimeout)
   394  	defer cancel()
   395  
   396  	server, newClient, closer := newServerForTesting(t, "test-server-1")
   397  	defer closer()
   398  
   399  	cctx, cancelServer := context.WithCancel(ctx)
   400  	var wg sync.WaitGroup
   401  	wg.Add(1)
   402  	go func() {
   403  		defer wg.Done()
   404  		err := server.Run(cctx, nil)
   405  		require.Regexp(t, ".*context canceled.*", err.Error())
   406  	}()
   407  
   408  	client, closeClient := newClient()
   409  	defer closeClient()
   410  
   411  	stream, err := client.SendMessage(ctx)
   412  	require.NoError(t, err)
   413  
   414  	err = stream.Send(&p2p.MessagePacket{
   415  		Meta: &p2p.StreamMeta{
   416  			SenderId:   "test-client-1",
   417  			ReceiverId: "test-server-1",
   418  			Epoch:      0,
   419  		},
   420  	})
   421  	require.NoError(t, err)
   422  
   423  	cancelServer()
   424  
   425  	var clientErr error
   426  	require.Eventually(t, func() bool {
   427  		_, clientErr = stream.Recv()
   428  		return clientErr != nil
   429  	}, time.Second*1, time.Millisecond*10)
   430  	require.Regexp(t, ".*message server is closing.*", clientErr.Error())
   431  
   432  	wg.Wait()
   433  }
   434  
   435  func TestServerTopicCongestedDueToNoHandler(t *testing.T) {
   436  	ctx, cancel := context.WithTimeout(context.TODO(), defaultTimeout)
   437  	defer cancel()
   438  
   439  	server, newClient, closer := newServerForTesting(t, "test-server-1")
   440  	defer closer()
   441  
   442  	var wg sync.WaitGroup
   443  	wg.Add(1)
   444  	go func() {
   445  		defer wg.Done()
   446  		err := server.Run(ctx, nil)
   447  		require.Regexp(t, ".*context canceled.*", err.Error())
   448  	}()
   449  
   450  	client, closeClient := newClient()
   451  	defer closeClient()
   452  
   453  	stream, err := client.SendMessage(ctx)
   454  	require.NoError(t, err)
   455  
   456  	err = stream.Send(&p2p.MessagePacket{
   457  		Meta: &p2p.StreamMeta{
   458  			SenderId:   "test-client-1",
   459  			ReceiverId: "test-server-1",
   460  			Epoch:      0,
   461  		},
   462  	})
   463  	require.NoError(t, err)
   464  
   465  	wg.Add(1)
   466  	go func() {
   467  		defer wg.Done()
   468  		for {
   469  			resp, err := stream.Recv()
   470  			require.NoError(t, err)
   471  			if resp.ExitReason == p2p.ExitReason_OK {
   472  				continue
   473  			}
   474  			require.Equal(t, p2p.ExitReason_CONGESTED, resp.ExitReason)
   475  			cancel()
   476  			return
   477  		}
   478  	}()
   479  
   480  	for i := 0; i < defaultMessageBatchSizeMedium; i++ {
   481  		content := &testTopicContent{Index: int64(i + 1)}
   482  		bytes, err := json.Marshal(content)
   483  		require.NoError(t, err)
   484  
   485  		err = stream.Send(&p2p.MessagePacket{
   486  			Entries: []*p2p.MessageEntry{
   487  				{
   488  					Topic:    "test-topic-1",
   489  					Content:  bytes,
   490  					Sequence: int64(i + 1),
   491  				},
   492  			},
   493  		})
   494  		if err != nil {
   495  			require.Regexp(t, "EOF", err.Error())
   496  			return
   497  		}
   498  	}
   499  
   500  	wg.Wait()
   501  }
   502  
   503  func TestServerIncomingConnectionStale(t *testing.T) {
   504  	ctx, cancel := context.WithTimeout(context.TODO(), defaultTimeout)
   505  	defer cancel()
   506  
   507  	server, newClient, closer := newServerForTesting(t, "test-server-1")
   508  	defer closer()
   509  
   510  	var wg sync.WaitGroup
   511  	wg.Add(1)
   512  	go func() {
   513  		defer wg.Done()
   514  		err := server.Run(ctx, nil)
   515  		require.Regexp(t, ".*context canceled.*", err.Error())
   516  	}()
   517  
   518  	client, closeClient := newClient()
   519  	defer closeClient()
   520  
   521  	streamCtx, cancelStream := context.WithCancel(ctx)
   522  	defer cancelStream()
   523  	stream, err := client.SendMessage(streamCtx)
   524  	require.NoError(t, err)
   525  
   526  	err = stream.Send(&p2p.MessagePacket{
   527  		Meta: &p2p.StreamMeta{
   528  			SenderId:   "test-client-1",
   529  			ReceiverId: "test-server-1",
   530  			Epoch:      5, // a larger epoch
   531  		},
   532  	})
   533  	require.NoError(t, err)
   534  	_ = stream.CloseSend()
   535  
   536  	require.Eventually(t, func() bool {
   537  		server.peerLock.RLock()
   538  		defer server.peerLock.RUnlock()
   539  		_, ok := server.peers["test-client-1"]
   540  		return ok
   541  	}, time.Millisecond*100, time.Millisecond*10)
   542  
   543  	stream, err = client.SendMessage(ctx)
   544  	require.NoError(t, err)
   545  
   546  	err = stream.Send(&p2p.MessagePacket{
   547  		Meta: &p2p.StreamMeta{
   548  			SenderId:   "test-client-1",
   549  			ReceiverId: "test-server-1",
   550  			Epoch:      4, // a smaller epoch
   551  		},
   552  	})
   553  	require.NoError(t, err)
   554  
   555  	for {
   556  		resp, err := stream.Recv()
   557  		require.NoError(t, err)
   558  		if resp.ExitReason == p2p.ExitReason_OK {
   559  			continue
   560  		}
   561  		require.Equal(t, resp.ExitReason, p2p.ExitReason_STALE_CONNECTION)
   562  		break
   563  	}
   564  
   565  	cancel()
   566  	wg.Wait()
   567  }
   568  
   569  func TestServerOldConnectionStale(t *testing.T) {
   570  	ctx, cancel := context.WithTimeout(context.TODO(), defaultTimeout)
   571  	defer cancel()
   572  
   573  	server, newClient, closer := newServerForTesting(t, "test-server-1")
   574  	defer closer()
   575  
   576  	var wg sync.WaitGroup
   577  	wg.Add(1)
   578  	go func() {
   579  		defer wg.Done()
   580  		err := server.Run(ctx, nil)
   581  		require.Regexp(t, ".*context canceled.*", err.Error())
   582  	}()
   583  
   584  	client, closeClient := newClient()
   585  	defer closeClient()
   586  
   587  	streamCtx, cancelStream := context.WithCancel(ctx)
   588  	defer cancelStream()
   589  	stream, err := client.SendMessage(streamCtx)
   590  	require.NoError(t, err)
   591  
   592  	err = stream.Send(&p2p.MessagePacket{
   593  		Meta: &p2p.StreamMeta{
   594  			SenderId:   "test-client-1",
   595  			ReceiverId: "test-server-1",
   596  			Epoch:      4, // a smaller epoch
   597  		},
   598  	})
   599  	require.NoError(t, err)
   600  
   601  	require.Eventually(t, func() bool {
   602  		server.peerLock.RLock()
   603  		defer server.peerLock.RUnlock()
   604  		_, ok := server.peers["test-client-1"]
   605  		return ok
   606  	}, time.Millisecond*100, time.Millisecond*10)
   607  
   608  	stream1, err := client.SendMessage(ctx)
   609  	require.NoError(t, err)
   610  
   611  	err = stream1.Send(&p2p.MessagePacket{
   612  		Meta: &p2p.StreamMeta{
   613  			SenderId:   "test-client-1",
   614  			ReceiverId: "test-server-1",
   615  			Epoch:      5, // a larger epoch
   616  		},
   617  	})
   618  	require.NoError(t, err)
   619  
   620  	for {
   621  		resp, err := stream.Recv()
   622  		require.NoError(t, err)
   623  		if resp.ExitReason == p2p.ExitReason_OK {
   624  			continue
   625  		}
   626  		require.Equal(t, resp.ExitReason, p2p.ExitReason_STALE_CONNECTION)
   627  		break
   628  	}
   629  
   630  	cancel()
   631  	wg.Wait()
   632  }
   633  
   634  func TestServerRepeatedMessages(t *testing.T) {
   635  	ctx, cancel := context.WithTimeout(context.TODO(), defaultTimeout)
   636  	defer cancel()
   637  
   638  	server, newClient, closer := newServerForTesting(t, "test-server-1")
   639  	defer closer()
   640  
   641  	var wg sync.WaitGroup
   642  	wg.Add(1)
   643  	go func() {
   644  		defer wg.Done()
   645  		err := server.Run(ctx, nil)
   646  		require.Regexp(t, ".*context canceled.*", err.Error())
   647  	}()
   648  
   649  	var lastIndex int64
   650  	_ = mustAddHandler(ctx, t, server, "test-topic-1",
   651  		&testTopicContent{}, func(senderID string, i interface{}) error {
   652  			require.Equal(t, "test-client-1", senderID)
   653  			require.IsType(t, &testTopicContent{}, i)
   654  			content := i.(*testTopicContent)
   655  			require.Equal(t, content.Index-1, atomic.LoadInt64(&lastIndex))
   656  			atomic.StoreInt64(&lastIndex, content.Index)
   657  			return nil
   658  		})
   659  
   660  	client, closeClient := newClient()
   661  	defer closeClient()
   662  
   663  	stream, err := client.SendMessage(ctx)
   664  	require.NoError(t, err)
   665  
   666  	err = stream.Send(&p2p.MessagePacket{
   667  		Meta: &p2p.StreamMeta{
   668  			SenderId:   "test-client-1",
   669  			ReceiverId: "test-server-1",
   670  			Epoch:      0,
   671  		},
   672  	})
   673  	require.NoError(t, err)
   674  
   675  	wg.Add(1)
   676  	go func() {
   677  		defer wg.Done()
   678  		defer cancel()
   679  		for {
   680  			resp, err := stream.Recv()
   681  			require.NoError(t, err)
   682  			require.Equal(
   683  				t,
   684  				p2p.ExitReason_OK,
   685  				resp.ExitReason,
   686  				"unexpected error: %s",
   687  				resp.ErrorMessage,
   688  			)
   689  			if resp.Ack[0].LastSeq == defaultMessageBatchSizeSmall {
   690  				return
   691  			}
   692  		}
   693  	}()
   694  
   695  	for i := 0; i < defaultMessageBatchSizeSmall; i++ {
   696  		content := &testTopicContent{Index: int64(i + 1)}
   697  		bytes, err := json.Marshal(content)
   698  		require.NoError(t, err)
   699  
   700  		err = stream.Send(&p2p.MessagePacket{
   701  			Entries: []*p2p.MessageEntry{
   702  				{
   703  					Topic:    "test-topic-1",
   704  					Content:  bytes,
   705  					Sequence: int64(i + 1),
   706  				},
   707  			},
   708  		})
   709  		if err != nil {
   710  			require.Regexp(t, "EOF", err.Error())
   711  			return
   712  		}
   713  
   714  		err = stream.Send(&p2p.MessagePacket{
   715  			Entries: []*p2p.MessageEntry{
   716  				{
   717  					Topic:    "test-topic-1",
   718  					Content:  bytes,
   719  					Sequence: int64(i + 1),
   720  				},
   721  			},
   722  		})
   723  		if err != nil {
   724  			require.Regexp(t, "EOF", err.Error())
   725  			return
   726  		}
   727  	}
   728  
   729  	wg.Wait()
   730  }
   731  
   732  func TestServerExitWhileAddingHandler(t *testing.T) {
   733  	ctx, cancel := context.WithTimeout(context.TODO(), defaultTimeout)
   734  	defer cancel()
   735  
   736  	server, _, closer := newServerForTesting(t, "test-server-1")
   737  	defer closer()
   738  
   739  	serverCtx, cancelServer := context.WithCancel(ctx)
   740  	defer cancelServer()
   741  
   742  	var wg sync.WaitGroup
   743  	wg.Add(1)
   744  	go func() {
   745  		defer wg.Done()
   746  		err := server.Run(serverCtx, nil)
   747  		require.Regexp(t, ".*context canceled.*", err.Error())
   748  	}()
   749  
   750  	waitCh := make(chan struct{})
   751  
   752  	wg.Add(1)
   753  	go func() {
   754  		defer wg.Done()
   755  		err := server.scheduleTask(ctx, taskDebugDelay{
   756  			doneCh: waitCh,
   757  		})
   758  		require.NoError(t, err)
   759  	}()
   760  
   761  	wg.Add(1)
   762  	go func() {
   763  		defer wg.Done()
   764  		time.Sleep(time.Millisecond * 100)
   765  		_, err := server.SyncAddHandler(ctx, "test-topic", &testTopicContent{}, func(s string, i interface{}) error {
   766  			return nil
   767  		})
   768  		require.Error(t, err)
   769  		require.Regexp(t, ".*ErrPeerMessageServerClosed.*", err.Error())
   770  	}()
   771  
   772  	wg.Add(1)
   773  	go func() {
   774  		defer wg.Done()
   775  		time.Sleep(time.Millisecond * 300)
   776  		cancelServer()
   777  	}()
   778  
   779  	wg.Wait()
   780  }
   781  
   782  func TestServerExitWhileRemovingHandler(t *testing.T) {
   783  	ctx, cancel := context.WithTimeout(context.TODO(), defaultTimeout)
   784  	defer cancel()
   785  
   786  	server, _, closer := newServerForTesting(t, "test-server-1")
   787  	defer closer()
   788  
   789  	serverCtx, cancelServer := context.WithCancel(ctx)
   790  	defer cancelServer()
   791  
   792  	var wg sync.WaitGroup
   793  	wg.Add(1)
   794  	go func() {
   795  		defer wg.Done()
   796  		err := server.Run(serverCtx, nil)
   797  		require.Regexp(t, ".*context canceled.*", err.Error())
   798  	}()
   799  
   800  	waitCh := make(chan struct{})
   801  
   802  	wg.Add(1)
   803  	go func() {
   804  		defer wg.Done()
   805  		_, err := server.SyncAddHandler(ctx, "test-topic", &testTopicContent{}, func(s string, i interface{}) error {
   806  			return nil
   807  		})
   808  		require.NoError(t, err)
   809  		err = server.scheduleTask(ctx, taskDebugDelay{
   810  			doneCh: waitCh,
   811  		})
   812  		require.NoError(t, err)
   813  		time.Sleep(time.Millisecond * 100)
   814  		err = server.SyncRemoveHandler(ctx, "test-topic")
   815  		require.NoError(t, err)
   816  	}()
   817  
   818  	wg.Add(1)
   819  	go func() {
   820  		defer wg.Done()
   821  		time.Sleep(time.Millisecond * 500)
   822  		cancelServer()
   823  	}()
   824  
   825  	wg.Wait()
   826  }
   827  
   828  func TestReceiverIDMismatch(t *testing.T) {
   829  	ctx, cancel := context.WithTimeout(context.TODO(), defaultTimeout)
   830  	defer cancel()
   831  
   832  	server, newClient, closer := newServerForTesting(t, "test-server-1")
   833  	defer closer()
   834  
   835  	var wg sync.WaitGroup
   836  	wg.Add(1)
   837  	go func() {
   838  		defer wg.Done()
   839  		err := server.Run(ctx, nil)
   840  		require.Regexp(t, ".*context canceled.*", err.Error())
   841  	}()
   842  
   843  	client, closeClient := newClient()
   844  	defer closeClient()
   845  
   846  	stream, err := client.SendMessage(ctx)
   847  	require.NoError(t, err)
   848  
   849  	err = stream.Send(&p2p.MessagePacket{
   850  		Meta: &p2p.StreamMeta{
   851  			SenderId:   "test-client-1",
   852  			ReceiverId: "test-server-2", // mismatch
   853  			Epoch:      0,
   854  		},
   855  	})
   856  	require.NoError(t, err)
   857  
   858  	resp, err := stream.Recv()
   859  	require.NoError(t, err)
   860  	require.Equal(t, p2p.ExitReason_CAPTURE_ID_MISMATCH, resp.ExitReason)
   861  
   862  	cancel()
   863  	wg.Wait()
   864  }
   865  
   866  func TestServerDataLossAfterUnregisterHandle(t *testing.T) {
   867  	ctx, cancel := context.WithTimeout(context.TODO(), time.Second*20)
   868  	defer cancel()
   869  
   870  	server, newClient, closer := newServerForTesting(t, "test-server-1")
   871  	defer closer()
   872  
   873  	serverCtx, cancelServer := context.WithCancel(ctx)
   874  	defer cancelServer()
   875  
   876  	var wg sync.WaitGroup
   877  	wg.Add(1)
   878  	go func() {
   879  		defer wg.Done()
   880  		err := server.Run(serverCtx, nil)
   881  		require.Regexp(t, ".*context canceled.*", err.Error())
   882  	}()
   883  
   884  	var called int32
   885  	mustAddHandler(ctx, t, server, "blocking-handler", &testTopicContent{}, func(_ string, _ interface{}) error {
   886  		if atomic.AddInt32(&called, 1) == 2 {
   887  			time.Sleep(1 * time.Second)
   888  		}
   889  		return nil
   890  	})
   891  
   892  	client, closeClient := newClient()
   893  	defer closeClient()
   894  
   895  	stream, err := client.SendMessage(ctx)
   896  	require.NoError(t, err)
   897  
   898  	err = stream.Send(&p2p.MessagePacket{
   899  		Meta: &p2p.StreamMeta{
   900  			SenderId:   "test-client-1",
   901  			ReceiverId: "test-server-1",
   902  			Epoch:      0,
   903  		},
   904  	})
   905  	require.NoError(t, err)
   906  
   907  	err = stream.Send(&p2p.MessagePacket{
   908  		Entries: []*p2p.MessageEntry{
   909  			{
   910  				Topic:    "blocking-handler",
   911  				Content:  []byte(`{"index":1}`), // just a dummy message
   912  				Sequence: 1,
   913  			},
   914  			{
   915  				Topic:    "blocking-handler",
   916  				Content:  []byte(`{"index":2}`), // just a dummy message
   917  				Sequence: 2,
   918  			},
   919  			{
   920  				// This message will be lost
   921  				Topic:    "blocking-handler",
   922  				Content:  []byte(`{"index":3}`), // just a dummy message
   923  				Sequence: 3,
   924  			},
   925  		},
   926  	})
   927  	require.NoError(t, err)
   928  	require.Eventually(t, func() bool {
   929  		return atomic.LoadInt32(&called) == 2
   930  	}, time.Millisecond*500, time.Millisecond*10)
   931  
   932  	err = server.SyncRemoveHandler(ctx, "blocking-handler")
   933  
   934  	require.NoError(t, err)
   935  
   936  	// re-registers the handler
   937  	errCh := mustAddHandler(ctx, t, server, "blocking-handler", &testTopicContent{}, func(_ string, msg interface{}) error {
   938  		require.Fail(t, "should not have been called", "value: %v", msg)
   939  		return nil
   940  	})
   941  
   942  	err = stream.Send(&p2p.MessagePacket{
   943  		Entries: []*p2p.MessageEntry{
   944  			{
   945  				// This message should never have reached the handler because the
   946  				// message with Sequence = 2 has been lost.
   947  				Topic:    "blocking-handler",
   948  				Content:  []byte(`{"index":4}`), // just a dummy message
   949  				Sequence: 4,
   950  			},
   951  		},
   952  	})
   953  	require.NoError(t, err)
   954  
   955  	select {
   956  	case <-ctx.Done():
   957  		require.Fail(t, "test timed out: TestServerDataLossAfterUnregisterHandle")
   958  	case err = <-errCh:
   959  	}
   960  	require.Error(t, err)
   961  	require.Regexp(t, "ErrPeerMessageDataLost", err.Error())
   962  
   963  	cancel()
   964  	wg.Wait()
   965  }
   966  
   967  func TestServerDeregisterPeer(t *testing.T) {
   968  	ctx, cancel := context.WithTimeout(context.TODO(), defaultTimeout)
   969  	defer cancel()
   970  
   971  	server, newClient, closer := newServerForTesting(t, "test-server-2")
   972  	defer closer()
   973  
   974  	// Avoids server returning error due to congested topic.
   975  	server.config.MaxPendingMessageCountPerTopic = math.MaxInt64
   976  
   977  	var wg sync.WaitGroup
   978  	wg.Add(1)
   979  	go func() {
   980  		defer wg.Done()
   981  		err := server.Run(ctx, nil)
   982  		require.Regexp(t, ".*context canceled.*", err)
   983  	}()
   984  
   985  	var sendGroup sync.WaitGroup
   986  	sendGroup.Add(1)
   987  	client, closeClient := newClient()
   988  	go func() {
   989  		defer sendGroup.Done()
   990  		stream, err := client.SendMessage(ctx)
   991  		require.NoError(t, err)
   992  
   993  		err = stream.Send(&p2p.MessagePacket{
   994  			Meta: &p2p.StreamMeta{
   995  				SenderId:   "test-client-1",
   996  				ReceiverId: "test-server-2",
   997  				Epoch:      0,
   998  			},
   999  		})
  1000  		require.NoError(t, err)
  1001  	}()
  1002  	sendGroup.Wait()
  1003  	time.Sleep(1 * time.Second)
  1004  
  1005  	server.peerLock.Lock()
  1006  	require.Equal(t, 1, len(server.peers))
  1007  	server.peerLock.Unlock()
  1008  	require.Nil(t, server.ScheduleDeregisterPeerTask(ctx, "test-client-1"))
  1009  	time.Sleep(1 * time.Second)
  1010  	server.peerLock.Lock()
  1011  	require.Equal(t, 0, len(server.peers))
  1012  	server.peerLock.Unlock()
  1013  
  1014  	closeClient()
  1015  	closer()
  1016  	cancel()
  1017  	wg.Wait()
  1018  }