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 }