sewenew / redis-plus-plus

Redis client written in C++
Apache License 2.0
1.66k stars 351 forks source link

Invalid subscribe message #266

Closed gnikol closed 3 years ago

gnikol commented 3 years ago

Hi,

I have written a library that instantiates a subscriber and calls psubscribe on a simple pattern. My library is dynamically loaded by one application and everything works as expected.

The problem is that I have another application that loads the same library but when I call subscriber.consume() I get an "Invalid subscribe message" error. On the redis server side the PSUBSCRIBE patterns look identical (as observed with MONITOR).

Both applications are using the same .so binary and the library code is completely independent from the rest of the application logic i.e. it should not matter that the two calling applications are different because the library basically creates a separate thread, and it does not interact with any other threads. My library is statically linked to hiredis and redis-plus-plus.

Any ideas what could cause a difference in behavior?

sewenew commented 3 years ago

The error message means that Redis sever returns some invalid reply for subscribe command, i.e. a non-array reply or the array reply size is less than 1. That should not happen.

Can you show me the code that can reproduce the problem, e.g. how your calls redis-plus-plus related code in your library? Also what's your version of Redis, redis-plus-plus and hiredis?

Regards

gnikol commented 3 years ago

I'm doing something along these lines:

db_ = std::make_unique<sw::redis::Redis>(db_address);
db_->command("config", "set", "notify-keyspace-events", "KEA");
auto subscriber = db_->subscriber();
subscriber.on_pmessage(
        [this](std::string pattern, std::string channel, std::string message) {
          ProcessMessage(channel, message);
        });
std::string pattern = "__keyevent@0__:*";
subscriber.psubscribe(pattern);

while (true) {
  try {
    subscriber.consume();
  } catch (const sw::redis::TimeoutError& e) {
    continue;
  }
}

One of the executables runs this code just fine and the other throws the "Invalid subscribe message" error.

I am running redis-plus-plus 1.2.1-6-g1010a2b (ref:1010a2bdac1e) and hiredis v1.0.0-36-g297ecbe (ref:297ecbecb716) on Debian 10, and compiling with gcc 8.3.

Thanks

gnikol commented 3 years ago

The issue appears with redis-plus-plus 1.3.1 and hiredis 1.0.0 as well.

gnikol commented 3 years ago

Doing a bit more digging, I found out that the calling executable that shows the buggy behavior loads a dynamic version of hiredis (v0.14). This calling application is a large open source project, which I cannot change. Within this application there is a facility to dynamically load libraries (with dl) and that's how my library is loaded. Is there a chance that something similar to #140 happens, i.e. the shared library the application is linked to somehow shadows the static linking of my library to hiredis?

sewenew commented 3 years ago

Your application code looks good to me. I think the problem is that you have two versions of hiredis. As I mentioned in the doc, if you installed two versions of hiredis, you'll get some wired problems.

I suggest you dynamically linking hiredis and redis-plus-plus by, and also keep hiredis the same version as that large open source project.

Regards

gnikol commented 3 years ago

You are right, the issue is caused by the fact that the external application also links to (a different version of) hiredis, those symbols shadow the ones from my statically linked ones and that causes undefined behavior. The fix is to either use the same version as the external project or (which is what is better for my application) to hide the statically linked symbols with -Wl,--exclude-libs,ALL (or -Wl,--exclude-libs,redis++ just for redis++).