github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/storage/bootstrap_instrumentation.go (about)

     1  // Copyright (c) 2021 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 storage
    22  
    23  import (
    24  	"github.com/uber-go/tally"
    25  	"go.uber.org/zap"
    26  	"go.uber.org/zap/zapcore"
    27  
    28  	"github.com/m3db/m3/src/dbnode/storage/bootstrap"
    29  	"github.com/m3db/m3/src/x/clock"
    30  	xerrors "github.com/m3db/m3/src/x/errors"
    31  	"github.com/m3db/m3/src/x/ident"
    32  	"github.com/m3db/m3/src/x/instrument"
    33  	xtime "github.com/m3db/m3/src/x/time"
    34  )
    35  
    36  type instrumentationContext struct {
    37  	start                       xtime.UnixNano
    38  	log                         *zap.Logger
    39  	logFields                   []zapcore.Field
    40  	bootstrapDuration           tally.Timer
    41  	bootstrapNamespacesDuration tally.Timer
    42  	nowFn                       clock.NowFn
    43  	instrumentOptions           instrument.Options
    44  }
    45  
    46  func newInstrumentationContext(
    47  	nowFn clock.NowFn,
    48  	log *zap.Logger,
    49  	scope tally.Scope,
    50  	opts Options,
    51  ) *instrumentationContext {
    52  	return &instrumentationContext{
    53  		start:                       xtime.ToUnixNano(nowFn()),
    54  		log:                         log,
    55  		nowFn:                       nowFn,
    56  		bootstrapDuration:           scope.Timer("bootstrap-duration"),
    57  		bootstrapNamespacesDuration: scope.Timer("bootstrap-namespaces-duration"),
    58  		instrumentOptions:           opts.InstrumentOptions(),
    59  	}
    60  }
    61  
    62  func (i *instrumentationContext) bootstrapStarted(shards int) {
    63  	i.logFields = append(i.logFields, zap.Int("numShards", shards))
    64  	i.log.Info("bootstrap started", i.logFields...)
    65  }
    66  
    67  func (i *instrumentationContext) bootstrapSucceeded() {
    68  	bootstrapDuration := xtime.ToUnixNano(i.nowFn()).Sub(i.start)
    69  	i.bootstrapDuration.Record(bootstrapDuration)
    70  	i.logFields = append(i.logFields, zap.Duration("bootstrapDuration", bootstrapDuration))
    71  	i.log.Info("bootstrap succeeded, marking namespaces complete", i.logFields...)
    72  }
    73  
    74  func (i *instrumentationContext) bootstrapFailed(err error) {
    75  	bootstrapDuration := xtime.ToUnixNano(i.nowFn()).Sub(i.start)
    76  	i.bootstrapDuration.Record(bootstrapDuration)
    77  	i.logFields = append(i.logFields, zap.Duration("bootstrapDuration", bootstrapDuration))
    78  	i.log.Error("bootstrap failed", append(i.logFields, zap.Error(err))...)
    79  }
    80  
    81  func (i *instrumentationContext) bootstrapNamespacesStarted() {
    82  	i.start = xtime.ToUnixNano(i.nowFn())
    83  	i.log.Info("bootstrap namespaces start", i.logFields...)
    84  }
    85  
    86  func (i *instrumentationContext) bootstrapNamespacesSucceeded() {
    87  	duration := xtime.ToUnixNano(i.nowFn()).Sub(i.start)
    88  	i.bootstrapNamespacesDuration.Record(duration)
    89  	i.logFields = append(i.logFields, zap.Duration("bootstrapNamespacesDuration", duration))
    90  	i.log.Info("bootstrap namespaces success", i.logFields...)
    91  }
    92  
    93  func (i *instrumentationContext) bootstrapNamespaceFailed(
    94  	err error,
    95  	namespaceID ident.ID,
    96  ) {
    97  	i.log.Info("bootstrap namespace error", append(i.logFields,
    98  		zap.String("namespace", namespaceID.String()),
    99  		zap.Error(err))...)
   100  }
   101  
   102  func (i *instrumentationContext) bootstrapNamespacesFailed(err error) {
   103  	duration := xtime.ToUnixNano(i.nowFn()).Sub(i.start)
   104  	i.bootstrapNamespacesDuration.Record(duration)
   105  	i.logFields = append(i.logFields, zap.Duration("bootstrapNamespacesDuration", duration))
   106  	i.log.Info("bootstrap namespaces failed", append(i.logFields, zap.Error(err))...)
   107  }
   108  
   109  func (i *instrumentationContext) emitAndLogInvariantViolation(err error, msg string) {
   110  	instrument.EmitAndLogInvariantViolation(i.instrumentOptions, func(l *zap.Logger) {
   111  		l.Error(msg, append(i.logFields, zap.Error(err))...)
   112  	})
   113  }
   114  
   115  type bootstrapRetriesMetrics struct {
   116  	obsoleteRanges tally.Counter
   117  	other          tally.Counter
   118  }
   119  
   120  func newBootstrapRetriesMetrics(scope tally.Scope) bootstrapRetriesMetrics {
   121  	const metricName = "bootstrap-retries"
   122  
   123  	reason := func(reason string) map[string]string {
   124  		return map[string]string{
   125  			"reason": reason,
   126  		}
   127  	}
   128  	return bootstrapRetriesMetrics{
   129  		obsoleteRanges: scope.Tagged(reason("obsolete-ranges")).Counter(metricName),
   130  		other:          scope.Tagged(reason("other")).Counter(metricName),
   131  	}
   132  }
   133  
   134  type bootstrapInstrumentation struct {
   135  	opts          Options
   136  	scope         tally.Scope
   137  	log           *zap.Logger
   138  	nowFn         clock.NowFn
   139  	status        tally.Gauge
   140  	durableStatus tally.Gauge
   141  	numRetries    bootstrapRetriesMetrics
   142  }
   143  
   144  func newBootstrapInstrumentation(opts Options) *bootstrapInstrumentation {
   145  	scope := opts.InstrumentOptions().MetricsScope()
   146  	return &bootstrapInstrumentation{
   147  		opts:          opts,
   148  		scope:         scope,
   149  		log:           opts.InstrumentOptions().Logger(),
   150  		nowFn:         opts.ClockOptions().NowFn(),
   151  		status:        scope.Gauge("bootstrapped"),
   152  		durableStatus: scope.Gauge("bootstrapped-durable"),
   153  		numRetries:    newBootstrapRetriesMetrics(scope),
   154  	}
   155  }
   156  
   157  func (i *bootstrapInstrumentation) bootstrapPreparing() *instrumentationContext {
   158  	i.log.Info("bootstrap prepare")
   159  	return newInstrumentationContext(i.nowFn, i.log, i.scope, i.opts)
   160  }
   161  
   162  func (i *bootstrapInstrumentation) bootstrapFailed(retry int, err error) {
   163  	numRetries := i.numRetries.other
   164  	if xerrors.Is(err, bootstrap.ErrFileSetSnapshotTypeRangeAdvanced) {
   165  		numRetries = i.numRetries.obsoleteRanges
   166  	}
   167  	numRetries.Inc(1)
   168  
   169  	i.log.Warn("retrying bootstrap after backoff",
   170  		zap.Duration("backoff", bootstrapRetryInterval),
   171  		zap.Int("numRetries", retry))
   172  }
   173  
   174  func (i *bootstrapInstrumentation) bootstrapPrepareFailed(err error) {
   175  	i.log.Error("bootstrap prepare failed", zap.Error(err))
   176  }
   177  
   178  func (i *bootstrapInstrumentation) setIsBootstrapped(isBootstrapped bool) {
   179  	var status float64 = 0
   180  	if isBootstrapped {
   181  		status = 1
   182  	}
   183  	i.status.Update(status)
   184  }
   185  
   186  func (i *bootstrapInstrumentation) setIsBootstrappedAndDurable(isBootstrappedAndDurable bool) {
   187  	var status float64 = 0
   188  	if isBootstrappedAndDurable {
   189  		status = 1
   190  	}
   191  	i.durableStatus.Update(status)
   192  }
   193  
   194  func (i *bootstrapInstrumentation) emitAndLogInvariantViolation(err error, msg string) {
   195  	instrument.EmitAndLogInvariantViolation(i.opts.InstrumentOptions(), func(l *zap.Logger) {
   196  		l.Error(msg, zap.Error(err))
   197  	})
   198  }