-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Do not record local sources in runtime config defaults. #39604
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,8 @@ | |
import static io.quarkus.runtime.configuration.PropertiesUtil.filterPropertiesInRoots; | ||
import static io.smallrye.config.ConfigMappings.ConfigClassWithPrefix.configClassWithPrefix; | ||
import static io.smallrye.config.Expressions.withoutExpansion; | ||
import static io.smallrye.config.PropertiesConfigSourceProvider.classPathSources; | ||
import static io.smallrye.config.SmallRyeConfigBuilder.META_INF_MICROPROFILE_CONFIG_PROPERTIES; | ||
import static java.util.stream.Collectors.toSet; | ||
|
||
import java.io.IOException; | ||
|
@@ -512,10 +514,12 @@ ReadResult run() { | |
nameBuilder.setLength(0); | ||
} | ||
|
||
SmallRyeConfig runtimeConfig = getConfigForRuntimeRecording(); | ||
|
||
// Register defaults for Roots | ||
allBuildTimeValues.putAll(getDefaults(buildTimePatternMap)); | ||
buildTimeRunTimeValues.putAll(getDefaults(buildTimeRunTimePatternMap)); | ||
runTimeDefaultValues.putAll(getDefaults(runTimePatternMap)); | ||
allBuildTimeValues.putAll(getDefaults(config, buildTimePatternMap)); | ||
buildTimeRunTimeValues.putAll(getDefaults(config, buildTimeRunTimePatternMap)); | ||
runTimeDefaultValues.putAll(getDefaults(runtimeConfig, runTimePatternMap)); | ||
|
||
// Register defaults for Mappings | ||
// Runtime defaults are added in ConfigGenerationBuildStep.generateBuilders to include user mappings | ||
|
@@ -602,7 +606,7 @@ ReadResult run() { | |
knownProperty = knownProperty || matched != null; | ||
if (matched != null) { | ||
// it's a run-time default (record for later) | ||
ConfigValue configValue = withoutExpansion(() -> config.getConfigValue(propertyName)); | ||
ConfigValue configValue = withoutExpansion(() -> runtimeConfig.getConfigValue(propertyName)); | ||
if (configValue.getValue() != null) { | ||
runTimeValues.put(configValue.getNameProfiled(), configValue.getValue()); | ||
} | ||
|
@@ -613,7 +617,7 @@ ReadResult run() { | |
} | ||
} else { | ||
// it's not managed by us; record it | ||
ConfigValue configValue = withoutExpansion(() -> config.getConfigValue(propertyName)); | ||
ConfigValue configValue = withoutExpansion(() -> runtimeConfig.getConfigValue(propertyName)); | ||
if (configValue.getValue() != null) { | ||
runTimeValues.put(configValue.getNameProfiled(), configValue.getValue()); | ||
} | ||
|
@@ -640,7 +644,7 @@ ReadResult run() { | |
for (String property : mappedProperties) { | ||
unknownBuildProperties.remove(property); | ||
ConfigValue value = config.getConfigValue(property); | ||
if (value != null && value.getRawValue() != null) { | ||
if (value.getRawValue() != null) { | ||
allBuildTimeValues.put(value.getNameProfiled(), value.getRawValue()); | ||
} | ||
} | ||
|
@@ -652,7 +656,7 @@ ReadResult run() { | |
for (String property : mappedProperties) { | ||
unknownBuildProperties.remove(property); | ||
ConfigValue value = config.getConfigValue(property); | ||
if (value != null && value.getRawValue() != null) { | ||
if (value.getRawValue() != null) { | ||
allBuildTimeValues.put(value.getNameProfiled(), value.getRawValue()); | ||
buildTimeRunTimeValues.put(value.getNameProfiled(), value.getRawValue()); | ||
} | ||
|
@@ -664,8 +668,8 @@ ReadResult run() { | |
Set<String> mappedProperties = ConfigMappings.mappedProperties(mapping, allProperties); | ||
for (String property : mappedProperties) { | ||
unknownBuildProperties.remove(property); | ||
ConfigValue value = config.getConfigValue(property); | ||
if (value != null && value.getRawValue() != null) { | ||
ConfigValue value = runtimeConfig.getConfigValue(property); | ||
if (value.getRawValue() != null) { | ||
runTimeValues.put(value.getNameProfiled(), value.getRawValue()); | ||
} | ||
} | ||
|
@@ -1117,6 +1121,41 @@ public Set<String> getPropertyNames() { | |
return properties; | ||
} | ||
|
||
/** | ||
* Use this Config instance to record the runtime default values. We cannot use the main Config | ||
* instance because it may record values coming from local development sources (Environment Variables, | ||
* System Properties, etc.) in at build time. Local config source values may be completely different between the | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we be a bit more specific here than just "etc" - what are we actually using from recording? looking at code below we are not excluding sources but just gathering a list of sources...what is actually defining what we include/exclude here?~ i see explicit exlucde of EnvConfig and SysVar ...does that include .env? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we build a new Config instance, excluding specific sources. The |
||
* build environment and the runtime environment, so it doesn't make sense to record these. | ||
* | ||
* @return a new {@link SmallRyeConfig} instance without the local sources, including SysPropConfigSource, | ||
* EnvConfigSource, .env, and Build system sources. | ||
*/ | ||
private SmallRyeConfig getConfigForRuntimeRecording() { | ||
SmallRyeConfigBuilder builder = ConfigUtils.emptyConfigBuilder(); | ||
builder.getSources().clear(); | ||
builder.getSourceProviders().clear(); | ||
builder.setAddDefaultSources(false) | ||
// Customizers may duplicate sources, but not much we can do about it, we need to run them | ||
.addDiscoveredCustomizers() | ||
// Read microprofile-config.properties, because we disabled the default sources | ||
.withSources(classPathSources(META_INF_MICROPROFILE_CONFIG_PROPERTIES, classLoader)); | ||
|
||
// TODO - Should we reset quarkus.config.location to not record from these sources? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As for But it resonates with the comment I made above: I don't fully understand why we need to register defaults for runtime values? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Originally (IIRC) the idea was that if the user put a value in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It depends on the phase. We need to record the values for build time - runtime fixed to ensure they do not change between build and runtime. For runtime, it has been a convenience; to flatten the sources to reduce lookups, avoid loading the config files, and ensure that Quarkus would still run even if a property was missing by falling back to the recorded value from the build. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, so I'm not aware of all the consequences and maybe it could have some consequences but my intuition tells me to drop the recording for runtime and resolve things at runtime. Of course, I would try to avoid reading the Now, I might be missing something entirely and it might cause issues I don't foresee, be it for performance or other things. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can certainly do that. I don't foresee any big consequences, and regarding performance, it is hard to tell the impact without measuring it, but we should be able to figure out something. We must still record the runtime defaults from roots, mappings, and build steps, but we don't need the config instance in that case. |
||
for (ConfigSource configSource : config.getConfigSources()) { | ||
if (configSource instanceof SysPropConfigSource) { | ||
continue; | ||
} | ||
if (configSource instanceof EnvConfigSource) { | ||
continue; | ||
} | ||
if ("PropertiesConfigSource[source=Build system]".equals(configSource.getName())) { | ||
radcortez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
continue; | ||
} | ||
builder.withSources(configSource); | ||
} | ||
return builder.build(); | ||
} | ||
|
||
private Map<String, String> filterActiveProfileProperties(final Map<String, String> properties) { | ||
Set<String> propertiesToRemove = new HashSet<>(); | ||
for (String property : properties.keySet()) { | ||
|
@@ -1131,13 +1170,15 @@ private Map<String, String> filterActiveProfileProperties(final Map<String, Stri | |
return properties; | ||
} | ||
|
||
private Map<String, String> getDefaults(final ConfigPatternMap<Container> patternMap) { | ||
private static Map<String, String> getDefaults(final SmallRyeConfig config, | ||
final ConfigPatternMap<Container> patternMap) { | ||
Map<String, String> defaultValues = new TreeMap<>(); | ||
getDefaults(defaultValues, new StringBuilder(), patternMap); | ||
getDefaults(config, defaultValues, new StringBuilder(), patternMap); | ||
return defaultValues; | ||
} | ||
|
||
private void getDefaults( | ||
private static void getDefaults( | ||
final SmallRyeConfig config, | ||
final Map<String, String> defaultValues, | ||
final StringBuilder propertyName, | ||
final ConfigPatternMap<Container> patternMap) { | ||
|
@@ -1164,7 +1205,7 @@ private void getDefaults( | |
} | ||
|
||
for (String childName : patternMap.childNames()) { | ||
getDefaults(defaultValues, | ||
getDefaults(config, defaultValues, | ||
new StringBuilder(propertyName).append(childName.equals(ConfigPatternMap.WILD_CARD) ? "*" : childName), | ||
patternMap.getChild(childName)); | ||
} | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,13 @@ | ||
package org.acme; | ||
|
||
import io.quarkus.arc.profile.IfBuildProfile; | ||
import org.eclipse.microprofile.config.inject.ConfigProperty; | ||
|
||
import jakarta.enterprise.context.ApplicationScoped; | ||
|
||
@IfBuildProfile("foo") | ||
@ApplicationScoped | ||
public class HelloService { | ||
|
||
@ConfigProperty(name = "name") | ||
String name; | ||
|
||
public String name() { | ||
return name; | ||
return "from foo"; | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -152,8 +152,8 @@ quarkus.arc.unremovable-types=foo | |
# The YAML source may add an indexed property (depending on how the YAML is laid out). This is not supported by @ConfigRoot | ||
quarkus.arc.unremovable-types[0]=foo | ||
|
||
### Do not record env values in build time | ||
bt.ok.to.record=properties | ||
### recording | ||
bt.ok.to.record=from-app | ||
%test.bt.profile.record=properties | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @radcortez, quick question, does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We record all profiled names. We need to ensure that the recorded properties retain their original property name, to not override each other. Local sources are excluded from any recording for any case with this PR. |
||
|
||
### mappings | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package io.quarkus.extest.runtime.config; | ||
|
||
import java.util.Map; | ||
|
||
import io.quarkus.runtime.annotations.StaticInitSafe; | ||
import io.smallrye.config.EnvConfigSource; | ||
|
||
@StaticInitSafe | ||
public class DoNotRecordEnvConfigSource extends EnvConfigSource { | ||
public DoNotRecordEnvConfigSource() { | ||
super(Map.of( | ||
"SHOULD_NOT_BE_RECORDED", "value", | ||
"should.not.be.recorded", "value", | ||
"quarkus.rt.do-not-record", "value", | ||
"quarkus.mapping.rt.do-not-record", "value", | ||
"%dev.quarkus.mapping.rt.do-not-record", "dev", | ||
"_PROD_QUARKUS_MAPPING_RT_DO_NOT_RECORD", "prod"), 300); | ||
} | ||
} |
This file was deleted.
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 to clarify something: when you start your runtime app, how are the config sources evaluated?
What I'm wondering is if we need to register default values for runtime? Is it because we don't parse the existing config such as
application.properties
at runtime?Asking because my inclination would have been to record nothing for runtime values and let things be determined at runtime.
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.
Correct, we flatten the sources to a single source to optimize lookup and save on loading. We could try to remove the runtime recording and generate the runtime source of each
application.properties
and let it be loaded by the Config.