Skip to content

Commit

Permalink
ArC: introduce quarkus.arc.optimize-contexts=auto
Browse files Browse the repository at this point in the history
- if "auto" is used then only optimize if there is less than 1000 beans
in the app; removed beans are excluded
- use this as the default value
  • Loading branch information
mkouba committed Jan 10, 2024
1 parent 694caaa commit 02e10fa
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,21 @@ public class ArcConfig {
public ArcContextPropagationConfig contextPropagation;

/**
* If set to {@code true}, the container should try to optimize the contexts for some of the scopes.
* If set to {@code true}, the container should try to optimize the contexts for some of the scopes. If set to {@code auto}
* then optimize the contexts if there's less than 1000 beans in the application. If set to {@code false} do not optimize
* the contexts.
* <p>
* Typically, some implementation parts of the context for {@link jakarta.enterprise.context.ApplicationScoped} could be
* pregenerated during build.
*/
@ConfigItem(defaultValue = "true", generateDocumentation = false)
public boolean optimizeContexts;
@ConfigItem(defaultValue = "auto", generateDocumentation = false)
public OptimizeContexts optimizeContexts;

public enum OptimizeContexts {
TRUE,
FALSE,
AUTO
}

public final boolean isRemoveUnusedBeansFieldValid() {
return ALLOWED_REMOVE_UNUSED_BEANS_VALUES.contains(removeUnusedBeans.toLowerCase());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,23 @@ public Integer compute(AnnotationTarget target, Collection<StereotypeInfo> stere
}

builder.setBuildCompatibleExtensions(buildCompatibleExtensions.entrypoint);
builder.setOptimizeContexts(arcConfig.optimizeContexts);
builder.setOptimizeContexts(new Predicate<BeanDeployment>() {
@Override
public boolean test(BeanDeployment deployment) {
switch (arcConfig.optimizeContexts) {
case TRUE:
return true;
case FALSE:
return false;
case AUTO:
// Optimize the context if there is less than 1000 beans in the app
// Note that removed beans are excluded
return deployment.getBeans().size() < 1000;
default:
throw new IllegalArgumentException("Unexpected value: " + arcConfig.optimizeContexts);
}
}
});

BeanProcessor beanProcessor = builder.build();
ContextRegistrar.RegistrationContext context = beanProcessor.registerCustomContexts();
Expand Down Expand Up @@ -598,7 +614,7 @@ public ArcContainerBuildItem initializeContainer(ArcConfig config, ArcRecorder r
throws Exception {
ArcContainer container = recorder.initContainer(shutdown,
currentContextFactory.isPresent() ? currentContextFactory.get().getFactory() : null,
config.strictCompatibility, config.optimizeContexts);
config.strictCompatibility);
return new ArcContainerBuildItem(container);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.quarkus.arc.test.context.optimized;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.ServiceLoader;

import jakarta.inject.Inject;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.ComponentsProvider;
import io.quarkus.test.QuarkusUnitTest;

public class OptimizeContextsAutoTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot(root -> root
.addClasses(SimpleBean.class))
.overrideConfigKey("quarkus.arc.optimize-contexts", "auto");

@Inject
SimpleBean bean;

@Test
public void testContexts() {
assertTrue(bean.ping());
for (ComponentsProvider componentsProvider : ServiceLoader.load(ComponentsProvider.class)) {
// We have less than 1000 beans
assertFalse(componentsProvider.getComponents().getContextInstances().isEmpty());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.quarkus.arc.test.context.optimized;

import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.ServiceLoader;

import jakarta.inject.Inject;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.ComponentsProvider;
import io.quarkus.test.QuarkusUnitTest;

public class OptimizeContextsDisabledTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot(root -> root
.addClasses(SimpleBean.class))
.overrideConfigKey("quarkus.arc.optimize-contexts", "false");

@Inject
SimpleBean bean;

@Test
public void testContexts() {
assertTrue(bean.ping());
for (ComponentsProvider componentsProvider : ServiceLoader.load(ComponentsProvider.class)) {
assertTrue(componentsProvider.getComponents().getContextInstances().isEmpty());
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.quarkus.arc.test.context.optimized;

import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
class SimpleBean {

public boolean ping() {
return true;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,10 @@ public class ArcRecorder {
public static volatile Map<String, Function<SyntheticCreationalContext<?>, ?>> syntheticBeanProviders;

public ArcContainer initContainer(ShutdownContext shutdown, RuntimeValue<CurrentContextFactory> currentContextFactory,
boolean strictCompatibility, boolean optimizeContexts)
throws Exception {
boolean strictCompatibility) throws Exception {
ArcInitConfig.Builder builder = ArcInitConfig.builder();
builder.setCurrentContextFactory(currentContextFactory != null ? currentContextFactory.getValue() : null);
builder.setStrictCompatibility(strictCompatibility);
builder.setOptimizeContexts(optimizeContexts);
ArcContainer container = Arc.initialize(builder.build());
shutdown.addShutdownTask(new Runnable() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public static Builder builder() {
private final boolean generateSources;
private final boolean allowMocking;
private final boolean transformUnproxyableClasses;
private final boolean optimizeContexts;
private final Predicate<BeanDeployment> optimizeContexts;
private final List<Function<BeanInfo, Consumer<BytecodeCreator>>> suppressConditionGenerators;

// This predicate is used to filter annotations for InjectionPoint metadata
Expand Down Expand Up @@ -187,6 +187,7 @@ public List<Resource> generateResources(ReflectionRegistration reflectionRegistr

ReflectionRegistration refReg = reflectionRegistration != null ? reflectionRegistration : this.reflectionRegistration;
PrivateMembersCollector privateMembers = new PrivateMembersCollector();
boolean optimizeContextsValue = optimizeContexts != null ? optimizeContexts.test(beanDeployment) : false;

// These maps are precomputed and then used in the ComponentsProviderGenerator which is generated first
Map<BeanInfo, String> beanToGeneratedName = new HashMap<>();
Expand Down Expand Up @@ -240,7 +241,7 @@ public List<Resource> generateResources(ReflectionRegistration reflectionRegistr

ContextInstancesGenerator contextInstancesGenerator = new ContextInstancesGenerator(generateSources,
refReg, beanDeployment, scopeToGeneratedName);
if (optimizeContexts) {
if (optimizeContextsValue) {
contextInstancesGenerator.precomputeGeneratedName(BuiltinScope.APPLICATION.getName());
contextInstancesGenerator.precomputeGeneratedName(BuiltinScope.REQUEST.getName());
}
Expand Down Expand Up @@ -364,7 +365,7 @@ public Collection<Resource> call() throws Exception {
}));
}

if (optimizeContexts) {
if (optimizeContextsValue) {
// Generate _ContextInstances
primaryTasks.add(executor.submit(new Callable<Collection<Resource>>() {

Expand Down Expand Up @@ -450,7 +451,7 @@ public Collection<Resource> call() throws Exception {
observerToGeneratedName,
scopeToGeneratedName));

if (optimizeContexts) {
if (optimizeContextsValue) {
// Generate _ContextInstances
resources.addAll(contextInstancesGenerator.generate(BuiltinScope.APPLICATION.getName()));
resources.addAll(contextInstancesGenerator.generate(BuiltinScope.REQUEST.getName()));
Expand Down Expand Up @@ -564,7 +565,7 @@ public static class Builder {
boolean failOnInterceptedPrivateMethod;
boolean allowMocking;
boolean strictCompatibility;
boolean optimizeContexts;
Predicate<BeanDeployment> optimizeContexts;

AlternativePriorities alternativePriorities;
final List<Predicate<ClassInfo>> excludeTypes;
Expand Down Expand Up @@ -600,7 +601,6 @@ public Builder() {
failOnInterceptedPrivateMethod = false;
allowMocking = false;
strictCompatibility = false;
optimizeContexts = false;

excludeTypes = new ArrayList<>();

Expand Down Expand Up @@ -842,7 +842,21 @@ public Builder setStrictCompatibility(boolean strictCompatibility) {
* @return self
*/
public Builder setOptimizeContexts(boolean value) {
this.optimizeContexts = value;
return setOptimizeContexts(new Predicate<BeanDeployment>() {
@Override
public boolean test(BeanDeployment t) {
return value;
}
});
}

/**
*
* @param fun
* @return self
*/
public Builder setOptimizeContexts(Predicate<BeanDeployment> fun) {
this.optimizeContexts = fun;
return this;
}

Expand Down

0 comments on commit 02e10fa

Please sign in to comment.