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 }