Problem to build native image using Vert.x-redis with Quarkus

1.1k views Asked by At

I got a problem when building my quarkus application in native mode using the vertx-redis-client

I`m trying create a cache class with the methods to connect, set and add. When i use the set and the get method i trying to open a new redis connection.

Using ./mvnw compile quarkus:dev the application run ok. But, when i try to build a native image i receive the error:

Error: No instances of io.netty.buffer.UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf are allowed in the image heap as this class should be initialized at image runtime. To see how this object got instantiated use -H:+TraceClassInitialization.
Detailed message:
Trace:  object io.netty.buffer.UnreleasableByteBuf
        object io.vertx.core.buffer.impl.BufferImpl
        object io.vertx.redis.client.impl.types.BulkType
        method io.vertx.redis.client.impl.RESPParser.handle(Buffer)
Call path from entry point to io.vertx.redis.client.impl.RESPParser.handle(Buffer): 
        at io.vertx.redis.client.impl.RESPParser.handle(RESPParser.java:51)
        at io.vertx.redis.client.impl.RESPParser.handle(RESPParser.java:25)
        at io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:369)
        at io.vertx.core.impl.WorkerContext.lambda$wrapTask$0(WorkerContext.java:35)
        at io.vertx.core.impl.WorkerContext$$Lambda$547/789610232.run(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
        at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:460)
        at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:193)
        at com.oracle.svm.core.code.IsolateEnterStub.PosixJavaThreads_pthreadStartRoutine_e1f4a8c0039f8337338252cd8734f63a79b5e3df(generated:0)

com.oracle.svm.core.util.UserError$UserException: No instances of io.netty.buffer.UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf are allowed in the image heap as this class should be initialized at image runtime. To see how this object got instantiated use -H:+TraceClassInitialization.
Detailed message:
Trace:  object io.netty.buffer.UnreleasableByteBuf
        object io.vertx.core.buffer.impl.BufferImpl
        object io.vertx.redis.client.impl.types.BulkType
        method io.vertx.redis.client.impl.RESPParser.handle(Buffer)
Call path from entry point to io.vertx.redis.client.impl.RESPParser.handle(Buffer): 
        at io.vertx.redis.client.impl.RESPParser.handle(RESPParser.java:51)
        at io.vertx.redis.client.impl.RESPParser.handle(RESPParser.java:25)
        at io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:369)
        at io.vertx.core.impl.WorkerContext.lambda$wrapTask$0(WorkerContext.java:35)
        at io.vertx.core.impl.WorkerContext$$Lambda$547/789610232.run(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
        at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:460)
        at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:193)
        at com.oracle.svm.core.code.IsolateEnterStub.PosixJavaThreads_pthreadStartRoutine_e1f4a8c0039f8337338252cd8734f63a79b5e3df(generated:0)

        at com.oracle.svm.core.util.UserError.abort(UserError.java:75)
        at com.oracle.svm.hosted.FallbackFeature.reportAsFallback(FallbackFeature.java:223)
        at com.oracle.svm.hosted.NativeImageGenerator.runPointsToAnalysis(NativeImageGenerator.java:737)
        at com.oracle.svm.hosted.NativeImageGenerator.doRun(NativeImageGenerator.java:526)
        at com.oracle.svm.hosted.NativeImageGenerator.lambda$run$0(NativeImageGenerator.java:444)
        at java.util.concurrent.ForkJoinTask$AdaptedRunnableAction.exec(ForkJoinTask.java:1386)
        at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
        at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
        at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
        at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: No instances of io.netty.buffer.UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf are allowed in the image heap as this class should be initialized at image runtime. To see how this object got instantiated use -H:+TraceClassInitialization.
Detailed message:
Trace:  object io.netty.buffer.UnreleasableByteBuf
        object io.vertx.core.buffer.impl.BufferImpl
        object io.vertx.redis.client.impl.types.BulkType
        method io.vertx.redis.client.impl.RESPParser.handle(Buffer)
Call path from entry point to io.vertx.redis.client.impl.RESPParser.handle(Buffer): 
        at io.vertx.redis.client.impl.RESPParser.handle(RESPParser.java:51)
        at io.vertx.redis.client.impl.RESPParser.handle(RESPParser.java:25)
        at io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:369)
        at io.vertx.core.impl.WorkerContext.lambda$wrapTask$0(WorkerContext.java:35)
        at io.vertx.core.impl.WorkerContext$$Lambda$547/789610232.run(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
        at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:460)
        at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:193)
        at com.oracle.svm.core.code.IsolateEnterStub.PosixJavaThreads_pthreadStartRoutine_e1f4a8c0039f8337338252cd8734f63a79b5e3df(generated:0)

        at com.oracle.graal.pointsto.constraints.UnsupportedFeatures.report(UnsupportedFeatures.java:130)
        at com.oracle.graal.pointsto.BigBang.finish(BigBang.java:565)
        at com.oracle.svm.hosted.NativeImageGenerator.runPointsToAnalysis(NativeImageGenerator.java:688)
        ... 7 more

My cache class:

import io.quarkus.runtime.annotations.RegisterForReflection;
import io.vertx.core.Vertx;
import io.vertx.redis.client.Redis;
import io.vertx.redis.client.RedisAPI;
import io.vertx.redis.client.RedisOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import java.util.concurrent.CompletableFuture;

@ApplicationScoped
public class Cache {
    Logger logger = LoggerFactory.getLogger(Cache.class);

    RedisAPI redis;

    @Inject
    Vertx vertx;

    private void verifyConnection() {
        if(redis == null) {
            CompletableFuture<RedisAPI> connectionResult = new CompletableFuture<RedisAPI>();
            Redis.createClient(vertx, new RedisOptions())
                    .connect(onConnect -> {
                        if (onConnect.succeeded()) {
                            Redis client = onConnect.result();
                            connectionResult.complete(RedisAPI.api(client));
                        } else {
                            connectionResult.complete(null);
                        }
                    });
            redis = connectionResult.join();
        }
    }

    public String get (String key) {
        verifyConnection();
        CompletableFuture<String> lambdaResult = new CompletableFuture<>();
        redis.get(key, res -> {
            if (res.succeeded()) {
                if (res.result() != null) {
                    lambdaResult.complete(res.result().toString());
                } else {
                    lambdaResult.complete(null);
                }
            } else {
                lambdaResult.complete(null);
            }
        });

        return lambdaResult.join();
    }

    public void set(String key, String data) {
        verifyConnection();
        redis.append(key, data, res -> {
            if (res.failed()) {
                logger.error("Error to send data to Cache");
            }
        });
    }
}
1

There are 1 answers

0
Guillaume Smet On

This is a sign that we will probably need an extension to make it work but you can try doing it in your application first by tweaking the GraalVM settings.

You can try making io.vertx.redis.client.impl.RESPParser initializable at runtime. You can check https://quarkus.io/guides/writing-native-applications-tips#delaying-class-initialization for more information about how to do that in your application.

If you end up having it working, please report back on quarkus-dev as we have a Redis extension in the works and your feedback might be useful to the people that will work on it soon.