sewenew / redis-plus-plus

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

[QUESTION] #325

Closed michaeldain closed 2 years ago

michaeldain commented 2 years ago

Before Asking A Question

For general questions on redis-plus-plus, you can ask questions at StackOverflow with redis tag, and normally, you'll get faster response on StackOverflow.

However, if you still don't get answers, feel free to ask a question here.

Describe the problem Question: How to block reading data in stream. Describe : I use this way from READ.me."id" is the last id for stream, i want to block read data from stream like command "xread count 1 block 0 streams stream_key lastId". But try catch throw error. error msg is "Failed to get reply: Resource temporarily unavailable" redis.xread("stream_key", id, std::chrono::seconds(0), 10, std::inserter(result, result.end()));

Environment:

Additional context catch info: "Failed to get reply: Resource temporarily unavailable" I guess this info return after socket timeout.

sewenew commented 2 years ago

Yes, you're right the socket_timeout has been reached. If you want to block forever, you have to make socket_timeout to be 0.

Check the doc for detail:

NOTE: if you set ConnectionOptions::socket_timeout, and try to call blocking commands, e.g. Redis::brpop, Redis::blpop, Redis::bzpopmax, Redis::bzpopmin, you must ensure that ConnectionOptions::socket_timeout is larger than the timeout specified with these blocking commands. Otherwise, you might get TimeoutError, and lose message.

If you have to set a non-zero socket timeout, you can do xread in a loop. If Redis::xread throw TimeoutError, retry it, i.e. continue the loop.

Regards

michaeldain commented 2 years ago

First of all, thank you for your answer。 But my usage scenario is sentinel,so can't I set the ConnectionOptions::socket_timeout to 0 ?

wingunder commented 2 years ago

Hi @michaeldain,

I cut-and-pasted (and slightly modified) the following from examples in the top level README. Maybe this is what you are after?

ConnectionOptions connection_opts;
connection_opts.password = "auth";  // Optional. No password by default.
connection_opts.connect_timeout = std::chrono::milliseconds(100);   // Required.
connection_opts.socket_timeout = std::chrono::milliseconds(100);    // Required.

ConnectionPoolOptions pool_opts;
pool_opts.size = 3; // Optional. The default size is 1.

auto redis = Redis(sentinel, "master_name", Role::MASTER, connection_opts, pool_opts);

while (true) {
    try {
        // Do a blocked xread for more than connection_opts.socket_timeout.
        redis.xread("stream_key", id, connection_opts.socket_timeout * 2, 10, std::inserter(result, result.end()));
    } catch (const TimeoutError &e) {
        // We reached the connection_opts.socket_timeout.
        continue;
    } catch (const Error &err) {
        // Handle other exceptions.
    }
}

This way you'll block-xread, but will time-out after connection_opts.socket_timeout, in which case you'll just repeat the block-xread.

Regards

michaeldain commented 2 years ago

I know you. and i have done as your way. But block way don't handle timeout error again and again like try catch ( waste cpu?)

wingunder commented 2 years ago

But block way don't handle timeout error again and again like try catch ( waste cpu?)

It's a trade-off. You can increase your socket_timeout if these few cpu cycles bother you.

sewenew commented 2 years ago

@michaeldain You can try the following pseudo code:

while (true) {
    try {
        redis.xread("key", id, timeout-smaller-than-socket_timeout, 10, std::inserter(result, result.end()));
        if (result.empty()) {
            // no result, continue xread
           continue;
        } else {
            break; // got a result.
        }
    } catch (const TimeoutError &e) {
        continue;    // since timeout for xread is smaller than socket_timeout, normally you should not get a TimeoutError.
    }
}

If the socket_timeout is not too small, e.g. 1ms, it will not cost too much cpu cycles. You can do some benchmark for it.

Regards

michaeldain commented 2 years ago

tks,Is it because of the internal mechanism of the current implementation that reading cannot be blocked forever?

sewenew commented 2 years ago

@michaeldain Yes, if you use sentinel, you cannot block forever.

Regards

sewenew commented 2 years ago

Since there's no update, I'll close this issue.

Regards