// Copyright 2021 TiKV Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // NOTE: The code in this file is based on code from the // TiDB project, licensed under the Apache License v 2.0 // // https://github.com/pingcap/tidb/tree/cc5e161ac06827589c4966674597c137cc9e809c/store/tikv/tests/ticlient_test.go // // Copyright 2021 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tikv_test import ( "context" "errors" "fmt" "testing" "time" "github.com/ninedraft/israce" "github.com/stretchr/testify/suite" kverr "github.com/tikv/client-go/v2/error" tikvstore "github.com/tikv/client-go/v2/kv" "github.com/tikv/client-go/v2/tikv" ) func TestTiclient(t *testing.T) { suite.Run(t, new(testTiclientSuite)) } type testTiclientSuite struct { suite.Suite store *tikv.KVStore // prefix is prefix of each key in this test. It is used for table isolation, // or it may pollute other data. prefix string } func (s *testTiclientSuite) SetupSuite() { s.store = NewTestStore(s.T()) s.prefix = fmt.Sprintf("ticlient_%d", time.Now().Unix()) } func (s *testTiclientSuite) TearDownSuite() { // Clean all data, or it may pollute other data. txn := s.beginTxn() scanner, err := txn.Iter(encodeKey(s.prefix, ""), nil) require := s.Require() require.Nil(err) require.NotNil(scanner) for scanner.Valid() { k := scanner.Key() err = txn.Delete(k) s.Require().Nil(err) scanner.Next() } err = txn.Commit(context.Background()) require.Nil(err) err = s.store.Close() require.Nil(err) } func (s *testTiclientSuite) beginTxn() *tikv.KVTxn { txn, err := s.store.Begin() s.Require().Nil(err) return txn } func (s *testTiclientSuite) TestSingleKey() { txn := s.beginTxn() err := txn.Set(encodeKey(s.prefix, "key"), []byte("value")) s.Nil(err) err = txn.LockKeys(context.Background(), new(tikvstore.LockCtx), encodeKey(s.prefix, "key")) s.Nil(err) err = txn.Commit(context.Background()) s.Nil(err) txn = s.beginTxn() val, err := txn.Get(context.TODO(), encodeKey(s.prefix, "key")) s.Nil(err) s.Equal(val, []byte("value")) txn = s.beginTxn() err = txn.Delete(encodeKey(s.prefix, "key")) s.Nil(err) err = txn.Commit(context.Background()) s.Nil(err) } func (s *testTiclientSuite) TestMultiKeys() { const keyNum = 100 txn := s.beginTxn() for i := 0; i < keyNum; i++ { err := txn.Set(encodeKey(s.prefix, s08d("key", i)), valueBytes(i)) s.Nil(err) } err := txn.Commit(context.Background()) s.Nil(err) txn = s.beginTxn() for i := 0; i < keyNum; i++ { val, err1 := txn.Get(context.TODO(), encodeKey(s.prefix, s08d("key", i))) s.Nil(err1) s.Equal(val, valueBytes(i)) } txn = s.beginTxn() for i := 0; i < keyNum; i++ { err = txn.Delete(encodeKey(s.prefix, s08d("key", i))) s.Nil(err) } err = txn.Commit(context.Background()) s.Nil(err) } func (s *testTiclientSuite) TestNotExist() { txn := s.beginTxn() _, err := txn.Get(context.TODO(), encodeKey(s.prefix, "noSuchKey")) s.NotNil(err) } func (s *testTiclientSuite) TestLargeRequest() { largeValue := make([]byte, 9*1024*1024) // 9M value. txn := s.beginTxn() txn.GetUnionStore().SetEntrySizeLimit(1024*1024, 100*1024*1024) err := txn.Set([]byte("key"), largeValue) s.NotNil(err) err = txn.Commit(context.Background()) s.Nil(err) var retryableErr *kverr.ErrRetryable s.False(errors.As(err, &retryableErr)) } func (s *testTiclientSuite) TestSplitRegionIn2PC() { if *withTiKV { s.T().Skip("scatter will timeout with single node TiKV") } if israce.Race { s.T().Skip("skip slow test when race is enabled") } config := tikv.ConfigProbe{} const preSplitThresholdInTest = 500 old := config.LoadPreSplitDetectThreshold() defer config.StorePreSplitDetectThreshold(old) config.StorePreSplitDetectThreshold(preSplitThresholdInTest) old = config.LoadPreSplitSizeThreshold() defer config.StorePreSplitSizeThreshold(old) config.StorePreSplitSizeThreshold(5000) bo := tikv.NewBackofferWithVars(context.Background(), 1, nil) checkKeyRegion := func(bo *tikv.Backoffer, start, end []byte, eq bool) { // Check regions after split. loc1, err := s.store.GetRegionCache().LocateKey(bo, start) s.Nil(err) loc2, err := s.store.GetRegionCache().LocateKey(bo, end) s.Nil(err) s.Equal(loc1.Region.GetID() == loc2.Region.GetID(), eq) } mode := []string{"optimistic", "pessimistic"} var ( startKey []byte endKey []byte ) ctx := context.Background() for _, m := range mode { if m == "optimistic" { startKey = encodeKey(s.prefix, s08d("key", 0)) endKey = encodeKey(s.prefix, s08d("key", preSplitThresholdInTest)) } else { startKey = encodeKey(s.prefix, s08d("pkey", 0)) endKey = encodeKey(s.prefix, s08d("pkey", preSplitThresholdInTest)) } // Check before test. checkKeyRegion(bo, startKey, endKey, true) txn := s.beginTxn() if m == "pessimistic" { txn.SetPessimistic(true) lockCtx := &tikvstore.LockCtx{} lockCtx.ForUpdateTS = txn.StartTS() keys := make([][]byte, 0, preSplitThresholdInTest) for i := 0; i < preSplitThresholdInTest; i++ { keys = append(keys, encodeKey(s.prefix, s08d("pkey", i))) } err := txn.LockKeys(ctx, lockCtx, keys...) s.Nil(err) checkKeyRegion(bo, startKey, endKey, false) } var err error for i := 0; i < preSplitThresholdInTest; i++ { if m == "optimistic" { err = txn.Set(encodeKey(s.prefix, s08d("key", i)), valueBytes(i)) } else { err = txn.Set(encodeKey(s.prefix, s08d("pkey", i)), valueBytes(i)) } s.Nil(err) } err = txn.Commit(context.Background()) s.Nil(err) // Check region split after test. checkKeyRegion(bo, startKey, endKey, false) } }