Skip to content

Avoid reading System Properties during configuration phase with disableCreatingBuildConfigDuringConfiguration property#52507

Open
cdsap wants to merge 1 commit intoquarkusio:mainfrom
cdsap:skipping_buildconfig_configutation
Open

Avoid reading System Properties during configuration phase with disableCreatingBuildConfigDuringConfiguration property#52507
cdsap wants to merge 1 commit intoquarkusio:mainfrom
cdsap:skipping_buildconfig_configutation

Conversation

@cdsap
Copy link
Contributor

@cdsap cdsap commented Feb 9, 2026

This PR covers the issue described in #52464 and expanded on in discussion #52506.

The Quarkus Gradle plugin extension introduces a new property, disableCreatingBuildConfigDuringConfiguration, which disables EffectConfig initialization during configuration. This avoids reading system properties at configuration time, which can cause configuration cache misses. The EffectConfig still happens during task execution.

To opt out of the EffectConfig / BaseConfig configuration, we still need to provide the required properties used as inputs for GetBuildFiles (QuarkuBuild). To do that, and only when disableCreatingBuildConfigDuringConfiguration is enabled, we provide a Provider that supplies the required system properties.

At the moment, we support two levels of properties:

  • System property arguments
  • Application properties

For this experimental phase, this is sufficient. If we continue with this approach, we will likely need to replicate the other ConfigSource providers as well.

From the client perspective, the user only needs to enable the flag:

quarkus {
  setDisableCreatingBuildConfigDuringConfiguration(true)
  // ...
}

With this property enabled, a sequence of builds like:

./gradlew build -Da=1
./gradlew build -Da=2

will reuse the configuration cache on the second build.

@quarkus-bot quarkus-bot bot added area/devtools Issues/PR related to maven, gradle, platform and cli tooling/plugins area/gradle Gradle labels Feb 9, 2026
@quarkus-bot

This comment has been minimized.

@geoand
Copy link
Contributor

geoand commented Feb 16, 2026

@aloubyansky @radcortez can you please take a look at this?

Thanks

@radcortez
Copy link
Member

I'll have a look.

@radcortez
Copy link
Member

Being discussed in #52506.

@quarkus-bot

This comment has been minimized.

@geoand
Copy link
Contributor

geoand commented Feb 18, 2026

Please rebase this onto main as we don't allow merge commits in PRs.

Thanks

@cdsap cdsap force-pushed the skipping_buildconfig_configutation branch from 5f9012b to 55116ed Compare February 18, 2026 18:37
@cdsap
Copy link
Contributor Author

cdsap commented Feb 18, 2026

my mistake @geoand, fixed

@geoand
Copy link
Contributor

geoand commented Feb 18, 2026

🙏

@quarkus-bot
Copy link

quarkus-bot bot commented Feb 18, 2026

Status for workflow Quarkus CI

This is the status report for running Quarkus CI on commit 55116ed.

✅ The latest workflow run for the pull request has completed successfully.

It should be safe to merge provided you have a look at the other checks in the summary.

You can consult the Develocity build scans.

Copy link
Member

@radcortez radcortez left a comment

Choose a reason for hiding this comment

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

Based on our discussion in #52506 (comment), we recommend providing a new Map of values containing the configurations from PackageConfig and NativeConfig, and using that in the relevant tasks, instead of the full map of configuration values.

You should be able to use io.smallrye.config.ConfigMappings#propertyNamesMatcher, which takes the config classes and then use it to iterate the list of available configurations and match the properties to build a list of the names provided by the mapping classes.

@cdsap
Copy link
Contributor Author

cdsap commented Feb 25, 2026

@radcortez Thanks for the clarifications. I think I have a clear path to follow, but I would need some guidance to cover the next case:

Using the new map and iterating over the different configurations works fine when operating in the task action or outputs. However, there is a case with getBuildInputFiles in the QuarkusBuild task:
https://github.com/quarkusio/quarkus/blob/main/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuild.java#L160

