github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/logtail/service/server_test.go (about)

     1  // Copyright 2021 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 service
    16  
    17  import (
    18  	"context"
    19  	"math"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/stretchr/testify/require"
    24  
    25  	"github.com/matrixorigin/matrixone/pkg/common/morpc"
    26  	"github.com/matrixorigin/matrixone/pkg/common/mpool"
    27  	"github.com/matrixorigin/matrixone/pkg/common/runtime"
    28  	"github.com/matrixorigin/matrixone/pkg/logutil"
    29  	"github.com/matrixorigin/matrixone/pkg/pb/api"
    30  	"github.com/matrixorigin/matrixone/pkg/pb/logtail"
    31  	"github.com/matrixorigin/matrixone/pkg/pb/metadata"
    32  	"github.com/matrixorigin/matrixone/pkg/pb/timestamp"
    33  	"github.com/matrixorigin/matrixone/pkg/tests"
    34  	"github.com/matrixorigin/matrixone/pkg/txn/clock"
    35  	taelogtail "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/logtail"
    36  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/options"
    37  )
    38  
    39  func TestService(t *testing.T) {
    40  	tableA := mockTable(1, 1, 1)
    41  	tableB := mockTable(2, 2, 2)
    42  	tableC := mockTable(3, 3, 3)
    43  
    44  	addrs, err := tests.GetAddressBatch("127.0.0.1", 1)
    45  	require.NoError(t, err)
    46  
    47  	address := addrs[0]
    48  	rt := mockRuntime()
    49  
    50  	/* ---- construct logtail server ---- */
    51  	stop := startLogtailServer(t, address, rt, tableA, tableB, tableC)
    52  	defer stop()
    53  
    54  	/* ---- construct logtail client ---- */
    55  	codec := morpc.NewMessageCodec(func() morpc.Message { return &LogtailResponseSegment{} },
    56  		morpc.WithCodecEnableChecksum(),
    57  		morpc.WithCodecMaxBodySize(16*mpool.KB),
    58  	)
    59  	bf := morpc.NewGoettyBasedBackendFactory(codec)
    60  	rpcClient, err := morpc.NewClient("", bf, morpc.WithClientMaxBackendPerHost(1))
    61  	require.NoError(t, err)
    62  
    63  	rpcStream, err := rpcClient.NewStream(address, false)
    64  	require.NoError(t, err)
    65  
    66  	logtailClient, err := NewLogtailClient(rpcStream, WithClientRequestPerSecond(100))
    67  	require.NoError(t, err)
    68  	defer func() {
    69  		err := logtailClient.Close()
    70  		require.NoError(t, err)
    71  	}()
    72  
    73  	/* ---- send subscription request via logtail client ---- */
    74  	{
    75  		t.Log("===> send subscription request via logtail client")
    76  		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    77  		defer cancel()
    78  		err := logtailClient.Subscribe(ctx, tableA)
    79  		require.NoError(t, err)
    80  	}
    81  
    82  	/* ---- wait subscription response via logtail client ---- */
    83  	{
    84  		t.Log("===> wait subscription response via logtail client")
    85  		resp, err := logtailClient.Receive(context.Background())
    86  		require.NoError(t, err)
    87  		require.NotNil(t, resp.GetSubscribeResponse())
    88  		require.Equal(t, tableA.String(), resp.GetSubscribeResponse().Logtail.Table.String())
    89  	}
    90  
    91  	/* ---- wait update response via logtail client ---- */
    92  	{
    93  		t.Log("===> wait update response via logtail client")
    94  		resp, err := logtailClient.Receive(context.Background())
    95  		require.NoError(t, err)
    96  		require.NotNil(t, resp.GetUpdateResponse())
    97  		require.Equal(t, 1, len(resp.GetUpdateResponse().LogtailList))
    98  		require.Equal(t, tableA.String(), resp.GetUpdateResponse().LogtailList[0].Table.String())
    99  	}
   100  
   101  	/* ---- send unsubscription request via logtail client ---- */
   102  	{
   103  		t.Log("===> send unsubscription request via logtail client")
   104  		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   105  		defer cancel()
   106  		err := logtailClient.Unsubscribe(ctx, tableA)
   107  		require.NoError(t, err)
   108  	}
   109  
   110  	/* ---- wait unsubscription response via logtail client ---- */
   111  	{
   112  		t.Log("===> wait unsubscription response via logtail client")
   113  		for {
   114  			resp, err := logtailClient.Receive(context.Background())
   115  			require.NoError(t, err)
   116  			if resp.GetUnsubscribeResponse() != nil {
   117  				require.Equal(t, tableA.String(), resp.GetUnsubscribeResponse().Table.String())
   118  				break
   119  			}
   120  		}
   121  	}
   122  }
   123  
   124  type logtailer struct {
   125  	tables []api.TableID
   126  }
   127  
   128  func mockLocktailer(tables ...api.TableID) taelogtail.Logtailer {
   129  	return &logtailer{
   130  		tables: tables,
   131  	}
   132  }
   133  
   134  func (m *logtailer) RangeLogtail(
   135  	ctx context.Context, from, to timestamp.Timestamp,
   136  ) ([]logtail.TableLogtail, []func(), error) {
   137  	tails := make([]logtail.TableLogtail, 0, len(m.tables))
   138  	for _, table := range m.tables {
   139  		tails = append(tails, mockLogtail(table, to))
   140  	}
   141  	return tails, nil, nil
   142  }
   143  
   144  func (m *logtailer) RegisterCallback(cb func(from, to timestamp.Timestamp, closeCB func(), tails ...logtail.TableLogtail) error) {
   145  }
   146  
   147  func (m *logtailer) TableLogtail(
   148  	ctx context.Context, table api.TableID, from, to timestamp.Timestamp,
   149  ) (logtail.TableLogtail, func(), error) {
   150  	for _, t := range m.tables {
   151  		if t.String() == table.String() {
   152  			return mockLogtail(table, to), nil, nil
   153  		}
   154  	}
   155  	return logtail.TableLogtail{CkpLocation: "checkpoint", Table: &table, Ts: &to}, nil, nil
   156  }
   157  
   158  func (m *logtailer) Now() (timestamp.Timestamp, timestamp.Timestamp) {
   159  	panic("not implemented")
   160  }
   161  
   162  func mockRuntime() runtime.Runtime {
   163  	return runtime.NewRuntime(
   164  		metadata.ServiceType_TN,
   165  		"uuid",
   166  		logutil.GetLogger(),
   167  		runtime.WithClock(
   168  			clock.NewHLCClock(
   169  				func() int64 { return time.Now().UTC().UnixNano() },
   170  				time.Duration(math.MaxInt64),
   171  			),
   172  		),
   173  	)
   174  }
   175  func mockTable(dbID, tbID, ptID uint64) api.TableID {
   176  	return api.TableID{
   177  		DbId:        dbID,
   178  		TbId:        tbID,
   179  		PartitionId: ptID,
   180  	}
   181  }
   182  
   183  func mockTimestamp(physical int64, logical uint32) timestamp.Timestamp {
   184  	return timestamp.Timestamp{
   185  		PhysicalTime: physical,
   186  		LogicalTime:  logical,
   187  	}
   188  }
   189  
   190  func startLogtailServer(
   191  	t *testing.T, address string, rt runtime.Runtime, tables ...api.TableID,
   192  ) func() {
   193  	logtailer := mockLocktailer(tables...)
   194  
   195  	/* ---- construct logtail server ---- */
   196  	logtailServer, err := NewLogtailServer(
   197  		address, options.NewDefaultLogtailServerCfg(), logtailer, rt,
   198  		WithServerCollectInterval(20*time.Millisecond),
   199  		WithServerSendTimeout(5*time.Second),
   200  		WithServerEnableChecksum(true),
   201  		WithServerMaxMessageSize(32+7),
   202  	)
   203  	require.NoError(t, err)
   204  
   205  	/* ---- start logtail server ---- */
   206  	err = logtailServer.Start()
   207  	require.NoError(t, err)
   208  
   209  	/* ---- generate incremental logtail ---- */
   210  	go func() {
   211  		from := timestamp.Timestamp{}
   212  
   213  		for {
   214  			now, _ := rt.Clock().Now()
   215  
   216  			tails := make([]logtail.TableLogtail, 0, len(tables))
   217  			for _, table := range tables {
   218  				tails = append(tails, mockLogtail(table, now))
   219  			}
   220  
   221  			err := logtailServer.NotifyLogtail(from, now, nil, tails...)
   222  			if err != nil {
   223  				return
   224  			}
   225  			from = now
   226  
   227  			time.Sleep(10 * time.Millisecond)
   228  		}
   229  	}()
   230  
   231  	stop := func() {
   232  		err := logtailServer.Close()
   233  		require.NoError(t, err)
   234  	}
   235  	return stop
   236  }