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