github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/soliton/chunk/list.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  	"fmt"
    18  
    19  	"github.com/whtcorpsinc/errors"
    20  	"github.com/whtcorpsinc/milevadb/types"
    21  	"github.com/whtcorpsinc/milevadb/soliton/memory"
    22  	"github.com/whtcorpsinc/milevadb/soliton/stringutil"
    23  )
    24  
    25  // List holds a slice of chunks, use to append rows with max chunk size properly handled.
    26  type List struct {
    27  	fieldTypes    []*types.FieldType
    28  	initChunkSize int
    29  	maxChunkSize  int
    30  	length        int
    31  	chunks        []*Chunk
    32  	freelist      []*Chunk
    33  
    34  	memTracker  *memory.Tracker // track memory usage.
    35  	consumedIdx int             // chunk index in "chunks", has been consumed.
    36  }
    37  
    38  // RowPtr is used to get a event from a list.
    39  // It is only valid for the list that returns it.
    40  type RowPtr struct {
    41  	ChkIdx uint32
    42  	RowIdx uint32
    43  }
    44  
    45  var chunkListLabel fmt.Stringer = stringutil.StringerStr("chunk.List")
    46  
    47  // NewList creates a new List with field types, init chunk size and max chunk size.
    48  func NewList(fieldTypes []*types.FieldType, initChunkSize, maxChunkSize int) *List {
    49  	l := &List{
    50  		fieldTypes:    fieldTypes,
    51  		initChunkSize: initChunkSize,
    52  		maxChunkSize:  maxChunkSize,
    53  		memTracker:    memory.NewTracker(memory.LabelForChunkList, -1),
    54  		consumedIdx:   -1,
    55  	}
    56  	return l
    57  }
    58  
    59  // GetMemTracker returns the memory tracker of this List.
    60  func (l *List) GetMemTracker() *memory.Tracker {
    61  	return l.memTracker
    62  }
    63  
    64  // Len returns the length of the List.
    65  func (l *List) Len() int {
    66  	return l.length
    67  }
    68  
    69  // NumChunks returns the number of chunks in the List.
    70  func (l *List) NumChunks() int {
    71  	return len(l.chunks)
    72  }
    73  
    74  // FieldTypes returns the fieldTypes of the list
    75  func (l *List) FieldTypes() []*types.FieldType {
    76  	return l.fieldTypes
    77  }
    78  
    79  // NumRowsOfChunk returns the number of rows of a chunk in the ListInDisk.
    80  func (l *List) NumRowsOfChunk(chkID int) int {
    81  	return l.chunks[chkID].NumRows()
    82  }
    83  
    84  // GetChunk gets the Chunk by ChkIdx.
    85  func (l *List) GetChunk(chkIdx int) *Chunk {
    86  	return l.chunks[chkIdx]
    87  }
    88  
    89  // AppendRow appends a event to the List, the event is copied to the List.
    90  func (l *List) AppendRow(event Row) RowPtr {
    91  	chkIdx := len(l.chunks) - 1
    92  	if chkIdx == -1 || l.chunks[chkIdx].NumRows() >= l.chunks[chkIdx].Capacity() || chkIdx == l.consumedIdx {
    93  		newChk := l.allocChunk()
    94  		l.chunks = append(l.chunks, newChk)
    95  		if chkIdx != l.consumedIdx {
    96  			l.memTracker.Consume(l.chunks[chkIdx].MemoryUsage())
    97  			l.consumedIdx = chkIdx
    98  		}
    99  		chkIdx++
   100  	}
   101  	chk := l.chunks[chkIdx]
   102  	rowIdx := chk.NumRows()
   103  	chk.AppendRow(event)
   104  	l.length++
   105  	return RowPtr{ChkIdx: uint32(chkIdx), RowIdx: uint32(rowIdx)}
   106  }
   107  
   108  // Add adds a chunk to the List, the chunk may be modified later by the list.
   109  // Caller must make sure the input chk is not empty and not used any more and has the same field types.
   110  func (l *List) Add(chk *Chunk) {
   111  	// FixMe: we should avoid add a Chunk that chk.NumRows() > list.maxChunkSize.
   112  	if chk.NumRows() == 0 {
   113  		// TODO: return error here.
   114  		panic("chunk appended to List should have at least 1 event")
   115  	}
   116  	if chkIdx := len(l.chunks) - 1; l.consumedIdx != chkIdx {
   117  		l.memTracker.Consume(l.chunks[chkIdx].MemoryUsage())
   118  		l.consumedIdx = chkIdx
   119  	}
   120  	l.memTracker.Consume(chk.MemoryUsage())
   121  	l.consumedIdx++
   122  	l.chunks = append(l.chunks, chk)
   123  	l.length += chk.NumRows()
   124  }
   125  
   126  func (l *List) allocChunk() (chk *Chunk) {
   127  	if len(l.freelist) > 0 {
   128  		lastIdx := len(l.freelist) - 1
   129  		chk = l.freelist[lastIdx]
   130  		l.freelist = l.freelist[:lastIdx]
   131  		l.memTracker.Consume(-chk.MemoryUsage())
   132  		chk.Reset()
   133  		return
   134  	}
   135  	if len(l.chunks) > 0 {
   136  		return Renew(l.chunks[len(l.chunks)-1], l.maxChunkSize)
   137  	}
   138  	return New(l.fieldTypes, l.initChunkSize, l.maxChunkSize)
   139  }
   140  
   141  // GetRow gets a Row from the list by RowPtr.
   142  func (l *List) GetRow(ptr RowPtr) Row {
   143  	chk := l.chunks[ptr.ChkIdx]
   144  	return chk.GetRow(int(ptr.RowIdx))
   145  }
   146  
   147  // Reset resets the List.
   148  func (l *List) Reset() {
   149  	if lastIdx := len(l.chunks) - 1; lastIdx != l.consumedIdx {
   150  		l.memTracker.Consume(l.chunks[lastIdx].MemoryUsage())
   151  	}
   152  	l.freelist = append(l.freelist, l.chunks...)
   153  	l.chunks = l.chunks[:0]
   154  	l.length = 0
   155  	l.consumedIdx = -1
   156  }
   157  
   158  // Clear triggers GC for all the allocated chunks and reset the list
   159  func (l *List) Clear() {
   160  	l.memTracker.Consume(-l.memTracker.BytesConsumed())
   161  	l.freelist = nil
   162  	l.chunks = nil
   163  	l.length = 0
   164  	l.consumedIdx = -1
   165  }
   166  
   167  // preAlloc4Row pre-allocates the storage memory for a Row.
   168  // NOTE: only used in test
   169  // 1. The List must be empty or holds no useful data.
   170  // 2. The schemaReplicant of the Row must be the same with the List.
   171  // 3. This API is paired with the `Insert()` function, which inserts all the
   172  //    rows data into the List after the pre-allocation.
   173  func (l *List) preAlloc4Row(event Row) (ptr RowPtr) {
   174  	chkIdx := len(l.chunks) - 1
   175  	if chkIdx == -1 || l.chunks[chkIdx].NumRows() >= l.chunks[chkIdx].Capacity() {
   176  		newChk := l.allocChunk()
   177  		l.chunks = append(l.chunks, newChk)
   178  		if chkIdx != l.consumedIdx {
   179  			l.memTracker.Consume(l.chunks[chkIdx].MemoryUsage())
   180  			l.consumedIdx = chkIdx
   181  		}
   182  		chkIdx++
   183  	}
   184  	chk := l.chunks[chkIdx]
   185  	rowIdx := chk.preAlloc(event)
   186  	l.length++
   187  	return RowPtr{ChkIdx: uint32(chkIdx), RowIdx: rowIdx}
   188  }
   189  
   190  // Insert inserts `event` on the position specified by `ptr`.
   191  // Note: Insert will cover the origin data, it should be called after
   192  // PreAlloc.
   193  func (l *List) Insert(ptr RowPtr, event Row) {
   194  	l.chunks[ptr.ChkIdx].insert(int(ptr.RowIdx), event)
   195  }
   196  
   197  // ListWalkFunc is used to walk the list.
   198  // If error is returned, it will stop walking.
   199  type ListWalkFunc = func(event Row) error
   200  
   201  // Walk iterate the list and call walkFunc for each event.
   202  func (l *List) Walk(walkFunc ListWalkFunc) error {
   203  	for i := 0; i < len(l.chunks); i++ {
   204  		chk := l.chunks[i]
   205  		for j := 0; j < chk.NumRows(); j++ {
   206  			err := walkFunc(chk.GetRow(j))
   207  			if err != nil {
   208  				return errors.Trace(err)
   209  			}
   210  		}
   211  	}
   212  	return nil
   213  }