github.com/matrixorigin/matrixone@v1.2.0/pkg/tests/txn/cluster_basic_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 txn
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"sync"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/lni/goutils/leaktest"
    25  	"github.com/matrixorigin/matrixone/pkg/common/runtime"
    26  	"github.com/matrixorigin/matrixone/pkg/container/vector"
    27  	"github.com/matrixorigin/matrixone/pkg/tests/service"
    28  	"github.com/matrixorigin/matrixone/pkg/txn/client"
    29  	"github.com/matrixorigin/matrixone/pkg/util/executor"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  var (
    34  	testOptionsSet = map[string][]func(opts service.Options) service.Options{
    35  		"tae-cn-tae-dn": {useDistributedTAEEngine, useTAEStorage},
    36  	}
    37  )
    38  
    39  func TestBasicSingleShard(t *testing.T) {
    40  	defer leaktest.AfterTest(t)()
    41  	if testing.Short() {
    42  		t.Skip("skipping in short mode.")
    43  		return
    44  	}
    45  	ctx := context.Background()
    46  
    47  	// this case will start a mo cluster with 1 CNService, 1 DNService and 3 LogService.
    48  	// A Txn read and write will success.
    49  	for name, options := range testOptionsSet {
    50  		t.Run(name, func(t *testing.T) {
    51  			c, err := NewCluster(ctx, t,
    52  				getBasicClusterOptions(options...))
    53  			require.NoError(t, err)
    54  			defer c.Stop()
    55  			c.Start()
    56  
    57  			cli := c.NewClient()
    58  
    59  			key := "k"
    60  			value := "v"
    61  
    62  			checkRead(t, mustNewTxn(t, cli), key, "", nil, true)
    63  			checkWrite(t, mustNewTxn(t, cli), key, value, nil, true)
    64  			checkRead(t, mustNewTxn(t, cli), key, value, nil, true)
    65  		})
    66  	}
    67  }
    68  
    69  func TestBasicSingleShardCannotReadUncommittedValue(t *testing.T) {
    70  	defer leaktest.AfterTest(t)()
    71  	if testing.Short() {
    72  		t.Skip("skipping in short mode.")
    73  		return
    74  	}
    75  	ctx := context.Background()
    76  
    77  	// this case will start a mo cluster with 1 CNService, 1 DNService and 3 LogService.
    78  	// 1. start t1
    79  	// 2. start t2
    80  	// 3. t1 write
    81  	// 4. t2 can not read t1's write
    82  	for name, options := range testOptionsSet {
    83  		t.Run(name, func(t *testing.T) {
    84  			c, err := NewCluster(ctx, t,
    85  				getBasicClusterOptions(options...))
    86  			require.NoError(t, err)
    87  			defer c.Stop()
    88  			c.Start()
    89  
    90  			cli := c.NewClient()
    91  
    92  			key := "k"
    93  			value := "v"
    94  
    95  			t1 := mustNewTxn(t, cli)
    96  			t2 := mustNewTxn(t, cli)
    97  
    98  			checkWrite(t, t1, key, value, nil, false)
    99  			checkRead(t, t2, key, "", nil, true)
   100  
   101  			require.NoError(t, t1.Commit())
   102  		})
   103  	}
   104  }
   105  
   106  func TestWriteSkewIsAllowed(t *testing.T) {
   107  	defer leaktest.AfterTest(t)()
   108  	// this case will start a mo cluster with 1 CNService, 1 DNService and 3 LogService.
   109  	// 1. start t1
   110  	// 2. start t2
   111  	// 3. t1 reads x
   112  	// 4. t2 reads y
   113  	// 5. t1 writes x -> y
   114  	// 6. t2 writes y -> x
   115  	// 7. t1 commits
   116  	// 8. t2 commits
   117  	if testing.Short() {
   118  		t.Skip("skipping in short mode.")
   119  		return
   120  	}
   121  	ctx := context.Background()
   122  
   123  	for name, options := range testOptionsSet {
   124  		t.Run(name, func(t *testing.T) {
   125  			c, err := NewCluster(ctx, t,
   126  				getBasicClusterOptions(options...))
   127  			require.NoError(t, err)
   128  			defer c.Stop()
   129  			c.Start()
   130  
   131  			cli := c.NewClient()
   132  
   133  			k1 := "x"
   134  			k2 := "y"
   135  
   136  			checkWrite(t, mustNewTxn(t, cli), k1, "a", nil, true)
   137  			checkWrite(t, mustNewTxn(t, cli), k2, "b", nil, true)
   138  
   139  			t1 := mustNewTxn(t, cli)
   140  			t2 := mustNewTxn(t, cli)
   141  
   142  			x, err := t1.Read(k1)
   143  			require.NoError(t, err)
   144  			err = t1.Write(k2, x)
   145  			require.NoError(t, err)
   146  			y, err := t2.Read(k2)
   147  			require.NoError(t, err)
   148  			err = t2.Write(k1, y)
   149  			require.NoError(t, err)
   150  			err = t1.Commit()
   151  			require.NoError(t, err)
   152  			err = t2.Commit()
   153  			require.NoError(t, err)
   154  
   155  			checkRead(t, mustNewTxn(t, cli), k1, "b", nil, true)
   156  			checkRead(t, mustNewTxn(t, cli), k2, "a", nil, true)
   157  		})
   158  	}
   159  }
   160  
   161  func TestBasicSingleShardWithInternalSQLExecutor(t *testing.T) {
   162  	if testing.Short() {
   163  		t.Skip("skipping in short mode.")
   164  		return
   165  	}
   166  	ctx := context.Background()
   167  
   168  	// this case will start a mo cluster with 1 CNService, 1 DNService and 3 LogService.
   169  	// A Txn read and write will success.
   170  	for name, options := range testOptionsSet {
   171  		t.Run(name, func(t *testing.T) {
   172  			c, err := NewCluster(ctx, t,
   173  				getBasicClusterOptions(options...))
   174  			require.NoError(t, err)
   175  			defer c.Stop()
   176  			c.Start()
   177  
   178  			v, ok := runtime.ProcessLevelRuntime().GetGlobalVariables(runtime.InternalSQLExecutor)
   179  			if !ok {
   180  				panic("missing internal sql executor")
   181  			}
   182  			exec := v.(executor.SQLExecutor)
   183  			ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
   184  			defer cancel()
   185  			exec.ExecTxn(
   186  				ctx,
   187  				func(te executor.TxnExecutor) error {
   188  					res, err := te.Exec("create database zx", executor.StatementOption{})
   189  					require.NoError(t, err)
   190  					res.Close()
   191  
   192  					res, err = te.Exec("create table t1 (id int primary key, name varchar(255))", executor.StatementOption{})
   193  					require.NoError(t, err)
   194  					res.Close()
   195  
   196  					res, err = te.Exec("insert into t1 values (1, 'a'),(2, 'b'),(3, 'c')", executor.StatementOption{})
   197  					require.NoError(t, err)
   198  					require.Equal(t, uint64(3), res.AffectedRows)
   199  					res.Close()
   200  
   201  					res, err = te.Exec("select id,name from t1 order by id", executor.StatementOption{})
   202  					require.NoError(t, err)
   203  					var ids []int32
   204  					var names []string
   205  					res.ReadRows(func(_ int, cols []*vector.Vector) bool {
   206  						ids = append(ids, executor.GetFixedRows[int32](cols[0])...)
   207  						names = append(names, executor.GetStringRows(cols[1])...)
   208  						return true
   209  					})
   210  					require.Equal(t, []int32{1, 2, 3}, ids)
   211  					require.Equal(t, []string{"a", "b", "c"}, names)
   212  					res.Close()
   213  					return nil
   214  				},
   215  				executor.Options{}.WithDatabase("zx"))
   216  		})
   217  	}
   218  }
   219  
   220  func TestSingleShardWithCreateTable(t *testing.T) {
   221  	defer leaktest.AfterTest(t)()
   222  	if testing.Short() {
   223  		t.Skip("skipping in short mode.")
   224  		return
   225  	}
   226  	ctx := context.Background()
   227  
   228  	c, err := NewCluster(ctx, t,
   229  		getBasicClusterOptions(useTAEStorage, useDistributedTAEEngine))
   230  	require.NoError(t, err)
   231  	defer c.Stop()
   232  	c.Start()
   233  
   234  	cli := c.NewClient()
   235  
   236  	txn, err := cli.NewTxn()
   237  	require.NoError(t, err)
   238  	sqlTxn := txn.(SQLBasedTxn)
   239  
   240  	_, err = sqlTxn.ExecSQL("create database test_db")
   241  	require.NoError(t, err)
   242  	require.NoError(t, sqlTxn.Commit())
   243  
   244  	txn, err = cli.NewTxn()
   245  	require.NoError(t, err)
   246  	sqlTxn = txn.(SQLBasedTxn)
   247  	_, err = sqlTxn.ExecSQL("use test_db")
   248  	require.NoError(t, err)
   249  	require.NoError(t, sqlTxn.Commit())
   250  }
   251  
   252  // # issue for #7748
   253  // SQL statement here refers to func_aggr_avg.test
   254  func TestAggTable(t *testing.T) {
   255  	defer leaktest.AfterTest(t)()
   256  	if testing.Short() {
   257  		t.Skip("skipping in short mode.")
   258  		return
   259  	}
   260  	ctx := context.Background()
   261  
   262  	c, err := NewCluster(ctx, t,
   263  		getBasicClusterOptions(useTAEStorage, useDistributedTAEEngine))
   264  	require.NoError(t, err)
   265  	defer c.Stop()
   266  	c.Start()
   267  
   268  	cli1 := c.NewClient()
   269  	cli2 := c.NewClient()
   270  
   271  	txn1, err := cli1.NewTxn()
   272  	require.NoError(t, err)
   273  	txn2, err := cli2.NewTxn()
   274  	require.NoError(t, err)
   275  
   276  	sqlTxn1 := txn1.(SQLBasedTxn)
   277  	sqlTxn2 := txn2.(SQLBasedTxn)
   278  	var txnList []SQLBasedTxn = []SQLBasedTxn{sqlTxn1, sqlTxn2}
   279  	var tblList []string = []string{"t1", "t2"}
   280  	type avgline struct {
   281  		a int
   282  		b float64
   283  	}
   284  
   285  	_, err = sqlTxn1.ExecSQL("create database test_avg;")
   286  	require.NoError(t, err)
   287  	_, err = sqlTxn1.ExecSQL("use test_avg;")
   288  	require.NoError(t, err)
   289  	_, err = sqlTxn1.ExecSQL("CREATE TABLE t1 (a INT, b INT);")
   290  	require.NoError(t, err)
   291  	_, err = sqlTxn1.ExecSQL("INSERT INTO t1 VALUES (1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8);")
   292  	require.NoError(t, err)
   293  	_, err = sqlTxn2.ExecSQL("CREATE TABLE t2 (a INT, b INT);")
   294  	require.NoError(t, err)
   295  	_, err = sqlTxn2.ExecSQL("INSERT INTO t2 VALUES (1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7);")
   296  	require.NoError(t, err)
   297  
   298  	wg := sync.WaitGroup{}
   299  	for i := 0; i < 2; i++ {
   300  		wg.Add(1)
   301  		go func(i int) {
   302  			defer wg.Done()
   303  			insertTable(txnList[i], t, tblList[i])
   304  		}(i)
   305  	}
   306  	wg.Wait()
   307  
   308  	for i := 0; i < 2; i++ {
   309  		wg.Add(1)
   310  		go func(i int, t *testing.T) {
   311  			defer wg.Done()
   312  			rows, err := txnList[i].ExecSQLQuery(fmt.Sprintf("SELECT DISTINCT a, AVG( b) FROM %s GROUP BY a HAVING AVG( b) > 50;", tblList[i]))
   313  			defer mustCloseRows(t, rows)
   314  			defer func() {
   315  				err := rows.Err()
   316  				require.NoError(t, err)
   317  			}()
   318  			require.NoError(t, err)
   319  			var l avgline
   320  			if !rows.Next() {
   321  				return
   322  			}
   323  			err = rows.Scan(&l.a, &l.b)
   324  			require.NoError(t, err)
   325  			if tblList[i] == "t1" {
   326  				require.Equal(t, 32768.5, l.b)
   327  			} else {
   328  				require.Equal(t, 32774.5, l.b)
   329  			}
   330  		}(i, t)
   331  	}
   332  	wg.Wait()
   333  
   334  	require.NoError(t, sqlTxn1.Commit())
   335  	require.NoError(t, sqlTxn2.Commit())
   336  }
   337  
   338  func checkRead(t *testing.T, txn Txn, key string, expectValue string, expectError error, commit bool) {
   339  	v, err := txn.Read(key)
   340  	defer func() {
   341  		if commit {
   342  			require.NoError(t, txn.Commit())
   343  		}
   344  	}()
   345  	require.Equal(t, expectError, err)
   346  	require.Equal(t, expectValue, v)
   347  }
   348  
   349  func checkWrite(t *testing.T, txn Txn, key, value string, expectError error, commit bool) {
   350  	defer func() {
   351  		if commit {
   352  			require.NoError(t, txn.Commit())
   353  		}
   354  	}()
   355  	require.Equal(t, expectError, txn.Write(key, value))
   356  }
   357  
   358  func getBasicClusterOptions(opts ...func(opts service.Options) service.Options) service.Options {
   359  	basic := service.DefaultOptions().
   360  		WithTNShardNum(1).
   361  		WithLogShardNum(1).
   362  		WithTNServiceNum(1).
   363  		WithLogServiceNum(3).
   364  		WithCNShardNum(1).
   365  		WithCNServiceNum(1)
   366  	for _, opt := range opts {
   367  		basic = opt(basic)
   368  	}
   369  	return basic
   370  }
   371  
   372  func useTAEStorage(opts service.Options) service.Options {
   373  	return opts.WithTNUseTAEStorage()
   374  }
   375  
   376  func useDistributedTAEEngine(opts service.Options) service.Options {
   377  	return opts.WithCNUseDistributedTAEEngine()
   378  }
   379  
   380  func mustNewTxn(t *testing.T, cli Client, options ...client.TxnOption) Txn {
   381  	txn, err := cli.NewTxn(options...)
   382  	require.NoError(t, err)
   383  	return txn
   384  }
   385  
   386  // helper function for TestAggTable
   387  func insertTable(s SQLBasedTxn, t *testing.T, tbl string) {
   388  	var arr []int = []int{8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768}
   389  	for _, v := range arr {
   390  		if tbl == "t1" {
   391  			_, err := s.ExecSQL(fmt.Sprintf("INSERT INTO %s SELECT a, b+%d       FROM %s;", tbl, v, tbl))
   392  			require.NoError(t, err)
   393  		} else {
   394  			_, err := s.ExecSQL(fmt.Sprintf("INSERT INTO %s SELECT a, b+%d       FROM %s;", tbl, v+1, tbl))
   395  			require.NoError(t, err)
   396  		}
   397  	}
   398  }
   399  
   400  // helper function for TestAggTable