/************************************************************************** Copyright (c) 2021 sewenew Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. *************************************************************************/ #ifndef SEWENEW_REDISPLUSPLUS_ASYNC_REDIS_H #define SEWENEW_REDISPLUSPLUS_ASYNC_REDIS_H #include "async_connection.h" #include "async_connection_pool.h" #include "async_sentinel.h" #include "event_loop.h" #include "utils.h" #include "command.h" #include "command_args.h" #include "command_options.h" #include "cmd_formatter.h" namespace sw { namespace redis { class AsyncRedis { public: AsyncRedis(const ConnectionOptions &opts, const ConnectionPoolOptions &pool_opts = {}, const EventLoopSPtr &loop = nullptr); AsyncRedis(const std::shared_ptr &sentinel, const std::string &master_name, Role role, const ConnectionOptions &connection_opts, const ConnectionPoolOptions &pool_opts = {}, const EventLoopSPtr &loop = nullptr); AsyncRedis(const AsyncRedis &) = delete; AsyncRedis& operator=(const AsyncRedis &) = delete; AsyncRedis(AsyncRedis &&) = default; AsyncRedis& operator=(AsyncRedis &&) = default; ~AsyncRedis() = default; template Future command(const StringView &cmd_name, Args &&...args) { auto formatter = [](const StringView &cmd_name, Args &&...args) { CmdArgs cmd_args; cmd_args.append(cmd_name, std::forward(args)...); return fmt::format_cmd(cmd_args); }; return _command(formatter, cmd_name, std::forward(args)...); } template auto command(Input first, Input last) -> typename std::enable_if::value, Future>::type { auto formatter = [](Input first, Input last) { CmdArgs cmd_args; while (first != last) { cmd_args.append(*first); ++first; } return fmt::format_cmd(cmd_args); }; return _command(formatter, first, last); } // CONNECTION commands. Future echo(const StringView &msg) { return _command(fmt::echo, msg); } Future ping() { return _command(fmt::ping); } Future ping(const StringView &msg) { return _command(fmt::ping, msg); } // KEY commands. Future del(const StringView &key) { return _command(fmt::del, key); } template Future del(Input first, Input last) { range_check("DEL", first, last); return _command(fmt::del_range, first, last); } template Future del(std::initializer_list il) { return del(il.begin(), il.end()); } Future exists(const StringView &key) { return _command(fmt::exists, key); } template Future exists(Input first, Input last) { range_check("EXISTS", first, last); return _command(fmt::exists_range, first, last); } template Future exists(std::initializer_list il) { return exists(il.begin(), il.end()); } Future expire(const StringView &key, const std::chrono::seconds &timeout) { return _command(fmt::expire, key, timeout); } Future expireat(const StringView &key, const std::chrono::time_point &tp) { return _command(fmt::expireat, key, tp); } Future pexpire(const StringView &key, const std::chrono::milliseconds &timeout) { return _command(fmt::pexpire, key, timeout); } Future pexpireat(const StringView &key, const std::chrono::time_point &tp) { return _command(fmt::pexpireat, key, tp); } Future pttl(const StringView &key) { return _command(fmt::pttl, key); } Future rename(const StringView &key, const StringView &newkey) { return _command(fmt::rename, key, newkey); } Future renamenx(const StringView &key, const StringView &newkey) { return _command(fmt::renamenx, key, newkey); } Future ttl(const StringView &key) { return _command(fmt::ttl, key); } Future unlink(const StringView &key) { return _command(fmt::unlink, key); } template Future unlink(Input first, Input last) { range_check("UNLINK", first, last); return _command(fmt::unlink_range, first, last); } template Future unlink(std::initializer_list il) { return unlink(il.begin(), il.end()); } // STRING commands. Future get(const StringView &key) { return _command(fmt::get, key); } Future incr(const StringView &key) { return _command(fmt::incr, key); } Future incrby(const StringView &key, long long increment) { return _command(fmt::incrby, key, increment); } Future incrbyfloat(const StringView &key, double increment) { return _command(fmt::incrbyfloat, key, increment); } template Future mget(Input first, Input last) { range_check("MGET", first, last); return _command(fmt::mget, first, last); } template Future mget(std::initializer_list il) { return mget(il.begin(), il.end()); } template Future mset(Input first, Input last) { range_check("MSET", first, last); return _command(fmt::mset, first, last); } template Future mset(std::initializer_list il) { return mset(il.begin(), il.end()); } template Future msetnx(Input first, Input last) { range_check("MSETNX", first, last); return _command(fmt::msetnx, first, last); } template Future msetnx(std::initializer_list il) { return msetnx(il.begin(), il.end()); } Future set(const StringView &key, const StringView &val, const std::chrono::milliseconds &ttl = std::chrono::milliseconds(0), UpdateType type = UpdateType::ALWAYS) { return _command_with_parser(fmt::set, key, val, ttl, type); } Future strlen(const StringView &key) { return _command(fmt::strlen, key); } // LIST commands. Future blpop(const StringView &key, const std::chrono::seconds &timeout = std::chrono::seconds{0}) { return _command(fmt::blpop, key, timeout); } template Future blpop(Input first, Input last, const std::chrono::seconds &timeout = std::chrono::seconds{0}) { range_check("BLPOP", first, last); return _command(fmt::blpop_range, first, last, timeout); } template Future blpop(std::initializer_list il, const std::chrono::seconds &timeout = std::chrono::seconds{0}) { return blpop(il.begin(), il.end(), timeout); } Future brpop(const StringView &key, const std::chrono::seconds &timeout = std::chrono::seconds{0}) { return _command(fmt::brpop, key, timeout); } template Future brpop(Input first, Input last, const std::chrono::seconds &timeout = std::chrono::seconds{0}) { range_check("BRPOP", first, last); return _command(fmt::brpop_range, first, last, timeout); } template Future brpop(std::initializer_list il, const std::chrono::seconds &timeout = std::chrono::seconds{0}) { return brpop(il.begin(), il.end(), timeout); } Future brpoplpush(const StringView &source, const StringView &destination, const std::chrono::seconds &timeout = std::chrono::seconds{0}) { return _command(fmt::brpoplpush, source, destination, timeout); } Future llen(const StringView &key) { return _command(fmt::llen, key); } Future lpop(const StringView &key) { return _command(fmt::lpop, key); } Future lpush(const StringView &key, const StringView &val) { return _command(fmt::lpush, key, val); } template Future lpush(const StringView &key, Input first, Input last) { range_check("LPUSH", first, last); return _command(fmt::lpush_range, key, first, last); } template Future lpush(const StringView &key, std::initializer_list il) { return lpush(key, il.begin(), il.end()); } template Future lrange(const StringView &key, long long start, long long stop) { return _command(fmt::lrange, key, start, stop); } Future lrem(const StringView &key, long long count, const StringView &val) { return _command(fmt::lrem, key, count, val); } Future ltrim(const StringView &key, long long start, long long stop) { return _command(fmt::ltrim, key, start, stop); } Future rpop(const StringView &key) { return _command(fmt::rpop, key); } Future rpoplpush(const StringView &source, const StringView &destination) { return _command(fmt::rpoplpush, source, destination); } Future rpush(const StringView &key, const StringView &val) { return _command(fmt::rpush, key, val); } template Future rpush(const StringView &key, Input first, Input last) { range_check("RPUSH", first, last); return _command(fmt::rpush_range, key, first, last); } template Future rpush(const StringView &key, std::initializer_list il) { return rpush(key, il.begin(), il.end()); } // HASH commands. Future hdel(const StringView &key, const StringView &field) { return _command(fmt::hdel, key, field); } template Future hdel(const StringView &key, Input first, Input last) { range_check("HDEL", first, last); return _command(fmt::hdel_range, key, first, last); } template Future hdel(const StringView &key, std::initializer_list il) { return hdel(key, il.begin(), il.end()); } Future hexists(const StringView &key, const StringView &field) { return _command(fmt::hexists, key, field); } Future hget(const StringView &key, const StringView &field) { return _command(fmt::hget, key, field); } template Future hgetall(const StringView &key) { return _command(fmt::hgetall, key); } Future hincrby(const StringView &key, const StringView &field, long long increment) { return _command(fmt::hincrby, key, field, increment); } Future hincrbyfloat(const StringView &key, const StringView &field, double increment) { return _command(fmt::hincrbyfloat, key, field, increment); } template Future hkeys(const StringView &key) { return _command(fmt::hkeys, key); } Future hlen(const StringView &key) { return _command(fmt::hlen, key); } template Future hmget(const StringView &key, Input first, Input last) { range_check("HMGET", first, last); return _command(fmt::hmget, key, first, last); } template Future hmget(const StringView &key, std::initializer_list il) { return hmget(key, il.begin(), il.end()); } template Future hmset(const StringView &key, Input first, Input last) { range_check("HMSET", first, last); return _command(fmt::hmset, key, first, last); } template Future hmset(const StringView &key, std::initializer_list il) { return hmset(key, il.begin(), il.end()); } Future hset(const StringView &key, const StringView &field, const StringView &val) { return _command(fmt::hset, key, field, val); } Future hset(const StringView &key, const std::pair &item) { return hset(key, item.first, item.second); } template auto hset(const StringView &key, Input first, Input last) -> typename std::enable_if::value, Future>::type { range_check("HSET", first, last); return _command(fmt::hset_range, key, first, last); } template Future hset(const StringView &key, std::initializer_list il) { return hset(key, il.begin(), il.end()); } template Future hvals(const StringView &key) { return _command(fmt::hvals, key); } // SET commands. Future sadd(const StringView &key, const StringView &member) { return _command(fmt::sadd, key, member); } template Future sadd(const StringView &key, Input first, Input last) { range_check("SADD", first, last); return _command(fmt::sadd_range, key, first, last); } template Future sadd(const StringView &key, std::initializer_list il) { return sadd(key, il.begin(), il.end()); } Future scard(const StringView &key) { return _command(fmt::scard, key); } Future sismember(const StringView &key, const StringView &member) { return _command(fmt::sismember, key, member); } template Future smembers(const StringView &key) { return _command(fmt::smembers, key); } Future spop(const StringView &key) { return _command(fmt::spop, key); } template Future spop(const StringView &key, long long count) { return _command(fmt::spop, key, count); } Future srem(const StringView &key, const StringView &member) { return _command(fmt::srem, key, member); } template Future srem(const StringView &key, Input first, Input last) { range_check("SREM", first, last); return _command(fmt::srem_range, key, first, last); } template Future srem(const StringView &key, std::initializer_list il) { return srem(key, il.begin(), il.end()); } // SORTED SET commands. auto bzpopmax(const StringView &key, const std::chrono::seconds &timeout = std::chrono::seconds{0}) -> Future>> { return _command>>( fmt::bzpopmax, key, timeout); } template auto bzpopmax(Input first, Input last, const std::chrono::seconds &timeout = std::chrono::seconds{0}) -> Future>> { range_check("BZPOPMAX", first, last); return _command>>( fmt::bzpopmax_range, first, last, timeout); } template auto bzpopmax(std::initializer_list il, const std::chrono::seconds &timeout = std::chrono::seconds{0}) -> Future>> { return bzpopmax(il.begin(), il.end(), timeout); } auto bzpopmin(const StringView &key, const std::chrono::seconds &timeout = std::chrono::seconds{0}) -> Future>> { return _command>>( fmt::bzpopmin, key, timeout); } template auto bzpopmin(Input first, Input last, const std::chrono::seconds &timeout = std::chrono::seconds{0}) -> Future>> { range_check("BZPOPMIN", first, last); return _command>>( fmt::bzpopmin_range, first, last, timeout); } template auto bzpopmin(std::initializer_list il, const std::chrono::seconds &timeout = std::chrono::seconds{0}) -> Future>> { return bzpopmin(il.begin(), il.end(), timeout); } Future zadd(const StringView &key, const StringView &member, double score, UpdateType type = UpdateType::ALWAYS, bool changed = false) { return _command(fmt::zadd, key, member, score, type, changed); } template Future zadd(const StringView &key, Input first, Input last, UpdateType type = UpdateType::ALWAYS, bool changed = false) { range_check("ZADD", first, last); return _command(fmt::zadd_range, key, first, last, type, changed); } template Future zadd(const StringView &key, std::initializer_list il, UpdateType type = UpdateType::ALWAYS, bool changed = false) { return zadd(key, il.begin(), il.end(), type, changed); } Future zcard(const StringView &key) { return _command(fmt::zcard, key); } template Future zcount(const StringView &key, const Interval &interval) { return _command(fmt::zcount, key, interval); } Future zincrby(const StringView &key, double increment, const StringView &member) { return _command(fmt::zincrby, key, increment, member); } template Future zlexcount(const StringView &key, const Interval &interval) { return _command(fmt::zlexcount, key, interval); } Future>> zpopmax(const StringView &key) { return _command>, FormattedCommand (*)(const StringView &)>(fmt::zpopmax, key); } template Future zpopmax(const StringView &key, long long count) { return _command( fmt::zpopmax, key, count); } Future>> zpopmin(const StringView &key) { return _command>, FormattedCommand (*)(const StringView &)>(fmt::zpopmin, key); } template Future zpopmin(const StringView &key, long long count) { return _command( fmt::zpopmin, key, count); } template Future zrange(const StringView &key, long long start, long long stop) { return _command(fmt::zrange, key, start, stop); } template Future zrangebylex(const StringView &key, const Interval &interval, const LimitOptions &opts) { return _command(fmt::zrangebylex, key, interval, opts); } template Future zrangebylex(const StringView &key, const Interval &interval) { return zrangebylex(key, interval, {}); } // TODO: withscores parameter template Future zrangebyscore(const StringView &key, const Interval &interval, const LimitOptions &opts) { return _command(fmt::zrangebyscore, key, interval, opts); } template Future zrangebyscore(const StringView &key, const Interval &interval) { return zrangebyscore(key, interval, {}); } Future zrank(const StringView &key, const StringView &member) { return _command(fmt::zrank, key, member); } Future zrem(const StringView &key, const StringView &member) { return _command(fmt::zrem, key, member); } template Future zrem(const StringView &key, Input first, Input last) { range_check("ZREM", first, last); return _command(fmt::zrem_range, key, first, last); } template Future zrem(const StringView &key, std::initializer_list il) { return zrem(key, il.begin(), il.end()); } template Future zremrangebylex(const StringView &key, const Interval &interval) { return _command(fmt::zremrangebylex, key, interval); } Future zremrangebyrank(const StringView &key, long long start, long long stop) { return _command(fmt::zremrangebyrank, key, start, stop); } template Future zremrangebyscore(const StringView &key, const Interval &interval) { return _command(fmt::zremrangebyscore, key, interval); } template Future zrevrangebylex(const StringView &key, const Interval &interval, const LimitOptions &opts) { return _command(fmt::zrevrangebylex, key, interval, opts); } template Future zrevrangebylex(const StringView &key, const Interval &interval) { return zrevrangebylex(key, interval, {}); } Future zrevrank(const StringView &key, const StringView &member) { return _command(fmt::zrevrank, key, member); } Future zscore(const StringView &key, const StringView &member) { return _command(fmt::zscore, key, member); } // SCRIPTING commands. template Future eval(const StringView &script, Keys keys_first, Keys keys_last, Args args_first, Args args_last) { return _command(fmt::eval, script, keys_first, keys_last, args_first, args_last); } template Future eval(const StringView &script, std::initializer_list keys, std::initializer_list args) { return eval(script, keys.begin(), keys.end(), args.begin(), args.end()); } template Future evalsha(const StringView &script, Keys keys_first, Keys keys_last, Args args_first, Args args_last) { return _command(fmt::evalsha, script, keys_first, keys_last, args_first, args_last); } template Future evalsha(const StringView &script, std::initializer_list keys, std::initializer_list args) { return evalsha(script, keys.begin(), keys.end(), args.begin(), args.end()); } private: template Future _command(Formatter formatter, Args &&...args) { return _command_with_parser>( formatter, std::forward(args)...); } template Future _command_with_parser(Formatter formatter, Args &&...args) { auto formatted_cmd = formatter(std::forward(args)...); assert(_pool); SafeAsyncConnection connection(*_pool); return connection.connection().send(std::move(formatted_cmd)); } EventLoopSPtr _loop; AsyncConnectionPoolSPtr _pool; }; } } #endif // end SEWENEW_REDISPLUSPLUS_ASYNC_REDIS_H