diff --git a/extensions/caffeine/deployment/src/main/java/io/quarkus/caffeine/deployment/CaffeineProcessor.java b/extensions/caffeine/deployment/src/main/java/io/quarkus/caffeine/deployment/CaffeineProcessor.java index 362742fff2d4b8..0493b452a6acb8 100644 --- a/extensions/caffeine/deployment/src/main/java/io/quarkus/caffeine/deployment/CaffeineProcessor.java +++ b/extensions/caffeine/deployment/src/main/java/io/quarkus/caffeine/deployment/CaffeineProcessor.java @@ -7,13 +7,13 @@ import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; -import io.quarkus.caffeine.runtime.graal.CacheConstructorsFeature; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; -import io.quarkus.deployment.builditem.NativeImageFeatureBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.deployment.metrics.MetricsCapabilityBuildItem; import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild; +import io.quarkus.runtime.metrics.MetricsFactory; public class CaffeineProcessor { @@ -26,6 +26,45 @@ public class CaffeineProcessor { private static final DotName CACHE_LOADER_NAME = DotName.createSimple(CACHE_LOADER_CLASS_NAME); + /** + * this list is not complete, but a selection of the types we expect being most useful. + * unfortunately registering all of them has been shown to have a very significant impact + * on executable sizes. See https://github.com/quarkusio/quarkus/issues/12961 + */ + private static final String[] DEFAULT_CACHE_IMPLEMENTATIONS_TO_REGISTER_FOR_REFLECTION = { + "com.github.benmanes.caffeine.cache.PDMS", + "com.github.benmanes.caffeine.cache.PSA", + "com.github.benmanes.caffeine.cache.PSMS", + "com.github.benmanes.caffeine.cache.PSW", + "com.github.benmanes.caffeine.cache.PSMW", + "com.github.benmanes.caffeine.cache.PSWMS", + "com.github.benmanes.caffeine.cache.PSWMW", + "com.github.benmanes.caffeine.cache.SILMS", + "com.github.benmanes.caffeine.cache.SSA", + "com.github.benmanes.caffeine.cache.SSLA", + "com.github.benmanes.caffeine.cache.SSLMS", + "com.github.benmanes.caffeine.cache.SSMS", + "com.github.benmanes.caffeine.cache.SSMSA", + "com.github.benmanes.caffeine.cache.SSMSW", + "com.github.benmanes.caffeine.cache.SSW" + }; + + /** + * When the Micrometer extension is around, we add the cache classes with metrics. + *

+ * Note that we don't know if they will actually be used given the cache configuration is defined at runtime. + */ + private static final String[] METRICS_CACHE_IMPLEMENTATIONS_TO_REGISTER_FOR_REFLECTION = { + "com.github.benmanes.caffeine.cache.SILSMS", + "com.github.benmanes.caffeine.cache.SSSA", + "com.github.benmanes.caffeine.cache.SSLSA", + "com.github.benmanes.caffeine.cache.SSLSMS", + "com.github.benmanes.caffeine.cache.SSSMS", + "com.github.benmanes.caffeine.cache.SSSMSA", + "com.github.benmanes.caffeine.cache.SSSMSW", + "com.github.benmanes.caffeine.cache.SSSW" + }; + @BuildStep void cacheLoaders(CombinedIndexBuildItem combinedIndex, BuildProducer reflectiveClasses) { final Collection implementors = combinedIndex.getIndex().getAllKnownImplementors(CACHE_LOADER_NAME); @@ -46,7 +85,14 @@ void cacheLoaders(CombinedIndexBuildItem combinedIndex, BuildProducer reflectiveClasses) { + reflectiveClasses + .produce(new ReflectiveClassBuildItem(false, false, DEFAULT_CACHE_IMPLEMENTATIONS_TO_REGISTER_FOR_REFLECTION)); + + if (metricsCapability.metricsSupported(MetricsFactory.MICROMETER)) { + reflectiveClasses + .produce(new ReflectiveClassBuildItem(false, false, + METRICS_CACHE_IMPLEMENTATIONS_TO_REGISTER_FOR_REFLECTION)); + } } } diff --git a/extensions/caffeine/runtime/src/main/java/io/quarkus/caffeine/runtime/graal/CacheConstructorsFeature.java b/extensions/caffeine/runtime/src/main/java/io/quarkus/caffeine/runtime/graal/CacheConstructorsFeature.java deleted file mode 100644 index 599100d1aad72c..00000000000000 --- a/extensions/caffeine/runtime/src/main/java/io/quarkus/caffeine/runtime/graal/CacheConstructorsFeature.java +++ /dev/null @@ -1,85 +0,0 @@ -package io.quarkus.caffeine.runtime.graal; - -import java.lang.reflect.Constructor; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.graalvm.nativeimage.hosted.Feature; -import org.graalvm.nativeimage.hosted.RuntimeReflection; - -/** - * This Automatic Feature for GraalVM will register for reflection - * the most commonly used cache implementations from Caffeine. - * It's implemented as an explicit @{@link Feature} rather than - * using the Quarkus builditems because it doesn't need to be - * dynamically tuned (the list is static), and to take advantage - * of the reachability information we can infer from @{@link org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess}. - * - * This allows us to register for reflection these resources only if - * Caffeine is indeed being used: only if the cache builder is reachable - * in the application code. - */ -public class CacheConstructorsFeature implements Feature { - - private final AtomicBoolean triggered = new AtomicBoolean(false); - - /** - * To set this, add `-J-Dio.quarkus.caffeine.graalvm.diagnostics=true` to the native-image parameters - */ - private static final boolean log = Boolean.getBoolean("io.quarkus.caffeine.graalvm.diagnostics"); - - @Override - public void beforeAnalysis(BeforeAnalysisAccess access) { - Class caffeineCoreClazz = access.findClassByName("com.github.benmanes.caffeine.cache.Caffeine"); - access.registerReachabilityHandler(this::ensureCaffeineSupportEnabled, caffeineCoreClazz); - } - - private void ensureCaffeineSupportEnabled(DuringAnalysisAccess duringAnalysisAccess) { - final boolean needsEnablingYet = triggered.compareAndSet(false, true); - if (needsEnablingYet) { - if (log) { - System.out.println( - "Quarkus's automatic feature for GraalVM native images: enabling support for core Caffeine caches"); - } - registerCaffeineReflections(duringAnalysisAccess); - } - } - - private void registerCaffeineReflections(DuringAnalysisAccess duringAnalysisAccess) { - final String[] needsHavingSimpleConstructors = typesNeedingConstructorsRegistered(); - for (String className : needsHavingSimpleConstructors) { - registerForReflection(className, duringAnalysisAccess); - } - } - - private void registerForReflection( - String className, - DuringAnalysisAccess duringAnalysisAccess) { - final Class aClass = duringAnalysisAccess.findClassByName(className); - final Constructor[] z = aClass.getDeclaredConstructors(); - RuntimeReflection.register(aClass); - RuntimeReflection.register(z); - } - - public static String[] typesNeedingConstructorsRegistered() { - return new String[] { - //N.B. this list is not complete, but a selection of the types we expect being most useful. - //unfortunately registering all of them has been shown to have a very significant impact - //on executable sizes. See https://github.com/quarkusio/quarkus/issues/12961 - "com.github.benmanes.caffeine.cache.PDMS", - "com.github.benmanes.caffeine.cache.PSA", - "com.github.benmanes.caffeine.cache.PSMS", - "com.github.benmanes.caffeine.cache.PSW", - "com.github.benmanes.caffeine.cache.PSWMS", - "com.github.benmanes.caffeine.cache.PSWMW", - "com.github.benmanes.caffeine.cache.SILMS", - "com.github.benmanes.caffeine.cache.SSA", - "com.github.benmanes.caffeine.cache.SSLA", - "com.github.benmanes.caffeine.cache.SSLMS", - "com.github.benmanes.caffeine.cache.SSMS", - "com.github.benmanes.caffeine.cache.SSMSA", - "com.github.benmanes.caffeine.cache.SSMSW", - "com.github.benmanes.caffeine.cache.SSW", - }; - } - -} diff --git a/integration-tests/cache/src/main/resources/application.properties b/integration-tests/cache/src/main/resources/application.properties index 4e01418c2e2ada..b94edbb9836783 100644 --- a/integration-tests/cache/src/main/resources/application.properties +++ b/integration-tests/cache/src/main/resources/application.properties @@ -5,6 +5,8 @@ quarkus.hibernate-orm.sql-load-script=import.sql # configure the caches quarkus.cache.caffeine."forest".expire-after-write=10M + +quarkus.cache.caffeine."expensiveResourceCache".expire-after-write=10M quarkus.cache.caffeine."expensiveResourceCache".metrics-enabled=true io.quarkus.it.cache.SunriseRestClient/mp-rest/url=${test.url}