Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NodeTopologyView passes null-Value to StringReader constructor #2035

Closed
m-ibot opened this issue Mar 4, 2022 · 2 comments
Closed

NodeTopologyView passes null-Value to StringReader constructor #2035

m-ibot opened this issue Mar 4, 2022 · 2 comments
Labels
type: bug A general bug
Milestone

Comments

@m-ibot
Copy link

m-ibot commented Mar 4, 2022

Bug Report

I ran into this issue after a Spring Boot update from Spring Boot 2.4.x to 2.6.3. This bug appears with lettuce-core 6.1.6.RELEASE. After downgrading it manually to 6.1.5.RELEASE, the error did not show up anymore.

Based on the git history, this bug might be a result of #1923

Unfortunately I could not figure out the root cause of this error. It appeared only on one environment and I could not reproduce it anywhere else. All I can provide is the stack trace and what I could figure out by reading the code.

Anyway, I hope this information helps:

Current Behavior

We have a scheduled task that writes data to Redis. Whenever this tasks runs the NullPointerException is thrown.

Stack trace
java.lang.NullPointerException: null
	at java.base/java.io.StringReader.<init>(StringReader.java:50)
	at io.lettuce.core.cluster.topology.NodeTopologyView.parseInfo(NodeTopologyView.java:116)
	at io.lettuce.core.cluster.topology.NodeTopologyView.<init>(NodeTopologyView.java:67)
	at io.lettuce.core.cluster.topology.NodeTopologyView.from(NodeTopologyView.java:90)
	at io.lettuce.core.cluster.topology.DefaultClusterTopologyRefresh.getNodeSpecificViews(DefaultClusterTopologyRefresh.java:232)
	at io.lettuce.core.cluster.topology.DefaultClusterTopologyRefresh.lambda$null$4(DefaultClusterTopologyRefresh.java:110)
	at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1072)
	at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
	at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2088)
	at io.lettuce.core.protocol.AsyncCommand.doCompleteExceptionally(AsyncCommand.java:139)
	at io.lettuce.core.protocol.AsyncCommand.completeResult(AsyncCommand.java:120)
	at io.lettuce.core.protocol.AsyncCommand.complete(AsyncCommand.java:111)
	at io.lettuce.core.cluster.topology.TimedAsyncCommand.complete(TimedAsyncCommand.java:52)
	at io.lettuce.core.protocol.CommandWrapper.complete(CommandWrapper.java:63)
	at io.lettuce.core.protocol.CommandHandler.complete(CommandHandler.java:746)
	at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:681)
	at io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:598)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1372)
	at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1235)
	at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1284)
	at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:510)
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:449)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:279)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:722)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:658)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:584)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:496)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:829)

StringReader.java is a JDK class that will throw a NullPointerException if a null value is passed as parameter, because it calls .length() on it.

This value is passed in NodeTopologyView#parseInfo

    private static Properties parseInfo(String info) {
        Properties properties = new Properties();
        try {
            properties.load(new StringReader(info));
        } catch (IOException e) {
            // wtf?
        }
        return properties;
    }

It would be interesting to know where the null-value for info comes from. the only point that I could see is the method NodeTopologyView#optionallyGet that must have been called in Line NodeTopologyView.java:90 (see Stacktrace). This method might return null:

    private static <T> T optionallyGet(TimedAsyncCommand<?, ?, T> command) {
        if (command.isCompletedExceptionally()) {
            return null;
        }
        return command.join();
    }

Since this seems to happen if the TimedAsyncCommand was completed exceptionally, this NullPointerException seems to be a follow up error. Anyway if a null value is used it should be ensured that it does not lead to a NullPointerException.

Expected behavior/code

No NullPointerException is thrown.

Environment

  • Lettuce version(s): 6.1.6.RELEASE

Possible Solution

null values are handled in a way that they don't lead to a NullPointerException.

In the best case some information why TimedAsyncCommand is completed exceptionally in optionallyGet is provided (e.g. by provide some logging) so that it is possible to see what exception it was and react on it as user of lettuce-core.

@mp911de mp911de added the type: bug A general bug label Mar 4, 2022
@mp911de mp911de added this to the 6.1.7 milestone Mar 16, 2022
@mp911de mp911de changed the title NullPointerException: NodeTopologyView passes null-Value to StringReader constructor NodeTopologyView passes null-Value to StringReader constructor Mar 16, 2022
mp911de added a commit that referenced this issue Mar 16, 2022
Lettuce now ignores when the INFO command completes exceptionally when obtaining a cluster topology and falls back to no-detail handling.
mp911de added a commit that referenced this issue Mar 16, 2022
Lettuce now ignores when the INFO command completes exceptionally when obtaining a cluster topology and falls back to no-detail handling.
@mp911de
Copy link
Collaborator

mp911de commented Mar 16, 2022

That's fixed now.

@mp911de mp911de closed this as completed Mar 16, 2022
@m-ibot
Copy link
Author

m-ibot commented Mar 18, 2022

Thank you for the fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants