github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/namespace/dynamic.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  	"sync"
    26  	"time"
    27  
    28  	"github.com/m3db/m3/src/cluster/kv"
    29  	nsproto "github.com/m3db/m3/src/dbnode/generated/proto/namespace"
    30  	xwatch "github.com/m3db/m3/src/x/watch"
    31  
    32  	"github.com/uber-go/tally"
    33  	"go.uber.org/zap"
    34  )
    35  
    36  var (
    37  	errRegistryAlreadyClosed = errors.New("registry already closed")
    38  	errInvalidRegistry       = errors.New("could not parse latest value from config service")
    39  )
    40  
    41  type dynamicInitializer struct {
    42  	sync.Mutex
    43  	opts DynamicOptions
    44  	reg  Registry
    45  }
    46  
    47  // NewDynamicInitializer returns a dynamic namespace initializer
    48  func NewDynamicInitializer(opts DynamicOptions) Initializer {
    49  	return &dynamicInitializer{opts: opts}
    50  }
    51  
    52  func (i *dynamicInitializer) Init() (Registry, error) {
    53  	i.Lock()
    54  	defer i.Unlock()
    55  
    56  	if i.reg != nil {
    57  		return i.reg, nil
    58  	}
    59  
    60  	if err := i.opts.Validate(); err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	reg, err := newDynamicRegistry(i.opts)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	i.reg = reg
    70  	return i.reg, nil
    71  }
    72  
    73  type dynamicRegistry struct {
    74  	sync.RWMutex
    75  	opts         DynamicOptions
    76  	logger       *zap.Logger
    77  	metrics      dynamicRegistryMetrics
    78  	watchable    xwatch.Watchable
    79  	kvWatch      kv.ValueWatch
    80  	currentValue kv.Value
    81  	currentMap   Map
    82  	closed       bool
    83  }
    84  
    85  type dynamicRegistryMetrics struct {
    86  	numInvalidUpdates tally.Counter
    87  	currentVersion    tally.Gauge
    88  }
    89  
    90  func newDynamicRegistryMetrics(opts DynamicOptions) dynamicRegistryMetrics {
    91  	scope := opts.InstrumentOptions().MetricsScope().SubScope("namespace-registry")
    92  	return dynamicRegistryMetrics{
    93  		numInvalidUpdates: scope.Counter("invalid-update"),
    94  		currentVersion:    scope.Gauge("current-version"),
    95  	}
    96  }
    97  
    98  func newDynamicRegistry(opts DynamicOptions) (Registry, error) {
    99  	kvStore, err := opts.ConfigServiceClient().KV()
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  
   104  	watch, err := kvStore.Watch(opts.NamespaceRegistryKey())
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	logger := opts.InstrumentOptions().Logger()
   110  	var (
   111  		watchable = xwatch.NewWatchable()
   112  		initValue kv.Value
   113  		dt        *dynamicRegistry
   114  	)
   115  	if opts.AllowEmptyInitialNamespaceRegistry() {
   116  		initValue, err = kvStore.Get(opts.NamespaceRegistryKey())
   117  		if err == kv.ErrNotFound {
   118  			logger.Info("no initial namespaces found. namespaces will be added via watch updates")
   119  			dt = &dynamicRegistry{
   120  				opts:      opts,
   121  				logger:    logger,
   122  				metrics:   newDynamicRegistryMetrics(opts),
   123  				watchable: watchable,
   124  				kvWatch:   watch,
   125  			}
   126  
   127  			startRegistryWatch(dt)
   128  
   129  			return dt, nil
   130  		} else if err != nil {
   131  			return nil, err
   132  		}
   133  	} else {
   134  		logger.Info("waiting for dynamic namespace registry initialization, " +
   135  			"if this takes a long time, make sure that a namespace is configured")
   136  
   137  		<-watch.C()
   138  
   139  		initValue = watch.Get()
   140  	}
   141  
   142  	logger.Info("initial namespace value received")
   143  
   144  	m, err := getMapFromUpdate(initValue, opts.ForceColdWritesEnabled())
   145  	if err != nil {
   146  		logger.Error("dynamic namespace registry received invalid initial value", zap.Error(err))
   147  		return nil, err
   148  	}
   149  
   150  	watchable.Update(m)
   151  
   152  	dt = &dynamicRegistry{
   153  		opts:         opts,
   154  		logger:       logger,
   155  		metrics:      newDynamicRegistryMetrics(opts),
   156  		watchable:    watchable,
   157  		kvWatch:      watch,
   158  		currentValue: initValue,
   159  		currentMap:   m,
   160  	}
   161  
   162  	startRegistryWatch(dt)
   163  
   164  	return dt, nil
   165  }
   166  
   167  func startRegistryWatch(reg *dynamicRegistry) {
   168  	go reg.run()
   169  	go reg.reportMetrics()
   170  }
   171  
   172  func (r *dynamicRegistry) isClosed() bool {
   173  	r.RLock()
   174  	closed := r.closed
   175  	r.RUnlock()
   176  	return closed
   177  }
   178  
   179  func (r *dynamicRegistry) value() (kv.Value, bool) {
   180  	r.RLock()
   181  	defer r.RUnlock()
   182  
   183  	if r.currentValue == nil {
   184  		return nil, false
   185  	}
   186  
   187  	return r.currentValue, true
   188  }
   189  
   190  func (r *dynamicRegistry) maps() (Map, bool) {
   191  	r.RLock()
   192  	defer r.RUnlock()
   193  
   194  	if r.currentMap == nil {
   195  		return nil, false
   196  	}
   197  
   198  	return r.currentMap, true
   199  }
   200  
   201  func (r *dynamicRegistry) reportMetrics() {
   202  	ticker := time.NewTicker(r.opts.InstrumentOptions().ReportInterval())
   203  	defer ticker.Stop()
   204  
   205  	for range ticker.C {
   206  		if r.isClosed() {
   207  			return
   208  		}
   209  
   210  		if val, ok := r.value(); ok {
   211  			r.metrics.currentVersion.Update(float64(val.Version()))
   212  		}
   213  	}
   214  }
   215  
   216  func (r *dynamicRegistry) run() {
   217  	for !r.isClosed() {
   218  		if _, ok := <-r.kvWatch.C(); !ok {
   219  			r.Close()
   220  			break
   221  		}
   222  
   223  		val := r.kvWatch.Get()
   224  		if val == nil {
   225  			r.metrics.numInvalidUpdates.Inc(1)
   226  			r.logger.Warn("dynamic namespace registry received nil, skipping")
   227  			continue
   228  		}
   229  
   230  		currentValue, ok := r.value()
   231  		if ok && !val.IsNewer(currentValue) {
   232  			r.metrics.numInvalidUpdates.Inc(1)
   233  			r.logger.Warn("dynamic namespace registry received older version, skipping",
   234  				zap.Int("version", val.Version()))
   235  			continue
   236  		} else if !ok {
   237  			r.logger.Debug("current value for dynamic registry is nil. this should only happen on initialization")
   238  		}
   239  
   240  		m, err := getMapFromUpdate(val, r.opts.ForceColdWritesEnabled())
   241  		if err != nil {
   242  			r.metrics.numInvalidUpdates.Inc(1)
   243  			r.logger.Warn("dynamic namespace registry received invalid update, skipping",
   244  				zap.Error(err))
   245  			continue
   246  		}
   247  
   248  		currentMap, ok := r.maps()
   249  		if ok && m.Equal(currentMap) {
   250  			r.metrics.numInvalidUpdates.Inc(1)
   251  			r.logger.Warn("dynamic namespace registry received identical update, skipping",
   252  				zap.Int("version", val.Version()))
   253  			continue
   254  		} else if !ok {
   255  			r.logger.Debug("current map for dynamic registry is nil. this should only happen on initialization")
   256  		}
   257  
   258  		r.logger.Info("dynamic namespace registry updated to version",
   259  			zap.Int("version", val.Version()))
   260  		r.Lock()
   261  		r.currentValue = val
   262  		r.currentMap = m
   263  		r.watchable.Update(m)
   264  		r.Unlock()
   265  	}
   266  }
   267  
   268  func (r *dynamicRegistry) Watch() (Watch, error) {
   269  	_, w, err := r.watchable.Watch()
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  	return NewWatch(w), err
   274  }
   275  
   276  func (r *dynamicRegistry) Close() error {
   277  	r.Lock()
   278  	defer r.Unlock()
   279  
   280  	if r.closed {
   281  		return errRegistryAlreadyClosed
   282  	}
   283  
   284  	r.closed = true
   285  
   286  	r.kvWatch.Close()
   287  	r.watchable.Close()
   288  	return nil
   289  }
   290  
   291  func getMapFromUpdate(val kv.Value, forceColdWritesEnabled bool) (Map, error) {
   292  	if val == nil {
   293  		return nil, errInvalidRegistry
   294  	}
   295  
   296  	var protoRegistry nsproto.Registry
   297  	if err := val.Unmarshal(&protoRegistry); err != nil {
   298  		return nil, errInvalidRegistry
   299  	}
   300  
   301  	m, err := FromProto(protoRegistry)
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  
   306  	// NB(bodu): Force cold writes to be enabled for all ns if specified.
   307  	if forceColdWritesEnabled {
   308  		m, err = NewMap(ForceColdWritesEnabledForMetadatas(m.Metadatas()))
   309  		if err != nil {
   310  			return nil, err
   311  		}
   312  	}
   313  
   314  	return m, nil
   315  }