github.com/m3db/m3@v1.5.0/src/dbnode/storage/bootstrap/result/result_data.go (about) 1 // Copyright (c) 2016 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package result 22 23 import ( 24 "github.com/m3db/m3/src/dbnode/storage/block" 25 "github.com/m3db/m3/src/x/ident" 26 xtime "github.com/m3db/m3/src/x/time" 27 ) 28 29 type dataBootstrapResult struct { 30 unfulfilled ShardTimeRanges 31 } 32 33 // NewDataBootstrapResult creates a new result. 34 func NewDataBootstrapResult() DataBootstrapResult { 35 return &dataBootstrapResult{ 36 unfulfilled: NewShardTimeRanges(), 37 } 38 } 39 40 func (r *dataBootstrapResult) Unfulfilled() ShardTimeRanges { 41 return r.unfulfilled 42 } 43 44 func (r *dataBootstrapResult) SetUnfulfilled(unfulfilled ShardTimeRanges) { 45 r.unfulfilled = unfulfilled 46 } 47 48 // MergedDataBootstrapResult returns a merged result of two bootstrap results. 49 // It is a mutating function that mutates the larger result by adding the 50 // smaller result to it and then finally returns the mutated result. 51 func MergedDataBootstrapResult(i, j DataBootstrapResult) DataBootstrapResult { 52 if i == nil { 53 return j 54 } 55 if j == nil { 56 return i 57 } 58 i.Unfulfilled().AddRanges(j.Unfulfilled()) 59 return i 60 } 61 62 type shardResult struct { 63 opts Options 64 blocks *Map 65 } 66 67 // NewShardResult creates a new shard result. 68 func NewShardResult(opts Options) ShardResult { 69 return &shardResult{ 70 opts: opts, 71 blocks: NewMap(MapOptions{ 72 KeyCopyPool: opts.DatabaseBlockOptions().BytesPool().BytesPool(), 73 }), 74 } 75 } 76 77 // IsEmpty returns whether the result is empty. 78 func (sr *shardResult) IsEmpty() bool { 79 return sr.blocks.Len() == 0 80 } 81 82 // AddBlock adds a data block. 83 func (sr *shardResult) AddBlock(id ident.ID, tags ident.Tags, b block.DatabaseBlock) { 84 curSeries, exists := sr.blocks.Get(id) 85 if !exists { 86 curSeries = sr.newBlocks(id, tags) 87 sr.blocks.Set(id, curSeries) 88 } 89 curSeries.Blocks.AddBlock(b) 90 } 91 92 // AddSeries adds a single series. 93 func (sr *shardResult) AddSeries(id ident.ID, tags ident.Tags, rawSeries block.DatabaseSeriesBlocks) { 94 curSeries, exists := sr.blocks.Get(id) 95 if !exists { 96 curSeries = sr.newBlocks(id, tags) 97 sr.blocks.Set(id, curSeries) 98 } 99 curSeries.Blocks.AddSeries(rawSeries) 100 } 101 102 func (sr *shardResult) newBlocks(id ident.ID, tags ident.Tags) DatabaseSeriesBlocks { 103 size := sr.opts.NewBlocksLen() 104 return DatabaseSeriesBlocks{ 105 ID: id, 106 Tags: tags, 107 Blocks: block.NewDatabaseSeriesBlocks(size), 108 } 109 } 110 111 // AddResult adds a shard result. 112 func (sr *shardResult) AddResult(other ShardResult) { 113 if other == nil { 114 return 115 } 116 otherSeries := other.AllSeries() 117 for _, entry := range otherSeries.Iter() { 118 series := entry.Value() 119 sr.AddSeries(series.ID, series.Tags, series.Blocks) 120 } 121 } 122 123 // RemoveBlockAt removes a data block at a given timestamp 124 func (sr *shardResult) RemoveBlockAt(id ident.ID, t xtime.UnixNano) { 125 curSeries, exists := sr.blocks.Get(id) 126 if !exists { 127 return 128 } 129 curSeries.Blocks.RemoveBlockAt(t) 130 if curSeries.Blocks.Len() == 0 { 131 sr.RemoveSeries(id) 132 } 133 } 134 135 // RemoveSeries removes a single series of blocks. 136 func (sr *shardResult) RemoveSeries(id ident.ID) { 137 sr.blocks.Delete(id) 138 } 139 140 // AllSeries returns all series in the map. 141 func (sr *shardResult) AllSeries() *Map { 142 return sr.blocks 143 } 144 145 func (sr *shardResult) NumSeries() int64 { 146 return int64(sr.blocks.Len()) 147 } 148 149 func (sr *shardResult) BlockAt(id ident.ID, t xtime.UnixNano) (block.DatabaseBlock, bool) { 150 series, exists := sr.blocks.Get(id) 151 if !exists { 152 return nil, false 153 } 154 return series.Blocks.BlockAt(t) 155 } 156 157 // Close closes a shard result. 158 func (sr *shardResult) Close() { 159 for _, entry := range sr.blocks.Iter() { 160 series := entry.Value() 161 series.Blocks.Close() 162 } 163 } 164 165 // NumSeries returns the number of series' across all shards. 166 func (r ShardResults) NumSeries() int64 { 167 var numSeries int64 168 for _, result := range r { 169 numSeries += result.NumSeries() 170 } 171 return numSeries 172 } 173 174 // AddResults adds other shard results to the current shard results. 175 func (r ShardResults) AddResults(other ShardResults) { 176 for shard, result := range other { 177 if result == nil || result.NumSeries() == 0 { 178 continue 179 } 180 if existing, ok := r[shard]; ok { 181 existing.AddResult(result) 182 } else { 183 r[shard] = result 184 } 185 } 186 } 187 188 // Equal returns whether another shard results is equal to the current shard results, 189 // will not perform a deep equal only a shallow equal of series and their block addresses. 190 func (r ShardResults) Equal(other ShardResults) bool { 191 for shard, result := range r { 192 otherResult, ok := r[shard] 193 if !ok { 194 return false 195 } 196 allSeries := result.AllSeries() 197 otherAllSeries := otherResult.AllSeries() 198 if allSeries.Len() != otherAllSeries.Len() { 199 return false 200 } 201 for _, entry := range allSeries.Iter() { 202 id, series := entry.Key(), entry.Value() 203 otherSeries, ok := otherAllSeries.Get(id) 204 if !ok { 205 return false 206 } 207 allBlocks := series.Blocks.AllBlocks() 208 otherAllBlocks := otherSeries.Blocks.AllBlocks() 209 if len(allBlocks) != len(otherAllBlocks) { 210 return false 211 } 212 for start, block := range allBlocks { 213 otherBlock, ok := otherAllBlocks[start] 214 if !ok { 215 return false 216 } 217 // Just performing shallow equals so simply compare block addresses 218 if block != otherBlock { 219 return false 220 } 221 } 222 } 223 } 224 return true 225 } 226 227 // EstimateMapBytesSize estimates the size (in bytes) of the results map. It's only an 228 // estimate because its impossible to know if some of the references like the series 229 // name as well as tags are exclusive to this object or shared with other structures in 230 // memory. 231 func EstimateMapBytesSize(m *Map) int64 { 232 if m == nil { 233 return 0 234 } 235 236 var sum int64 237 for _, elem := range m.Iter() { 238 id := elem.Key() 239 sum += int64(len(id.Bytes())) 240 241 blocks := elem.Value() 242 for _, tag := range blocks.Tags.Values() { 243 // Name/Value should never be nil but be precautious. 244 if tag.Name != nil { 245 sum += int64(len(tag.Name.Bytes())) 246 } 247 if tag.Value != nil { 248 sum += int64(len(tag.Value.Bytes())) 249 } 250 } 251 for _, block := range blocks.Blocks.AllBlocks() { 252 sum += int64(block.Len()) 253 } 254 } 255 return sum 256 }