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

Configuration Management via MonitoringContext #8497

Merged
merged 47 commits into from
Dec 28, 2021

Conversation

anthony-murphy
Copy link
Contributor

@anthony-murphy anthony-murphy commented Dec 8, 2021

This change adds a simple configuration provider abstraction that can be injected via the loader. The configuration and logger are then available on an object currently called the MonitoringContext. To avoid needing to update all our api, we smuggle the MonitoringContext around via the logger. After this initial change has had time to soak we can slowly move our apis over to leveraging MonitoringContexts all rather than logger.

One are we have explicitly not tackled here is namespaces. this means we've gone with a flat structure or all config key look ups. In the future we may want to expand this introduce some kind of namespacing, but there are concerns we'll need to tackle around naming consistency. We face similar naming consistency issues with our child logger. Ideally whatever strategy we land on can help to solve the issues for both.

related to #5921

@github-actions github-actions bot added area: driver Driver related issues area: loader Loader related issues area: odsp-driver area: runtime Runtime related issues labels Dec 8, 2021
@github-actions github-actions bot added the dependencies Pull requests that update a dependency file label Dec 15, 2021
@github-actions github-actions bot removed the request for review from a team December 15, 2021 18:54
@github-actions github-actions bot removed the area: tests Tests to add, test infrastructure improvements, etc label Dec 15, 2021
@anthony-murphy anthony-murphy linked an issue Dec 15, 2021 that may be closed by this pull request

export function loggerToMonitoringContext<T extends ITelemetryBaseLogger = ITelemetryLogger>(
logger: T): MonitoringContext<T> {
if(loggerIsMonitoringContext(logger)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think there's some formatting inconsistencies in this file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i'll take a look

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if(loggerIsMonitoringContext(logger)) {
if (loggerIsMonitoringContext(logger)) {

packages/utils/telemetry-utils/src/index.ts Show resolved Hide resolved
assert.equal(config1.getBoolean("featureEnabled"), false); // from settings1.BreakGlass
});

// #region SettingsProvider
Copy link
Contributor

Choose a reason for hiding this comment

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

How do you feel about removing this region altogether? I was thinking this would be useful but only as a guideline for how consumers of SettingsProvider are able to use our interfaces but I'm not entirely sure it belongs in the main branch

Copy link
Member

@markfields markfields left a comment

Choose a reason for hiding this comment

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

Phew I made it through config.ts... I will have to come back to review the rest later (I guess after it's merged).

packages/utils/telemetry-utils/src/config.ts Outdated Show resolved Hide resolved
packages/utils/telemetry-utils/src/config.ts Outdated Show resolved Hide resolved
packages/utils/telemetry-utils/src/config.ts Show resolved Hide resolved
packages/utils/telemetry-utils/src/config.ts Outdated Show resolved Hide resolved
},
});
}
return NullConfigProvider;
Copy link
Member

Choose a reason for hiding this comment

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

Do we want an API so a caller could check if the provider is operational v. the Null provider? I guess the only usage I can imagine would be a single log line when getting the provider to know whether config is going to work for that session or not.

}

