github.com/matrixorigin/matrixone@v1.2.0/pkg/frontend/routine_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 frontend 16 17 import ( 18 "context" 19 "database/sql" 20 "fmt" 21 "sync" 22 "sync/atomic" 23 "testing" 24 "time" 25 26 "github.com/golang/mock/gomock" 27 "github.com/google/uuid" 28 "github.com/prashantv/gostub" 29 pcg "github.com/prometheus/client_model/go" 30 "github.com/stretchr/testify/assert" 31 "github.com/stretchr/testify/require" 32 "golang.org/x/sync/errgroup" 33 34 "github.com/matrixorigin/matrixone/pkg/config" 35 "github.com/matrixorigin/matrixone/pkg/defines" 36 mock_frontend "github.com/matrixorigin/matrixone/pkg/frontend/test" 37 "github.com/matrixorigin/matrixone/pkg/logutil" 38 "github.com/matrixorigin/matrixone/pkg/sql/parsers" 39 "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect" 40 "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" 41 util "github.com/matrixorigin/matrixone/pkg/util" 42 "github.com/matrixorigin/matrixone/pkg/util/metric" 43 "github.com/matrixorigin/matrixone/pkg/vm/engine" 44 "github.com/matrixorigin/matrixone/pkg/vm/process" 45 ) 46 47 func Test_inc_dec(t *testing.T) { 48 rt := &Routine{} 49 counter := int32(0) 50 eg := errgroup.Group{} 51 52 eg.Go(func() error { 53 rt.increaseCount(func() { 54 atomic.AddInt32(&counter, 1) 55 }) 56 return nil 57 }) 58 time.Sleep(100 * time.Millisecond) 59 60 eg.Go(func() error { 61 rt.decreaseCount(func() { 62 atomic.AddInt32(&counter, -1) 63 }) 64 return nil 65 }) 66 time.Sleep(100 * time.Millisecond) 67 68 err := eg.Wait() 69 assert.NoError(t, err) 70 assert.Equal(t, counter, int32(0)) 71 assert.False(t, rt.connectionBeCounted.Load()) 72 } 73 74 const ( 75 contextCancel int32 = -2 76 timeout int32 = -1 77 ) 78 79 type genMrs func(ses *Session) *MysqlResultSet 80 81 type result struct { 82 gen genMrs 83 isSleepSql bool 84 seconds int 85 resultX atomic.Int32 86 } 87 88 var newMockWrapper = func(ctrl *gomock.Controller, ses *Session, 89 sql2result map[string]*result, 90 sql2NoResultSet map[string]bool, sql string, stmt tree.Statement, proc *process.Process) ComputationWrapper { 91 var mrs *MysqlResultSet 92 var columns []interface{} 93 var ok, ok2 bool 94 var err error 95 var res *result 96 if res, ok = sql2result[sql]; ok { 97 mrs = res.gen(ses) 98 for _, col := range mrs.Columns { 99 columns = append(columns, col) 100 } 101 } else if _, ok2 = sql2NoResultSet[sql]; ok2 { 102 //no result set 103 } else { 104 panic(fmt.Sprintf("there is no mysqlResultset for the sql %s", sql)) 105 } 106 uuid, _ := uuid.NewV7() 107 runner := mock_frontend.NewMockComputationRunner(ctrl) 108 runner.EXPECT().Run(gomock.Any()).DoAndReturn(func(uint64) (*util.RunResult, error) { 109 proto := ses.GetMysqlProtocol() 110 if mrs != nil { 111 if res.isSleepSql { 112 select { 113 case <-time.After(time.Duration(res.seconds) * time.Second): 114 res.resultX.Store(timeout) 115 case <-proc.Ctx.Done(): 116 res.resultX.Store(contextCancel) 117 } 118 } 119 err = proto.SendResultSetTextBatchRowSpeedup(mrs, mrs.GetRowCount()) 120 if err != nil { 121 logutil.Errorf("flush error %v", err) 122 return nil, err 123 } 124 } 125 return &util.RunResult{AffectRows: 0}, nil 126 }).AnyTimes() 127 mcw := mock_frontend.NewMockComputationWrapper(ctrl) 128 mcw.EXPECT().GetAst().Return(stmt).AnyTimes() 129 mcw.EXPECT().GetProcess().Return(proc).AnyTimes() 130 mcw.EXPECT().GetColumns(gomock.Any()).Return(columns, nil).AnyTimes() 131 mcw.EXPECT().Compile(gomock.Any(), gomock.Any()).Return(runner, nil).AnyTimes() 132 mcw.EXPECT().GetUUID().Return(uuid[:]).AnyTimes() 133 mcw.EXPECT().RecordExecPlan(gomock.Any()).Return(nil).AnyTimes() 134 mcw.EXPECT().GetLoadTag().Return(false).AnyTimes() 135 mcw.EXPECT().Clear().AnyTimes() 136 mcw.EXPECT().Free().AnyTimes() 137 mcw.EXPECT().Plan().Return(nil).AnyTimes() 138 return mcw 139 } 140 141 func Test_ConnectionCount(t *testing.T) { 142 //client connection method: mysql -h 127.0.0.1 -P 6001 --default-auth=mysql_native_password -uroot -p 143 //client connect 144 //ion method: mysql -h 127.0.0.1 -P 6001 -udump -p 145 ctrl := gomock.NewController(t) 146 defer ctrl.Finish() 147 var conn1, conn2 *sql.DB 148 var err error 149 150 //before anything using the configuration 151 eng := mock_frontend.NewMockEngine(ctrl) 152 eng.EXPECT().New(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() 153 txnClient := mock_frontend.NewMockTxnClient(ctrl) 154 pu, err := getParameterUnit("test/system_vars_config.toml", eng, txnClient) 155 require.NoError(t, err) 156 pu.SV.SkipCheckUser = true 157 setGlobalPu(pu) 158 159 noResultSet := make(map[string]bool) 160 resultSet := make(map[string]*result) 161 162 var wrapperStubFunc = func(execCtx *ExecCtx, db string, user string, eng engine.Engine, proc *process.Process, ses *Session) ([]ComputationWrapper, error) { 163 var cw []ComputationWrapper = nil 164 var stmts []tree.Statement = nil 165 var cmdFieldStmt *InternalCmdFieldList 166 var err error 167 if isCmdFieldListSql(execCtx.input.getSql()) { 168 cmdFieldStmt, err = parseCmdFieldList(execCtx.reqCtx, execCtx.input.getSql()) 169 if err != nil { 170 return nil, err 171 } 172 stmts = append(stmts, cmdFieldStmt) 173 } else { 174 stmts, err = parsers.Parse(execCtx.reqCtx, dialect.MYSQL, execCtx.input.getSql(), 1, 0) 175 if err != nil { 176 return nil, err 177 } 178 } 179 180 for _, stmt := range stmts { 181 cw = append(cw, newMockWrapper(ctrl, ses, resultSet, noResultSet, execCtx.input.getSql(), stmt, proc)) 182 } 183 return cw, nil 184 } 185 186 bhStub := gostub.Stub(&GetComputationWrapper, wrapperStubFunc) 187 defer bhStub.Reset() 188 189 ctx := context.WithValue(context.TODO(), config.ParameterUnitKey, pu) 190 191 // A mock autoincrcache manager. 192 setGlobalAicm(&defines.AutoIncrCacheManager{}) 193 rm, _ := NewRoutineManager(ctx) 194 setGlobalRtMgr(rm) 195 196 wg := sync.WaitGroup{} 197 wg.Add(1) 198 199 //running server 200 go func() { 201 defer wg.Done() 202 echoServer(getGlobalRtMgr().Handler, getGlobalRtMgr(), NewSqlCodec()) 203 }() 204 205 cCounter := metric.ConnectionCounter(sysAccountName) 206 cCounter.Set(0) 207 208 time.Sleep(time.Second * 2) 209 conn1, err = openDbConn(t, 6001) 210 require.NoError(t, err) 211 212 time.Sleep(time.Second * 2) 213 conn2, err = openDbConn(t, 6001) 214 require.NoError(t, err) 215 216 time.Sleep(time.Second * 2) 217 218 cc := rm.clientCount() 219 assert.GreaterOrEqual(t, cc, 2) 220 221 x := &pcg.Metric{} 222 err = cCounter.Write(x) 223 assert.NoError(t, err) 224 assert.GreaterOrEqual(t, x.Gauge.GetValue(), float64(2)) 225 226 time.Sleep(time.Second * 2) 227 228 cc = rm.clientCount() 229 assert.GreaterOrEqual(t, cc, 0) 230 231 err = cCounter.Write(x) 232 assert.NoError(t, err) 233 assert.GreaterOrEqual(t, x.Gauge.GetValue(), float64(0)) 234 235 time.Sleep(time.Millisecond * 10) 236 //close server 237 setServer(1) 238 wg.Wait() 239 240 //close the connection 241 closeDbConn(t, conn1) 242 closeDbConn(t, conn2) 243 }