micronaut-cache icon indicating copy to clipboard operation
micronaut-cache copied to clipboard

Caffeine and Graal native-image

Open phxql opened this issue 4 years ago • 8 comments

Task List

  • [x] Steps to reproduce provided
  • [x] Stacktrace (if present) provided
  • [ ] Example that reproduces the problem uploaded to Github
  • [x] Full description of the issue provided (see below)

Steps to Reproduce

  1. Include
<dependency>
    <groupId>io.micronaut.cache</groupId>
    <artifactId>micronaut-cache-caffeine</artifactId>
    <scope>compile</scope>
</dependency>
  1. Add
<annotationProcessorPaths>
  <path>
      <groupId>io.micronaut</groupId>
      <artifactId>micronaut-graal</artifactId>
      <version>${micronaut.version}</version>
  </path>
</annotationProcessorPaths>
  1. Add method annotated with @Cacheable
  2. run ./mvnw package -Dpackaging=native-image
  3. run binary

Expected Behaviour

It runs.

Actual Behaviour

Exception when accessing the cache:

Message: com.github.benmanes.caffeine.cache.SSW
Path Taken: new CacheHeater([CoolestProjectService service]) --> new $CoolestProjectServiceImplDefinition$Intercepted(GitHubClient client,BeanContext $beanContext,Qualifier $qualifier,[Interceptor[] $interceptors]) --> new CacheInterceptor([CacheManager cacheManager],CacheErrorHandler errorHandler,AsyncCacheErrorHandler asyncCacheErrorHandler,ExecutorService ioExecutor,BeanContext beanContext) --> new DefaultCacheManager([List caches],Provider dynamicCacheManager)
io.micronaut.context.exceptions.BeanInstantiationException: Error instantiating bean of type  [io.micronaut.cache.DefaultCacheManager]

Message: com.github.benmanes.caffeine.cache.SSW
Path Taken: new CacheHeater([CoolestProjectService service]) --> new $CoolestProjectServiceImplDefinition$Intercepted(GitHubClient client,BeanContext $beanContext,Qualifier $qualifier,[Interceptor[] $interceptors]) --> new CacheInterceptor([CacheManager cacheManager],CacheErrorHandler errorHandler,AsyncCacheErrorHandler asyncCacheErrorHandler,ExecutorService ioExecutor,BeanContext beanContext) --> new DefaultCacheManager([List caches],Provider dynamicCacheManager)
	at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1925)
	at io.micronaut.context.DefaultBeanContext.addCandidateToList(DefaultBeanContext.java:3022)
	at io.micronaut.context.DefaultBeanContext.getBeansOfTypeInternal(DefaultBeanContext.java:2923)
	at io.micronaut.context.DefaultBeanContext.getBeansOfType(DefaultBeanContext.java:1045)
	at io.micronaut.context.AbstractBeanDefinition.lambda$getBeansOfTypeForConstructorArgument$9(AbstractBeanDefinition.java:1143)
	at io.micronaut.context.AbstractBeanDefinition.resolveBeanWithGenericsFromConstructorArgument(AbstractBeanDefinition.java:1822)
	at io.micronaut.context.AbstractBeanDefinition.getBeansOfTypeForConstructorArgument(AbstractBeanDefinition.java:1138)
	at io.micronaut.context.AbstractBeanDefinition.getBeanForConstructorArgument(AbstractBeanDefinition.java:990)
	at io.micronaut.cache.$DefaultCacheManagerDefinition.build(Unknown Source)
	at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1898)
	at io.micronaut.context.DefaultBeanContext.createAndRegisterSingletonInternal(DefaultBeanContext.java:2679)
	at io.micronaut.context.DefaultBeanContext.createAndRegisterSingleton(DefaultBeanContext.java:2665)
	at io.micronaut.context.DefaultBeanContext.getBeanForDefinition(DefaultBeanContext.java:2337)
	at io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:2311)
	at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:1245)
	at io.micronaut.context.AbstractBeanDefinition.getBeanForConstructorArgument(AbstractBeanDefinition.java:1013)
	at io.micronaut.cache.interceptor.$CacheInterceptorDefinition.build(Unknown Source)
	at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1898)
	at io.micronaut.context.DefaultBeanContext.addCandidateToList(DefaultBeanContext.java:3022)
	at io.micronaut.context.DefaultBeanContext.getBeansOfTypeInternal(DefaultBeanContext.java:2897)
	at io.micronaut.context.DefaultBeanContext.getBeansOfType(DefaultBeanContext.java:1045)
	at io.micronaut.context.AbstractBeanDefinition.lambda$getBeansOfTypeForConstructorArgument$9(AbstractBeanDefinition.java:1143)
	at io.micronaut.context.AbstractBeanDefinition.resolveBeanWithGenericsFromConstructorArgument(AbstractBeanDefinition.java:1822)
	at io.micronaut.context.AbstractBeanDefinition.getBeansOfTypeForConstructorArgument(AbstractBeanDefinition.java:1138)
	at io.micronaut.context.AbstractBeanDefinition.getBeanForConstructorArgument(AbstractBeanDefinition.java:987)
	at mn.demo.service.impl.$$CoolestProjectServiceImplDefinition$InterceptedDefinition.build(Unknown Source)
	at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1898)
	at io.micronaut.context.DefaultBeanContext.createAndRegisterSingletonInternal(DefaultBeanContext.java:2679)
	at io.micronaut.context.DefaultBeanContext.createAndRegisterSingleton(DefaultBeanContext.java:2665)
	at io.micronaut.context.DefaultBeanContext.getBeanForDefinition(DefaultBeanContext.java:2337)
	at io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:2311)
	at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:1245)
	at io.micronaut.context.AbstractBeanDefinition.getBeanForConstructorArgument(AbstractBeanDefinition.java:1013)
	at mn.demo.$CacheHeaterDefinition.build(Unknown Source)
	at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1898)
	at io.micronaut.context.DefaultBeanContext.createAndRegisterSingletonInternal(DefaultBeanContext.java:2679)
	at io.micronaut.context.DefaultBeanContext.createAndRegisterSingleton(DefaultBeanContext.java:2665)
	at io.micronaut.context.DefaultBeanContext.getBeanForDefinition(DefaultBeanContext.java:2337)
	at io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:2311)
	at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:721)
	at io.micronaut.scheduling.processor.ScheduledMethodProcessor.lambda$process$5(ScheduledMethodProcessor.java:123)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.lang.Thread.run(Thread.java:834)
	at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:519)
	at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:192)
