github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/namespace/convert.go (about) 1 // Copyright (c) 2017 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 namespace 22 23 import ( 24 "errors" 25 "fmt" 26 "strings" 27 "sync" 28 "time" 29 30 nsproto "github.com/m3db/m3/src/dbnode/generated/proto/namespace" 31 "github.com/m3db/m3/src/dbnode/retention" 32 "github.com/m3db/m3/src/x/ident" 33 xtime "github.com/m3db/m3/src/x/time" 34 35 "github.com/gogo/protobuf/proto" 36 protobuftypes "github.com/gogo/protobuf/types" 37 ) 38 39 var ( 40 errRetentionNil = errors.New("retention options must be set") 41 errNamespaceNil = errors.New("namespace options must be set") 42 errExtendedOptionsNil = errors.New("extendedOptions.Options must be set") 43 44 dynamicExtendedOptionsConverters = sync.Map{} 45 ) 46 47 // FromNanos converts nanoseconds to a namespace-compatible duration. 48 func FromNanos(n int64) time.Duration { 49 return xtime.FromNormalizedDuration(n, time.Nanosecond) 50 } 51 52 // ToRetention converts nsproto.RetentionOptions to retention.Options 53 func ToRetention( 54 ro *nsproto.RetentionOptions, 55 ) (retention.Options, error) { 56 if ro == nil { 57 return nil, errRetentionNil 58 } 59 60 ropts := retention.NewOptions(). 61 SetRetentionPeriod(FromNanos(ro.RetentionPeriodNanos)). 62 SetFutureRetentionPeriod(FromNanos(ro.FutureRetentionPeriodNanos)). 63 SetBlockSize(FromNanos(ro.BlockSizeNanos)). 64 SetBufferFuture(FromNanos(ro.BufferFutureNanos)). 65 SetBufferPast(FromNanos(ro.BufferPastNanos)). 66 SetBlockDataExpiry(ro.BlockDataExpiry). 67 SetBlockDataExpiryAfterNotAccessedPeriod( 68 FromNanos(ro.BlockDataExpiryAfterNotAccessPeriodNanos)) 69 70 if err := ropts.Validate(); err != nil { 71 return nil, err 72 } 73 74 return ropts, nil 75 } 76 77 // ToIndexOptions converts nsproto.IndexOptions to IndexOptions 78 func ToIndexOptions( 79 io *nsproto.IndexOptions, 80 defaultBlockSize time.Duration, 81 ) (IndexOptions, error) { 82 iopts := NewIndexOptions(). 83 SetBlockSize(defaultBlockSize). 84 SetEnabled(false) 85 if io == nil { 86 return iopts, nil 87 } 88 89 iopts = iopts.SetEnabled(io.Enabled). 90 SetBlockSize(FromNanos(io.BlockSizeNanos)) 91 92 return iopts, nil 93 } 94 95 // ToRuntimeOptions converts nsproto.NamespaceRuntimeOptions to RuntimeOptions. 96 func ToRuntimeOptions( 97 opts *nsproto.NamespaceRuntimeOptions, 98 ) (RuntimeOptions, error) { 99 runtimeOpts := NewRuntimeOptions() 100 if opts == nil { 101 return runtimeOpts, nil 102 } 103 if v := opts.WriteIndexingPerCPUConcurrency; v != nil { 104 newValue := v.Value 105 runtimeOpts = runtimeOpts.SetWriteIndexingPerCPUConcurrency(&newValue) 106 } 107 if v := opts.FlushIndexingPerCPUConcurrency; v != nil { 108 newValue := v.Value 109 runtimeOpts = runtimeOpts.SetFlushIndexingPerCPUConcurrency(&newValue) 110 } 111 return runtimeOpts, nil 112 } 113 114 // ExtendedOptsConverter is function for converting from protobuf message to ExtendedOptions. 115 type ExtendedOptsConverter func(p *protobuftypes.Struct) (ExtendedOptions, error) 116 117 // RegisterExtendedOptionsConverter registers conversion function from protobuf message to ExtendedOptions. 118 func RegisterExtendedOptionsConverter(_type string, converter ExtendedOptsConverter) { 119 dynamicExtendedOptionsConverters.Store(_type, converter) 120 } 121 122 // ToExtendedOptions converts protobuf message to ExtendedOptions. 123 func ToExtendedOptions( 124 extendedOptsProto *nsproto.ExtendedOptions, 125 ) (ExtendedOptions, error) { 126 var extendedOpts ExtendedOptions 127 if extendedOptsProto == nil { 128 return extendedOpts, nil 129 } 130 131 converter, ok := dynamicExtendedOptionsConverters.Load(extendedOptsProto.Type) 132 if !ok { 133 return nil, fmt.Errorf("dynamic ExtendedOptions converter not registered for type %s", extendedOptsProto.Type) 134 } 135 136 if extendedOptsProto.Options == nil { 137 return nil, errExtendedOptionsNil 138 } 139 140 extendedOpts, err := converter.(ExtendedOptsConverter)(extendedOptsProto.Options) 141 if err != nil { 142 return nil, err 143 } 144 145 if err = extendedOpts.Validate(); err != nil { 146 return nil, err 147 } 148 149 return extendedOpts, nil 150 } 151 152 // ToMetadata converts nsproto.Options to Metadata 153 func ToMetadata( 154 id string, 155 opts *nsproto.NamespaceOptions, 156 ) (Metadata, error) { 157 if opts == nil { 158 return nil, errNamespaceNil 159 } 160 161 rOpts, err := ToRetention(opts.RetentionOptions) 162 if err != nil { 163 return nil, err 164 } 165 166 iOpts, err := ToIndexOptions(opts.IndexOptions, 167 // Default to the retention block size if no index options are specified. 168 rOpts.BlockSize(), 169 ) 170 if err != nil { 171 return nil, err 172 } 173 174 sr, err := LoadSchemaHistory(opts.GetSchemaOptions()) 175 if err != nil { 176 return nil, err 177 } 178 179 runtimeOpts, err := ToRuntimeOptions(opts.RuntimeOptions) 180 if err != nil { 181 return nil, err 182 } 183 184 extendedOpts, err := ToExtendedOptions(opts.ExtendedOptions) 185 if err != nil { 186 return nil, err 187 } 188 189 aggOpts, err := ToAggregationOptions(opts.AggregationOptions) 190 if err != nil { 191 return nil, err 192 } 193 194 stagingState, err := ToStagingState(opts.StagingState) 195 if err != nil { 196 return nil, err 197 } 198 199 mOpts := NewOptions(). 200 SetBootstrapEnabled(opts.BootstrapEnabled). 201 SetFlushEnabled(opts.FlushEnabled). 202 SetCleanupEnabled(opts.CleanupEnabled). 203 SetRepairEnabled(opts.RepairEnabled). 204 SetWritesToCommitLog(opts.WritesToCommitLog). 205 SetSnapshotEnabled(opts.SnapshotEnabled). 206 SetSchemaHistory(sr). 207 SetRetentionOptions(rOpts). 208 SetIndexOptions(iOpts). 209 SetColdWritesEnabled(opts.ColdWritesEnabled). 210 SetRuntimeOptions(runtimeOpts). 211 SetExtendedOptions(extendedOpts). 212 SetAggregationOptions(aggOpts). 213 SetStagingState(stagingState) 214 215 if opts.CacheBlocksOnRetrieve != nil { 216 mOpts = mOpts.SetCacheBlocksOnRetrieve(opts.CacheBlocksOnRetrieve.Value) 217 } 218 219 if err := mOpts.Validate(); err != nil { 220 return nil, err 221 } 222 223 return NewMetadata(ident.StringID(id), mOpts) 224 } 225 226 // ToStagingState converts nsproto.StagingState to StagingState. 227 func ToStagingState(state *nsproto.StagingState) (StagingState, error) { 228 if state == nil { 229 return StagingState{}, nil 230 } 231 232 return NewStagingState(state.Status) 233 } 234 235 // ToAggregationOptions converts nsproto.AggregationOptions to AggregationOptions. 236 func ToAggregationOptions(opts *nsproto.AggregationOptions) (AggregationOptions, error) { 237 aggOpts := NewAggregationOptions() 238 if opts == nil || len(opts.Aggregations) == 0 { 239 return aggOpts, nil 240 } 241 aggregations := make([]Aggregation, 0, len(opts.Aggregations)) 242 for _, agg := range opts.Aggregations { 243 if agg.Aggregated { 244 if agg.Attributes == nil { 245 return nil, errors.New("must set Attributes when aggregated is true") 246 } 247 248 var dsOpts DownsampleOptions 249 if agg.Attributes.DownsampleOptions == nil { 250 dsOpts = NewDownsampleOptions(true) 251 } else { 252 dsOpts = NewDownsampleOptions(agg.Attributes.DownsampleOptions.All) 253 } 254 255 attrs, err := NewAggregatedAttributes(time.Duration(agg.Attributes.ResolutionNanos), dsOpts) 256 if err != nil { 257 return nil, err 258 } 259 aggregations = append(aggregations, NewAggregatedAggregation(attrs)) 260 } else { 261 aggregations = append(aggregations, NewUnaggregatedAggregation()) 262 } 263 } 264 return aggOpts.SetAggregations(aggregations), nil 265 } 266 267 // ToProto converts Map to nsproto.Registry 268 func ToProto(m Map) (*nsproto.Registry, error) { 269 reg := nsproto.Registry{ 270 Namespaces: make(map[string]*nsproto.NamespaceOptions, len(m.Metadatas())), 271 } 272 273 for _, md := range m.Metadatas() { 274 protoMsg, err := OptionsToProto(md.Options()) 275 if err != nil { 276 return nil, err 277 } 278 reg.Namespaces[md.ID().String()] = protoMsg 279 } 280 281 return ®, nil 282 } 283 284 // FromProto converts nsproto.Registry -> Map 285 func FromProto(protoRegistry nsproto.Registry) (Map, error) { 286 metadatas := make([]Metadata, 0, len(protoRegistry.Namespaces)) 287 for ns, opts := range protoRegistry.Namespaces { 288 md, err := ToMetadata(ns, opts) 289 if err != nil { 290 return nil, err 291 } 292 metadatas = append(metadatas, md) 293 } 294 return NewMap(metadatas) 295 } 296 297 // OptionsToProto converts Options -> nsproto.NamespaceOptions 298 func OptionsToProto(opts Options) (*nsproto.NamespaceOptions, error) { 299 var extendedOpts *nsproto.ExtendedOptions 300 if extOpts := opts.ExtendedOptions(); extOpts != nil { 301 extOptsType, extOptsStruct := extOpts.ToProto() 302 extendedOpts = &nsproto.ExtendedOptions{ 303 Type: extOptsType, 304 Options: extOptsStruct, 305 } 306 } 307 308 ropts := opts.RetentionOptions() 309 iopts := opts.IndexOptions() 310 311 stagingState, err := toProtoStagingState(opts.StagingState()) 312 if err != nil { 313 return nil, err 314 } 315 316 nsOpts := &nsproto.NamespaceOptions{ 317 BootstrapEnabled: opts.BootstrapEnabled(), 318 FlushEnabled: opts.FlushEnabled(), 319 CleanupEnabled: opts.CleanupEnabled(), 320 SnapshotEnabled: opts.SnapshotEnabled(), 321 RepairEnabled: opts.RepairEnabled(), 322 WritesToCommitLog: opts.WritesToCommitLog(), 323 SchemaOptions: toSchemaOptions(opts.SchemaHistory()), 324 RetentionOptions: &nsproto.RetentionOptions{ 325 BlockSizeNanos: ropts.BlockSize().Nanoseconds(), 326 RetentionPeriodNanos: ropts.RetentionPeriod().Nanoseconds(), 327 FutureRetentionPeriodNanos: ropts.FutureRetentionPeriod().Nanoseconds(), 328 BufferFutureNanos: ropts.BufferFuture().Nanoseconds(), 329 BufferPastNanos: ropts.BufferPast().Nanoseconds(), 330 BlockDataExpiry: ropts.BlockDataExpiry(), 331 BlockDataExpiryAfterNotAccessPeriodNanos: ropts.BlockDataExpiryAfterNotAccessedPeriod().Nanoseconds(), 332 }, 333 IndexOptions: &nsproto.IndexOptions{ 334 Enabled: iopts.Enabled(), 335 BlockSizeNanos: iopts.BlockSize().Nanoseconds(), 336 }, 337 ColdWritesEnabled: opts.ColdWritesEnabled(), 338 RuntimeOptions: toRuntimeOptions(opts.RuntimeOptions()), 339 CacheBlocksOnRetrieve: &protobuftypes.BoolValue{Value: opts.CacheBlocksOnRetrieve()}, 340 ExtendedOptions: extendedOpts, 341 AggregationOptions: toProtoAggregationOptions(opts.AggregationOptions()), 342 StagingState: stagingState, 343 } 344 345 return nsOpts, nil 346 } 347 348 func toProtoStagingState(state StagingState) (*nsproto.StagingState, error) { 349 var protoStatus nsproto.StagingStatus 350 switch state.Status() { 351 case UnknownStagingStatus: 352 protoStatus = nsproto.StagingStatus_UNKNOWN 353 case InitializingStagingStatus: 354 protoStatus = nsproto.StagingStatus_INITIALIZING 355 case ReadyStagingStatus: 356 protoStatus = nsproto.StagingStatus_READY 357 default: 358 return nil, fmt.Errorf("invalid StagingState: %v", state.Status()) 359 } 360 361 return &nsproto.StagingState{Status: protoStatus}, nil 362 } 363 364 func toProtoAggregationOptions(aggOpts AggregationOptions) *nsproto.AggregationOptions { 365 if aggOpts == nil || len(aggOpts.Aggregations()) == 0 { 366 return nil 367 } 368 protoAggs := make([]*nsproto.Aggregation, 0, len(aggOpts.Aggregations())) 369 for _, agg := range aggOpts.Aggregations() { 370 protoAgg := nsproto.Aggregation{Aggregated: agg.Aggregated} 371 if agg.Aggregated { 372 protoAgg.Attributes = &nsproto.AggregatedAttributes{ 373 ResolutionNanos: agg.Attributes.Resolution.Nanoseconds(), 374 DownsampleOptions: &nsproto.DownsampleOptions{All: agg.Attributes.DownsampleOptions.All}, 375 } 376 } 377 protoAggs = append(protoAggs, &protoAgg) 378 } 379 return &nsproto.AggregationOptions{Aggregations: protoAggs} 380 } 381 382 // toRuntimeOptions returns the corresponding RuntimeOptions proto. 383 func toRuntimeOptions(opts RuntimeOptions) *nsproto.NamespaceRuntimeOptions { 384 if opts == nil || opts.IsDefault() { 385 return nil 386 } 387 var ( 388 writeIndexingPerCPUConcurrency *protobuftypes.DoubleValue 389 flushIndexingPerCPUConcurrency *protobuftypes.DoubleValue 390 ) 391 if v := opts.WriteIndexingPerCPUConcurrency(); v != nil { 392 writeIndexingPerCPUConcurrency = &protobuftypes.DoubleValue{ 393 Value: *v, 394 } 395 } 396 if v := opts.FlushIndexingPerCPUConcurrency(); v != nil { 397 flushIndexingPerCPUConcurrency = &protobuftypes.DoubleValue{ 398 Value: *v, 399 } 400 } 401 return &nsproto.NamespaceRuntimeOptions{ 402 WriteIndexingPerCPUConcurrency: writeIndexingPerCPUConcurrency, 403 FlushIndexingPerCPUConcurrency: flushIndexingPerCPUConcurrency, 404 } 405 } 406 407 func typeUrlForMessage(typeURLPrefix string, msg proto.Message) string { 408 if !strings.HasSuffix(typeURLPrefix, "/") { 409 typeURLPrefix += "/" 410 } 411 return typeURLPrefix + proto.MessageName(msg) 412 }