github.com/Jeffail/benthos/v3@v3.65.0/lib/manager/type.go (about) 1 package manager 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "path" 8 "sync" 9 "time" 10 11 "github.com/Jeffail/benthos/v3/internal/bloblang" 12 "github.com/Jeffail/benthos/v3/internal/bundle" 13 imetrics "github.com/Jeffail/benthos/v3/internal/component/metrics" 14 "github.com/Jeffail/benthos/v3/internal/docs" 15 "github.com/Jeffail/benthos/v3/lib/buffer" 16 "github.com/Jeffail/benthos/v3/lib/cache" 17 "github.com/Jeffail/benthos/v3/lib/condition" 18 "github.com/Jeffail/benthos/v3/lib/input" 19 "github.com/Jeffail/benthos/v3/lib/log" 20 "github.com/Jeffail/benthos/v3/lib/metrics" 21 "github.com/Jeffail/benthos/v3/lib/output" 22 "github.com/Jeffail/benthos/v3/lib/processor" 23 "github.com/Jeffail/benthos/v3/lib/ratelimit" 24 "github.com/Jeffail/benthos/v3/lib/types" 25 ) 26 27 // ErrResourceNotFound represents an error where a named resource could not be 28 // accessed because it was not found by the manager. 29 type ErrResourceNotFound string 30 31 // Error implements the standard error interface. 32 func (e ErrResourceNotFound) Error() string { 33 return fmt.Sprintf("unable to locate resource: %v", string(e)) 34 } 35 36 //------------------------------------------------------------------------------ 37 38 // APIReg is an interface representing an API builder. 39 type APIReg interface { 40 RegisterEndpoint(path, desc string, h http.HandlerFunc) 41 } 42 43 //------------------------------------------------------------------------------ 44 45 // Type is an implementation of types.Manager, which is expected by Benthos 46 // components that need to register service wide behaviours such as HTTP 47 // endpoints and event listeners, and obtain service wide shared resources such 48 // as caches and other resources. 49 type Type struct { 50 // An optional identifier given to a manager that is used by a unique stream 51 // and if specified should be used as a path prefix for API endpoints, and 52 // added as a label to logs and metrics. 53 stream string 54 55 // An optional identifier given to a manager that is used by a component and 56 // if specified should be added as a label to logs and metrics. 57 component string 58 59 apiReg APIReg 60 61 inputs map[string]types.Input 62 caches map[string]types.Cache 63 processors map[string]types.Processor 64 outputs map[string]types.OutputWriter 65 rateLimits map[string]types.RateLimit 66 plugins map[string]interface{} 67 resourceLock *sync.RWMutex 68 69 // Collections of component constructors 70 env *bundle.Environment 71 bloblEnv *bloblang.Environment 72 73 logger log.Modular 74 stats *imetrics.Namespaced 75 76 pipes map[string]<-chan types.Transaction 77 pipeLock *sync.RWMutex 78 79 // TODO: V4 Remove this 80 conditions map[string]types.Condition 81 } 82 83 // New returns an instance of manager.Type, which can be shared amongst 84 // components and logical threads of a Benthos service. 85 func New(conf Config, apiReg APIReg, log log.Modular, stats metrics.Type) (*Type, error) { 86 return NewV2(ResourceConfig{Manager: conf}, apiReg, log, stats) 87 } 88 89 // OptFunc is an opt setting for a manager type. 90 type OptFunc func(*Type) 91 92 // OptSetEnvironment determines the environment from which the manager 93 // initializes components and resources. This option is for internal use only. 94 func OptSetEnvironment(e *bundle.Environment) OptFunc { 95 return func(t *Type) { 96 t.env = e 97 } 98 } 99 100 // OptSetBloblangEnvironment determines the environment from which the manager 101 // parses bloblang functions and methods. This option is for internal use only. 102 func OptSetBloblangEnvironment(env *bloblang.Environment) OptFunc { 103 return func(t *Type) { 104 t.bloblEnv = env 105 } 106 } 107 108 // NewV2 returns an instance of manager.Type, which can be shared amongst 109 // components and logical threads of a Benthos service. 110 func NewV2(conf ResourceConfig, apiReg APIReg, log log.Modular, stats metrics.Type, opts ...OptFunc) (*Type, error) { 111 t := &Type{ 112 apiReg: apiReg, 113 114 inputs: map[string]types.Input{}, 115 caches: map[string]types.Cache{}, 116 processors: map[string]types.Processor{}, 117 outputs: map[string]types.OutputWriter{}, 118 rateLimits: map[string]types.RateLimit{}, 119 plugins: map[string]interface{}{}, 120 resourceLock: &sync.RWMutex{}, 121 122 // Environment defaults to global (everything that was imported). 123 env: bundle.GlobalEnvironment, 124 bloblEnv: bloblang.GlobalEnvironment(), 125 126 logger: log, 127 stats: imetrics.NewNamespaced(stats), 128 129 pipes: map[string]<-chan types.Transaction{}, 130 pipeLock: &sync.RWMutex{}, 131 132 conditions: map[string]types.Condition{}, 133 } 134 135 for _, opt := range opts { 136 opt(t) 137 } 138 139 conf, err := conf.collapsed() 140 if err != nil { 141 return nil, err 142 } 143 144 // Sometimes resources of a type might refer to other resources of the same 145 // type. When they are constructed they will check with the manager to 146 // ensure the resource they point to is valid, but not keep the reference. 147 // Since we cannot guarantee an order of initialisation we create 148 // placeholders during construction. 149 for k := range conf.Manager.Inputs { 150 t.inputs[k] = nil 151 } 152 for k := range conf.Manager.Caches { 153 t.caches[k] = nil 154 } 155 for k := range conf.Manager.Conditions { 156 t.conditions[k] = nil 157 } 158 for k := range conf.Manager.Processors { 159 t.processors[k] = nil 160 } 161 for k := range conf.Manager.Outputs { 162 t.outputs[k] = nil 163 } 164 for k := range conf.Manager.RateLimits { 165 t.rateLimits[k] = nil 166 } 167 for k, conf := range conf.Manager.Plugins { 168 if _, exists := pluginSpecs[conf.Type]; !exists { 169 continue 170 } 171 t.plugins[k] = nil 172 } 173 174 for k, conf := range conf.Manager.RateLimits { 175 if err := t.StoreRateLimit(context.Background(), k, conf); err != nil { 176 return nil, err 177 } 178 } 179 180 for k, conf := range conf.Manager.Caches { 181 if err := t.StoreCache(context.Background(), k, conf); err != nil { 182 return nil, err 183 } 184 } 185 186 // TODO: Prevent recursive conditions. 187 for k, newConf := range conf.Manager.Conditions { 188 cMgr := t.forChildComponent("resource.condition." + k) 189 newCond, err := condition.New(newConf, cMgr, cMgr.Logger(), cMgr.Metrics()) 190 if err != nil { 191 return nil, fmt.Errorf( 192 "failed to create condition resource '%v' of type '%v': %v", 193 k, newConf.Type, err, 194 ) 195 } 196 197 t.conditions[k] = newCond 198 } 199 200 // TODO: Prevent recursive processors. 201 for k, conf := range conf.Manager.Processors { 202 if err := t.StoreProcessor(context.Background(), k, conf); err != nil { 203 return nil, err 204 } 205 } 206 207 for k, conf := range conf.Manager.Inputs { 208 if err := t.StoreInput(context.Background(), k, conf); err != nil { 209 return nil, err 210 } 211 } 212 213 for k, conf := range conf.Manager.Outputs { 214 if err := t.StoreOutput(context.Background(), k, conf); err != nil { 215 return nil, err 216 } 217 } 218 219 for k, conf := range conf.Manager.Plugins { 220 spec, exists := pluginSpecs[conf.Type] 221 if !exists { 222 return nil, fmt.Errorf("unrecognised plugin type '%v'", conf.Type) 223 } 224 pMgr := t.forChildComponent("resource.plugin." + k) 225 newP, err := spec.constructor(conf.Plugin, pMgr, pMgr.Logger(), pMgr.Metrics()) 226 if err != nil { 227 return nil, fmt.Errorf( 228 "failed to create plugin resource '%v' of type '%v': %v", 229 k, conf.Type, err, 230 ) 231 } 232 t.plugins[k] = newP 233 } 234 235 return t, nil 236 } 237 238 //------------------------------------------------------------------------------ 239 240 // ForStream returns a variant of this manager to be used by a particular stream 241 // identifer, where APIs registered will be namespaced by that id. 242 func (t *Type) ForStream(id string) types.Manager { 243 return t.forStream(id) 244 } 245 246 func (t *Type) forStream(id string) *Type { 247 newT := *t 248 newT.stream = id 249 newT.logger = t.logger.WithFields(map[string]string{ 250 "stream": id, 251 }) 252 newT.stats = t.stats.WithPrefix(id) 253 return &newT 254 } 255 256 // ForComponent returns a variant of this manager to be used by a particular 257 // component identifer, where observability components will be automatically 258 // tagged with the label. 259 func (t *Type) ForComponent(id string) types.Manager { 260 return t.forComponent(id) 261 } 262 263 func (t *Type) forComponent(id string) *Type { 264 newT := *t 265 newT.component = id 266 newT.logger = t.logger.WithFields(map[string]string{ 267 "component": id, 268 }) 269 270 statsPrefix := id 271 if len(newT.stream) > 0 { 272 statsPrefix = newT.stream + "." + statsPrefix 273 } 274 newT.stats = t.stats.WithPrefix(statsPrefix) 275 return &newT 276 } 277 278 // ForChildComponent returns a variant of this manager to be used by a 279 // particular component identifer, which is a child of the current component, 280 // where observability components will be automatically tagged with the label. 281 func (t *Type) ForChildComponent(id string) types.Manager { 282 return t.forChildComponent(id) 283 } 284 285 func (t *Type) forChildComponent(id string) *Type { 286 newT := *t 287 newT.logger = t.logger.NewModule("." + id) 288 289 if len(newT.component) > 0 { 290 id = newT.component + "." + id 291 } 292 293 statsPrefix := id 294 if len(newT.stream) > 0 { 295 statsPrefix = newT.stream + "." + statsPrefix 296 } 297 298 newT.stats = t.stats.WithPrefix(statsPrefix) 299 newT.component = id 300 return &newT 301 } 302 303 // Label returns the current component label held by a manager. 304 func (t *Type) Label() string { 305 return t.component 306 } 307 308 //------------------------------------------------------------------------------ 309 310 // RegisterEndpoint registers a server wide HTTP endpoint. 311 func (t *Type) RegisterEndpoint(apiPath, desc string, h http.HandlerFunc) { 312 if len(t.stream) > 0 { 313 apiPath = path.Join("/", t.stream, apiPath) 314 } 315 if t.apiReg != nil { 316 t.apiReg.RegisterEndpoint(apiPath, desc, h) 317 } 318 } 319 320 // SetPipe registers a new transaction chan to a named pipe. 321 func (t *Type) SetPipe(name string, tran <-chan types.Transaction) { 322 t.pipeLock.Lock() 323 t.pipes[name] = tran 324 t.pipeLock.Unlock() 325 } 326 327 // GetPipe attempts to obtain and return a named output Pipe 328 func (t *Type) GetPipe(name string) (<-chan types.Transaction, error) { 329 t.pipeLock.RLock() 330 pipe, exists := t.pipes[name] 331 t.pipeLock.RUnlock() 332 if exists { 333 return pipe, nil 334 } 335 return nil, types.ErrPipeNotFound 336 } 337 338 // UnsetPipe removes a named pipe transaction chan. 339 func (t *Type) UnsetPipe(name string, tran <-chan types.Transaction) { 340 t.pipeLock.Lock() 341 if otran, exists := t.pipes[name]; exists && otran == tran { 342 delete(t.pipes, name) 343 } 344 t.pipeLock.Unlock() 345 } 346 347 //------------------------------------------------------------------------------ 348 349 // WithMetricsMapping returns a manager with the stored metrics exporter wrapped 350 // with a mapping. 351 func (t *Type) WithMetricsMapping(m *imetrics.Mapping) *Type { 352 newT := *t 353 newT.stats = t.stats.WithMapping(m) 354 return &newT 355 } 356 357 // Metrics returns an aggregator preset with the current component context. 358 func (t *Type) Metrics() metrics.Type { 359 return t.stats 360 } 361 362 // Logger returns a logger preset with the current component context. 363 func (t *Type) Logger() log.Modular { 364 return t.logger 365 } 366 367 // Environment returns a bundle environment used by the manager. This is for 368 // internal use only. 369 func (t *Type) Environment() *bundle.Environment { 370 return t.env 371 } 372 373 // BloblEnvironment returns a Bloblang environment used by the manager. This is 374 // for internal use only. 375 func (t *Type) BloblEnvironment() *bloblang.Environment { 376 return t.bloblEnv 377 } 378 379 //------------------------------------------------------------------------------ 380 381 // GetDocs returns a documentation spec for an implementation of a component. 382 func (t *Type) GetDocs(name string, ctype docs.Type) (docs.ComponentSpec, bool) { 383 return t.env.GetDocs(name, ctype) 384 } 385 386 //------------------------------------------------------------------------------ 387 388 func closeWithContext(ctx context.Context, c types.Closable) error { 389 c.CloseAsync() 390 waitFor := time.Second 391 deadline, hasDeadline := ctx.Deadline() 392 if hasDeadline { 393 waitFor = time.Until(deadline) 394 } 395 err := c.WaitForClose(waitFor) 396 for err != nil && !hasDeadline { 397 err = c.WaitForClose(time.Second) 398 } 399 return err 400 } 401 402 //------------------------------------------------------------------------------ 403 404 // NewBuffer attempts to create a new buffer component from a config. 405 func (t *Type) NewBuffer(conf buffer.Config) (buffer.Type, error) { 406 return t.env.BufferInit(conf, t) 407 } 408 409 //------------------------------------------------------------------------------ 410 411 // AccessCache attempts to access a cache resource by a unique identifier and 412 // executes a closure function with the cache as an argument. Returns an error 413 // if the cache does not exist (or is otherwise inaccessible). 414 // 415 // During the execution of the provided closure it is guaranteed that the 416 // resource will not be closed or removed. However, it is possible for the 417 // resource to be accessed by any number of components in parallel. 418 func (t *Type) AccessCache(ctx context.Context, name string, fn func(types.Cache)) error { 419 // TODO: Eventually use ctx to cancel blocking on the mutex lock. Needs 420 // profiling for heavy use within a busy loop. 421 t.resourceLock.RLock() 422 defer t.resourceLock.RUnlock() 423 c, ok := t.caches[name] 424 if !ok || c == nil { 425 return ErrResourceNotFound(name) 426 } 427 fn(c) 428 return nil 429 } 430 431 // NewCache attempts to create a new cache component from a config. 432 func (t *Type) NewCache(conf cache.Config) (types.Cache, error) { 433 mgr := t 434 // A configured label overrides any previously set component label. 435 if len(conf.Label) > 0 && t.component != conf.Label { 436 mgr = t.forComponent(conf.Label) 437 } 438 return t.env.CacheInit(conf, mgr) 439 } 440 441 // StoreCache attempts to store a new cache resource. If an existing resource 442 // has the same name it is closed and removed _before_ the new one is 443 // initialized in order to avoid duplicate connections. 444 func (t *Type) StoreCache(ctx context.Context, name string, conf cache.Config) error { 445 t.resourceLock.Lock() 446 defer t.resourceLock.Unlock() 447 448 c, ok := t.caches[name] 449 if ok && c != nil { 450 // If a previous resource exists with the same name then we do NOT allow 451 // it to be replaced unless it can be successfully closed. This ensures 452 // that we do not leak connections. 453 if err := closeWithContext(ctx, c); err != nil { 454 return err 455 } 456 } 457 458 newCache, err := t.forComponent("resource.cache." + name).NewCache(conf) 459 if err != nil { 460 return fmt.Errorf( 461 "failed to create cache resource '%v' of type '%v': %w", 462 name, conf.Type, err, 463 ) 464 } 465 466 t.caches[name] = newCache 467 return nil 468 } 469 470 //------------------------------------------------------------------------------ 471 472 // AccessInput attempts to access an input resource by a unique identifier and 473 // executes a closure function with the input as an argument. Returns an error 474 // if the input does not exist (or is otherwise inaccessible). 475 // 476 // During the execution of the provided closure it is guaranteed that the 477 // resource will not be closed or removed. However, it is possible for the 478 // resource to be accessed by any number of components in parallel. 479 func (t *Type) AccessInput(ctx context.Context, name string, fn func(types.Input)) error { 480 // TODO: Eventually use ctx to cancel blocking on the mutex lock. Needs 481 // profiling for heavy use within a busy loop. 482 t.resourceLock.RLock() 483 defer t.resourceLock.RUnlock() 484 i, ok := t.inputs[name] 485 if !ok || i == nil { 486 return ErrResourceNotFound(name) 487 } 488 fn(i) 489 return nil 490 } 491 492 // NewInput attempts to create a new input component from a config. 493 // 494 // TODO: V4 Remove the dumb batch field. 495 func (t *Type) NewInput(conf input.Config, hasBatchProc bool, pipelines ...types.PipelineConstructorFunc) (types.Input, error) { 496 mgr := t 497 // A configured label overrides any previously set component label. 498 if len(conf.Label) > 0 && t.component != conf.Label { 499 mgr = t.forComponent(conf.Label) 500 } 501 return t.env.InputInit(hasBatchProc, conf, mgr, pipelines...) 502 } 503 504 // StoreInput attempts to store a new input resource. If an existing resource 505 // has the same name it is closed and removed _before_ the new one is 506 // initialized in order to avoid duplicate connections. 507 func (t *Type) StoreInput(ctx context.Context, name string, conf input.Config) error { 508 t.resourceLock.Lock() 509 defer t.resourceLock.Unlock() 510 511 i, ok := t.inputs[name] 512 if ok && i != nil { 513 // If a previous resource exists with the same name then we do NOT allow 514 // it to be replaced unless it can be successfully closed. This ensures 515 // that we do not leak connections. 516 if err := closeWithContext(ctx, i); err != nil { 517 return err 518 } 519 } 520 521 if conf.Label != "" && conf.Label != name { 522 return fmt.Errorf("label '%v' must be empty or match the resource name '%v'", conf.Label, name) 523 } 524 525 newInput, err := t.forComponent("resource.input."+name).NewInput(conf, false) 526 if err != nil { 527 return fmt.Errorf( 528 "failed to create input resource '%v' of type '%v': %w", 529 name, conf.Type, err, 530 ) 531 } 532 533 t.inputs[name] = newInput 534 return nil 535 } 536 537 //------------------------------------------------------------------------------ 538 539 // AccessProcessor attempts to access a processor resource by a unique 540 // identifier and executes a closure function with the processor as an argument. 541 // Returns an error if the processor does not exist (or is otherwise 542 // inaccessible). 543 // 544 // During the execution of the provided closure it is guaranteed that the 545 // resource will not be closed or removed. However, it is possible for the 546 // resource to be accessed by any number of components in parallel. 547 func (t *Type) AccessProcessor(ctx context.Context, name string, fn func(types.Processor)) error { 548 // TODO: Eventually use ctx to cancel blocking on the mutex lock. Needs 549 // profiling for heavy use within a busy loop. 550 t.resourceLock.RLock() 551 defer t.resourceLock.RUnlock() 552 p, ok := t.processors[name] 553 if !ok || p == nil { 554 return ErrResourceNotFound(name) 555 } 556 fn(p) 557 return nil 558 } 559 560 // NewProcessor attempts to create a new processor component from a config. 561 func (t *Type) NewProcessor(conf processor.Config) (types.Processor, error) { 562 mgr := t 563 // A configured label overrides any previously set component label. 564 if len(conf.Label) > 0 && t.component != conf.Label { 565 mgr = t.forComponent(conf.Label) 566 } 567 return t.env.ProcessorInit(conf, mgr) 568 } 569 570 // StoreProcessor attempts to store a new processor resource. If an existing 571 // resource has the same name it is closed and removed _before_ the new one is 572 // initialized in order to avoid duplicate connections. 573 func (t *Type) StoreProcessor(ctx context.Context, name string, conf processor.Config) error { 574 t.resourceLock.Lock() 575 defer t.resourceLock.Unlock() 576 577 p, ok := t.processors[name] 578 if ok && p != nil { 579 // If a previous resource exists with the same name then we do NOT allow 580 // it to be replaced unless it can be successfully closed. This ensures 581 // that we do not leak connections. 582 if err := closeWithContext(ctx, p); err != nil { 583 return err 584 } 585 } 586 587 if conf.Label != "" && conf.Label != name { 588 return fmt.Errorf("label '%v' must be empty or match the resource name '%v'", conf.Label, name) 589 } 590 591 newProcessor, err := t.forComponent("resource.processor." + name).NewProcessor(conf) 592 if err != nil { 593 return fmt.Errorf( 594 "failed to create processor resource '%v' of type '%v': %w", 595 name, conf.Type, err, 596 ) 597 } 598 599 t.processors[name] = newProcessor 600 return nil 601 } 602 603 //------------------------------------------------------------------------------ 604 605 // AccessOutput attempts to access an output resource by a unique identifier and 606 // executes a closure function with the output as an argument. Returns an error 607 // if the output does not exist (or is otherwise inaccessible). 608 // 609 // During the execution of the provided closure it is guaranteed that the 610 // resource will not be closed or removed. However, it is possible for the 611 // resource to be accessed by any number of components in parallel. 612 func (t *Type) AccessOutput(ctx context.Context, name string, fn func(types.OutputWriter)) error { 613 // TODO: Eventually use ctx to cancel blocking on the mutex lock. Needs 614 // profiling for heavy use within a busy loop. 615 t.resourceLock.RLock() 616 defer t.resourceLock.RUnlock() 617 o, ok := t.outputs[name] 618 if !ok || o == nil { 619 return ErrResourceNotFound(name) 620 } 621 fn(o) 622 return nil 623 } 624 625 // NewOutput attempts to create a new output component from a config. 626 func (t *Type) NewOutput(conf output.Config, pipelines ...types.PipelineConstructorFunc) (types.Output, error) { 627 mgr := t 628 // A configured label overrides any previously set component label. 629 if len(conf.Label) > 0 && t.component != conf.Label { 630 mgr = t.forComponent(conf.Label) 631 } 632 return t.env.OutputInit(conf, mgr, pipelines...) 633 } 634 635 // StoreOutput attempts to store a new output resource. If an existing resource 636 // has the same name it is closed and removed _before_ the new one is 637 // initialized in order to avoid duplicate connections. 638 func (t *Type) StoreOutput(ctx context.Context, name string, conf output.Config) error { 639 t.resourceLock.Lock() 640 defer t.resourceLock.Unlock() 641 642 o, ok := t.outputs[name] 643 if ok && o != nil { 644 // If a previous resource exists with the same name then we do NOT allow 645 // it to be replaced unless it can be successfully closed. This ensures 646 // that we do not leak connections. 647 if err := closeWithContext(ctx, o); err != nil { 648 return err 649 } 650 } 651 652 if conf.Label != "" && conf.Label != name { 653 return fmt.Errorf("label '%v' must be empty or match the resource name '%v'", conf.Label, name) 654 } 655 656 tmpOutput, err := t.forComponent("resource.output." + name).NewOutput(conf) 657 if err == nil { 658 if t.outputs[name], err = wrapOutput(tmpOutput); err != nil { 659 tmpOutput.CloseAsync() 660 } 661 } 662 if err != nil { 663 return fmt.Errorf( 664 "failed to create output resource '%v' of type '%v': %w", 665 name, conf.Type, err, 666 ) 667 } 668 return nil 669 } 670 671 //------------------------------------------------------------------------------ 672 673 // AccessRateLimit attempts to access a rate limit resource by a unique 674 // identifier and executes a closure function with the rate limit as an 675 // argument. Returns an error if the rate limit does not exist (or is otherwise 676 // inaccessible). 677 // 678 // During the execution of the provided closure it is guaranteed that the 679 // resource will not be closed or removed. However, it is possible for the 680 // resource to be accessed by any number of components in parallel. 681 func (t *Type) AccessRateLimit(ctx context.Context, name string, fn func(types.RateLimit)) error { 682 // TODO: Eventually use ctx to cancel blocking on the mutex lock. Needs 683 // profiling for heavy use within a busy loop. 684 t.resourceLock.RLock() 685 defer t.resourceLock.RUnlock() 686 r, ok := t.rateLimits[name] 687 if !ok || r == nil { 688 return ErrResourceNotFound(name) 689 } 690 fn(r) 691 return nil 692 } 693 694 // NewRateLimit attempts to create a new rate limit component from a config. 695 func (t *Type) NewRateLimit(conf ratelimit.Config) (types.RateLimit, error) { 696 mgr := t 697 // A configured label overrides any previously set component label. 698 if len(conf.Label) > 0 && t.component != conf.Label { 699 mgr = t.forComponent(conf.Label) 700 } 701 return t.env.RateLimitInit(conf, mgr) 702 } 703 704 // StoreRateLimit attempts to store a new rate limit resource. If an existing 705 // resource has the same name it is closed and removed _before_ the new one is 706 // initialized in order to avoid duplicate connections. 707 func (t *Type) StoreRateLimit(ctx context.Context, name string, conf ratelimit.Config) error { 708 t.resourceLock.Lock() 709 defer t.resourceLock.Unlock() 710 711 r, ok := t.rateLimits[name] 712 if ok && r != nil { 713 // If a previous resource exists with the same name then we do NOT allow 714 // it to be replaced unless it can be successfully closed. This ensures 715 // that we do not leak connections. 716 if err := closeWithContext(ctx, r); err != nil { 717 return err 718 } 719 } 720 721 newRateLimit, err := t.forComponent("resource.rate_limit." + name).NewRateLimit(conf) 722 if err != nil { 723 return fmt.Errorf( 724 "failed to create rate limit resource '%v' of type '%v': %w", 725 name, conf.Type, err, 726 ) 727 } 728 729 t.rateLimits[name] = newRateLimit 730 return nil 731 } 732 733 //------------------------------------------------------------------------------ 734 735 // CloseAsync triggers the shut down of all resource types that implement the 736 // lifetime interface types.Closable. 737 func (t *Type) CloseAsync() { 738 t.resourceLock.Lock() 739 defer t.resourceLock.Unlock() 740 741 for _, c := range t.inputs { 742 c.CloseAsync() 743 } 744 for _, c := range t.caches { 745 c.CloseAsync() 746 } 747 for _, c := range t.conditions { 748 if closer, ok := c.(types.Closable); ok { 749 closer.CloseAsync() 750 } 751 } 752 for _, p := range t.processors { 753 p.CloseAsync() 754 } 755 for _, c := range t.plugins { 756 if closer, ok := c.(types.Closable); ok { 757 closer.CloseAsync() 758 } 759 } 760 for _, c := range t.rateLimits { 761 c.CloseAsync() 762 } 763 for _, c := range t.outputs { 764 c.CloseAsync() 765 } 766 } 767 768 // WaitForClose blocks until either all closable resource types are shut down or 769 // a timeout occurs. 770 func (t *Type) WaitForClose(timeout time.Duration) error { 771 t.resourceLock.Lock() 772 defer t.resourceLock.Unlock() 773 774 timesOut := time.Now().Add(timeout) 775 for k, c := range t.inputs { 776 if err := c.WaitForClose(time.Until(timesOut)); err != nil { 777 return fmt.Errorf("resource '%s' failed to cleanly shutdown: %v", k, err) 778 } 779 delete(t.inputs, k) 780 } 781 for k, c := range t.caches { 782 if err := c.WaitForClose(time.Until(timesOut)); err != nil { 783 return fmt.Errorf("resource '%s' failed to cleanly shutdown: %v", k, err) 784 } 785 delete(t.caches, k) 786 } 787 for k, p := range t.processors { 788 if err := p.WaitForClose(time.Until(timesOut)); err != nil { 789 return fmt.Errorf("resource '%s' failed to cleanly shutdown: %v", k, err) 790 } 791 delete(t.processors, k) 792 } 793 for k, c := range t.rateLimits { 794 if err := c.WaitForClose(time.Until(timesOut)); err != nil { 795 return fmt.Errorf("resource '%s' failed to cleanly shutdown: %v", k, err) 796 } 797 delete(t.rateLimits, k) 798 } 799 for k, c := range t.outputs { 800 if err := c.WaitForClose(time.Until(timesOut)); err != nil { 801 return fmt.Errorf("resource '%s' failed to cleanly shutdown: %v", k, err) 802 } 803 delete(t.outputs, k) 804 } 805 for k, c := range t.plugins { 806 if closer, ok := c.(types.Closable); ok { 807 if err := closer.WaitForClose(time.Until(timesOut)); err != nil { 808 return fmt.Errorf("resource '%s' failed to cleanly shutdown: %v", k, err) 809 } 810 } 811 delete(t.plugins, k) 812 } 813 return nil 814 } 815 816 //------------------------------------------------------------------------------ 817 818 // DEPRECATED 819 // TODO: V4 Remove this 820 821 // SwapMetrics attempts to swap the underlying metrics implementation of a 822 // manager. This function does nothing if the manager type is not a *Type. 823 func SwapMetrics(mgr types.Manager, stats metrics.Type) types.Manager { 824 if t, ok := mgr.(*Type); ok { 825 newMgr := *t 826 newMgr.stats = t.stats.WithStats(stats) 827 return &newMgr 828 } 829 return mgr 830 } 831 832 // GetInput attempts to find a service wide input by its name. 833 func (t *Type) GetInput(name string) (types.Input, error) { 834 if c, exists := t.inputs[name]; exists { 835 return c, nil 836 } 837 return nil, types.ErrInputNotFound 838 } 839 840 // GetCache attempts to find a service wide cache by its name. 841 func (t *Type) GetCache(name string) (types.Cache, error) { 842 if c, exists := t.caches[name]; exists { 843 return c, nil 844 } 845 return nil, types.ErrCacheNotFound 846 } 847 848 // GetCondition attempts to find a service wide condition by its name. 849 func (t *Type) GetCondition(name string) (types.Condition, error) { 850 if c, exists := t.conditions[name]; exists { 851 return c, nil 852 } 853 return nil, types.ErrConditionNotFound 854 } 855 856 // GetProcessor attempts to find a service wide processor by its name. 857 func (t *Type) GetProcessor(name string) (types.Processor, error) { 858 if p, exists := t.processors[name]; exists { 859 return p, nil 860 } 861 return nil, types.ErrProcessorNotFound 862 } 863 864 // GetRateLimit attempts to find a service wide rate limit by its name. 865 func (t *Type) GetRateLimit(name string) (types.RateLimit, error) { 866 if rl, exists := t.rateLimits[name]; exists { 867 return rl, nil 868 } 869 return nil, types.ErrRateLimitNotFound 870 } 871 872 // GetOutput attempts to find a service wide output by its name. 873 func (t *Type) GetOutput(name string) (types.OutputWriter, error) { 874 if c, exists := t.outputs[name]; exists { 875 return c, nil 876 } 877 return nil, types.ErrOutputNotFound 878 } 879 880 // GetPlugin attempts to find a service wide resource plugin by its name. 881 func (t *Type) GetPlugin(name string) (interface{}, error) { 882 if pl, exists := t.plugins[name]; exists { 883 return pl, nil 884 } 885 return nil, types.ErrPluginNotFound 886 }