Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add synchronous config access API #88981

Merged
merged 9 commits into from
Jan 28, 2021

Conversation

pgayvallet
Copy link
Contributor

@pgayvallet pgayvallet commented Jan 21, 2021

Summary

Fix #82723

PR adds a new PluginInitializerContext.config.get API to retrieve the current value of the plugin's configuration synchronously.

  • Add ConfigService.atPathSync and PluginInitializerContext.config.get
  • Remove the unsed PluginInitializerContext.config.createIfExists API
  • Cleanup the ConfigService, mostly the markAsHandled logic (see review comment)
  • Add TS doc on PluginInitializerContext

Example

 // plugins/my-plugin/server/plugin.ts

 export class MyPlugin implements Plugin {
   constructor(private readonly initContext: PluginInitializerContext) {}
   setup(core) {
     const config = this.initContext.config.get<MyPluginConfigType>();
     // do something with the config
   }
 }

Checklist

@pgayvallet pgayvallet changed the title add synchronous config accessor add synchronous config access API Jan 21, 2021
@pgayvallet pgayvallet added release_note:skip Skip the PR/issue when compiling release notes v7.12.0 v8.0.0 labels Jan 26, 2021
Comment on lines -65 to +70
public async setSchema(path: ConfigPath, schema: Type<unknown>) {
public setSchema(path: ConfigPath, schema: Type<unknown>) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method was async for now reason. Also adapted the consumers

Comment on lines -41 to +43
private readonly handledPaths: ConfigPath[] = [];
private readonly handledPaths: Set<ConfigPath> = new Set();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were using an array instead of a set to store the used paths, meaning that we were eventually having duplicates. (Impact was minimal as we are using a shareReplay in the plugin context's config.create observable, but still)

}

private getDistinctConfig(path: ConfigPath) {
this.markAsHandled(path);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were flagging configurations as handled every time we called atPath. That probably made sense in the initial implementation, but since we now validate the whole config at startups, there are now unnecessary calls.

The only calls to markAsHandled remaining are the ones done during config validation, and when invoking isEnabledAtPath to mark a disabled plugin's path as handled.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move validateAtPath to perform validation only once on every config$ emit? Then we get rid of validation overhead on every config read operation. WDYT? Can be done in a separate PR.a

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validating the config within the whole config obs

this.config$ = combineLatest([this.rawConfigProvider.getConfig$(), this.deprecations]).pipe(...

May be tricky because we would need to already prepare each child observable here, one per schema (we don't want this.config$ to error.).

In a similar way, ideally, atPathSync should only validate the config once and memoize the error/validation result until next emission.

Also, as you said on slack, the timing between emissions and schema registration needs to be taken into account here. When registering a schema, we need to validate the associated part of the config.

PluginInitializerContext.config is not an API that should be used a lot, even during plugin initialization (theoretically the config should only be read once, more or less, per plugin), so this should be minor.

I agree that this is still a performance improvement though, I will create an issue.

Comment on lines +133 to +138
public atPathSync<TSchema>(path: ConfigPath) {
if (!this.validated) {
throw new Error('`atPathSync` called before config was validated');
}
const configAtPath = this.lastConfig!.get(path);
return this.validateAtPath(path, configAtPath) as TSchema;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During testing, directly calling atPathSync after instantiating the service was causing errors because this.config$ has not emitted yet. Even if that would never occur in real scenarios, as we are awaiting for the config to validate before doing anything with it, I added an explicit rule requiring validate to have been called before being able to use atPathSync

@pgayvallet pgayvallet marked this pull request as ready for review January 26, 2021 14:30
@pgayvallet pgayvallet requested a review from a team as a code owner January 26, 2021 14:30
@pgayvallet pgayvallet added the Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc label Jan 26, 2021
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-core (Team:Core)

Copy link
Member

@Bamieh Bamieh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code LGTM!

Syncing my discussion with pierre about this feature:

The observable based implementation was meant to allow users to reconfigure Kibana at runtime.

hotreload is not active at the moment. We are not emitting a new config to plugins, and manually calling loggingService.reload instead.

Implementing hotreload will require a lot of effort and has several complexities like disabling a previously enabled plugin.

Sync lifecycle has its own benefits and cons but that was the compromise the team decided to take at some point.

Copy link
Contributor

@mshustov mshustov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are we going to communicate this change to the Solution teams? Should we create a meta issue to track migration progress for sync lifecycle methods?

*
* @remarks This should only be used when synchronous access is an absolute necessity, such
* as during the plugin's setup or start lifecycle. For all other usages,
* {@link create} should be used instead.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such an irony, considering that we are removing the only config value elasticsearch.logQueries supporting reactivity.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YAGNI 😉

}

private getDistinctConfig(path: ConfigPath) {
this.markAsHandled(path);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move validateAtPath to perform validation only once on every config$ emit? Then we get rid of validation overhead on every config read operation. WDYT? Can be done in a separate PR.a

@kibanamachine
Copy link
Contributor

💚 Build Succeeded

Metrics [docs]

✅ unchanged

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

@pgayvallet pgayvallet merged commit ec8738a into elastic:master Jan 28, 2021
pgayvallet added a commit to pgayvallet/kibana that referenced this pull request Jan 28, 2021
* add synchronous config accessor

* add `config.get` to plugin context and add tsdoc

* remove useless markAsHandled calls

* fix mocks

* update generated docs

* fix unit tests

* add sync accessor for legacy config
pgayvallet added a commit that referenced this pull request Jan 28, 2021
* add synchronous config accessor

* add `config.get` to plugin context and add tsdoc

* remove useless markAsHandled calls

* fix mocks

* update generated docs

* fix unit tests

* add sync accessor for legacy config
@lizozom lizozom mentioned this pull request Feb 3, 2021
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release_note:skip Skip the PR/issue when compiling release notes Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc v7.12.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Core should provide a synchronous API for reading config values
6 participants