github.com/matrixorigin/matrixone@v0.7.0/pkg/common/morpc/backend_test.go (about)

     1  // Copyright 2021 - 2022 Matrix Origin
     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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package morpc
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"os"
    21  	"runtime/debug"
    22  	"sync"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/fagongzi/goetty/v2"
    27  	"github.com/fagongzi/goetty/v2/buf"
    28  	"github.com/lni/goutils/leaktest"
    29  	"github.com/matrixorigin/matrixone/pkg/logutil"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  	"go.uber.org/zap"
    33  )
    34  
    35  var (
    36  	testProxyAddr = "unix:///tmp/proxy.sock"
    37  	testAddr      = "unix:///tmp/goetty.sock"
    38  	testUnixFile  = "/tmp/goetty.sock"
    39  )
    40  
    41  func TestSend(t *testing.T) {
    42  	testBackendSend(t,
    43  		func(conn goetty.IOSession, msg interface{}, _ uint64) error {
    44  			return conn.Write(msg, goetty.WriteOptions{Flush: true})
    45  		},
    46  		func(b *remoteBackend) {
    47  			ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    48  			defer cancel()
    49  			req := newTestMessage(1)
    50  			f, err := b.Send(ctx, req)
    51  			assert.NoError(t, err)
    52  			defer f.Close()
    53  
    54  			resp, err := f.Get()
    55  			assert.NoError(t, err)
    56  			assert.Equal(t, req, resp)
    57  		},
    58  	)
    59  }
    60  
    61  func TestSendWithPayloadCannotTimeout(t *testing.T) {
    62  	testBackendSend(t,
    63  		func(conn goetty.IOSession, msg interface{}, _ uint64) error {
    64  			return conn.Write(msg, goetty.WriteOptions{Flush: true})
    65  		},
    66  		func(b *remoteBackend) {
    67  			b.conn.RawConn().SetWriteDeadline(time.Now().Add(time.Millisecond))
    68  			time.Sleep(time.Millisecond)
    69  			ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
    70  			defer cancel()
    71  			req := newTestMessage(1)
    72  			req.payload = []byte("hello")
    73  			f, err := b.Send(ctx, req)
    74  			assert.NoError(t, err)
    75  			defer f.Close()
    76  
    77  			resp, err := f.Get()
    78  			assert.NoError(t, err)
    79  			assert.Equal(t, req, resp)
    80  		},
    81  	)
    82  }
    83  
    84  func TestSendWithPayloadCannotBlockIfFutureRemoved(t *testing.T) {
    85  	var wg sync.WaitGroup
    86  	wg.Add(1)
    87  	testBackendSend(t,
    88  		func(conn goetty.IOSession, msg interface{}, _ uint64) error {
    89  			wg.Wait()
    90  			return conn.Write(msg, goetty.WriteOptions{Flush: true})
    91  		},
    92  		func(b *remoteBackend) {
    93  			ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100)
    94  			defer cancel()
    95  			req := newTestMessage(1)
    96  			req.payload = []byte("hello")
    97  			f, err := b.Send(ctx, req)
    98  			require.NoError(t, err)
    99  			id := f.getSendMessageID()
   100  			// keep future in the futures map
   101  			f.ref()
   102  			defer f.unRef()
   103  			f.Close()
   104  			b.mu.RLock()
   105  			_, ok := b.mu.futures[id]
   106  			assert.True(t, ok)
   107  			b.mu.RUnlock()
   108  			wg.Done()
   109  			time.Sleep(time.Second)
   110  		},
   111  		WithBackendHasPayloadResponse())
   112  }
   113  
   114  func TestSendWithPayloadCannotBlockIfFutureClosed(t *testing.T) {
   115  	var wg sync.WaitGroup
   116  	wg.Add(1)
   117  	testBackendSend(t,
   118  		func(conn goetty.IOSession, msg interface{}, _ uint64) error {
   119  			wg.Wait()
   120  			return conn.Write(msg, goetty.WriteOptions{Flush: true})
   121  		},
   122  		func(b *remoteBackend) {
   123  			ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100)
   124  			defer cancel()
   125  			req := newTestMessage(1)
   126  			req.payload = []byte("hello")
   127  			f, err := b.Send(ctx, req)
   128  			require.NoError(t, err)
   129  			id := f.getSendMessageID()
   130  			f.mu.Lock()
   131  			f.mu.closed = true
   132  			f.releaseFunc = nil // make it nil to keep this future in b.mu.features
   133  			f.mu.Unlock()
   134  			b.mu.RLock()
   135  			_, ok := b.mu.futures[id]
   136  			b.mu.RUnlock()
   137  			assert.True(t, ok)
   138  			wg.Done()
   139  			time.Sleep(time.Second)
   140  		},
   141  		WithBackendHasPayloadResponse())
   142  }
   143  
   144  func TestCloseWhileContinueSending(t *testing.T) {
   145  	defer leaktest.AfterTest(t)()
   146  	testBackendSend(t,
   147  		func(conn goetty.IOSession, msg interface{}, seq uint64) error {
   148  			return conn.Write(msg, goetty.WriteOptions{Flush: true})
   149  		},
   150  		func(b *remoteBackend) {
   151  			c := make(chan struct{})
   152  			stopC := make(chan struct{})
   153  			var wg sync.WaitGroup
   154  			wg.Add(1)
   155  			go func() {
   156  				defer wg.Done()
   157  				sendFunc := func() {
   158  					ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   159  					defer cancel()
   160  					req := newTestMessage(1)
   161  					f, err := b.Send(ctx, req)
   162  					if err != nil {
   163  						return
   164  					}
   165  					defer f.Close()
   166  
   167  					resp, err := f.Get()
   168  					if err == nil {
   169  						assert.Equal(t, req, resp)
   170  					}
   171  					select {
   172  					case c <- struct{}{}:
   173  					default:
   174  					}
   175  				}
   176  
   177  				for {
   178  					select {
   179  					case <-stopC:
   180  						return
   181  					default:
   182  						sendFunc()
   183  					}
   184  				}
   185  			}()
   186  			<-c
   187  			b.Close()
   188  			close(stopC)
   189  			wg.Wait()
   190  		},
   191  	)
   192  }
   193  
   194  func TestSendWithAlreadyContextDone(t *testing.T) {
   195  	ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
   196  
   197  	testBackendSend(t,
   198  		func(conn goetty.IOSession, msg interface{}, seq uint64) error {
   199  			return conn.Write(msg, goetty.WriteOptions{Flush: true})
   200  		},
   201  		func(b *remoteBackend) {
   202  
   203  			req := newTestMessage(1)
   204  			f, err := b.Send(ctx, req)
   205  			assert.NoError(t, err)
   206  			defer f.Close()
   207  			resp, err := f.Get()
   208  			assert.Error(t, err)
   209  			assert.Nil(t, resp)
   210  		},
   211  		WithBackendFilter(func(Message, string) bool {
   212  			cancel()
   213  			return true
   214  		}))
   215  }
   216  
   217  func TestSendWithTimeout(t *testing.T) {
   218  	testBackendSend(t,
   219  		func(conn goetty.IOSession, msg interface{}, seq uint64) error {
   220  			return nil
   221  		},
   222  		func(b *remoteBackend) {
   223  			ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*200)
   224  			defer cancel()
   225  			req := &testMessage{id: 1}
   226  			f, err := b.Send(ctx, req)
   227  			assert.NoError(t, err)
   228  			defer f.Close()
   229  
   230  			resp, err := f.Get()
   231  			assert.Error(t, err)
   232  			assert.Nil(t, resp)
   233  			assert.Equal(t, err, ctx.Err())
   234  		},
   235  	)
   236  }
   237  
   238  func TestSendWithCannotConnect(t *testing.T) {
   239  	var rb *remoteBackend
   240  	testBackendSend(t,
   241  		func(conn goetty.IOSession, msg interface{}, seq uint64) error {
   242  			return conn.Write(msg, goetty.WriteOptions{Flush: true})
   243  		},
   244  		func(b *remoteBackend) {
   245  			rb = b
   246  			ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
   247  			defer cancel()
   248  			req := &testMessage{id: 1}
   249  			f, err := b.Send(ctx, req)
   250  			assert.NoError(t, err)
   251  			defer f.Close()
   252  
   253  			resp, err := f.Get()
   254  			assert.Error(t, err)
   255  			assert.Nil(t, resp)
   256  		},
   257  		WithBackendFilter(func(Message, string) bool {
   258  			assert.NoError(t, rb.conn.Disconnect())
   259  			rb.remote = ""
   260  			return true
   261  		}),
   262  		WithBackendConnectTimeout(time.Millisecond*200),
   263  	)
   264  }
   265  
   266  func TestFutureGetCannotBlockIfCloseBackend(t *testing.T) {
   267  	testBackendSend(t,
   268  		func(conn goetty.IOSession, msg interface{}, _ uint64) error {
   269  			return conn.Close()
   270  		},
   271  		func(b *remoteBackend) {
   272  			ctx, cancel := context.WithTimeout(context.Background(), time.Second*100)
   273  			defer cancel()
   274  
   275  			n := 2
   276  			futures := make([]*Future, 0, n)
   277  			for i := 0; i < n; i++ {
   278  				req := newTestMessage(1)
   279  				f, err := b.Send(ctx, req)
   280  				assert.NoError(t, err)
   281  				futures = append(futures, f)
   282  			}
   283  			b.Close()
   284  			for _, f := range futures {
   285  				_, err := f.Get()
   286  				assert.Error(t, err)
   287  			}
   288  		},
   289  		WithBackendBatchSendSize(1),
   290  		WithBackendFilter(func(m Message, s string) bool {
   291  			time.Sleep(time.Millisecond * 100)
   292  			return false
   293  		}),
   294  	)
   295  }
   296  
   297  func TestStream(t *testing.T) {
   298  	testBackendSend(t,
   299  		func(conn goetty.IOSession, msg interface{}, seq uint64) error {
   300  			return conn.Write(msg, goetty.WriteOptions{Flush: true})
   301  		},
   302  		func(b *remoteBackend) {
   303  			st, err := b.NewStream(false)
   304  			assert.NoError(t, err)
   305  			defer func() {
   306  				assert.NoError(t, st.Close())
   307  				b.mu.RLock()
   308  				assert.Equal(t, 0, len(b.mu.futures))
   309  				b.mu.RUnlock()
   310  			}()
   311  
   312  			ctx, cancel := context.WithTimeout(context.TODO(), time.Second*10)
   313  			defer cancel()
   314  
   315  			n := 1
   316  			for i := 0; i < n; i++ {
   317  				req := &testMessage{id: st.ID()}
   318  				assert.NoError(t, st.Send(ctx, req))
   319  			}
   320  
   321  			rc, err := st.Receive()
   322  			assert.NoError(t, err)
   323  			for i := 0; i < n; i++ {
   324  				v, ok := <-rc
   325  				assert.True(t, ok)
   326  				assert.Equal(t, &testMessage{id: st.ID()}, v)
   327  			}
   328  		},
   329  	)
   330  }
   331  
   332  func TestStreamSendWillPanicIfDeadlineNotSet(t *testing.T) {
   333  	testBackendSend(t,
   334  		func(conn goetty.IOSession, msg interface{}, seq uint64) error {
   335  			return conn.Write(msg, goetty.WriteOptions{Flush: true})
   336  		},
   337  		func(b *remoteBackend) {
   338  			st, err := b.NewStream(false)
   339  			assert.NoError(t, err)
   340  			defer func() {
   341  				assert.NoError(t, st.Close())
   342  				b.mu.RLock()
   343  				assert.Equal(t, 0, len(b.mu.futures))
   344  				b.mu.RUnlock()
   345  			}()
   346  
   347  			defer func() {
   348  				if err := recover(); err == nil {
   349  					assert.Fail(t, "must panic")
   350  				}
   351  			}()
   352  
   353  			req := &testMessage{id: st.ID()}
   354  			assert.NoError(t, st.Send(context.TODO(), req))
   355  		},
   356  	)
   357  }
   358  
   359  func TestStreamClosedByConnReset(t *testing.T) {
   360  	testBackendSend(t,
   361  		func(conn goetty.IOSession, msg interface{}, seq uint64) error {
   362  			return conn.Disconnect()
   363  		},
   364  		func(b *remoteBackend) {
   365  			ctx, cancel := context.WithTimeout(context.TODO(), time.Second*10)
   366  			defer cancel()
   367  
   368  			st, err := b.NewStream(false)
   369  			assert.NoError(t, err)
   370  			defer func() {
   371  				assert.NoError(t, st.Close())
   372  			}()
   373  			c, err := st.Receive()
   374  			assert.NoError(t, err)
   375  			assert.NoError(t, st.Send(ctx, &testMessage{id: st.ID()}))
   376  
   377  			v, ok := <-c
   378  			assert.True(t, ok)
   379  			assert.Nil(t, v)
   380  		},
   381  	)
   382  }
   383  
   384  func TestStreamClosedBySequenceNotMatch(t *testing.T) {
   385  	testBackendSend(t,
   386  		func(conn goetty.IOSession, msg interface{}, seq uint64) error {
   387  			resp := msg.(RPCMessage)
   388  			resp.streamSequence = 2
   389  			return conn.Write(resp, goetty.WriteOptions{Flush: true})
   390  		},
   391  		func(b *remoteBackend) {
   392  			ctx, cancel := context.WithTimeout(context.TODO(), time.Second*10)
   393  			defer cancel()
   394  
   395  			st, err := b.NewStream(false)
   396  			assert.NoError(t, err)
   397  			defer func() {
   398  				assert.NoError(t, st.Close())
   399  			}()
   400  			c, err := st.Receive()
   401  			assert.NoError(t, err)
   402  			assert.NoError(t, st.Send(ctx, &testMessage{id: st.ID()}))
   403  
   404  			v, ok := <-c
   405  			assert.True(t, ok)
   406  			assert.Nil(t, v)
   407  		},
   408  	)
   409  }
   410  
   411  func TestBusy(t *testing.T) {
   412  	n := 0
   413  	c := make(chan struct{})
   414  	testBackendSend(t,
   415  		func(conn goetty.IOSession, msg interface{}, seq uint64) error {
   416  			return nil
   417  		},
   418  		func(b *remoteBackend) {
   419  			assert.False(t, b.Busy())
   420  
   421  			ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*200)
   422  			defer cancel()
   423  			f1, err := b.Send(ctx, newTestMessage(1))
   424  			assert.NoError(t, err)
   425  			defer f1.Close()
   426  
   427  			f2, err := b.Send(ctx, newTestMessage(2))
   428  			assert.NoError(t, err)
   429  			defer f2.Close()
   430  
   431  			assert.True(t, b.Busy())
   432  			c <- struct{}{}
   433  		},
   434  		WithBackendFilter(func(Message, string) bool {
   435  			if n == 0 {
   436  				<-c
   437  				n++
   438  			}
   439  			return false
   440  		}),
   441  		WithBackendBatchSendSize(1),
   442  		WithBackendBufferSize(10),
   443  		WithBackendBusyBufferSize(1))
   444  }
   445  
   446  func TestDoneWithClosedStreamCannotPanic(t *testing.T) {
   447  	ctx, cancel := context.WithTimeout(context.TODO(), time.Second*10)
   448  	defer cancel()
   449  
   450  	c := make(chan Message, 1)
   451  	s := newStream(c,
   452  		func() *Future { return newFuture(nil) },
   453  		func(m *Future) error {
   454  			m.messageSended(nil)
   455  			return nil
   456  		},
   457  		func(s *stream) {},
   458  		func() {})
   459  	s.init(1, false)
   460  	assert.NoError(t, s.Send(ctx, &testMessage{id: s.ID()}))
   461  	assert.NoError(t, s.Close())
   462  	assert.Nil(t, <-c)
   463  
   464  	s.done(RPCMessage{})
   465  }
   466  
   467  func TestGCStream(t *testing.T) {
   468  	c := make(chan Message, 1)
   469  	s := newStream(c,
   470  		func() *Future { return newFuture(nil) },
   471  		func(m *Future) error {
   472  			return nil
   473  		},
   474  		func(s *stream) {},
   475  		func() {})
   476  	s.init(1, false)
   477  	s = nil
   478  	debug.FreeOSMemory()
   479  	_, ok := <-c
   480  	assert.False(t, ok)
   481  }
   482  
   483  func TestLastActiveWithNew(t *testing.T) {
   484  	testBackendSend(t,
   485  		func(conn goetty.IOSession, msg interface{}, seq uint64) error {
   486  			return nil
   487  		},
   488  		func(b *remoteBackend) {
   489  			assert.NotEqual(t, time.Time{}, b.LastActiveTime())
   490  		},
   491  	)
   492  }
   493  
   494  func TestLastActiveWithSend(t *testing.T) {
   495  	c := make(chan struct{})
   496  	testBackendSend(t,
   497  		func(conn goetty.IOSession, msg interface{}, _ uint64) error {
   498  			<-c
   499  			return conn.Write(msg, goetty.WriteOptions{Flush: true})
   500  		},
   501  		func(b *remoteBackend) {
   502  			t1 := b.LastActiveTime()
   503  
   504  			ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
   505  			defer cancel()
   506  			req := newTestMessage(1)
   507  			f, err := b.Send(ctx, req)
   508  			assert.NoError(t, err)
   509  			defer f.Close()
   510  
   511  			t2 := b.LastActiveTime()
   512  			assert.NotEqual(t, t1, t2)
   513  			assert.True(t, t2.After(t1))
   514  			c <- struct{}{}
   515  
   516  			resp, err := f.Get()
   517  			assert.NoError(t, err)
   518  			assert.Equal(t, req, resp)
   519  
   520  			t3 := b.LastActiveTime()
   521  			assert.NotEqual(t, t2, t3)
   522  			assert.True(t, t3.After(t2))
   523  
   524  		},
   525  	)
   526  }
   527  
   528  func TestLastActiveWithStream(t *testing.T) {
   529  	testBackendSend(t,
   530  		func(conn goetty.IOSession, msg interface{}, seq uint64) error {
   531  			return conn.Write(msg, goetty.WriteOptions{Flush: true})
   532  		},
   533  		func(b *remoteBackend) {
   534  			ctx, cancel := context.WithTimeout(context.TODO(), time.Second*10)
   535  			defer cancel()
   536  
   537  			t1 := b.LastActiveTime()
   538  
   539  			st, err := b.NewStream(false)
   540  			assert.NoError(t, err)
   541  			defer func() {
   542  				assert.NoError(t, st.Close())
   543  			}()
   544  
   545  			n := 1
   546  			for i := 0; i < n; i++ {
   547  				req := &testMessage{id: st.ID()}
   548  				assert.NoError(t, st.Send(ctx, req))
   549  				t2 := b.LastActiveTime()
   550  				assert.NotEqual(t, t1, t2)
   551  				assert.True(t, t2.After(t1))
   552  			}
   553  		},
   554  	)
   555  }
   556  
   557  func TestBackendConnectTimeout(t *testing.T) {
   558  	rb, err := NewRemoteBackend(testAddr, newTestCodec(),
   559  		WithBackendConnectTimeout(time.Millisecond*200),
   560  	)
   561  	assert.Error(t, err)
   562  	assert.Nil(t, rb)
   563  }
   564  
   565  func TestInactiveAfterCannotConnect(t *testing.T) {
   566  	app := newTestApp(t, func(conn goetty.IOSession, msg interface{}, _ uint64) error {
   567  		return conn.Write(msg, goetty.WriteOptions{Flush: true})
   568  	})
   569  	assert.NoError(t, app.Start())
   570  
   571  	testBackendSendWithoutServer(t,
   572  		testAddr,
   573  		func(b *remoteBackend) {
   574  			ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
   575  			defer cancel()
   576  			req := newTestMessage(1)
   577  			f, err := b.Send(ctx, req)
   578  			assert.NoError(t, err)
   579  			defer f.Close()
   580  
   581  			resp, err := f.Get()
   582  			assert.NoError(t, err)
   583  			assert.Equal(t, req, resp)
   584  
   585  			assert.NoError(t, app.Stop())
   586  			var v time.Time
   587  			for {
   588  				if b.LastActiveTime() == v {
   589  					break
   590  				}
   591  				time.Sleep(time.Millisecond * 100)
   592  			}
   593  		},
   594  		WithBackendConnectTimeout(time.Millisecond*100))
   595  }
   596  
   597  func TestTCPProxyExample(t *testing.T) {
   598  	assert.NoError(t, os.RemoveAll(testProxyAddr[7:]))
   599  	p := goetty.NewProxy(testProxyAddr, nil)
   600  	assert.NoError(t, p.Start())
   601  	defer func() {
   602  		assert.NoError(t, p.Stop())
   603  	}()
   604  	p.AddUpStream(testAddr, time.Second*10)
   605  
   606  	testBackendSendWithAddr(t,
   607  		testProxyAddr,
   608  		func(conn goetty.IOSession, msg interface{}, _ uint64) error {
   609  			return conn.Write(msg, goetty.WriteOptions{Flush: true})
   610  		},
   611  		func(b *remoteBackend) {
   612  			ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
   613  			defer cancel()
   614  			req := newTestMessage(1)
   615  			f, err := b.Send(ctx, req)
   616  			assert.NoError(t, err)
   617  			defer f.Close()
   618  
   619  			resp, err := f.Get()
   620  			assert.NoError(t, err)
   621  			assert.Equal(t, req, resp)
   622  		},
   623  	)
   624  }
   625  
   626  func TestLockedStream(t *testing.T) {
   627  	testBackendSend(t,
   628  		func(conn goetty.IOSession, msg interface{}, seq uint64) error {
   629  			return conn.Write(msg, goetty.WriteOptions{Flush: true})
   630  		},
   631  		func(b *remoteBackend) {
   632  			assert.False(t, b.Locked())
   633  			b.Lock()
   634  			st, err := b.NewStream(true)
   635  			assert.NoError(t, err)
   636  			assert.True(t, b.Locked())
   637  			assert.NoError(t, st.Close())
   638  			assert.False(t, b.Locked())
   639  		},
   640  	)
   641  }
   642  
   643  func TestIssue7678(t *testing.T) {
   644  	s := &stream{lastReceivedSequence: 10}
   645  	s.init(0, false)
   646  	assert.Equal(t, uint32(0), s.lastReceivedSequence)
   647  }
   648  
   649  func testBackendSend(t *testing.T,
   650  	handleFunc func(goetty.IOSession, interface{}, uint64) error,
   651  	testFunc func(b *remoteBackend),
   652  	options ...BackendOption) {
   653  	testBackendSendWithAddr(t, testAddr, handleFunc, testFunc, options...)
   654  }
   655  
   656  func testBackendSendWithAddr(t *testing.T, addr string,
   657  	handleFunc func(goetty.IOSession, interface{}, uint64) error,
   658  	testFunc func(b *remoteBackend),
   659  	options ...BackendOption) {
   660  	app := newTestApp(t, handleFunc)
   661  	assert.NoError(t, app.Start())
   662  	defer func() {
   663  		assert.NoError(t, app.Stop())
   664  	}()
   665  
   666  	testBackendSendWithoutServer(t, addr, testFunc, options...)
   667  }
   668  
   669  func testBackendSendWithoutServer(t *testing.T, addr string,
   670  	testFunc func(b *remoteBackend),
   671  	options ...BackendOption) {
   672  
   673  	options = append(options,
   674  		WithBackendBufferSize(1),
   675  		WithBackendLogger(logutil.GetPanicLoggerWithLevel(zap.DebugLevel).With(zap.String("testcase", t.Name()))))
   676  	rb, err := NewRemoteBackend(addr, newTestCodec(), options...)
   677  	assert.NoError(t, err)
   678  
   679  	b := rb.(*remoteBackend)
   680  	defer func() {
   681  		b.Close()
   682  		assert.False(t, b.conn.Connected())
   683  	}()
   684  	testFunc(b)
   685  }
   686  
   687  func newTestApp(t *testing.T,
   688  	handleFunc func(goetty.IOSession, interface{}, uint64) error,
   689  	opts ...goetty.AppOption) goetty.NetApplication {
   690  	assert.NoError(t, os.RemoveAll(testUnixFile))
   691  	codec := newTestCodec().(*messageCodec)
   692  	opts = append(opts, goetty.WithAppSessionOptions(goetty.WithSessionCodec(codec)))
   693  	app, err := goetty.NewApplication(testAddr, handleFunc, opts...)
   694  	assert.NoError(t, err)
   695  
   696  	return app
   697  }
   698  
   699  type testBackendFactory struct {
   700  	sync.RWMutex
   701  	id int
   702  }
   703  
   704  func newTestBackendFactory() *testBackendFactory {
   705  	return &testBackendFactory{}
   706  }
   707  
   708  func (bf *testBackendFactory) Create(backend string) (Backend, error) {
   709  	bf.Lock()
   710  	defer bf.Unlock()
   711  	b := &testBackend{id: bf.id}
   712  	b.activeTime = time.Now()
   713  	bf.id++
   714  	return b, nil
   715  }
   716  
   717  type testBackend struct {
   718  	sync.RWMutex
   719  	id         int
   720  	busy       bool
   721  	activeTime time.Time
   722  	closed     bool
   723  	locked     bool
   724  }
   725  
   726  func (b *testBackend) Send(ctx context.Context, request Message) (*Future, error) {
   727  	b.active()
   728  	f := newFuture(nil)
   729  	f.init(RPCMessage{Ctx: ctx, Message: request})
   730  	return f, nil
   731  }
   732  
   733  func (b *testBackend) SendInternal(ctx context.Context, request Message) (*Future, error) {
   734  	b.active()
   735  	f := newFuture(nil)
   736  	f.init(RPCMessage{Ctx: ctx, Message: request})
   737  	return f, nil
   738  }
   739  
   740  func (b *testBackend) NewStream(unlockAfterClose bool) (Stream, error) {
   741  	b.active()
   742  	st := newStream(make(chan Message, 1),
   743  		func() *Future { return newFuture(nil) },
   744  		func(m *Future) error {
   745  			m.messageSended(nil)
   746  			return nil
   747  		},
   748  		func(s *stream) {
   749  			if s.unlockAfterClose {
   750  				b.Unlock()
   751  			}
   752  		},
   753  		b.active)
   754  	st.init(1, false)
   755  	return st, nil
   756  }
   757  
   758  func (b *testBackend) Close() {
   759  	b.RWMutex.Lock()
   760  	defer b.RWMutex.Unlock()
   761  	b.closed = true
   762  }
   763  func (b *testBackend) Busy() bool { return b.busy }
   764  func (b *testBackend) LastActiveTime() time.Time {
   765  	b.RLock()
   766  	defer b.RUnlock()
   767  	return b.activeTime
   768  }
   769  
   770  func (b *testBackend) Lock() {
   771  	b.RWMutex.Lock()
   772  	defer b.RWMutex.Unlock()
   773  	if b.locked {
   774  		panic("backend is already locked")
   775  	}
   776  	b.locked = true
   777  }
   778  
   779  func (b *testBackend) Unlock() {
   780  	b.RWMutex.Lock()
   781  	defer b.RWMutex.Unlock()
   782  	if !b.locked {
   783  		panic("backend is not locked")
   784  	}
   785  	b.locked = false
   786  }
   787  
   788  func (b *testBackend) Locked() bool {
   789  	b.RLock()
   790  	defer b.RUnlock()
   791  	return b.locked
   792  }
   793  
   794  func (b *testBackend) active() {
   795  	b.RWMutex.Lock()
   796  	defer b.RWMutex.Unlock()
   797  	b.activeTime = time.Now()
   798  }
   799  
   800  type testMessage struct {
   801  	id      uint64
   802  	payload []byte
   803  }
   804  
   805  func newTestMessage(id uint64) *testMessage {
   806  	return &testMessage{id: id}
   807  }
   808  
   809  func (tm *testMessage) SetID(id uint64) {
   810  	tm.id = id
   811  }
   812  
   813  func (tm *testMessage) GetID() uint64 {
   814  	return tm.id
   815  }
   816  
   817  func (tm *testMessage) DebugString() string {
   818  	return fmt.Sprintf("%d:%d", tm.id, len(tm.payload))
   819  }
   820  
   821  func (tm *testMessage) Size() int {
   822  	return 8 + len(tm.payload)
   823  }
   824  
   825  func (tm *testMessage) MarshalTo(data []byte) (int, error) {
   826  	buf.Uint64ToBytesTo(tm.id, data)
   827  	return 8, nil
   828  }
   829  
   830  func (tm *testMessage) Unmarshal(data []byte) error {
   831  	tm.id = buf.Byte2Uint64(data)
   832  	return nil
   833  }
   834  
   835  func (tm *testMessage) GetPayloadField() []byte {
   836  	return tm.payload
   837  }
   838  
   839  func (tm *testMessage) SetPayloadField(data []byte) {
   840  	tm.payload = data
   841  }
   842  
   843  func newTestCodec(options ...CodecOption) Codec {
   844  	options = append(options,
   845  		WithCodecPayloadCopyBufferSize(1024))
   846  	return NewMessageCodec(func() Message { return messagePool.Get().(*testMessage) }, options...)
   847  }
   848  
   849  var (
   850  	messagePool = sync.Pool{
   851  		New: func() any {
   852  			return newTestMessage(0)
   853  		},
   854  	}
   855  )