Caused by: java.lang.IllegalStateException: com.github.benmanes.caffeine.cache.SSW
	at com.github.benmanes.caffeine.cache.LocalCacheFactory.newBoundedLocalCache(LocalCacheFactory.java:95)
	at com.github.benmanes.caffeine.cache.BoundedLocalCache$BoundedLocalManualCache.<init>(BoundedLocalCache.java:3353)
	at com.github.benmanes.caffeine.cache.BoundedLocalCache$BoundedLocalManualCache.<init>(BoundedLocalCache.java:3349)
	at com.github.benmanes.caffeine.cache.Caffeine.build(Caffeine.java:996)
	at io.micronaut.cache.caffeine.DefaultSyncCache.buildCache(DefaultSyncCache.java:209)
	at io.micronaut.cache.caffeine.DefaultSyncCache.<init>(DefaultSyncCache.java:90)
	at io.micronaut.cache.caffeine.$DefaultSyncCacheDefinition.build(Unknown Source)
	at io.micronaut.context.BeanDefinitionDelegate.build(BeanDefinitionDelegate.java:148)
	at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1898)
	... 48 common frames omitted
Caused by: java.lang.ClassNotFoundException: com.github.benmanes.caffeine.cache.SSW
	at com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:60)
	at java.lang.Class.forName(DynamicHub.java:1247)
	at com.github.benmanes.caffeine.cache.LocalCacheFactory.newBoundedLocalCache(LocalCacheFactory.java:87)
	... 56 common frames omitted

Environment Information

  • Operating System: Linux P1 5.10.10-200.fc33.x86_64 #1 SMP Sun Jan 24 19:58:54 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
  • Micronaut Version: 2.3.0
  • JDK Version: OpenJDK 64-Bit Server VM GraalVM CE 21.0.0 (build 11.0.10+8-jvmci-21.0-b06, mixed mode, sharing)

phxql avatar Jan 28 '21 12:01 phxql

Similar issue in Quarkus: https://github.com/quarkusio/quarkus/issues/10420.

brianwyka avatar Mar 04 '21 16:03 brianwyka

Looks like some additional reflect-config.json entries will be needed for the Caffeine classes. @phxql, doe sthis as a workaround for you? https://github.com/quarkusio/quarkus/issues/10420#issuecomment-653021260

brianwyka avatar Mar 04 '21 16:03 brianwyka

As per https://github.com/quarkusio/quarkus/issues/12961 we are in the same boat, we can't register all the different caffeine combinations because it would increase memory by a lot, users need to add the @TypeHint definitions for each CNFE produced

graemerocher avatar Mar 04 '21 17:03 graemerocher

In https://github.com/quarkusio/quarkus/issues/10420#issuecomment-654483795, @Sanne suggested an API to "warmup" with so that the AOT could determine which classes to include. I'm not sure how to implement this so if someone more familiar with Graal and how this might work wants to iterate with me, we can try to find a suitable factory construct.

ben-manes avatar Mar 04 '21 19:03 ben-manes

I got it working with this config: https://github.com/qaware/microservices-with-micronaut/blob/master/github-scraper-graal/src/main/graal/reflect.json

phxql avatar Mar 05 '21 09:03 phxql

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar May 05 '21 04:05 stale[bot]

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jul 05 '21 02:07 stale[bot]

Here is a Kotlin solution with TypeHints. Maybe access types could be reduced, I added all of them for now.

@Context
@TypeHint(
    typeNames = [
        "com.github.benmanes.caffeine.cache.SSW",
        "com.github.benmanes.caffeine.cache.SSMSW",
        "com.github.benmanes.caffeine.cache.BLCHeader\$DrainStatusRef",
        "com.github.benmanes.caffeine.cache.PSWMS",
        "com.github.benmanes.caffeine.cache.PS",
        "com.github.benmanes.caffeine.cache.PSW",
        "com.github.benmanes.caffeine.cache.StripedBuffer",
        "java.lang.Thread",
        "com.github.benmanes.caffeine.cache.BBHeader\$ReadCounterRef",
        "com.github.benmanes.caffeine.cache.BBHeader\$ReadAndWriteCounterRef",

    ], accessType = [
        TypeHint.AccessType.ALL_DECLARED_CONSTRUCTORS,
        TypeHint.AccessType.ALL_DECLARED_FIELDS,
        TypeHint.AccessType.ALL_DECLARED_METHODS,
        TypeHint.AccessType.ALL_PUBLIC,
        TypeHint.AccessType.ALL_PUBLIC_CONSTRUCTORS,
        TypeHint.AccessType.ALL_PUBLIC_FIELDS,
        TypeHint.AccessType.ALL_PUBLIC_METHODS
    ]

)
class CaffeineGraalConfig()

lazyFloyd avatar Sep 29 '21 09:09 lazyFloyd