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

Deprecate StatefulConnection.reset() #907

Closed
semberal opened this issue Oct 30, 2018 · 7 comments
Closed

Deprecate StatefulConnection.reset() #907

semberal opened this issue Oct 30, 2018 · 7 comments
Labels
type: task A general task
Milestone

Comments

@semberal
Copy link

Bug Report

Current Behavior

When StatefulRedisConnection is reset, results of subsequent commands are non-deterministic. Results of completely different commands are returned. I extracted minimal example, where there are 2 redis keys: a->a and b->b. Two parallel threads are reading these two keys in a loop. Then, at some point I reset StatefulRedisConnection. After that, the result of client.get("a") can be "b" and vice versa.

Input Code

Minimal example:

package test;

import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import org.junit.Test;

import java.time.Duration;
import java.util.concurrent.CancellationException;

public class LettuceTest {
    @Test
    public void testParallelExecution() throws InterruptedException {

        final RedisURI uri = new RedisURI("127.0.0.1", 6379, Duration.ofSeconds(1));
        final StatefulRedisConnection<String, String> conn = RedisClient.create(uri).connect();
        final RedisCommands<String, String> sync = conn.sync();
        sync.set("a", "a");
        sync.set("b", "b");

        final Thread a = runThread(sync, "a");
        final Thread b = runThread(sync, "b");

        a.start();
        b.start();

        while(a.isAlive() && b.isAlive()) {
            Thread.sleep(1000);
            conn.reset();
        }

        throw new IllegalStateException("Threads should not terminate");

    }

    private Thread runThread(RedisCommands<String, String> async, String key) {

        return new Thread(() -> {
            while (true) {
                try {
                    final String result = async.get(key);
                    System.out.println(String.format("Expected %s, received %s", key, result));
                    if (!key.equals(result)) break;
                } catch (CancellationException e) {
                    // ok
                }
            }
        });
    }
}

Environment

  • Lettuce version(s): 5.1.2.RELEASE
  • Redis version: 5.0.0
@mp911de mp911de added type: bug A general bug status: invalid An issue that we don't feel is valid labels Oct 30, 2018
@mp911de
Copy link
Collaborator

mp911de commented Oct 30, 2018

This behavior is expected. Calling .reset() should be a last resort that resets the protocol state. You should call reset() only if the connection is idle.

What is your use case to call .reset()?

@semberal
Copy link
Author

I am using Lettuce with fairly small max queue size of 20 commands. I am facing an issue that if the command queue is full when Redis is restarted, the client is somehow not reconnected successfully because all commands are rejected because of the full queue. Seems like the queue is not flushed or something. Occasionally resetting the connection seemed to help, but it lead to the problem I reported.

@semberal
Copy link
Author

Is it possible that after one such invalid reset() call, the connection is broken forever? When I modify the test to call reset() just once, I am still getting invalid responses a long time after that.

@anagamca
Copy link

anagamca commented Nov 6, 2018

In our application no where we are invoking conn.reset(). But still we are seeing the same behavior, with HGET command returns different values. Apart from conn.reset(), is there any other scenarios which can start behaving like this?

We are using AWS Elastic Cache Redis Cluster 3.2.6

RedisClusterCommands(RedisAdvancedClusterCommands<String, Object>) defined as class variable and created through constructor. All the HMSET and HGET calls from application made through class variable redisClusterCommands.

Does lettuce client make conn.reset() for anything? If yes, what are all situations it can do so.

Lettuce version we are using,

biz.paluch.redis
lettuce
4.3.3.Final

@mp911de
Copy link
Collaborator

mp911de commented Nov 6, 2018

@anagamca care to file a new issue along logs and a reproducer?

Regarding reset(): We introduced reset back while we had no SSL support yet. Back then, SSL usage was only possible through a client-side SSL tunnel. If connections broke in the SSL layer, we had to reset the protocol state to recover from external SSL issues.

@mp911de
Copy link
Collaborator

mp911de commented Nov 6, 2018

@semberal can you provide a reproducer? We should fix the queue size issue instead of trying to recover the client state in the application. This is probably a good time to deprecate .reset() as it causes more harm than good.

@mp911de mp911de added status: waiting-for-feedback We need additional information before we can continue and removed status: invalid An issue that we don't feel is valid labels Nov 6, 2018
mp911de added a commit that referenced this issue Nov 7, 2018
Deprecating reset() method as we no longer require this functionality. Reset was introduced with Lettuce 3.1 where we did not have SSL support and external SSL tunneling was the only possibility to use SSL connections. External tunneling with strunnel could break and the only way to recover was resetting the protocol state.
@mp911de mp911de added type: task A general task and removed status: waiting-for-feedback We need additional information before we can continue type: bug A general bug labels Nov 7, 2018
@mp911de mp911de added this to the 5.2.0 milestone Nov 7, 2018
@mp911de mp911de changed the title Connection reset causes subsequent commands to return invalid values Deprecate StatefulConnection.reset() Nov 7, 2018
@mp911de
Copy link
Collaborator

mp911de commented Nov 7, 2018

StatefulConnection.reset() is deprecated now. Please file a new ticket for everything else.

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

No branches or pull requests

3 participants