export function loggerIsMonitoringContext<T extends ITelemetryBaseLogger = ITelemetryLogger>(
obj: T): obj is T & MonitoringContext<T> {
Copy link
Member

Choose a reason for hiding this comment

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

You don't need to say T &, it knows it's still T.

Suggested change
obj: T): obj is T & MonitoringContext<T> {
obj: T,
): obj is MonitoringContext<T> {

export function loggerIsMonitoringContext<T extends ITelemetryBaseLogger = ITelemetryLogger>(
obj: T): obj is T & MonitoringContext<T> {
const maybeConfig = obj as Partial<MonitoringContext<T>> | undefined;
return isConfigProviderBase(maybeConfig?.config) && maybeConfig?.logger !== undefined;
Copy link
Member

Choose a reason for hiding this comment

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

No checks on the shape of logger prop?

Copy link
Member

Choose a reason for hiding this comment

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

Wait... I thought maybeConfig is a logger in this case, not has a logger... Haven't looked at usages yet. But that's what the types tell me, I think?

Copy link
Member

Choose a reason for hiding this comment

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

Ohhh... when you mix it in you do mc = logger; mc.logger = logger. Wanna do maybeConfig?.logger === maybeConfig? 🤪 jk


export function loggerToMonitoringContext<T extends ITelemetryBaseLogger = ITelemetryLogger>(
logger: T): MonitoringContext<T> {
if(loggerIsMonitoringContext(logger)) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if(loggerIsMonitoringContext(logger)) {
if (loggerIsMonitoringContext(logger)) {

}

export function loggerToMonitoringContext<T extends ITelemetryBaseLogger = ITelemetryLogger>(
logger: T): MonitoringContext<T> {
Copy link
Member

Choose a reason for hiding this comment

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

style nit:

Suggested change
logger: T): MonitoringContext<T> {
logger: T,
): MonitoringContext<T> {

}

export function mixinMonitoringContext<T extends ITelemetryBaseLogger = ITelemetryLogger>(
logger: T, ... configs: (IConfigProviderBase | undefined)[]) {
Copy link
Member

Choose a reason for hiding this comment

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

style nit:

Suggested change
logger: T, ... configs: (IConfigProviderBase | undefined)[]) {
logger: T,
...configs: (IConfigProviderBase | undefined)[],
) {

packages/utils/telemetry-utils/src/logger.ts Outdated Show resolved Hide resolved
packages/utils/telemetry-utils/src/config.ts Show resolved Hide resolved
assert.equal(config.getNumber("badNumber"), undefined);
assert.equal(config.getNumber("stringAndNumber"), 1);

assert.equal(config.getString("stringAndNumber"), "1");
Copy link
Member

Choose a reason for hiding this comment

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

I'd drop the "stringAnd" cases from this test, and simply test getString on every single setting. As-is, it's weird to have number: "1" above, and only be testing getString on those, when the arrays are also in there as strings.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd leave these cases but probably a rename is in order to iron out any confusion. The problem was that the converter would have looked at the value ("1"), interpret it as a primitive type and return a number. This broke the scenario of trying to read a string which was actually the serialization of a number so every time you'd try to read a string, you'd get the default value because you weren't getting the expected type. When that was fixed, these test cases have been added.

Copy link
Member

Choose a reason for hiding this comment

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

How about this, for each case:

  • getRawConfig should return the string value
  • getFoo should return the value parsed as Foo
  • getString should return the string value

Then remove the "stringAnd" cases as redundant.

packages/utils/telemetry-utils/src/test/config.spec.ts Outdated Show resolved Hide resolved
@github-actions github-actions bot removed the dependencies Pull requests that update a dependency file label Dec 27, 2021
Co-authored-by: Mark Fields <markfields@users.noreply.github.com>
packages/runtime/container-runtime/src/containerRuntime.ts Outdated Show resolved Hide resolved
const mc: T & Partial<MonitoringContext<T>> = logger;
mc.config = new CachedConfigProvider(... configs);
const mc: L & Partial<MonitoringContext<N,L>> = logger;
mc.config = new CachedConfigProvider(... configs) as unknown as NamespacedConfigProvider<N>;
Copy link
Member

Choose a reason for hiding this comment

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

Wondering about updating CachedConfigProvider to be CachedConfigProvider<N> extends NamespacedConfigProvider<N> to avoid the cast.

packages/utils/telemetry-utils/src/config.ts Show resolved Hide resolved
assert.equal(config.getNumber("badNumber"), undefined);
assert.equal(config.getNumber("stringAndNumber"), 1);

assert.equal(config.getString("stringAndNumber"), "1");
Copy link
Member

Choose a reason for hiding this comment

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

How about this, for each case:

  • getRawConfig should return the string value
  • getFoo should return the value parsed as Foo
  • getString should return the string value

Then remove the "stringAnd" cases as redundant.

return parsed;
}
}
// configs are immutable, if the first lookup returned no results, all lookups should
Copy link
Member

Choose a reason for hiding this comment

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

Just giving feedback that the wording here was hard to understand at first. Consider something like "cached config values are permanent" instead of "configs are immutable".

I'm bringing a different dialect to the table too, where "config" more naturally means what you're calling "config provider", and what you call "config" I would call "config value" or "setting". So it's very subjective feedback :)

try {
return localStorage !== undefined
&& typeof localStorage === "object"
&& localStorage.getItem(batchManagerDisabledKey) === "1";
Copy link
Member

@markfields markfields Dec 27, 2021

Choose a reason for hiding this comment

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

FC knows this is all changing?

@@ -289,17 +303,25 @@ export class Loader implements IHostLoader {
loaderId: uuid(),
loaderVersion: pkgVersion,
};

const subMc = mixinMonitoringContext(
DebugLogger.mixinDebugLogger("fluid:telemetry", loaderProps.logger, { all: telemetryProps }),
Copy link
Member

Choose a reason for hiding this comment

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

Tangent: I wish I could magically change this to just "fluid" namespace, the ":telemetry" is just unnecessary

packingLevel = 2,
) {
if (storage instanceof BlobAggregationStorage) {
return storage;
}
const mc = loggerToMonitoringContext(logger);
const realAllowPackaging = mc.config.getBoolean("FluidAggregateBlobs") ?? allowPacking ?? false;
Copy link
Member

Choose a reason for hiding this comment

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

namespace?

Copy link
Member

Choose a reason for hiding this comment

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

Also, I think this changes the semantics of the default value of allowPacking. Previously, config was only checked if the arg was not supplied. Now the config is the authority and the param is the fallback.

Copy link
Member

@markfields markfields left a comment

Choose a reason for hiding this comment

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

🎂 Really happy to see this get in! LGTM once you've considered the comments I left, some may be changes you want to take.

anthony-murphy and others added 2 commits December 27, 2021 16:36
Co-authored-by: Mark Fields <markfields@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: driver Driver related issues area: loader Loader related issues area: odsp-driver area: runtime Runtime related issues public api change Changes to a public API
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unified Configuration Management System
4 participants