Because this is an input, iterating over the configurations would resolve the different properties during configuration time. In this PR, I covered the case by iterating "manually" over the configurations for system properties and application properties, but other cases such as environment variables or YAML files are not covered yet.

I’m seeing some issues again with how this input is defined, and I’d appreciate your input here.
cc @aloubyansky

@radcortez
Copy link
Member

Because this is an input, iterating over the configurations would resolve the different properties during configuration time. In this PR, I covered the case by iterating "manually" over the configurations for system properties and application properties, but other cases such as environment variables or YAML files are not covered yet.

My proposal was to generate this Map from EffectiveConfig. You can populate the Map from a fully built SmallRyeConfig instance, which already reads all of the sources automatically.

@aloubyansky
Copy link
Member

My proposal was to generate this Map from EffectiveConfig. You can populate the Map from a fully built SmallRyeConfig instance, which already reads all of the sources automatically.

@radcortez would that result in reading all the system properties?

@radcortez
Copy link
Member

What triggers the read of all system properties is the call to getPropertyNames in:

@VisibleForTesting
static Map<String, String> generateFullConfigMap(SmallRyeConfig config) {
Set<String> defaultNames = new HashSet<>();
defaultNames.addAll(configClass(PackageConfig.class).getProperties().keySet());
defaultNames.addAll(configClass(NativeConfig.class).getProperties().keySet());
return Expressions.withoutExpansion(new Supplier<Map<String, String>>() {
@Override
public Map<String, String> get() {
Map<String, String> properties = new HashMap<>();
for (String propertyName : config.getPropertyNames()) {
ConfigValue configValue = config.getConfigValue(propertyName);
// Remove defaults coming from PackageConfig and NativeConfig, as this Map as passed as
// system properties to Gradle workers and, we loose the ability to determine if it was set by
// the user to evaluate deprecated configuration
if (configValue.getValue() != null && (!defaultNames.contains(configValue.getName())
|| !configValue.isDefault())) {
properties.put(propertyName, configValue.getValue());
}
}
return unmodifiableMap(properties);
}
});
}

The problem is that the Map generated by that method is passed down to BaseConfig and used to determine the caching properties:

BaseConfig(EffectiveConfig config) {
manifest = new Manifest();
packageConfig = config.getConfig().getConfigMapping(PackageConfig.class);
nativeConfig = config.getConfig().getConfigMapping(NativeConfig.class);
// populate the Gradle Manifest object
PackageConfig.JarConfig.ManifestConfig manifestConfig = packageConfig.jar().manifest();
manifest.attributes(manifestConfig.attributes());
manifestConfig.sections().forEach((section, attribs) -> manifest.attributes(attribs, section));
values = config.getValues();
}
PackageConfig packageConfig() {
return packageConfig;
}
NativeConfig nativeConfig() {
return nativeConfig;
}
PackageConfig.JarConfig.JarType jarType() {
return packageConfig().jar().type();
}
Manifest manifest() {
return manifest;
}
Map<String, String> cachingRelevantProperties(List<String> propertyPatterns) {
List<Pattern> patterns = propertyPatterns.stream().map(s -> "^(" + s + ")$").map(Pattern::compile)
.collect(Collectors.toList());
readMissingEnvVariables(propertyPatterns);
Predicate<Map.Entry<String, ?>> keyPredicate = e -> patterns.stream().anyMatch(p -> p.matcher(e.getKey()).matches());
return values.entrySet().stream()
.filter(keyPredicate)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (s, s2) -> {
throw new IllegalArgumentException("Duplicate key");
}, TreeMap::new));
}

Hence my suggestion to generate only a Map with the PackageConfig and NativeConfig properties to pass down to that caching method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/devtools Issues/PR related to maven, gradle, platform and cli tooling/plugins area/gradle Gradle triage/flaky-test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gradle Configuration cache invalidated by unrelated system property changes

4 participants