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

     1  // Copyright 2022 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 internal
    15  
    16  import (
    17  	"context"
    18  	"sync"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/stretchr/testify/require"
    23  	"go.uber.org/atomic"
    24  )
    25  
    26  const (
    27  	defaultSendChanCap       = 8
    28  	numProducers             = 8
    29  	numMsgPerProducer        = 1000
    30  	numMsgPerProducerForSync = 100
    31  )
    32  
    33  func TestSendChanBasics(t *testing.T) {
    34  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    35  	defer cancel()
    36  
    37  	seq := atomic.NewInt64(0)
    38  	c := NewSendChan(defaultSendChanCap)
    39  
    40  	var wg sync.WaitGroup
    41  
    42  	// Runs the producers
    43  	for i := 0; i < numProducers; i++ {
    44  		wg.Add(1)
    45  		go func() {
    46  			defer wg.Done()
    47  			lastSeq := int64(0)
    48  			for j := 0; j < numMsgPerProducer; {
    49  				ok, seq := c.SendAsync(
    50  					"test-topic",
    51  					[]byte("test-value"),
    52  					func() int64 {
    53  						return seq.Inc()
    54  					})
    55  				if !ok {
    56  					continue
    57  				}
    58  				j++
    59  				require.Greater(t, seq, lastSeq)
    60  				lastSeq = seq
    61  			}
    62  		}()
    63  	}
    64  
    65  	// Runs the consumer
    66  	wg.Add(1)
    67  	go func() {
    68  		defer wg.Done()
    69  		ticker := time.NewTicker(time.Millisecond * 10)
    70  
    71  		recvCount := 0
    72  		lastSeq := int64(0)
    73  		for {
    74  			msg, ok, err := c.Receive(ctx, ticker.C)
    75  			require.NoError(t, err)
    76  			if !ok {
    77  				continue
    78  			}
    79  			recvCount++
    80  			require.Equal(t, lastSeq+1, msg.Sequence)
    81  			lastSeq = msg.Sequence
    82  			if recvCount == numProducers*numMsgPerProducer {
    83  				return
    84  			}
    85  		}
    86  	}()
    87  
    88  	wg.Wait()
    89  	cancel()
    90  }
    91  
    92  func TestSendChanSendSync(t *testing.T) {
    93  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    94  	defer cancel()
    95  
    96  	dummyCloseCh := make(chan struct{})
    97  
    98  	seq := atomic.NewInt64(0)
    99  	c := NewSendChan(defaultSendChanCap)
   100  
   101  	var wg sync.WaitGroup
   102  
   103  	// Runs the producers
   104  	for i := 0; i < numProducers; i++ {
   105  		wg.Add(1)
   106  		go func() {
   107  			defer wg.Done()
   108  			lastSeq := int64(0)
   109  			for j := 0; j < numMsgPerProducerForSync; j++ {
   110  				seq, err := c.SendSync(
   111  					ctx,
   112  					"test-topic",
   113  					[]byte("test-value"),
   114  					dummyCloseCh,
   115  					func() int64 {
   116  						return seq.Inc()
   117  					})
   118  				require.NoError(t, err)
   119  				require.Greater(t, seq, lastSeq)
   120  				lastSeq = seq
   121  			}
   122  		}()
   123  	}
   124  
   125  	// Runs the consumer
   126  	wg.Add(1)
   127  	go func() {
   128  		defer wg.Done()
   129  		ticker := time.NewTicker(time.Millisecond * 10)
   130  
   131  		recvCount := 0
   132  		lastSeq := int64(0)
   133  		for {
   134  			msg, ok, err := c.Receive(ctx, ticker.C)
   135  			require.NoError(t, err)
   136  			if !ok {
   137  				continue
   138  			}
   139  			recvCount++
   140  			require.Equal(t, lastSeq+1, msg.Sequence)
   141  			lastSeq = msg.Sequence
   142  			if recvCount == numProducers*numMsgPerProducerForSync {
   143  				return
   144  			}
   145  		}
   146  	}()
   147  
   148  	wg.Wait()
   149  	cancel()
   150  }
   151  
   152  func BenchmarkSendChanSendAsyncSPSC(b *testing.B) {
   153  	var wg sync.WaitGroup
   154  
   155  	seq := atomic.NewInt64(0)
   156  	c := NewSendChan(defaultSendChanCap)
   157  
   158  	wg.Add(1)
   159  	go func() {
   160  		defer wg.Done()
   161  		for j := 0; j < b.N; {
   162  			ok, _ := c.SendAsync("test-topic", []byte("test-value"), func() int64 {
   163  				return seq.Inc()
   164  			})
   165  			if !ok {
   166  				continue
   167  			}
   168  			j++
   169  		}
   170  	}()
   171  
   172  	recvCount := 0
   173  	dummyTicker := make(chan time.Time)
   174  	for {
   175  		_, ok, err := c.Receive(context.Background(), dummyTicker)
   176  		if err != nil {
   177  			b.Fail()
   178  		}
   179  		if !ok {
   180  			continue
   181  		}
   182  		recvCount++
   183  		if recvCount == b.N {
   184  			break
   185  		}
   186  	}
   187  
   188  	wg.Wait()
   189  }
   190  
   191  func BenchmarkSendChanSendSyncSPSC(b *testing.B) {
   192  	var wg sync.WaitGroup
   193  
   194  	dummyCloseCh := make(chan struct{})
   195  
   196  	seq := atomic.NewInt64(0)
   197  	c := NewSendChan(defaultSendChanCap)
   198  
   199  	wg.Add(1)
   200  	go func() {
   201  		defer wg.Done()
   202  		for j := 0; j < b.N; j++ {
   203  			_, _ = c.SendSync(
   204  				context.TODO(),
   205  				"test-topic",
   206  				[]byte("test-value"),
   207  				dummyCloseCh, func() int64 {
   208  					return seq.Inc()
   209  				})
   210  		}
   211  	}()
   212  
   213  	recvCount := 0
   214  	dummyTicker := make(chan time.Time)
   215  	for {
   216  		_, ok, err := c.Receive(context.Background(), dummyTicker)
   217  		if err != nil {
   218  			b.Fail()
   219  		}
   220  		if !ok {
   221  			continue
   222  		}
   223  		recvCount++
   224  		if recvCount == b.N {
   225  			break
   226  		}
   227  	}
   228  
   229  	wg.Wait()
   230  }
   231  
   232  func BenchmarkSendChanSendAsyncMPSC8(b *testing.B) {
   233  	var wg sync.WaitGroup
   234  
   235  	seq := atomic.NewInt64(0)
   236  	c := NewSendChan(defaultSendChanCap)
   237  
   238  	for i := 0; i < 8; i++ {
   239  		wg.Add(1)
   240  		go func() {
   241  			defer wg.Done()
   242  			for j := 0; j < b.N; {
   243  				ok, _ := c.SendAsync("test-topic", []byte("test-value"), func() int64 {
   244  					return seq.Inc()
   245  				})
   246  				if !ok {
   247  					continue
   248  				}
   249  				j++
   250  			}
   251  		}()
   252  	}
   253  
   254  	recvCount := 0
   255  	dummyTicker := make(chan time.Time)
   256  	for {
   257  		_, ok, err := c.Receive(context.Background(), dummyTicker)
   258  		if err != nil {
   259  			b.Fail()
   260  		}
   261  		if !ok {
   262  			continue
   263  		}
   264  		recvCount++
   265  		if recvCount == b.N*8 {
   266  			break
   267  		}
   268  	}
   269  
   270  	wg.Wait()
   271  }
   272  
   273  func BenchmarkSendChanSendSyncMPSC8(b *testing.B) {
   274  	var wg sync.WaitGroup
   275  
   276  	seq := atomic.NewInt64(0)
   277  	c := NewSendChan(defaultSendChanCap)
   278  	dummyCloseCh := make(chan struct{})
   279  
   280  	for i := 0; i < 8; i++ {
   281  		wg.Add(1)
   282  		go func() {
   283  			defer wg.Done()
   284  			for j := 0; j < b.N; j++ {
   285  				_, _ = c.SendSync(
   286  					context.TODO(),
   287  					"test-topic",
   288  					[]byte("test-value"),
   289  					dummyCloseCh, func() int64 {
   290  						return seq.Inc()
   291  					})
   292  			}
   293  		}()
   294  	}
   295  
   296  	recvCount := 0
   297  	dummyTicker := make(chan time.Time)
   298  	for {
   299  		_, ok, err := c.Receive(context.Background(), dummyTicker)
   300  		if err != nil {
   301  			b.Fail()
   302  		}
   303  		if !ok {
   304  			continue
   305  		}
   306  		recvCount++
   307  		if recvCount == b.N*8 {
   308  			break
   309  		}
   310  	}
   311  
   312  	wg.Wait()
   313  }