Cylix / cpp_redis

C++11 Lightweight Redis client: async, thread-safe, no dependency, pipelining, multi-platform - NO LONGER MAINTAINED - Please check https://github.com/cpp-redis/cpp_redis
MIT License
1.25k stars 554 forks source link

One disconnected instance effect to another instance at multi thread. #188

Open hisaha opened 6 years ago

hisaha commented 6 years ago

Hi,

I create two client to connect two redis server with pthread. If a instance disconnected with server problem, another instance also blocked. I think it is strange result, I expect another instance keep to work.

The test code is below.

#include <pthread.h>
#include <cpp_redis/cpp_redis>

cpp_redis::client client1;
cpp_redis::client client2;

void redisChangeStateHanlder(const std::string& host, std::size_t port, cpp_redis::client::connect_state status)
{
    if(port==6379)
        std::cerr << "client1 ";
    if(port==6380)
        std::cerr << "client2 ";

    if (status == cpp_redis::client::connect_state::dropped) {
        std::cerr << "connection dropped from " << host << ":" << port << std::endl;
    }
    if (status == cpp_redis::client::connect_state::start) {
        std::cerr << "connection start to " << host << ":" << port << std::endl;
    }
    if (status == cpp_redis::client::connect_state::sleeping) {
        std::cerr << "connection sleeping to " << host << ":" << port << std::endl;
    }
    if (status == cpp_redis::client::connect_state::ok) {
        std::cerr << "connection ok to " << host << ":" << port << std::endl;
    }
    if (status == cpp_redis::client::connect_state::failed) {
        std::cerr << "connection failed to " << host << ":" << port << std::endl;
    }
    if (status == cpp_redis::client::connect_state::lookup_failed) {
        std::cerr << "connection lookup_failed to " << host << ":" << port << std::endl;
    }
    if (status == cpp_redis::client::connect_state::stopped) {
        std::cerr << "connection stopped to " << host << ":" << port << std::endl;
    }
} 

void* threadHandler(void* instance)
{
    #define _REDIS_TIMEOUT_ (std::chrono::seconds(3))
    cpp_redis::client* client=(cpp_redis::client*)instance;

    while(true)
    {
        if(client->is_connected())
        {
            auto ping = client->ping();
            client->commit();
            std::future_status result=ping.wait_for(_REDIS_TIMEOUT_);
            if (result == std::future_status::timeout)
            {
                if(&client1==client)
                    std::cerr << "client1 ping failed" << std::endl;
                if(&client2==client)
                    std::cerr << "client2 ping failed" << std::endl;
                continue;
            }
        }
    }
    return nullptr;
}

int main()
{
    cpp_redis::client::connect_callback_t f = redisChangeStateHanlder;
    client1.connect("localhost", 6379, f, 0, -1, 3000);
    client2.connect("localhost", 6380, f, 0, -1, 3000);

    pthread_t thread1;
    pthread_t thread2;
    pthread_create(&thread1 , NULL , ::threadHandler , (void*)&client1); 
    pthread_create(&thread2 , NULL , ::threadHandler , (void*)&client2); 

    pthread_join(thread1,NULL);
    pthread_join(thread2,NULL);

    return 0;
}
  1. compile
  2. launch redis-server with 6379/6380 port at localhost
  3. launch the program (./test)
  4. kill redis-server with port 6380 (for client2)

I expect the client1 keep to ping to 6379, but the bot client1/2 is stopped. The output log is below

client1 connection start to localhost:6379
client1 connection ok to localhost:6379
client2 connection start to localhost:6380
client2 connection ok to localhost:6380
client2 connection dropped from localhost:6380
client2 connection sleeping to localhost:6380
client2 ping failed
client2 client1 ping failedconnection start to
localhost:6380
client2 connection failed to localhost:6380
client2 connection sleeping to localhost:6380
client1 ping failed
client2 connection start to localhost:6380
client2 connection failed to localhost:6380
client2 connection sleeping to localhost:6380
client1 ping failed
client2 connection start to localhost:6380
client2 connection failed to localhost:6380
client2 connection sleeping to localhost:6380

As you see, the client1 failed to ping... But the redis-server of 6379 for client1 is alive. Is this bug or specification of cpp_redis?

Additionally, I used below cmake for build, and my environment is OSX(High sierra). The redis-server is installed with homebrew.

cmake_minimum_required(VERSION 2.8)
list( APPEND CMAKE_CXX_FLAGS "-std=c++11")
include_directories( /usr/local/include )
link_directories( /usr/local/lib )
add_executable(test main.cpp)
target_link_libraries(test pthread cpp_redis tacopie)
hisaha commented 6 years ago

I am investigating this issue. I have omitted the pthread part, this issue could be reproduced simply. Clinent2 is disconnected from localhost:6380, the ping process of client1 is also stopped. I think it is spec of io_service at tacopie... Any hints?

The sample code is below.

#include <unistd.h>
#include <cpp_redis/cpp_redis>

cpp_redis::client client1;
cpp_redis::client client2;

void redisChangeStateHanlder(const std::string& host, std::size_t port, cpp_redis::client::connect_state status)
{
    if(port==6379)
        std::cerr << "client1 ";
    if(port==6380)
        std::cerr << "client2 ";

    if (status == cpp_redis::client::connect_state::dropped) {
        std::cerr << "connection dropped from " << host << ":" << port << std::endl;
    }
} 

int main()
{
    cpp_redis::client::connect_callback_t f = redisChangeStateHanlder;
    client1.connect("localhost", 6379, f, 3000, -1, 3000);
    client2.connect("localhost", 6380, f, 3000, -1, 3000);

    while(true)
    {
        if(client1.is_connected())
        {
            client1.ping([](cpp_redis::reply& reply) {
                std::cerr << "ping1: " << reply << std::endl;
            });
            client1.commit();
        }
        if(client2.is_connected())
        {
            client2.ping([](cpp_redis::reply& reply) {
                std::cerr << "ping2: " << reply << std::endl;
            });
            client2.commit();
        }
        sleep(1);
    }

    return 0;
}