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  }