Skip to content

Commit

Permalink
Merge pull request quarkusio#39988 from FroMage/cp-perf-manager
Browse files Browse the repository at this point in the history
Context Propagation performance issue and init issue
  • Loading branch information
geoand committed Apr 15, 2024
2 parents 1e7403a + 08ba1d1 commit ba15f8e
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import io.quarkus.oidc.runtime.TenantConfigBean;
import io.quarkus.oidc.runtime.providers.AzureAccessTokenCustomizer;
import io.quarkus.runtime.TlsConfig;
import io.quarkus.smallrye.context.deployment.ContextPropagationInitializedBuildItem;
import io.quarkus.vertx.core.deployment.CoreVertxBuildItem;
import io.quarkus.vertx.http.deployment.EagerSecurityInterceptorBindingBuildItem;
import io.quarkus.vertx.http.deployment.HttpAuthMechanismAnnotationBuildItem;
Expand Down Expand Up @@ -220,7 +221,9 @@ public SyntheticBeanBuildItem setup(
OidcConfig config,
OidcRecorder recorder,
CoreVertxBuildItem vertxBuildItem,
TlsConfig tlsConfig) {
TlsConfig tlsConfig,
// this is required for setup ordering: we need CP set up
ContextPropagationInitializedBuildItem cpInitializedBuildItem) {
return SyntheticBeanBuildItem.configure(TenantConfigBean.class).unremovable().types(TenantConfigBean.class)
.supplier(recorder.setup(config, vertxBuildItem.getVertx(), tlsConfig))
.destroyer(TenantConfigBean.Destroyer.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkus.smallrye.context.deployment;

import io.quarkus.builder.item.SimpleBuildItem;

/**
* Marker build item for build ordering. Signifies that CP is set up
* and ready for use.
*/
public final class ContextPropagationInitializedBuildItem extends SimpleBuildItem {

}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ void buildStatic(SmallRyeContextPropagationRecorder recorder, List<ThreadContext
void build(SmallRyeContextPropagationRecorder recorder,
ExecutorBuildItem executorBuildItem,
ShutdownContextBuildItem shutdownContextBuildItem,
BuildProducer<ContextPropagationInitializedBuildItem> cpInitializedBuildItem,
BuildProducer<FeatureBuildItem> feature,
BuildProducer<SyntheticBeanBuildItem> syntheticBeans) {
feature.produce(new FeatureBuildItem(Feature.SMALLRYE_CONTEXT_PROPAGATION));
Expand All @@ -111,6 +112,8 @@ void build(SmallRyeContextPropagationRecorder recorder,
.unremovable()
.supplier(recorder.initializeManagedExecutor(executorBuildItem.getExecutorProxy()))
.setRuntimeInit().done());

cpInitializedBuildItem.produce(new ContextPropagationInitializedBuildItem());
}

// transform IPs for ManagedExecutor/ThreadContext that use config annotation and don't yet have @NamedInstance
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.quarkus.smallrye.context.runtime;

import org.eclipse.microprofile.context.spi.ContextManager;

import io.smallrye.context.SmallRyeContextManager;
import io.smallrye.context.SmallRyeContextManagerProvider;

/**
* Quarkus doesn't need one manager per CL, we only have the one
*/
public class QuarkusContextManagerProvider extends SmallRyeContextManagerProvider {

private SmallRyeContextManager contextManager;

@Override
public SmallRyeContextManager getContextManager(ClassLoader classLoader) {
return contextManager;
}

@Override
public SmallRyeContextManager getContextManager() {
return contextManager;
}

@Override
public ContextManager findContextManager(ClassLoader classLoader) {
return contextManager;
}

@Override
public void registerContextManager(ContextManager manager, ClassLoader classLoader) {
if (manager instanceof SmallRyeContextManager == false) {
throw new IllegalArgumentException("Only instances of SmallRyeContextManager are supported: " + manager);
}
contextManager = (SmallRyeContextManager) manager;
}

@Override
public void releaseContextManager(ContextManager manager) {
contextManager = null;
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package io.quarkus.smallrye.context.runtime;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;

import org.eclipse.microprofile.context.ManagedExecutor;
import org.eclipse.microprofile.context.ThreadContext;
import org.eclipse.microprofile.context.spi.ContextManager.Builder;
import org.eclipse.microprofile.context.spi.ContextManagerExtension;
import org.eclipse.microprofile.context.spi.ContextManagerProvider;
import org.eclipse.microprofile.context.spi.ThreadContextProvider;
Expand All @@ -14,7 +21,6 @@
import io.quarkus.runtime.ShutdownContext;
import io.quarkus.runtime.annotations.Recorder;
import io.smallrye.context.SmallRyeContextManager;
import io.smallrye.context.SmallRyeContextManagerProvider;
import io.smallrye.context.SmallRyeManagedExecutor;
import io.smallrye.context.SmallRyeThreadContext;

Expand All @@ -24,14 +30,100 @@
@Recorder
public class SmallRyeContextPropagationRecorder {

private static final ExecutorService NOPE_EXECUTOR_SERVICE = new ExecutorService() {

@Override
public void execute(Runnable command) {
nope();
}

@Override
public void shutdown() {
nope();
}

@Override
public List<Runnable> shutdownNow() {
nope();
return null;
}

@Override
public boolean isShutdown() {
nope();
return false;
}

@Override
public boolean isTerminated() {
nope();
return false;
}

@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
nope();
return false;
}

@Override
public <T> Future<T> submit(Callable<T> task) {
nope();
return null;
}

@Override
public <T> Future<T> submit(Runnable task, T result) {
nope();
return null;
}

@Override
public Future<?> submit(Runnable task) {
nope();
return null;
}

@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
nope();
return null;
}

@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException {
nope();
return null;
}

@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
nope();
return null;
}

@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
nope();
return null;
}

private void nope() {
throw new RuntimeException(
"Trying to invoke ContextPropagation on a partially-configured ContextManager instance. You should wait until runtime init is done. You can do that by consuming the ContextPropagationBuildItem.");
}
};
private static SmallRyeContextManager.Builder builder;

public void configureStaticInit(List<ThreadContextProvider> discoveredProviders,
List<ContextManagerExtension> discoveredExtensions) {
// build the manager at static init time
// in the live-reload mode, the provider instance may be already set in the previous start
if (ContextManagerProvider.INSTANCE.get() == null) {
ContextManagerProvider contextManagerProvider = new SmallRyeContextManagerProvider();
ContextManagerProvider contextManagerProvider = new QuarkusContextManagerProvider();
ContextManagerProvider.register(contextManagerProvider);
}

Expand All @@ -40,6 +132,16 @@ public void configureStaticInit(List<ThreadContextProvider> discoveredProviders,
.getContextManagerBuilder();
builder.withThreadContextProviders(discoveredProviders.toArray(new ThreadContextProvider[0]));
builder.withContextManagerExtensions(discoveredExtensions.toArray(new ContextManagerExtension[0]));

// During boot, if anyone is using CP, they will get no propagation and an error if they try to use
// the executor. This is (so far) only for spring-cloud-config-client which uses Vert.x via Mutiny
// to load config before we're ready for runtime init
SmallRyeContextManager.Builder noContextBuilder = (SmallRyeContextManager.Builder) ContextManagerProvider.instance()
.getContextManagerBuilder();
noContextBuilder.withThreadContextProviders(new ThreadContextProvider[0]);
noContextBuilder.withContextManagerExtensions(new ContextManagerExtension[0]);
noContextBuilder.withDefaultExecutorService(NOPE_EXECUTOR_SERVICE);
ContextManagerProvider.instance().registerContextManager(noContextBuilder.build(), null /* not used */);
}

public void configureRuntime(ExecutorService executorService, ShutdownContext shutdownContext) {
Expand All @@ -59,7 +161,7 @@ public void run() {
}
});
//Avoid leaking the classloader:
this.builder = null;
SmallRyeContextPropagationRecorder.builder = null;
}

public Supplier<Object> initializeManagedExecutor(ExecutorService executorService) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import io.quarkus.deployment.builditem.SystemPropertyBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveMethodBuildItem;
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem;
import io.quarkus.deployment.metrics.MetricsCapabilityBuildItem;
import io.quarkus.deployment.recording.RecorderContext;
Expand Down Expand Up @@ -87,14 +88,17 @@ public void build(BuildProducer<AnnotationsTransformerBuildItem> annotationsTran
CombinedIndexBuildItem combinedIndexBuildItem,
BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
BuildProducer<ReflectiveMethodBuildItem> reflectiveMethod,
BuildProducer<RunTimeConfigurationDefaultBuildItem> config) {
BuildProducer<RunTimeConfigurationDefaultBuildItem> config,
BuildProducer<RuntimeInitializedClassBuildItem> runtimeInitializedClassBuildItems) {

feature.produce(new FeatureBuildItem(Feature.SMALLRYE_FAULT_TOLERANCE));

serviceProvider.produce(new ServiceProviderBuildItem(RequestContextControllerProvider.class.getName(),
ContextPropagationRequestContextControllerProvider.class.getName()));
serviceProvider.produce(new ServiceProviderBuildItem(RunnableWrapper.class.getName(),
ContextPropagationRunnableWrapper.class.getName()));
// make sure this is initialised at runtime, otherwise it will get a non-initialised ContextPropagationManager
runtimeInitializedClassBuildItems.produce(new RuntimeInitializedClassBuildItem(RunnableWrapper.class.getName()));

IndexView index = combinedIndexBuildItem.getIndex();

Expand Down

0 comments on commit ba15f8e

Please sign in to comment.