drogon icon indicating copy to clipboard operation
drogon copied to clipboard

Synchronous Redis Interface

Open wagnrd opened this issue 2 years ago • 4 comments

Is your feature request related to a problem? Please describe. For Postgres, there is a synchronous interface to be able to wait for a response. This is important if you want to handle the response first before you return a response in the controller itself. For example, if you can't use co-routines or don't wan't to fall into the callback hell.

Describe the solution you'd like The solution I would like to see is a synchronous interface when setting off a redis request.

Describe alternatives you've considered Currently I use a custom solution with condition variables, which works, but it's weird that I have to do it myself.

wagnrd avatar Dec 08 '21 08:12 wagnrd

We will consider this.

BTW, how did you use RedisResult out side the callback? The hiredis documentation says you can't do that. https://github.com/redis/hiredis/blob/d5b4c69b7113213c1da3a0ccbfd1ee1b40443c7a/README.md?plain=1#L372-L374

If the reply for a command with a NULL callback is read, it is immediately freed. When the callback for a command is non-NULL, the memory is freed immediately following the callback: the reply is only valid for the duration of the callback.

hwc0919 avatar Dec 08 '21 09:12 hwc0919

I didn't. Inside of the callback I'm justing moving the result string out of the RedisResult object to a variable outside of the callback. This is my current workaround to get a synchronous redis request:

std::optional<std::string> SessionsDB::execRedisCommandSync(const std::string& command)
{
    std::mutex mutex;
    std::unique_lock<std::mutex> lock(mutex);
    std::condition_variable commandFinished;

    std::optional<std::string> result;

    redis->execCommandAsync(
            [&](const drogon::nosql::RedisResult& r) {
                if (!r.isNil())
                    result = r.asString();

                commandFinished.notify_all();
            },
            [](const std::exception& e) {
                throw e;
            },
            command
    );

    commandFinished.wait(lock);

    return result;
}

wagnrd avatar Dec 13 '21 14:12 wagnrd

Here is the reason why there isn't sync interfaces for redis in drogon.

In drogon, sync interfaces for pg and mysql are simulated by using async interfaces with a blocking operation (such as future.get()). However, as I mentioned above, we can not do this for redis. Hiredis doesn't allow us to hold a response object outside the async callback. So we must use hiredis sync interfaces to achieve that.

Currently, integrating hiredis sync interfaces is not of high priority for us, because:

  • It needs lots of code structure changes.
  • We already have coroutine, which has the same code flow as sync interface.
  • Using sync interfaces will only cause performance drop, which is quite contradictory against the purpose of using c++ web framework.

If hiredis provides a way to access response object outside async callback in future, we will provide sync interfaces based on that.

Hope you understand us.

hwc0919 avatar Dec 14 '21 02:12 hwc0919

#1216 What about this solution?

hwc0919 avatar Apr 17 '22 06:04 hwc0919