github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/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 finalResult.IndexResult.IndexResults().AddResults(currNamespace.IndexResult.IndexResults()) 193 } 194 195 // Map is by value, set the result altered struct. 196 finalResults.Results.Set(id, finalResult) 197 } 198 } 199 200 return finalResults, nil 201 } 202 203 func (b baseBootstrapper) logSuccessAndDetermineCurrResultsUnfulfilledAndNextBootstrapRanges( 204 requested bootstrap.Namespaces, 205 curr bootstrap.Namespaces, 206 currResults bootstrap.NamespaceResults, 207 baseLogFields []zapcore.Field, 208 ) (bootstrap.Namespaces, error) { 209 next := bootstrap.Namespaces{ 210 Namespaces: bootstrap.NewNamespacesMap(bootstrap.NamespacesMapOptions{}), 211 } 212 for _, elem := range requested.Namespaces.Iter() { 213 id := elem.Key() 214 requestedNamespace := elem.Value() 215 216 currResult, ok := currResults.Results.Get(id) 217 if !ok { 218 return bootstrap.Namespaces{}, 219 fmt.Errorf("namespace result not returned by bootstrapper: %v", id.String()) 220 } 221 222 currNamespace, ok := curr.Namespaces.Get(id) 223 if !ok { 224 return bootstrap.Namespaces{}, 225 fmt.Errorf("namespace prepared request not found: %v", id.String()) 226 } 227 228 // Shallow copy the current namespace for the next namespace prepared request. 229 nextNamespace := currNamespace 230 231 // Calculate bootstrap time ranges. 232 dataRequired := requestedNamespace.DataRunOptions.ShardTimeRanges.Copy() 233 dataCurrRequested := currNamespace.DataRunOptions.ShardTimeRanges.Copy() 234 dataCurrFulfilled := dataCurrRequested.Copy() 235 dataCurrFulfilled.Subtract(currResult.DataResult.Unfulfilled()) 236 237 dataUnfulfilled := dataRequired.Copy() 238 dataUnfulfilled.Subtract(dataCurrFulfilled) 239 240 // Modify the unfulfilled result. 241 currResult.DataResult.SetUnfulfilled(dataUnfulfilled.Copy()) 242 243 // Set the next bootstrapper required ranges. 244 nextNamespace.DataRunOptions.ShardTimeRanges = dataUnfulfilled.Copy() 245 246 var ( 247 indexCurrRequested = result.NewShardTimeRanges() 248 indexCurrFulfilled = result.NewShardTimeRanges() 249 indexUnfulfilled = result.NewShardTimeRanges() 250 ) 251 if currNamespace.Metadata.Options().IndexOptions().Enabled() { 252 // Calculate bootstrap time ranges. 253 indexRequired := requestedNamespace.IndexRunOptions.ShardTimeRanges.Copy() 254 indexCurrRequested = currNamespace.IndexRunOptions.ShardTimeRanges.Copy() 255 indexCurrFulfilled = indexCurrRequested.Copy() 256 indexCurrFulfilled.Subtract(currResult.IndexResult.Unfulfilled()) 257 258 indexUnfulfilled = indexRequired.Copy() 259 indexUnfulfilled.Subtract(indexCurrFulfilled) 260 261 // Modify the unfulfilled result. 262 currResult.IndexResult.SetUnfulfilled(indexUnfulfilled.Copy()) 263 } 264 265 // Set the next bootstrapper required ranges. 266 // NB(r): Make sure to always set an empty requested range so IsEmpty 267 // does not cause nil ptr deref. 268 nextNamespace.IndexRunOptions.ShardTimeRanges = indexUnfulfilled.Copy() 269 270 // Set the modified result. 271 currResults.Results.Set(id, currResult) 272 273 // Always set the next bootstrapper namespace run options regardless of 274 // whether there are unfulfilled index/data shard time ranges. 275 // NB(bodu): We perform short circuiting directly in the peers bootstrapper and the 276 // commitlog bootstrapper should always run for all time ranges. 277 next.Namespaces.Set(id, nextNamespace) 278 279 // Log the result. 280 _, _, dataRangeRequested := dataCurrRequested.MinMaxRange() 281 _, _, dataRangeFulfilled := dataCurrFulfilled.MinMaxRange() 282 successLogFields := append(logFieldsCopy(baseLogFields), 283 zap.String("namespace", id.String()), 284 zap.Int("numShards", len(currNamespace.Shards)), 285 zap.Duration("dataRangeRequested", dataRangeRequested), 286 zap.Duration("dataRangeFulfilled", dataRangeFulfilled), 287 ) 288 289 if currNamespace.Metadata.Options().IndexOptions().Enabled() { 290 _, _, indexRangeRequested := indexCurrRequested.MinMaxRange() 291 _, _, indexRangeFulfilled := indexCurrFulfilled.MinMaxRange() 292 successLogFields = append(successLogFields, 293 zap.Duration("indexRangeRequested", indexRangeRequested), 294 zap.Duration("indexRangeFulfilled", indexRangeFulfilled), 295 zap.Int("numIndexBlocks", len(currResult.IndexResult.IndexResults())), 296 ) 297 } 298 299 b.log.Info("bootstrapping from source completed successfully", 300 successLogFields...) 301 } 302 303 return next, nil 304 } 305 306 func (b baseBootstrapper) logShardTimeRanges( 307 msg string, 308 baseLogFields []zapcore.Field, 309 currNamespace bootstrap.Namespace, 310 ) { 311 dataShardTimeRanges := currNamespace.DataRunOptions.ShardTimeRanges 312 dataMin, dataMax, dataRange := dataShardTimeRanges.MinMaxRange() 313 logFields := append(logFieldsCopy(baseLogFields), 314 zap.Stringer("namespace", currNamespace.Metadata.ID()), 315 zap.Int("numShards", len(currNamespace.Shards)), 316 zap.Duration("dataRange", dataRange), 317 ) 318 if dataRange > 0 { 319 logFields = append(logFields, 320 zap.Time("dataFrom", dataMin.ToTime()), 321 zap.Time("dataTo", dataMax.ToTime()), 322 ) 323 } 324 if currNamespace.Metadata.Options().IndexOptions().Enabled() { 325 indexShardTimeRanges := currNamespace.IndexRunOptions.ShardTimeRanges 326 indexMin, indexMax, indexRange := indexShardTimeRanges.MinMaxRange() 327 logFields = append(logFields, 328 zap.Duration("indexRange", indexRange), 329 ) 330 if indexRange > 0 { 331 logFields = append(logFields, 332 zap.Time("indexFrom", indexMin.ToTime()), 333 zap.Time("indexTo", indexMax.ToTime()), 334 ) 335 } 336 } 337 338 b.log.Info(msg, logFields...) 339 } 340 341 func logFieldsCopy(logFields []zapcore.Field) []zapcore.Field { 342 return append(make([]zapcore.Field, 0, 2*len(logFields)), logFields...) 343 }