From b6bb7b3c0396417d9becc06ca627d0ad5de99017 Mon Sep 17 00:00:00 2001 From: Matthew Sainsbury Date: Wed, 22 Jan 2025 00:05:43 +0000 Subject: [PATCH] doc: add detail to confmap watcher --- confmap/README.md | 73 ++++++++++++++++++++++++++++++++++++++++++--- confmap/provider.go | 2 +- 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/confmap/README.md b/confmap/README.md index bfa24c8bcc0..a330965244c 100644 --- a/confmap/README.md +++ b/confmap/README.md @@ -37,7 +37,7 @@ that can be used by code that is oblivious to the usage of `Providers` and `Conv or an individual value (partial configuration) when the `configURI` is embedded into the `Conf` as a values using the syntax `${configURI}`. -**Limitation:** +**Limitation:** - When embedding a `${configURI}` the uri cannot contain dollar sign ("$") character unless it embeds another uri. - The number of URIs is limited to 100. @@ -87,7 +87,26 @@ The `Resolve` method proceeds in the following steps: After the configuration was processed, the `Resolver` can be used as a single point to watch for updates in the configuration retrieved via the `Provider` used to retrieve the “initial” configuration and to generate the “effective” one. -```terminal +```terminal + Resolver Provider + │ │ + Watch │ │ +───────────►│ │ + │ │ + . . + . . + . . + │ onChange │ + │◄────────────────────┤ +◄───────────┤ │ + +``` + +The `Resolver` does that by passing an `onChange` func to each `Provider.Retrieve` call and capturing all watch events. + +Calling the `onChange` func from a provider triggers the collector to re-resolve new configuration: + +```terminal Resolver Provider │ │ Watch │ │ @@ -99,16 +118,62 @@ configuration retrieved via the `Provider` used to retrieve the “initial” co │ onChange │ │◄────────────────────┤ ◄───────────┤ │ + | | + Resolve │ │ +───────────►│ │ + │ │ + │ Retrieve │ + ├────────────────────►│ + │ Conf │ + │◄────────────────────┤ +◄───────────┤ │ +``` + +Example of `Provider` with `onChange` func that periodically gets notified: + +```golang +type UpdatingProvider struct { } + +func (p UpdatingProvider) Retrieve(ctx context.Context, uri string, watcher confmap.WatcherFunc) (*confmap.Retrieved, error) { + ticker := time.Ticker(30 * time.Second) + + retrieved := provider.NewRetrieved(getCurrentConfig(), provider.WithRetrievedClose(func (ctx context) error { + // the provider should call this function when it no longer wants config updates + close(ticker) + })) + + go func() { + for { + select { + case <-ctx.Done(): + // if the context is closed, then we should stop sending updates + close(ticker) + return + case _, ticking <- ticker: + if !ticking { + // if ticking stopped, then closeFunc was called + return + } + // otherwise, notify the watcher that a new config is available + // the watcher is expected to call Provider.Retrieve again to get the update + // note that the collector calls closeFunc before calling Retrieve for the second time, + // so these go functions don't accumulate indefinitely. (see otelcol/collector.go, Collector.reloadConfiguration) + watcher(&confmap.ChangeEvent{}) + } + } + }() + + return retrieved, nil +} ``` -The `Resolver` does that by passing an `onChange` func to each `Provider.Retrieve` call and capturing all watch events. ## Troubleshooting ### Null Maps Due to how our underlying merge library, [koanf](https://github.com/knadh/koanf), behaves, configuration resolution -will treat configuration such as +will treat configuration such as ```yaml processors: diff --git a/confmap/provider.go b/confmap/provider.go index c462b9bb6fe..55d0d69341d 100644 --- a/confmap/provider.go +++ b/confmap/provider.go @@ -64,7 +64,7 @@ type Provider interface { // - For testing, all implementation MUST check that confmaptest.ValidateProviderScheme returns no error. // // `watcher` callback is called when the config changes. watcher may be called from - // a different go routine. After watcher is called Retrieved.Get should be called + // a different go routine. After watcher is called, Provider.Retrieve should be called // to get the new config. See description of Retrieved for more details. // watcher may be nil, which indicates that the caller is not interested in // knowing about the changes.