mirror of
https://github.com/boostorg/redis.git
synced 2026-01-19 04:42:09 +00:00
Commit of the following
- Progresses with aedis. - Improvements in the documentation.
This commit is contained in:
1
Makefile
1
Makefile
@@ -6,7 +6,6 @@
|
||||
|
||||
CPPFLAGS += -std=c++17 -g
|
||||
CPPFLAGS += -I/opt/boost_1_71_0/include
|
||||
CPPFLAGS += -I/opt/aedis-1.0.0
|
||||
CPPFLAGS += -DBOOST_ASIO_CONCURRENCY_HINT_1=BOOST_ASIO_CONCURRENCY_HINT_UNSAFE
|
||||
|
||||
examples: % : %.o
|
||||
|
||||
104
README.md
104
README.md
@@ -1,2 +1,102 @@
|
||||
# aedis
|
||||
A redis client designed for simplicity and reliability
|
||||
# Aedis
|
||||
Aedis is a redis client designed with the following in mind
|
||||
|
||||
* Simplicity and Minimalist
|
||||
* No overhead abstractions
|
||||
* Optimal use of Asio
|
||||
* Async
|
||||
|
||||
# Example
|
||||
|
||||
Talking to a redis server is as simple as
|
||||
|
||||
```
|
||||
void foo()
|
||||
{
|
||||
net::io_context ioc;
|
||||
session ss {ioc};
|
||||
|
||||
ss.send(ping());
|
||||
|
||||
ss.run();
|
||||
ioc.run();
|
||||
}
|
||||
```
|
||||
|
||||
Composition of commands is trivial and there is support for some stl
|
||||
containers
|
||||
|
||||
```
|
||||
void foo()
|
||||
{
|
||||
std::list<std::string> b
|
||||
{"one" ,"two", "three"};
|
||||
|
||||
std::set<std::string> c
|
||||
{"a" ,"b", "c"};
|
||||
|
||||
std::map<std::string, std::string> d
|
||||
{ {{"Name"}, {"Marcelo"}}
|
||||
, {{"Education"}, {"Physics"}}
|
||||
, {{"Job"}, {"Programmer"}}};
|
||||
|
||||
std::map<int, std::string> e
|
||||
{ {1, {"foo"}}
|
||||
, {2, {"bar"}}
|
||||
, {3, {"foobar"}}
|
||||
};
|
||||
|
||||
auto s = ping()
|
||||
+ rpush("b", b)
|
||||
+ lrange("b")
|
||||
+ del("b")
|
||||
+ multi()
|
||||
+ rpush("c", c)
|
||||
+ lrange("c")
|
||||
+ del("c")
|
||||
+ hset("d", d)
|
||||
+ hvals("d")
|
||||
+ zadd({"e"}, e)
|
||||
+ zrange("e")
|
||||
+ zrangebyscore("foo", 2, -1)
|
||||
+ set("f", {"39"})
|
||||
+ incr("f")
|
||||
+ get("f")
|
||||
+ expire("f", 10)
|
||||
+ publish("g", "A message")
|
||||
+ exec();
|
||||
|
||||
net::io_context ioc;
|
||||
session ss {ioc};
|
||||
|
||||
ss.send(std::move(s));
|
||||
|
||||
ss.run();
|
||||
ioc.run();
|
||||
}
|
||||
```
|
||||
|
||||
NOTE: Not all commands are implemented yet. Since this client was
|
||||
writen for my own use I implement new functionality on demand.
|
||||
|
||||
# Features
|
||||
|
||||
* Pubsub
|
||||
* Pipeline
|
||||
* Reconnection on connection lost.
|
||||
|
||||
The main missing features at the moment are
|
||||
|
||||
* Sentinel
|
||||
* Cluster
|
||||
|
||||
I will implement those on demand.
|
||||
|
||||
# Intallation
|
||||
|
||||
Aedis is header only. You only have to include `aedis.hpp` in your
|
||||
project. Further dependencies are
|
||||
|
||||
* Boost.Asio
|
||||
* libfmt
|
||||
|
||||
|
||||
186
aedis.hpp
186
aedis.hpp
@@ -14,6 +14,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <utility>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <numeric>
|
||||
@@ -54,7 +55,7 @@ std::string make_bulky_item(std::string const& param)
|
||||
}
|
||||
|
||||
inline
|
||||
std::string make_cmd_header(int size)
|
||||
std::string make_header(int size)
|
||||
{
|
||||
return "*" + std::to_string(size) + "\r\n";
|
||||
}
|
||||
@@ -66,37 +67,67 @@ struct accumulator {
|
||||
return a;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto operator()(std::string a, T b) const
|
||||
auto operator()(std::string a, int b) const
|
||||
{
|
||||
a += make_bulky_item(std::to_string(b));
|
||||
return a;
|
||||
}
|
||||
|
||||
auto operator()(std::string a, std::pair<std::string, std::string> b) const
|
||||
{
|
||||
a += make_bulky_item(b.first);
|
||||
a += make_bulky_item(b.second);
|
||||
return a;
|
||||
}
|
||||
|
||||
auto operator()(std::string a, std::pair<int, std::string> b) const
|
||||
{
|
||||
a += make_bulky_item(std::to_string(b.first));
|
||||
a += make_bulky_item(b.second);
|
||||
return a;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Iter>
|
||||
auto assemble(char const* cmd, Iter begin, Iter end)
|
||||
{
|
||||
auto const d = std::distance(begin, end);
|
||||
|
||||
auto payload = make_cmd_header(d + 1)
|
||||
+ make_bulky_item(cmd);
|
||||
|
||||
return std::accumulate(begin , end, std::move(payload), accumulator{});
|
||||
}
|
||||
|
||||
inline
|
||||
auto assemble(char const* cmd)
|
||||
{
|
||||
std::initializer_list<std::string> arg;
|
||||
return assemble(cmd, std::begin(arg), std::end(arg));
|
||||
return make_header(1) + make_bulky_item(cmd);
|
||||
}
|
||||
|
||||
template <class Iter>
|
||||
auto assemble( char const* cmd
|
||||
, std::initializer_list<std::string const> key
|
||||
, Iter begin
|
||||
, Iter end
|
||||
, int size = 1)
|
||||
{
|
||||
auto const d1 =
|
||||
std::distance( std::cbegin(key)
|
||||
, std::cend(key));
|
||||
|
||||
auto const d2 = std::distance(begin, end);
|
||||
|
||||
auto a = make_header(1 + d1 + size * d2)
|
||||
+ make_bulky_item(cmd);
|
||||
|
||||
auto b =
|
||||
std::accumulate( std::cbegin(key)
|
||||
, std::cend(key)
|
||||
, std::move(a)
|
||||
, accumulator{});
|
||||
|
||||
return
|
||||
std::accumulate( begin
|
||||
, end
|
||||
, std::move(b)
|
||||
, accumulator{});
|
||||
}
|
||||
|
||||
inline
|
||||
auto assemble(char const* cmd, std::string const& str)
|
||||
auto assemble(char const* cmd, std::string const& key)
|
||||
{
|
||||
auto arg = {str};
|
||||
return assemble(cmd, std::begin(arg), std::end(arg));
|
||||
std::initializer_list<std::string> dummy;
|
||||
return assemble(cmd, {key}, std::cbegin(dummy), std::cend(dummy));
|
||||
}
|
||||
|
||||
// Converts a decimal number in ascii format to integer.
|
||||
@@ -235,17 +266,7 @@ auto async_read_resp( AsyncStream& s
|
||||
template <class Iter>
|
||||
auto rpush(std::string const& key, Iter begin, Iter end)
|
||||
{
|
||||
auto const d = std::distance(begin, end);
|
||||
|
||||
auto payload = resp::make_cmd_header(2 + d)
|
||||
+ resp::make_bulky_item("RPUSH")
|
||||
+ resp::make_bulky_item(key);
|
||||
|
||||
auto cmd_str = std::accumulate( begin
|
||||
, end
|
||||
, std::move(payload)
|
||||
, resp::accumulator{});
|
||||
return cmd_str;
|
||||
return resp::assemble("RPUSH", {key}, begin, end);
|
||||
}
|
||||
|
||||
template <class T, class Allocator>
|
||||
@@ -272,17 +293,7 @@ auto rpush( std::string const& key
|
||||
template <class Iter>
|
||||
auto lpush(std::string const& key, Iter begin, Iter end)
|
||||
{
|
||||
auto const d = std::distance(begin, end);
|
||||
|
||||
auto payload = resp::make_cmd_header(2 + d)
|
||||
+ resp::make_bulky_item("LPUSH")
|
||||
+ resp::make_bulky_item(key);
|
||||
|
||||
auto cmd_str = std::accumulate( begin
|
||||
, end
|
||||
, std::move(payload)
|
||||
, resp::accumulator{});
|
||||
return cmd_str;
|
||||
return resp::assemble("LPUSH", {key}, begin, end);
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -291,6 +302,12 @@ auto multi()
|
||||
return resp::assemble("MULTI");
|
||||
}
|
||||
|
||||
inline
|
||||
auto ping()
|
||||
{
|
||||
return resp::assemble("PING");
|
||||
}
|
||||
|
||||
inline
|
||||
auto exec()
|
||||
{
|
||||
@@ -330,27 +347,42 @@ auto get(std::string const& key)
|
||||
inline
|
||||
auto publish(std::string const& key, std::string const& msg)
|
||||
{
|
||||
auto par = {key, msg};
|
||||
return resp::assemble("PUBLISH", std::begin(par), std::end(par));
|
||||
auto par = {msg};
|
||||
return resp::assemble("PUBLISH", {key}, std::begin(par), std::end(par));
|
||||
}
|
||||
|
||||
inline
|
||||
auto set(std::initializer_list<std::string const> const& args)
|
||||
auto set( std::string const& key
|
||||
, std::initializer_list<std::string const> const& args)
|
||||
{
|
||||
return resp::assemble("SET", std::begin(args), std::end(args));
|
||||
return resp::assemble("SET", {key}, std::begin(args), std::end(args));
|
||||
}
|
||||
|
||||
inline
|
||||
auto hset(std::initializer_list<std::string const> const& l)
|
||||
auto hset( std::string const& key
|
||||
, std::initializer_list<std::string const> const& l)
|
||||
{
|
||||
return resp::assemble("HSET", std::begin(l), std::end(l));
|
||||
return resp::assemble("HSET", {key}, std::begin(l), std::end(l));
|
||||
}
|
||||
|
||||
template <class Key, class T, class Compare, class Allocator>
|
||||
auto hset( std::string const& key
|
||||
, std::map<Key, T, Compare, Allocator> const& m)
|
||||
{
|
||||
return resp::assemble("HSET", {key}, std::begin(m), std::end(m), 2);
|
||||
}
|
||||
|
||||
inline
|
||||
auto hvals(std::string const& key)
|
||||
{
|
||||
return resp::assemble("HVALS", {key});
|
||||
}
|
||||
|
||||
inline
|
||||
auto hget(std::string const& key, std::string const& field)
|
||||
{
|
||||
auto par = {key, field};
|
||||
return resp::assemble("HGET", std::begin(par), std::end(par));
|
||||
auto par = {field};
|
||||
return resp::assemble("HGET", {key}, std::begin(par), std::end(par));
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -358,33 +390,36 @@ auto hmget( std::string const& key
|
||||
, std::string const& field1
|
||||
, std::string const& field2)
|
||||
{
|
||||
auto par = {key, field1, field2};
|
||||
return resp::assemble("HMGET", std::begin(par), std::end(par));
|
||||
auto par = {field1, field2};
|
||||
return resp::assemble("HMGET", {key}, std::cbegin(par), std::cend(par));
|
||||
}
|
||||
|
||||
inline
|
||||
auto expire(std::string const& key, int secs)
|
||||
{
|
||||
auto par = {key, std::to_string(secs)};
|
||||
return resp::assemble("EXPIRE", std::begin(par), std::end(par));
|
||||
auto par = {std::to_string(secs)};
|
||||
return resp::assemble("EXPIRE", {key}, std::begin(par), std::end(par));
|
||||
}
|
||||
|
||||
inline
|
||||
auto zadd(std::string const& key, int score, std::string const& value)
|
||||
{
|
||||
auto par = {key, std::to_string(score), value};
|
||||
return resp::assemble("ZADD", std::begin(par), std::end(par));
|
||||
auto par = {std::to_string(score), value};
|
||||
return resp::assemble("ZADD", {key}, std::cbegin(par), std::cend(par));
|
||||
}
|
||||
|
||||
template <class Key, class T, class Compare, class Allocator>
|
||||
auto zadd( std::initializer_list<std::string const> key
|
||||
, std::map<Key, T, Compare, Allocator> const& m)
|
||||
{
|
||||
return resp::assemble("ZADD", key, std::cbegin(m), std::cend(m), 2);
|
||||
}
|
||||
|
||||
inline
|
||||
auto zrange(std::string const& key, int min, int max)
|
||||
auto zrange(std::string const& key, int min = 0, int max = -1)
|
||||
{
|
||||
auto par = { key
|
||||
, std::to_string(min)
|
||||
, std::to_string(max)
|
||||
};
|
||||
|
||||
return resp::assemble("zrange", std::begin(par), std::end(par));
|
||||
auto par = { std::to_string(min), std::to_string(max) };
|
||||
return resp::assemble("ZRANGE", {key}, std::begin(par), std::end(par));
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -394,46 +429,35 @@ auto zrangebyscore(std::string const& key, int min, int max)
|
||||
if (max != -1)
|
||||
max_str = std::to_string(max);
|
||||
|
||||
auto par = { key
|
||||
, std::to_string(min)
|
||||
, max_str
|
||||
//, std::string {"withscores"}
|
||||
};
|
||||
|
||||
return resp::assemble("zrangebyscore", std::begin(par), std::end(par));
|
||||
auto par = { std::to_string(min) , max_str };
|
||||
return resp::assemble("zrangebyscore", {key}, std::begin(par), std::end(par));
|
||||
}
|
||||
|
||||
inline
|
||||
auto zremrangebyscore(std::string const& key, int score)
|
||||
{
|
||||
auto const s = std::to_string(score);
|
||||
auto par = {key, s, s};
|
||||
return resp::assemble("ZREMRANGEBYSCORE", std::begin(par), std::end(par));
|
||||
auto par = {s, s};
|
||||
return resp::assemble("ZREMRANGEBYSCORE", {key}, std::begin(par), std::end(par));
|
||||
}
|
||||
|
||||
inline
|
||||
auto lrange(std::string const& key, int min = 0, int max = -1)
|
||||
{
|
||||
auto par = { key
|
||||
, std::to_string(min)
|
||||
, std::to_string(max)
|
||||
};
|
||||
|
||||
return resp::assemble("lrange", std::begin(par), std::end(par));
|
||||
auto par = { std::to_string(min) , std::to_string(max) };
|
||||
return resp::assemble("lrange", {key}, std::begin(par), std::end(par));
|
||||
}
|
||||
|
||||
inline
|
||||
auto del(std::string const& key)
|
||||
{
|
||||
auto par = {key};
|
||||
return resp::assemble("del", std::begin(par), std::end(par));
|
||||
return resp::assemble("del", key);
|
||||
}
|
||||
|
||||
inline
|
||||
auto llen(std::string const& key)
|
||||
{
|
||||
auto par = {key};
|
||||
return resp::assemble("llen", std::begin(par), std::end(par));
|
||||
return resp::assemble("llen", key);
|
||||
}
|
||||
|
||||
namespace log
|
||||
|
||||
227
examples.cpp
227
examples.cpp
@@ -5,175 +5,16 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include <deque>
|
||||
#include <thread>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <aedis/aedis.hpp>
|
||||
#include "aedis.hpp"
|
||||
|
||||
using namespace aedis;
|
||||
|
||||
using args_type = std::initializer_list<std::string const>;
|
||||
|
||||
void set_get()
|
||||
void send(std::string cmd)
|
||||
{
|
||||
net::io_context ioc;
|
||||
session ss {ioc};
|
||||
|
||||
auto c = set({"Name", "Marcelo"})
|
||||
+ get("Name");
|
||||
|
||||
ss.send(std::move(c));
|
||||
|
||||
ss.run();
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
void transaction()
|
||||
{
|
||||
net::io_context ioc;
|
||||
session ss {ioc};
|
||||
|
||||
auto o = multi()
|
||||
+ set({"Age", "39"})
|
||||
+ incr("Age")
|
||||
+ get("Age")
|
||||
+ expire("Age", 10)
|
||||
+ exec();
|
||||
|
||||
ss.send(std::move(o));
|
||||
ss.run();
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
void rpush_vector()
|
||||
{
|
||||
net::io_context ioc;
|
||||
session ss {ioc};
|
||||
|
||||
std::vector<int> v1 {1 , 2, 3, 4, 5, 6, 7};
|
||||
ss.send(rpush("a", v1) + lrange("a") + del("a"));
|
||||
|
||||
std::list<std::string> v2 {"one" ,"two", "three"};
|
||||
ss.send(rpush("b", v2) + lrange("b") + del("b"));
|
||||
|
||||
std::set<std::string> v3 {"a" ,"b", "c"};
|
||||
ss.send(rpush("c", v3) + lrange("c") + del("c"));
|
||||
|
||||
ss.run();
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
void pub(int count, char const* channel)
|
||||
{
|
||||
net::io_context ioc;
|
||||
session pub_session(ioc);
|
||||
for (auto i = 0; i < count; ++i)
|
||||
pub_session.send(publish(channel, std::to_string(i)));
|
||||
|
||||
pub_session.run();
|
||||
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
auto msg_handler2 = [i = 0](auto ec, auto const& res) mutable
|
||||
{
|
||||
if (ec)
|
||||
throw std::runtime_error(ec.message());
|
||||
|
||||
auto const n = std::stoi(res.back());
|
||||
if (n != i + 1)
|
||||
std::cout << "===============> Error." << std::endl;
|
||||
std::cout << "Counter: " << n << std::endl;
|
||||
|
||||
i = n;
|
||||
|
||||
//for (auto const& o : res)
|
||||
// std::cout << o << " ";
|
||||
|
||||
//std::cout << std::endl;
|
||||
};
|
||||
|
||||
struct sub_arena {
|
||||
session s;
|
||||
|
||||
sub_arena( net::io_context& ioc
|
||||
, std::string channel)
|
||||
: s(ioc)
|
||||
{
|
||||
s.set_msg_handler(msg_handler2);
|
||||
|
||||
auto const on_conn_handler = [this, channel]()
|
||||
{ s.send(subscribe(channel)); };
|
||||
|
||||
s.set_on_conn_handler(on_conn_handler);
|
||||
s.run();
|
||||
}
|
||||
};
|
||||
|
||||
void sub(char const* channel)
|
||||
{
|
||||
net::io_context ioc;
|
||||
sub_arena arena(ioc, channel);
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
void zadd()
|
||||
{
|
||||
net::io_context ioc;
|
||||
session ss {ioc};
|
||||
|
||||
auto c1 = zadd("foo", 1, "bar1")
|
||||
+ zadd("foo", 2, "bar2")
|
||||
+ zadd("foo", 3, "bar3")
|
||||
+ zadd("foo", 4, "bar4")
|
||||
+ zadd("foo", 5, "bar5");
|
||||
|
||||
ss.send(c1);
|
||||
|
||||
ss.run();
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
void zrangebyscore()
|
||||
{
|
||||
net::io_context ioc;
|
||||
session ss(ioc);
|
||||
|
||||
auto c1 = zrangebyscore("foo", 2, -1);
|
||||
|
||||
ss.send(c1);
|
||||
|
||||
ss.run();
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
void zrange()
|
||||
{
|
||||
net::io_context ioc;
|
||||
session ss(ioc);
|
||||
|
||||
ss.send(zrange("foo", 2, -1));
|
||||
|
||||
ss.run();
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
void read_msg_op()
|
||||
{
|
||||
net::io_context ioc;
|
||||
session ss(ioc);
|
||||
|
||||
auto c1 = multi()
|
||||
+ lrange("foo")
|
||||
+ del("foo")
|
||||
+ exec();
|
||||
|
||||
ss.send(c1);
|
||||
ss.send(std::move(cmd));
|
||||
|
||||
ss.run();
|
||||
ioc.run();
|
||||
@@ -181,39 +22,43 @@ void read_msg_op()
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try {
|
||||
if (argc == 1) {
|
||||
std::cerr << "Usage: " << argv[0] << " n host port" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::list<std::string> b
|
||||
{"one" ,"two", "three"};
|
||||
|
||||
session::config cfg;
|
||||
auto const n = std::stoi(argv[1]);
|
||||
std::set<std::string> c
|
||||
{"a" ,"b", "c"};
|
||||
|
||||
if (argc == 3) {
|
||||
cfg.host = argv[2];
|
||||
cfg.port = argv[3];
|
||||
}
|
||||
std::map<std::string, std::string> d
|
||||
{ {{"Name"}, {"Marcelo"}}
|
||||
, {{"Education"}, {"Physics"}}
|
||||
, {{"Job"}, {"Programmer"}}};
|
||||
|
||||
char const* channel = "foo";
|
||||
std::map<int, std::string> e
|
||||
{ {1, {"foo"}}
|
||||
, {2, {"bar"}}
|
||||
, {3, {"foobar"}}
|
||||
};
|
||||
|
||||
switch (n) {
|
||||
case 0: set_get(); break;
|
||||
case 1: transaction(); break;
|
||||
case 2: pub(10, channel); break;
|
||||
case 3: sub(channel); break;
|
||||
case 4: zadd(); break;
|
||||
case 5: zrangebyscore(); break;
|
||||
case 6: zrange(); break;
|
||||
case 7: read_msg_op(); break;
|
||||
case 8: rpush_vector(); break;
|
||||
default:
|
||||
std::cerr << "Option not available." << std::endl;
|
||||
}
|
||||
} catch (std::exception const& e) {
|
||||
std::cerr << e.what() << "\n";
|
||||
}
|
||||
auto s = ping()
|
||||
+ rpush("b", b)
|
||||
+ lrange("b")
|
||||
+ del("b")
|
||||
+ multi()
|
||||
+ rpush("c", c)
|
||||
+ lrange("c")
|
||||
+ del("c")
|
||||
+ hset("d", d)
|
||||
+ hvals("d")
|
||||
+ zadd({"e"}, e)
|
||||
+ zrange("e")
|
||||
+ zrangebyscore("foo", 2, -1)
|
||||
+ set("f", {"39"})
|
||||
+ incr("f")
|
||||
+ get("f")
|
||||
+ expire("f", 10)
|
||||
+ publish("g", "A message")
|
||||
+ exec();
|
||||
|
||||
return 0;
|
||||
send(std::move(s));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user