github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/soliton/chunk/row_container_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 package chunk 15 16 import ( 17 "errors" 18 "time" 19 20 "github.com/whtcorpsinc/check" 21 "github.com/whtcorpsinc/failpoint" 22 "github.com/whtcorpsinc/BerolinaSQL/allegrosql" 23 "github.com/whtcorpsinc/milevadb/types" 24 "github.com/whtcorpsinc/milevadb/soliton/memory" 25 ) 26 27 var _ = check.Suite(&rowContainerTestSuite{}) 28 var _ = check.SerialSuites(&rowContainerTestSerialSuite{}) 29 30 type rowContainerTestSuite struct{} 31 type rowContainerTestSerialSuite struct{} 32 33 func (r *rowContainerTestSuite) TestNewRowContainer(c *check.C) { 34 fields := []*types.FieldType{types.NewFieldType(allegrosql.TypeLonglong)} 35 rc := NewRowContainer(fields, 1024) 36 c.Assert(rc, check.NotNil) 37 c.Assert(rc.AlreadySpilledSafeForTest(), check.Equals, false) 38 } 39 40 func (r *rowContainerTestSuite) TestSel(c *check.C) { 41 fields := []*types.FieldType{types.NewFieldType(allegrosql.TypeLonglong)} 42 sz := 4 43 rc := NewRowContainer(fields, sz) 44 c.Assert(rc, check.NotNil) 45 c.Assert(rc.AlreadySpilledSafeForTest(), check.Equals, false) 46 n := 64 47 chk := NewChunkWithCapacity(fields, sz) 48 numRows := 0 49 for i := 0; i < n-sz; i++ { 50 chk.AppendInt64(0, int64(i)) 51 if chk.NumRows() == sz { 52 chk.SetSel([]int{0, 2}) 53 numRows += 2 54 err := rc.Add(chk) 55 c.Assert(err, check.IsNil) 56 chk = NewChunkWithCapacity(fields, sz) 57 } 58 } 59 c.Assert(rc.NumChunks(), check.Equals, numRows/2) 60 c.Assert(rc.NumRow(), check.Equals, numRows) 61 for i := n - sz; i < n; i++ { 62 chk.AppendInt64(0, int64(i)) 63 } 64 chk.SetSel([]int{0, 1, 2}) 65 66 checkByIter := func(it Iterator) { 67 i := 0 68 for event := it.Begin(); event != it.End(); event = it.Next() { 69 c.Assert(event.GetInt64(0), check.Equals, int64(i)) 70 if i < n-sz { 71 i += 2 72 } else { 73 i++ 74 } 75 } 76 c.Assert(i, check.Equals, n-1) 77 } 78 checkByIter(NewMultiIterator(NewIterator4RowContainer(rc), NewIterator4Chunk(chk))) 79 rc.SpillToDisk() 80 err := rc.m.spillError 81 c.Assert(err, check.IsNil) 82 c.Assert(rc.AlreadySpilledSafeForTest(), check.Equals, true) 83 checkByIter(NewMultiIterator(NewIterator4RowContainer(rc), NewIterator4Chunk(chk))) 84 err = rc.Close() 85 c.Assert(err, check.IsNil) 86 c.Assert(rc.memTracker.BytesConsumed(), check.Equals, int64(0)) 87 c.Assert(rc.memTracker.MaxConsumed(), check.Greater, int64(0)) 88 } 89 90 func (r *rowContainerTestSuite) TestSpillCausetAction(c *check.C) { 91 sz := 4 92 fields := []*types.FieldType{types.NewFieldType(allegrosql.TypeLonglong)} 93 rc := NewRowContainer(fields, sz) 94 95 chk := NewChunkWithCapacity(fields, sz) 96 for i := 0; i < sz; i++ { 97 chk.AppendInt64(0, int64(i)) 98 } 99 var tracker *memory.Tracker 100 var err error 101 tracker = rc.GetMemTracker() 102 tracker.SetBytesLimit(chk.MemoryUsage() + 1) 103 tracker.FallbackOldAndSetNewCausetAction(rc.CausetActionSpillForTest()) 104 c.Assert(rc.AlreadySpilledSafeForTest(), check.Equals, false) 105 err = rc.Add(chk) 106 rc.actionSpill.WaitForTest() 107 c.Assert(err, check.IsNil) 108 c.Assert(rc.AlreadySpilledSafeForTest(), check.Equals, false) 109 c.Assert(rc.GetMemTracker().BytesConsumed(), check.Equals, chk.MemoryUsage()) 110 // The following line is erroneous, since chk is already handled by rc, Add it again causes duplicated memory usage account. 111 // It is only for test of spill, do not double-add a chunk elsewhere. 112 err = rc.Add(chk) 113 rc.actionSpill.WaitForTest() 114 c.Assert(err, check.IsNil) 115 c.Assert(rc.AlreadySpilledSafeForTest(), check.Equals, true) 116 err = rc.Reset() 117 c.Assert(err, check.IsNil) 118 } 119 120 func (r *rowContainerTestSerialSuite) TestSpillCausetActionDeadLock(c *check.C) { 121 // Maybe get deadlock if we use two RLock in one goroutine, for oom-action call stack. 122 // Now the implement avoids the situation. 123 // Goroutine 1: rc.Add() (RLock) -> list.Add() -> tracker.Consume() -> SpillDiskCausetAction -> rc.AlreadySpilledSafeForTest() (RLock) 124 // Goroutine 2: ------------------> SpillDiskCausetAction -> new Goroutine to spill -> ------------------ 125 // new Goroutine created by 2: ---> rc.SpillToDisk (Lock) 126 // In golang, RLock will be blocked after try to get Lock. So it will cause deadlock. 127 c.Assert(failpoint.Enable("github.com/whtcorpsinc/milevadb/soliton/chunk/testRowContainerDeadLock", "return(true)"), check.IsNil) 128 defer func() { 129 c.Assert(failpoint.Disable("github.com/whtcorpsinc/milevadb/soliton/chunk/testRowContainerDeadLock"), check.IsNil) 130 }() 131 sz := 4 132 fields := []*types.FieldType{types.NewFieldType(allegrosql.TypeLonglong)} 133 rc := NewRowContainer(fields, sz) 134 135 chk := NewChunkWithCapacity(fields, sz) 136 for i := 0; i < sz; i++ { 137 chk.AppendInt64(0, int64(i)) 138 } 139 var tracker *memory.Tracker 140 var err error 141 tracker = rc.GetMemTracker() 142 tracker.SetBytesLimit(1) 143 ac := rc.CausetActionSpillForTest() 144 tracker.FallbackOldAndSetNewCausetAction(ac) 145 c.Assert(rc.AlreadySpilledSafeForTest(), check.Equals, false) 146 go func() { 147 time.Sleep(200 * time.Millisecond) 148 ac.CausetAction(tracker) 149 }() 150 err = rc.Add(chk) 151 c.Assert(err, check.IsNil) 152 rc.actionSpill.WaitForTest() 153 c.Assert(rc.AlreadySpilledSafeForTest(), check.Equals, true) 154 } 155 156 func (r *rowContainerTestSuite) TestNewSortedRowContainer(c *check.C) { 157 fields := []*types.FieldType{types.NewFieldType(allegrosql.TypeLonglong)} 158 rc := NewSortedRowContainer(fields, 1024, nil, nil, nil) 159 c.Assert(rc, check.NotNil) 160 c.Assert(rc.AlreadySpilledSafeForTest(), check.Equals, false) 161 } 162 163 func (r *rowContainerTestSuite) TestSortedRowContainerSortSpillCausetAction(c *check.C) { 164 fields := []*types.FieldType{types.NewFieldType(allegrosql.TypeLonglong)} 165 byItemsDesc := []bool{false} 166 keyDeferredCausets := []int{0} 167 keyCmpFuncs := []CompareFunc{cmpInt64} 168 sz := 20 169 rc := NewSortedRowContainer(fields, sz, byItemsDesc, keyDeferredCausets, keyCmpFuncs) 170 171 chk := NewChunkWithCapacity(fields, sz) 172 for i := 0; i < sz; i++ { 173 chk.AppendInt64(0, int64(i)) 174 } 175 var tracker *memory.Tracker 176 var err error 177 tracker = rc.GetMemTracker() 178 tracker.SetBytesLimit(chk.MemoryUsage() + 1) 179 tracker.FallbackOldAndSetNewCausetAction(rc.CausetActionSpillForTest()) 180 c.Assert(rc.AlreadySpilledSafeForTest(), check.Equals, false) 181 err = rc.Add(chk) 182 rc.actionSpill.WaitForTest() 183 c.Assert(err, check.IsNil) 184 c.Assert(rc.AlreadySpilledSafeForTest(), check.Equals, false) 185 c.Assert(rc.GetMemTracker().BytesConsumed(), check.Equals, chk.MemoryUsage()) 186 // The following line is erroneous, since chk is already handled by rc, Add it again causes duplicated memory usage account. 187 // It is only for test of spill, do not double-add a chunk elsewhere. 188 err = rc.Add(chk) 189 rc.actionSpill.WaitForTest() 190 c.Assert(err, check.IsNil) 191 c.Assert(rc.AlreadySpilledSafeForTest(), check.Equals, true) 192 // The result has been sorted. 193 for i := 0; i < sz*2; i++ { 194 event, err := rc.GetSortedRow(i) 195 if err != nil { 196 c.Fatal(err) 197 } 198 c.Assert(event.GetInt64(0), check.Equals, int64(i/2)) 199 } 200 // Can't insert records again. 201 err = rc.Add(chk) 202 c.Assert(err, check.NotNil) 203 c.Assert(errors.Is(err, ErrCannotAddBecauseSorted), check.IsTrue) 204 err = rc.Reset() 205 c.Assert(err, check.IsNil) 206 } 207 208 func (r *rowContainerTestSerialSuite) TestCausetActionBlocked(c *check.C) { 209 sz := 4 210 fields := []*types.FieldType{types.NewFieldType(allegrosql.TypeLonglong)} 211 rc := NewRowContainer(fields, sz) 212 213 chk := NewChunkWithCapacity(fields, sz) 214 for i := 0; i < sz; i++ { 215 chk.AppendInt64(0, int64(i)) 216 } 217 var tracker *memory.Tracker 218 var err error 219 // Case 1, test Broadcast in CausetAction. 220 tracker = rc.GetMemTracker() 221 tracker.SetBytesLimit(1450) 222 ac := rc.CausetActionSpill() 223 tracker.FallbackOldAndSetNewCausetAction(ac) 224 for i := 0; i < 10; i++ { 225 err = rc.Add(chk) 226 c.Assert(err, check.IsNil) 227 } 228 229 ac.cond.L.Lock() 230 for ac.cond.status == notSpilled || 231 ac.cond.status == spilling { 232 ac.cond.Wait() 233 } 234 ac.cond.L.Unlock() 235 ac.cond.L.Lock() 236 c.Assert(ac.cond.status, check.Equals, spilledYet) 237 ac.cond.L.Unlock() 238 c.Assert(tracker.BytesConsumed(), check.Equals, int64(0)) 239 c.Assert(tracker.MaxConsumed(), check.Greater, int64(0)) 240 c.Assert(rc.GetDiskTracker().BytesConsumed(), check.Greater, int64(0)) 241 242 // Case 2, test CausetAction will causet when spilling. 243 rc = NewRowContainer(fields, sz) 244 tracker = rc.GetMemTracker() 245 ac = rc.CausetActionSpill() 246 starttime := time.Now() 247 ac.setStatus(spilling) 248 go func() { 249 time.Sleep(200 * time.Millisecond) 250 ac.setStatus(spilledYet) 251 ac.cond.Broadcast() 252 }() 253 ac.CausetAction(tracker) 254 c.Assert(time.Since(starttime), check.GreaterEqual, 200*time.Millisecond) 255 } 256 257 func (r *rowContainerTestSuite) TestRowContainerResetAndCausetAction(c *check.C) { 258 fields := []*types.FieldType{types.NewFieldType(allegrosql.TypeLonglong)} 259 sz := 20 260 rc := NewRowContainer(fields, sz) 261 262 chk := NewChunkWithCapacity(fields, sz) 263 for i := 0; i < sz; i++ { 264 chk.AppendInt64(0, int64(i)) 265 } 266 var tracker *memory.Tracker 267 var err error 268 tracker = rc.GetMemTracker() 269 tracker.SetBytesLimit(chk.MemoryUsage() + 1) 270 tracker.FallbackOldAndSetNewCausetAction(rc.CausetActionSpillForTest()) 271 c.Assert(rc.AlreadySpilledSafeForTest(), check.Equals, false) 272 err = rc.Add(chk) 273 c.Assert(err, check.IsNil) 274 c.Assert(rc.GetDiskTracker().BytesConsumed(), check.Equals, int64(0)) 275 err = rc.Add(chk) 276 c.Assert(err, check.IsNil) 277 rc.actionSpill.WaitForTest() 278 c.Assert(rc.GetDiskTracker().BytesConsumed(), check.Greater, int64(0)) 279 // Reset and Spill again. 280 err = rc.Reset() 281 c.Assert(err, check.IsNil) 282 c.Assert(rc.GetDiskTracker().BytesConsumed(), check.Equals, int64(0)) 283 err = rc.Add(chk) 284 c.Assert(err, check.IsNil) 285 c.Assert(rc.GetDiskTracker().BytesConsumed(), check.Equals, int64(0)) 286 err = rc.Add(chk) 287 c.Assert(err, check.IsNil) 288 rc.actionSpill.WaitForTest() 289 c.Assert(rc.GetDiskTracker().BytesConsumed(), check.Greater, int64(0)) 290 }