github.com/m3db/m3@v1.5.0/src/query/storage/m3/encoded_block_builder.go (about) 1 // Copyright (c) 2018 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 m3 22 23 import ( 24 "bytes" 25 "sort" 26 27 "github.com/m3db/m3/src/dbnode/encoding" 28 "github.com/m3db/m3/src/query/block" 29 "github.com/m3db/m3/src/query/models" 30 "github.com/m3db/m3/src/query/storage/m3/consolidators" 31 "github.com/m3db/m3/src/x/ident" 32 xtime "github.com/m3db/m3/src/x/time" 33 ) 34 35 const initBlockLength = 10 36 37 type blockAtTime struct { 38 time xtime.UnixNano 39 block encodedBlock 40 } 41 42 type blocksAtTime []blockAtTime 43 44 type encodedBlockBuilder struct { 45 resultMeta block.ResultMetadata 46 blocksAtTime blocksAtTime 47 options Options 48 } 49 50 func newEncodedBlockBuilder( 51 result consolidators.SeriesFetchResult, 52 options Options, 53 ) *encodedBlockBuilder { 54 return &encodedBlockBuilder{ 55 resultMeta: result.Metadata, 56 blocksAtTime: make(blocksAtTime, 0, initBlockLength), 57 options: options, 58 } 59 } 60 61 func (b *encodedBlockBuilder) add( 62 iter encoding.SeriesIterator, 63 tags models.Tags, 64 meta block.ResultMetadata, 65 bounds models.Bounds, 66 lastBlock bool, 67 ) error { 68 start := bounds.Start 69 consolidation := consolidationSettings{ 70 consolidationFn: b.options.ConsolidationFunc(), 71 currentTime: start, 72 bounds: bounds, 73 } 74 75 seriesMeta := block.SeriesMeta{ 76 Name: iter.ID().Bytes(), 77 Tags: tags, 78 } 79 80 for idx, bl := range b.blocksAtTime { 81 if bl.time.Equal(start) { 82 block := bl.block 83 block.seriesBlockIterators = append(block.seriesBlockIterators, iter) 84 block.seriesMetas = append(block.seriesMetas, seriesMeta) 85 b.blocksAtTime[idx].block = block 86 return nil 87 } 88 } 89 90 bl, err := newEncodedBlock( 91 consolidators.NewEmptyFetchResult(meta), 92 consolidation, 93 lastBlock, 94 b.options, 95 ) 96 97 if err != nil { 98 return err 99 } 100 101 bl.seriesBlockIterators = append(bl.seriesBlockIterators, iter) 102 bl.seriesMetas = append(bl.seriesMetas, seriesMeta) 103 b.blocksAtTime = append(b.blocksAtTime, blockAtTime{ 104 time: start, 105 block: *bl, 106 }) 107 108 return nil 109 } 110 111 func (b *encodedBlockBuilder) build() ([]block.Block, error) { 112 if err := b.backfillMissing(); err != nil { 113 return nil, err 114 } 115 116 // Sort blocks by ID. 117 for _, bl := range b.blocksAtTime { 118 sort.Sort(seriesIteratorByID(bl.block.seriesBlockIterators)) 119 } 120 121 blocks := make([]block.Block, 0, len(b.blocksAtTime)) 122 for _, bl := range b.blocksAtTime { 123 block := bl.block 124 blocks = append(blocks, &block) 125 } 126 127 return blocks, nil 128 } 129 130 // Since some series can be missing if there are no values in composing blocks, 131 // need to backfill these, since downstream functions rely on series order. 132 // 133 // TODO: this will be removed after https://github.com/m3db/m3/issues/1281 is 134 // completed, which will allow temporal functions, and final read accumulator 135 // to empty series, and do its own matching rather than relying on matching 136 // series order. This is functionally throwaway code and should be regarded 137 // as such. 138 func (b *encodedBlockBuilder) backfillMissing() error { 139 // map series to indeces of b.blocksAtTime at which they are seen 140 seenMap := make(map[string]seriesIteratorDetails, initBlockReplicaLength) 141 for idx, bl := range b.blocksAtTime { 142 block := bl.block 143 for i, iter := range block.seriesBlockIterators { 144 id := iter.ID().String() 145 if seen, found := seenMap[id]; !found { 146 seenMap[id] = seriesIteratorDetails{ 147 start: iter.Start(), 148 end: iter.End(), 149 id: iter.ID(), 150 ns: iter.Namespace(), 151 tags: block.seriesMetas[i].Tags, 152 present: []int{idx}, 153 } 154 } else { 155 seen.present = append(seen.present, idx) 156 seenMap[id] = seen 157 } 158 } 159 } 160 161 // make sure that each seen series exists in every block. 162 blockLen := len(b.blocksAtTime) 163 for _, iterDetails := range seenMap { 164 present := iterDetails.present 165 if len(present) == blockLen { 166 // This series exists in every block already, thus no backfilling necessary. 167 continue 168 } 169 170 for blockIdx, bl := range b.blocksAtTime { 171 found := false 172 for _, presentVal := range present { 173 if presentVal == blockIdx { 174 found = true 175 break 176 } 177 } 178 179 // this series exists in the current block. 180 if found { 181 continue 182 } 183 184 iter := encoding.NewSeriesIterator(encoding.SeriesIteratorOptions{ 185 ID: ident.StringID(iterDetails.id.String()), 186 Namespace: ident.StringID(iterDetails.ns.String()), 187 StartInclusive: iterDetails.start, 188 EndExclusive: iterDetails.end, 189 }, nil) 190 191 newBl := bl.block 192 newBl.seriesBlockIterators = append(newBl.seriesBlockIterators, iter) 193 newBl.seriesMetas = append(newBl.seriesMetas, block.SeriesMeta{ 194 Name: iter.ID().Bytes(), 195 Tags: iterDetails.tags, 196 }) 197 198 b.blocksAtTime[blockIdx].block = newBl 199 } 200 } 201 202 return nil 203 } 204 205 type seriesIteratorDetails struct { 206 start, end xtime.UnixNano 207 id, ns ident.ID 208 tags models.Tags 209 // NB: the indices that this series iterator exists in already. 210 present []int 211 } 212 213 type seriesIteratorByID []encoding.SeriesIterator 214 215 func (s seriesIteratorByID) Len() int { return len(s) } 216 func (s seriesIteratorByID) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 217 func (s seriesIteratorByID) Less(i, j int) bool { 218 return bytes.Compare(s[i].ID().Bytes(), s[j].ID().Bytes()) == -1 219 }