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 &reg, 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  }