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  }