github.com/m3db/m3@v1.5.0/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 }