github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/milevadb-server/einsteindb/isolation_test.go (about)

     1  // Copyright 2020 WHTCORPS INC, Inc.
     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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  //go:build !race
    15  // +build !race
    16  
    17  package einsteindb
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"sort"
    23  	"sync"
    24  	"time"
    25  
    26  	. "github.com/whtcorpsinc/check"
    27  	"github.com/whtcorpsinc/milevadb/ekv"
    28  )
    29  
    30  // testIsolationSuite represents test isolation suite.
    31  // The test suite takes too long under the race detector.
    32  type testIsolationSuite struct {
    33  	OneByOneSuite
    34  	causetstore *einsteindbStore
    35  }
    36  
    37  var _ = Suite(&testIsolationSuite{})
    38  
    39  func (s *testIsolationSuite) SetUpSuite(c *C) {
    40  	s.OneByOneSuite.SetUpSuite(c)
    41  	s.causetstore = NewTestStore(c).(*einsteindbStore)
    42  }
    43  
    44  func (s *testIsolationSuite) TearDownSuite(c *C) {
    45  	s.causetstore.Close()
    46  	s.OneByOneSuite.TearDownSuite(c)
    47  }
    48  
    49  type writeRecord struct {
    50  	startTS  uint64
    51  	commitTS uint64
    52  }
    53  
    54  type writeRecords []writeRecord
    55  
    56  func (r writeRecords) Len() int           { return len(r) }
    57  func (r writeRecords) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
    58  func (r writeRecords) Less(i, j int) bool { return r[i].startTS <= r[j].startTS }
    59  
    60  func (s *testIsolationSuite) SetWithRetry(c *C, k, v []byte) writeRecord {
    61  	for {
    62  		txn, err := s.causetstore.Begin()
    63  		c.Assert(err, IsNil)
    64  
    65  		err = txn.Set(k, v)
    66  		c.Assert(err, IsNil)
    67  
    68  		err = txn.Commit(context.Background())
    69  		if err == nil {
    70  			return writeRecord{
    71  				startTS:  txn.StartTS(),
    72  				commitTS: txn.(*einsteindbTxn).commitTS,
    73  			}
    74  		}
    75  	}
    76  }
    77  
    78  type readRecord struct {
    79  	startTS uint64
    80  	value   []byte
    81  }
    82  
    83  type readRecords []readRecord
    84  
    85  func (r readRecords) Len() int           { return len(r) }
    86  func (r readRecords) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
    87  func (r readRecords) Less(i, j int) bool { return r[i].startTS <= r[j].startTS }
    88  
    89  func (s *testIsolationSuite) GetWithRetry(c *C, k []byte) readRecord {
    90  	for {
    91  		txn, err := s.causetstore.Begin()
    92  		c.Assert(err, IsNil)
    93  
    94  		val, err := txn.Get(context.TODO(), k)
    95  		if err == nil {
    96  			return readRecord{
    97  				startTS: txn.StartTS(),
    98  				value:   val,
    99  			}
   100  		}
   101  		c.Assert(ekv.IsTxnRetryableError(err), IsTrue)
   102  	}
   103  }
   104  
   105  func (s *testIsolationSuite) TestWriteWriteConflict(c *C) {
   106  	const (
   107  		threadCount  = 10
   108  		setPerThread = 50
   109  	)
   110  	var (
   111  		mu     sync.Mutex
   112  		writes []writeRecord
   113  		wg     sync.WaitGroup
   114  	)
   115  	wg.Add(threadCount)
   116  	for i := 0; i < threadCount; i++ {
   117  		go func() {
   118  			defer wg.Done()
   119  			for j := 0; j < setPerThread; j++ {
   120  				w := s.SetWithRetry(c, []byte("k"), []byte("v"))
   121  				mu.Lock()
   122  				writes = append(writes, w)
   123  				mu.Unlock()
   124  			}
   125  		}()
   126  	}
   127  	wg.Wait()
   128  
   129  	// Check all transactions' [startTS, commitTS] are not overlapped.
   130  	sort.Sort(writeRecords(writes))
   131  	for i := 0; i < len(writes)-1; i++ {
   132  		c.Assert(writes[i].commitTS, Less, writes[i+1].startTS)
   133  	}
   134  }
   135  
   136  func (s *testIsolationSuite) TestReadWriteConflict(c *C) {
   137  	const (
   138  		readThreadCount = 10
   139  		writeCount      = 10
   140  	)
   141  
   142  	var (
   143  		writes []writeRecord
   144  		mu     sync.Mutex
   145  		reads  []readRecord
   146  		wg     sync.WaitGroup
   147  	)
   148  
   149  	s.SetWithRetry(c, []byte("k"), []byte("0"))
   150  
   151  	writeDone := make(chan struct{})
   152  	go func() {
   153  		for i := 1; i <= writeCount; i++ {
   154  			w := s.SetWithRetry(c, []byte("k"), []byte(fmt.Sprintf("%d", i)))
   155  			writes = append(writes, w)
   156  			time.Sleep(time.Microsecond * 10)
   157  		}
   158  		close(writeDone)
   159  	}()
   160  
   161  	wg.Add(readThreadCount)
   162  	for i := 0; i < readThreadCount; i++ {
   163  		go func() {
   164  			defer wg.Done()
   165  			for {
   166  				select {
   167  				case <-writeDone:
   168  					return
   169  				default:
   170  				}
   171  				r := s.GetWithRetry(c, []byte("k"))
   172  				mu.Lock()
   173  				reads = append(reads, r)
   174  				mu.Unlock()
   175  			}
   176  		}()
   177  	}
   178  	wg.Wait()
   179  
   180  	sort.Sort(readRecords(reads))
   181  
   182  	// Check all reads got the value committed before it's startTS.
   183  	var i, j int
   184  	for ; i < len(writes); i++ {
   185  		for ; j < len(reads); j++ {
   186  			w, r := writes[i], reads[j]
   187  			if r.startTS >= w.commitTS {
   188  				break
   189  			}
   190  			c.Assert(string(r.value), Equals, fmt.Sprintf("%d", i))
   191  		}
   192  	}
   193  	for ; j < len(reads); j++ {
   194  		c.Assert(string(reads[j].value), Equals, fmt.Sprintf("%d", len(writes)))
   195  	}
   196  }