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 }