github.com/m3db/m3@v1.5.0/src/dbnode/storage/bootstrap/bootstrapper/base.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 bootstrapper
    22  
    23  import (
    24  	"fmt"
    25  
    26  	"github.com/m3db/m3/src/dbnode/storage/bootstrap"
    27  	"github.com/m3db/m3/src/dbnode/storage/bootstrap/result"
    28  	"github.com/m3db/m3/src/x/context"
    29  
    30  	"go.uber.org/zap"
    31  	"go.uber.org/zap/zapcore"
    32  )
    33  
    34  const (
    35  	baseBootstrapperName = "base"
    36  )
    37  
    38  // baseBootstrapper provides a skeleton for the interface methods.
    39  type baseBootstrapper struct {
    40  	opts result.Options
    41  	log  *zap.Logger
    42  	name string
    43  	src  bootstrap.Source
    44  	next bootstrap.Bootstrapper
    45  }
    46  
    47  // NewBaseBootstrapper creates a new base bootstrapper.
    48  func NewBaseBootstrapper(
    49  	name string,
    50  	src bootstrap.Source,
    51  	opts result.Options,
    52  	next bootstrap.Bootstrapper,
    53  ) (bootstrap.Bootstrapper, error) {
    54  	var (
    55  		bs  = next
    56  		err error
    57  	)
    58  	if next == nil {
    59  		bs, err = NewNoOpNoneBootstrapperProvider().Provide()
    60  		if err != nil {
    61  			return nil, err
    62  		}
    63  	}
    64  	return baseBootstrapper{
    65  		opts: opts,
    66  		log:  opts.InstrumentOptions().Logger(),
    67  		name: name,
    68  		src:  src,
    69  		next: bs,
    70  	}, nil
    71  }
    72  
    73  // String returns the name of the bootstrapper.
    74  func (b baseBootstrapper) String() string {
    75  	return baseBootstrapperName
    76  }
    77  
    78  func (b baseBootstrapper) Bootstrap(
    79  	ctx context.Context,
    80  	namespaces bootstrap.Namespaces,
    81  	cache bootstrap.Cache,
    82  ) (bootstrap.NamespaceResults, error) {
    83  	logFields := []zapcore.Field{
    84  		zap.String("bootstrapper", b.name),
    85  	}
    86  
    87  	curr := bootstrap.Namespaces{
    88  		Namespaces: bootstrap.NewNamespacesMap(bootstrap.NamespacesMapOptions{}),
    89  	}
    90  	for _, elem := range namespaces.Namespaces.Iter() {
    91  		id := elem.Key()
    92  
    93  		// Shallow copy the namespace, do not modify namespaces input to bootstrap call.
    94  		currNamespace := elem.Value()
    95  
    96  		b.logShardTimeRanges("bootstrap from source requested",
    97  			logFields, currNamespace)
    98  
    99  		dataAvailable, err := b.src.AvailableData(currNamespace.Metadata,
   100  			currNamespace.DataRunOptions.ShardTimeRanges.Copy(), cache,
   101  			currNamespace.DataRunOptions.RunOptions)
   102  		if err != nil {
   103  			return bootstrap.NamespaceResults{}, err
   104  		}
   105  
   106  		currNamespace.DataRunOptions.ShardTimeRanges = dataAvailable
   107  
   108  		// Prepare index if required.
   109  		if currNamespace.Metadata.Options().IndexOptions().Enabled() {
   110  			indexAvailable, err := b.src.AvailableIndex(currNamespace.Metadata,
   111  				currNamespace.IndexRunOptions.ShardTimeRanges.Copy(), cache,
   112  				currNamespace.IndexRunOptions.RunOptions)
   113  			if err != nil {
   114  				return bootstrap.NamespaceResults{}, err
   115  			}
   116  
   117  			currNamespace.IndexRunOptions.ShardTimeRanges = indexAvailable
   118  		}
   119  
   120  		// Set the namespace options for the current bootstrapper source.
   121  		curr.Namespaces.Set(id, currNamespace)
   122  
   123  		// Log the metadata about bootstrapping this namespace based on
   124  		// the availability returned.
   125  		b.logShardTimeRanges("bootstrap from source ready after availability query",
   126  			logFields, currNamespace)
   127  	}
   128  
   129  	nowFn := b.opts.ClockOptions().NowFn()
   130  	begin := nowFn()
   131  
   132  	// Run the bootstrap source begin hook.
   133  	b.log.Info("bootstrap from source hook begin started", logFields...)
   134  	if err := namespaces.Hooks().BootstrapSourceBegin(); err != nil {
   135  		return bootstrap.NamespaceResults{}, err
   136  	}
   137  
   138  	b.log.Info("bootstrap from source started", logFields...)
   139  
   140  	// Run the bootstrap source.
   141  	currResults, err := b.src.Read(ctx, curr, cache)
   142  
   143  	logFields = append(logFields, zap.Duration("took", nowFn().Sub(begin)))
   144  	if err != nil {
   145  		errorLogFields := append(logFieldsCopy(logFields), zap.Error(err))
   146  		b.log.Error("error bootstrapping from source", errorLogFields...)
   147  		return bootstrap.NamespaceResults{}, err
   148  	}
   149  
   150  	// Run the bootstrap source end hook.
   151  	b.log.Info("bootstrap from source hook end started", logFields...)
   152  	if err := namespaces.Hooks().BootstrapSourceEnd(); err != nil {
   153  		return bootstrap.NamespaceResults{}, err
   154  	}
   155  
   156  	b.log.Info("bootstrap from source completed", logFields...)
   157  	// Determine the unfulfilled and the unattempted ranges to execute next.
   158  	next, err := b.logSuccessAndDetermineCurrResultsUnfulfilledAndNextBootstrapRanges(namespaces,
   159  		curr, currResults, logFields)
   160  	if err != nil {
   161  		return bootstrap.NamespaceResults{}, err
   162  	}
   163  
   164  	// Unless next bootstrapper is required, this is the final results.
   165  	finalResults := currResults
   166  
   167  	// If there are some time ranges the current bootstrapper could not fulfill,
   168  	// that we can attempt then pass it along to the next bootstrapper.
   169  	if next.Namespaces.Len() > 0 {
   170  		nextResults, err := b.next.Bootstrap(ctx, next, cache)
   171  		if err != nil {
   172  			return bootstrap.NamespaceResults{}, err
   173  		}
   174  
   175  		// Now merge the final results.
   176  		for _, elem := range nextResults.Results.Iter() {
   177  			id := elem.Key()
   178  			currNamespace := elem.Value()
   179  
   180  			finalResult, ok := finalResults.Results.Get(id)
   181  			if !ok {
   182  				return bootstrap.NamespaceResults{},
   183  					fmt.Errorf("expected result for namespace: %s", id.String())
   184  			}
   185  
   186  			// NB(r): Since we originally passed all unfulfilled ranges to the
   187  			// next bootstrapper, the final unfulfilled is simply what it could
   188  			// not fulfill.
   189  			finalResult.DataResult.SetUnfulfilled(currNamespace.DataResult.Unfulfilled().Copy())
   190  			if currNamespace.Metadata.Options().IndexOptions().Enabled() {
   191  				finalResult.IndexResult.SetUnfulfilled(currNamespace.IndexResult.Unfulfilled().Copy())
   192  			}
   193  
   194  			// Map is by value, set the result altered struct.
   195  			finalResults.Results.Set(id, finalResult)
   196  		}
   197  	}
   198  
   199  	return finalResults, nil
   200  }
   201  
   202  func (b baseBootstrapper) logSuccessAndDetermineCurrResultsUnfulfilledAndNextBootstrapRanges(
   203  	requested bootstrap.Namespaces,
   204  	curr bootstrap.Namespaces,
   205  	currResults bootstrap.NamespaceResults,
   206  	baseLogFields []zapcore.Field,
   207  ) (bootstrap.Namespaces, error) {
   208  	next := bootstrap.Namespaces{
   209  		Namespaces: bootstrap.NewNamespacesMap(bootstrap.NamespacesMapOptions{}),
   210  	}
   211  	for _, elem := range requested.Namespaces.Iter() {
   212  		id := elem.Key()
   213  		requestedNamespace := elem.Value()
   214  
   215  		currResult, ok := currResults.Results.Get(id)
   216  		if !ok {
   217  			return bootstrap.Namespaces{},
   218  				fmt.Errorf("namespace result not returned by bootstrapper: %v", id.String())
   219  		}
   220  
   221  		currNamespace, ok := curr.Namespaces.Get(id)
   222  		if !ok {
   223  			return bootstrap.Namespaces{},
   224  				fmt.Errorf("namespace prepared request not found: %v", id.String())
   225  		}
   226  
   227  		// Shallow copy the current namespace for the next namespace prepared request.
   228  		nextNamespace := currNamespace
   229  
   230  		// Calculate bootstrap time ranges.
   231  		dataRequired := requestedNamespace.DataRunOptions.ShardTimeRanges.Copy()
   232  		dataCurrRequested := currNamespace.DataRunOptions.ShardTimeRanges.Copy()
   233  		dataCurrFulfilled := dataCurrRequested.Copy()
   234  		dataCurrFulfilled.Subtract(currResult.DataResult.Unfulfilled())
   235  
   236  		dataUnfulfilled := dataRequired.Copy()
   237  		dataUnfulfilled.Subtract(dataCurrFulfilled)
   238  
   239  		// Modify the unfulfilled result.
   240  		currResult.DataResult.SetUnfulfilled(dataUnfulfilled.Copy())
   241  
   242  		// Set the next bootstrapper required ranges.
   243  		nextNamespace.DataRunOptions.ShardTimeRanges = dataUnfulfilled.Copy()
   244  
   245  		var (
   246  			indexCurrRequested = result.NewShardTimeRanges()
   247  			indexCurrFulfilled = result.NewShardTimeRanges()
   248  			indexUnfulfilled   = result.NewShardTimeRanges()
   249  		)
   250  		if currNamespace.Metadata.Options().IndexOptions().Enabled() {
   251  			// Calculate bootstrap time ranges.
   252  			indexRequired := requestedNamespace.IndexRunOptions.ShardTimeRanges.Copy()
   253  			indexCurrRequested = currNamespace.IndexRunOptions.ShardTimeRanges.Copy()
   254  			indexCurrFulfilled = indexCurrRequested.Copy()
   255  			indexCurrFulfilled.Subtract(currResult.IndexResult.Unfulfilled())
   256  
   257  			indexUnfulfilled = indexRequired.Copy()
   258  			indexUnfulfilled.Subtract(indexCurrFulfilled)
   259  
   260  			// Modify the unfulfilled result.
   261  			currResult.IndexResult.SetUnfulfilled(indexUnfulfilled.Copy())
   262  		}
   263  
   264  		// Set the next bootstrapper required ranges.
   265  		// NB(r): Make sure to always set an empty requested range so IsEmpty
   266  		// does not cause nil ptr deref.
   267  		nextNamespace.IndexRunOptions.ShardTimeRanges = indexUnfulfilled.Copy()
   268  
   269  		// Set the modified result.
   270  		currResults.Results.Set(id, currResult)
   271  
   272  		// Always set the next bootstrapper namespace run options regardless of
   273  		// whether there are unfulfilled index/data shard time ranges.
   274  		// NB(bodu): We perform short circuiting directly in the peers bootstrapper and the
   275  		// commitlog bootstrapper should always run for all time ranges.
   276  		next.Namespaces.Set(id, nextNamespace)
   277  
   278  		// Log the result.
   279  		_, _, dataRangeRequested := dataCurrRequested.MinMaxRange()
   280  		_, _, dataRangeFulfilled := dataCurrFulfilled.MinMaxRange()
   281  		successLogFields := append(logFieldsCopy(baseLogFields),
   282  			zap.String("namespace", id.String()),
   283  			zap.Int("numShards", len(currNamespace.Shards)),
   284  			zap.Duration("dataRangeRequested", dataRangeRequested),
   285  			zap.Duration("dataRangeFulfilled", dataRangeFulfilled),
   286  		)
   287  
   288  		if currNamespace.Metadata.Options().IndexOptions().Enabled() {
   289  			_, _, indexRangeRequested := indexCurrRequested.MinMaxRange()
   290  			_, _, indexRangeFulfilled := indexCurrFulfilled.MinMaxRange()
   291  			successLogFields = append(successLogFields,
   292  				zap.Duration("indexRangeRequested", indexRangeRequested),
   293  				zap.Duration("indexRangeFulfilled", indexRangeFulfilled),
   294  				zap.Int("numIndexBlocks", len(currResult.IndexResult.IndexResults())),
   295  			)
   296  		}
   297  
   298  		b.log.Info("bootstrapping from source completed successfully",
   299  			successLogFields...)
   300  	}
   301  
   302  	return next, nil
   303  }
   304  
   305  func (b baseBootstrapper) logShardTimeRanges(
   306  	msg string,
   307  	baseLogFields []zapcore.Field,
   308  	currNamespace bootstrap.Namespace,
   309  ) {
   310  	dataShardTimeRanges := currNamespace.DataRunOptions.ShardTimeRanges
   311  	dataMin, dataMax, dataRange := dataShardTimeRanges.MinMaxRange()
   312  	logFields := append(logFieldsCopy(baseLogFields),
   313  		zap.Stringer("namespace", currNamespace.Metadata.ID()),
   314  		zap.Int("numShards", len(currNamespace.Shards)),
   315  		zap.Duration("dataRange", dataRange),
   316  	)
   317  	if dataRange > 0 {
   318  		logFields = append(logFields,
   319  			zap.Time("dataFrom", dataMin.ToTime()),
   320  			zap.Time("dataTo", dataMax.ToTime()),
   321  		)
   322  	}
   323  	if currNamespace.Metadata.Options().IndexOptions().Enabled() {
   324  		indexShardTimeRanges := currNamespace.IndexRunOptions.ShardTimeRanges
   325  		indexMin, indexMax, indexRange := indexShardTimeRanges.MinMaxRange()
   326  		logFields = append(logFields,
   327  			zap.Duration("indexRange", indexRange),
   328  		)
   329  		if indexRange > 0 {
   330  			logFields = append(logFields,
   331  				zap.Time("indexFrom", indexMin.ToTime()),
   332  				zap.Time("indexTo", indexMax.ToTime()),
   333  			)
   334  		}
   335  	}
   336  
   337  	b.log.Info(msg, logFields...)
   338  }
   339  
   340  func logFieldsCopy(logFields []zapcore.Field) []zapcore.Field {
   341  	return append(make([]zapcore.Field, 0, 2*len(logFields)), logFields...)
   342  }