-
Notifications
You must be signed in to change notification settings - Fork 535
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
Configuration Management via MonitoringContext #8497
Conversation
…to config-provider
…to config-provider
…to config-provider
|
||
export function loggerToMonitoringContext<T extends ITelemetryBaseLogger = ITelemetryLogger>( | ||
logger: T): MonitoringContext<T> { | ||
if(loggerIsMonitoringContext(logger)) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if(loggerIsMonitoringContext(logger)) { | |
if (loggerIsMonitoringContext(logger)) { |
assert.equal(config1.getBoolean("featureEnabled"), false); // from settings1.BreakGlass | ||
}); | ||
|
||
// #region SettingsProvider |
There was a problem hiding this comment.
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
There was a problem hiding this 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).
}, | ||
}); | ||
} | ||
return NullConfigProvider; |
There was a problem hiding this comment.
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> { |
There was a problem hiding this comment.
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
.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if(loggerIsMonitoringContext(logger)) { | |
if (loggerIsMonitoringContext(logger)) { |
} | ||
|
||
export function loggerToMonitoringContext<T extends ITelemetryBaseLogger = ITelemetryLogger>( | ||
logger: T): MonitoringContext<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style nit:
logger: T): MonitoringContext<T> { | |
logger: T, | |
): MonitoringContext<T> { |
} | ||
|
||
export function mixinMonitoringContext<T extends ITelemetryBaseLogger = ITelemetryLogger>( | ||
logger: T, ... configs: (IConfigProviderBase | undefined)[]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style nit:
logger: T, ... configs: (IConfigProviderBase | undefined)[]) { | |
logger: T, | |
...configs: (IConfigProviderBase | undefined)[], | |
) { |
assert.equal(config.getNumber("badNumber"), undefined); | ||
assert.equal(config.getNumber("stringAndNumber"), 1); | ||
|
||
assert.equal(config.getString("stringAndNumber"), "1"); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Co-authored-by: Mark Fields <markfields@users.noreply.github.com>
packages/drivers/odsp-driver/src/odspDocumentDeltaConnection.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>; |
There was a problem hiding this comment.
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.
assert.equal(config.getNumber("badNumber"), undefined); | ||
assert.equal(config.getNumber("stringAndNumber"), 1); | ||
|
||
assert.equal(config.getString("stringAndNumber"), "1"); |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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"; |
There was a problem hiding this comment.
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 }), |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
namespace?
There was a problem hiding this comment.
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.
There was a problem hiding this 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.
Co-authored-by: Mark Fields <markfields@users.noreply.github.com>
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