redis / lettuce

Advanced Java Redis client for thread-safe sync, async, and reactive usage. Supports Cluster, Sentinel, Pipelining, and codecs.
https://lettuce.io
MIT License
5.35k stars 960 forks source link

Refactor optional dependencies into separate modules #2363

Open mgroth0 opened 1 year ago

mgroth0 commented 1 year ago

I want to start by saying this is an incredibly useful library. It is helping me learn how to use Redis and implement it in an asynchronous way, and I'm very grateful for it.

Feature Request

I have checked the wiki and searched the issues here, and I did not find any discussion on why there are optional dependencies in the pom.xml. My understanding is that optional dependencies reduce stability because they allow the possibility of a java.lang.NoClassDefFoundError to be thrown if the user did not manually include the depednencies they require.

Is your feature request related to a problem? Please describe

This comes up for me in the following way:

java.lang.NoClassDefFoundError: kotlinx.coroutines.reactive.AwaitKt
    at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt:46)
    at io.ktor.server.engine.BaseApplicationEngineKt$installDefaultTransformationChecker$1.invokeSuspend(BaseApplicationEngine.kt:123)
    at io.ktor.server.application.hooks.CallFailed$install$1$1.invokeSuspend(CommonHooks.kt:43)
    at io.ktor.server.application.hooks.CallFailed$install$1.invokeSuspend(CommonHooks.kt:42)
    at io.ktor.util.pipeline.PipelineKt$execute$2.invokeSuspend(Pipeline.kt:478)
    at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$1.invokeSuspend(DefaultEnginePipeline.kt:118)
Caused by: java.lang.NoClassDefFoundError: kotlinx.coroutines.reactive.AwaitKt
    at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt:46)
    at io.ktor.server.engine.BaseApplicationEngineKt$installDefaultTransformationChecker$1.invokeSuspend(BaseApplicationEngine.kt:123)
Caused by: java.lang.NoClassDefFoundError: kotlinx.coroutines.reactive.AwaitKt
    at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt:46)
    at io.ktor.util.pipeline.PipelineKt$execute$2.invokeSuspend(Pipeline.kt:478)
    at io.ktor.server.routing.Routing.executeResult(Routing.kt:188)
    at io.ktor.server.routing.Routing$Plugin$install$1.invokeSuspend(Routing.kt:140)
    at io.ktor.server.engine.BaseApplicationEngineKt$installDefaultTransformationChecker$1.invokeSuspend(BaseApplicationEngine.kt:123)
    at io.ktor.server.application.hooks.CallFailed$install$1$1.invokeSuspend(CommonHooks.kt:43)
    at io.ktor.server.application.hooks.CallFailed$install$1.invokeSuspend(CommonHooks.kt:42)
    at io.ktor.util.pipeline.PipelineKt$execute$2.invokeSuspend(Pipeline.kt:478)
    at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$1.invokeSuspend(DefaultEnginePipeline.kt:118)
    at io.ktor.util.pipeline.PipelineKt$execute$2.invokeSuspend(Pipeline.kt:478)
    at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invokeSuspend(NettyApplicationCallHandler.kt:119)
Caused by: java.lang.ClassNotFoundException: kotlinx.coroutines.reactive.AwaitKt
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
    at io.lettuce.core.api.coroutines.RedisHashCoroutinesCommandsImpl.hget(RedisHashCoroutinesCommandsImpl.kt:42)
    at io.lettuce.core.api.coroutines.RedisCoroutinesCommandsImpl.hget$suspendImpl(RedisCoroutinesCommandsImpl.kt)
    at io.lettuce.core.api.coroutines.RedisCoroutinesCommandsImpl.hget(RedisCoroutinesCommandsImpl.kt)

I can solve this by manually adding a dependency on org.jetbrains.kotlinx:kotlinx-coroutines-reactive:1.7.0-Beta.

Describe the solution you'd like

According to the Maven Docs, optional dependencies are not ideal and not the cleanest solution. Ideally, the project would be split into multiple modules. If we want to keep Lettuce-Core minimal, we don't need to make it depend on kotlinx-coroutines-reactive. But then all features that rely on that dependency should be in a separate module. I want to know that if my code compiles, that all classes can be found at runtime. I generally assume that all of the libraries I use follow this contract, and it is abnormal that lettuce does not.

Describe alternatives you've considered

Lettuce-core could have a normal dependency on org.jetbrains.kotlinx:kotlinx-coroutines-reactive. However, I can understand the drawbacks of this since it could increase the size of lettuce-core with bloated sub-dependencies that many users may not need.

Teachability, Documentation, Adoption, Migration Strategy

Looking at maven, it seems there is currently only one lettuce module. It would be nice to see lettuce-core alongside lettuce-coroutines (which could itself depend on the core module).

mp911de commented 1 year ago

Thanks for your suggestion. Maintaining two modules outweighs the benefits in terms of complexity and maintenance. We support many optional dependencies, such as HdrHistogram, native transports and many more. What would make sense is having a Wiki page outlining what dependencies are required to simplify the getting started experience. Paging @sokomishalov for visibility.