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 }