micronaut-projects / micronaut-cache

This project includes caching support for Micronaut
Apache License 2.0
29 stars 31 forks source link

Caffeine and Graal native-image #280

Open phxql opened 3 years ago

phxql commented 3 years ago

Task List

Steps to Reproduce

  1. Include
    <dependency>
    <groupId>io.micronaut.cache</groupId>
    <artifactId>micronaut-cache-caffeine</artifactId>
    <scope>compile</scope>
    </dependency>
  2. 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

brianwyka commented 3 years ago

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

brianwyka commented 3 years ago

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

graemerocher commented 3 years ago

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

ben-manes commented 3 years ago

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.

phxql commented 3 years ago

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

stale[bot] commented 3 years ago

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] commented 3 years ago

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.

lazyFloyd commented 3 years ago

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()