Skip to content
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

Context Propagation performance issue and init issue #39988

Merged
merged 4 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading