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 }