/Server/GameServer/redis/redisclient.h
C Header | 3197 lines | 2552 code | 507 blank | 138 comment | 286 complexity | b3b6e186a284494de02e206ffc45274b MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- /* redisclient.h -- a C++ client library for redis.
- *
- * Copyright (c) 2009, Brian Hammond <brian at fictorial dot com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Redis nor the names of its contributors may be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
- #ifndef REDISCLIENT_H
- #define REDISCLIENT_H
- #ifndef _MSC_VER
- #include <errno.h>
- #include <sys/socket.h>
- #include <string>
- #include <vector>
- #include <map>
- #include <set>
- #include <stdexcept>
- #include <ctime>
- #include <sstream>
- #include <boost/concept_check.hpp>
- #include <boost/lexical_cast.hpp>
- #include <boost/functional/hash.hpp>
- #include <boost/foreach.hpp>
- #include <boost/thread/mutex.hpp>
- #include <boost/thread/condition_variable.hpp>
- #include <boost/random.hpp>
- #include <boost/cstdint.hpp>
- #include <boost/date_time/posix_time/ptime.hpp>
- #include <boost/date_time/posix_time/posix_time_types.hpp>
- #include <boost/optional.hpp>
- #include <boost/variant.hpp>
- #include "anet.h"
- #define REDIS_LBR "\r\n"
- #define REDIS_STATUS_REPLY_OK "OK"
- #define REDIS_PREFIX_STATUS_REPLY_ERROR "-ERR "
- #define REDIS_PREFIX_STATUS_REPLY_ERR_C '-'
- #define REDIS_PREFIX_STATUS_REPLY_VALUE '+'
- #define REDIS_PREFIX_SINGLE_BULK_REPLY '$'
- #define REDIS_PREFIX_MULTI_BULK_REPLY '*'
- #define REDIS_PREFIX_INT_REPLY ':'
- #define REDIS_WHITESPACE " \f\n\r\t\v"
- template<class Object >
- struct make;
- namespace redis
- {
- template<typename CONSISTENT_HASHER>
- class base_client;
-
- enum reply_t
- {
- no_reply,
- status_code_reply,
- error_reply,
- int_reply,
- bulk_reply,
- multi_bulk_reply
- };
-
- struct connection_data
- {
- connection_data(const std::string & host = "localhost", uint16_t port = 6379, int dbindex = 0)
- : host(host), port(port), dbindex(dbindex), socket(ANET_ERR)
- {
- }
- bool operator==(const connection_data & other) const
- {
- if(host != other.host)
- return false;
- if(port != other.port)
- return false;
- if(dbindex != other.dbindex)
- return false;
- return true;
- }
-
- std::string host;
- boost::uint16_t port;
- int dbindex;
- private:
- int socket;
- template<typename CONSISTENT_HASHER>
- friend class base_client;
- };
-
- enum server_role
- {
- role_master,
- role_slave
- };
-
- // Generic error that is thrown when communicating with the redis server.
-
- class redis_error : public std::exception
- {
- public:
- redis_error(const std::string & err) : err_(err) {}
- virtual ~redis_error() throw () {}
- operator const std::string () const { return err_; }
- virtual const char* what() const throw ()
- {
- return err_.c_str();
- }
-
- private:
- std::string err_;
- };
-
- // Some socket-level I/O or general connection error.
-
- class connection_error : public redis_error
- {
- public:
- connection_error(const std::string & err) : redis_error(err) {}
- };
-
- // Redis gave us a reply we were not expecting.
- // Possibly an internal error (here or in redis, probably here).
-
- class protocol_error : public redis_error
- {
- public:
- protocol_error(const std::string & err) : redis_error(err) {};
- };
-
- // A key that you expected to exist does not in fact exist.
-
- class key_error : public redis_error
- {
- public:
- key_error(const std::string & err) : redis_error(err) {};
- };
-
- // A operation with time limit does not deliver an result early enough.
-
- class timeout_error : public redis_error
- {
- public:
- timeout_error(const std::string & err) : redis_error(err) {};
- };
-
- // A value of an expected type or other semantics was found to be invalid.
-
- class value_error : public redis_error
- {
- public:
- value_error(const std::string & err) : redis_error(err) {};
- };
- struct key
- {
- explicit key(const std::string & name)
- : name(name)
- {
- }
-
- std::string name;
- };
-
- class makecmd
- {
- public:
- explicit makecmd(const std::string & cmd_name)
- {
- append(cmd_name);
- //if (!finalize)
- // buffer_ << " ";
- }
- const std::string & key_name() const
- {
- if(!key_name_)
- throw std::runtime_error("No key defined!");
- return *key_name_;
- }
-
- inline makecmd & operator<<(const key & datum)
- {
- if(key_name_)
- throw std::runtime_error("You could not add a second key");
- else
- key_name_ = datum.name;
- append(datum.name);
- return *this;
- }
-
- inline makecmd & operator<<(const std::string & datum)
- {
- append(datum);
- return *this;
- }
-
- template <typename T>
- makecmd & operator<<(T const & datum)
- {
- append( boost::lexical_cast<std::string>(datum) );
- return *this;
- }
-
- makecmd & operator<<(const std::vector<std::string> & data)
- {
- lines_.insert( lines_.end(), data.begin(), data.end() );
- return *this;
- }
-
- template <typename T>
- makecmd & operator<<(const std::vector<T> & data)
- {
- size_t n = data.size();
- for (size_t i = 0; i < n; ++i)
- {
- append( boost::lexical_cast<std::string>( data[i] ) );
- //if (i < n - 1)
- // buffer_ << " ";
- }
- return *this;
- }
-
- operator std::string () const
- {
- std::ostringstream oss;
- size_t n = lines_.size();
- oss << REDIS_PREFIX_MULTI_BULK_REPLY << n << REDIS_LBR;
-
- for (size_t i = 0; i < n; ++i)
- {
- const std::string & param = lines_[i];
- oss << REDIS_PREFIX_SINGLE_BULK_REPLY << param.size() << REDIS_LBR;
- oss << param << REDIS_LBR;
- }
-
- return oss.str();
- }
-
- private:
- void append(const std::string & param)
- {
- lines_.push_back(param);
- }
-
- std::vector<std::string> lines_;
- boost::optional<std::string> key_name_;
- };
- template<typename CONSISTENT_HASHER>
- class base_client;
- typedef boost::variant< std::string, int, std::vector<std::string> > reply_val_t;
- typedef std::pair<reply_t, reply_val_t> reply_data_t;
-
- class command
- {
- private:
- std::string request_;
- std::string hash_key_;
- reply_data_t reply_;
- void check_reply_t(reply_t reply_type) const
- {
- if( reply_.first != reply_type )
- throw std::runtime_error("invalid reply type");
- }
- void set_reply(const reply_data_t & reply)
- {
- reply_ = reply;
- }
- template<typename CONSISTENT_HASHER>
- friend class base_client;
-
- public:
- command( const makecmd & cmd_input )
- : request_(cmd_input), hash_key_(cmd_input.key_name())
- {
- reply_.first = no_reply;
- }
- reply_t reply_type() const
- {
- return reply_.first;
- }
-
- const std::string & get_status_code_reply() const
- {
- check_reply_t(status_code_reply);
- return boost::get<std::string>(reply_.second);
- }
-
- const std::string & get_error_reply() const
- {
- check_reply_t(error_reply);
- return boost::get<std::string>(reply_.second);
- }
-
- int get_int_reply() const
- {
- check_reply_t(int_reply);
- return boost::get<int>(reply_.second);
- }
-
- const std::string & get_bulk_reply() const
- {
- check_reply_t(bulk_reply);
- return boost::get<std::string>(reply_.second);
- }
-
- const std::vector<std::string> & get_multi_bulk_reply() const
- {
- check_reply_t(multi_bulk_reply);
- return boost::get< std::vector<std::string> >(reply_.second);
- }
- };
- struct server_info
- {
- std::string version;
- bool bgsave_in_progress;
- unsigned long connected_clients;
- unsigned long connected_slaves;
- unsigned long used_memory;
- unsigned long changes_since_last_save;
- unsigned long last_save_time;
- unsigned long total_connections_received;
- unsigned long total_commands_processed;
- unsigned long uptime_in_seconds;
- unsigned long uptime_in_days;
- server_role role;
- unsigned short arch_bits;
- std::string multiplexing_api;
- std::map<std::string, std::string> param_map;
- };
-
- inline ssize_t recv_or_throw(int fd, void* buf, size_t n, int flags)
- {
- ssize_t bytes_received;
-
- do
- bytes_received = ::recv(fd, buf, n, flags);
- while(bytes_received < static_cast<ssize_t>(0) && errno == EINTR);
-
- if( bytes_received == static_cast<ssize_t>(0) )
- throw connection_error("connection was closed");
-
- // Handle receive errors. I overlooked it totally, thanks mkx!
- if( bytes_received == static_cast<ssize_t>(-1) )
- throw connection_error(std::string("recv error: ") + strerror(errno));
-
- return bytes_received;
- }
-
- // You should construct a 'client' object per connection to a redis-server.
- //
- // Please read the online redis command reference:
- // http://code.google.com/p/redis/wiki/CommandReference
- //
- // No provisions for customizing the allocator on the string/bulk value type
- // (std::string) are provided. If needed, you can always change the
- // string_type typedef in your local version.
- template<typename CONSISTENT_HASHER>
- class base_client
- {
- private:
- void init(connection_data & con)
- {
- char err[ANET_ERR_LEN];
- con.socket = anetTcpConnect(err, const_cast<char*>(con.host.c_str()), con.port);
- if (con.socket == ANET_ERR)
- {
- std::ostringstream os;
- os << err << " (redis://" << con.host << ':' << con.port << ")";
- throw connection_error( os.str() );
- }
- anetTcpNoDelay(NULL, con.socket);
- select(con.dbindex, con);
- }
-
- public:
- typedef std::string string_type;
- typedef std::vector<string_type> string_vector;
- typedef std::pair<string_type, string_type> string_pair;
- typedef std::vector<string_pair> string_pair_vector;
- typedef std::pair<string_type, double> string_score_pair;
- typedef std::vector<string_score_pair> string_score_vector;
- typedef std::set<string_type> string_set;
- typedef long int_type;
- explicit base_client(const string_type & host = "localhost",
- uint16_t port = 6379, int_type dbindex = 0)
- {
- connection_data con;
- con.host = host;
- con.port = port;
- con.dbindex = dbindex;
- init(con);
- connections_.push_back(con);
- }
- template<typename CON_ITERATOR>
- base_client(CON_ITERATOR begin, CON_ITERATOR end)
- {
- while(begin != end)
- {
- connection_data con = *begin;
- init(con);
- connections_.push_back(con);
- begin++;
- }
- if( connections_.empty() )
- throw std::runtime_error("No connections given!");
- }
- base_client<CONSISTENT_HASHER>* clone() const
- {
- return new base_client<CONSISTENT_HASHER>(connections_.begin(), connections_.end());
- }
- inline static string_type missing_value()
- {
- return "**nonexistent-key**";
- }
- enum datatype
- {
- datatype_none, // key doesn't exist
- datatype_string,
- datatype_list,
- datatype_set,
- datatype_zset,
- datatype_hash,
- datatype_unknown
- };
- int_type get_list(const string_type & key, string_vector & out)
- {
- return lrange(key, 0, -1, out);
- }
- void lrem_exact(const string_type & key,
- int_type count,
- const string_type & value)
- {
- if (lrem(key, count, value) != count)
- throw value_error("failed to remove exactly N elements from list");
- }
- enum range_specifier
- {
- exclude_min = 1 << 0,
- exclude_max = 1 << 1
- };
- enum aggregate_type
- {
- aggregate_sum = 1 << 0,
- aggregate_min = 1 << 1,
- aggregate_max = 1 << 2
- };
- enum sort_order
- {
- sort_order_ascending,
- sort_order_descending
- };
-
- ~base_client()
- {
- BOOST_FOREACH(connection_data & con, connections_)
- {
- if (con.socket != ANET_ERR)
- close(con.socket);
- }
- }
- const std::vector<connection_data> & connections() const
- {
- return connections_;
- }
-
- void auth(const string_type & pass)
- {
- if( connections_.size() > 1 )
- throw std::runtime_error("feature is not available in cluster mode");
- int socket = connections_[0].socket;
- send_(socket, makecmd("AUTH") << pass);
- recv_ok_reply_(socket);
- }
-
- void set(const string_type & key,
- const string_type & value)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("SET") << key << value);
- recv_ok_reply_(socket);
- }
-
- void mset( const string_vector & keys, const string_vector & values )
- {
- assert( keys.size() == values.size() );
- std::map< int, boost::optional<makecmd> > socket_commands;
- for(size_t i=0; i < keys.size(); i++)
- {
- int socket = get_socket(keys);
- boost::optional<makecmd> & cmd = socket_commands[socket];
- if(!cmd)
- cmd = makecmd("MSET");
- *cmd << keys[i] << values[i];
- }
- typedef std::pair< int, boost::optional<makecmd> > sock_pair;
- BOOST_FOREACH(const sock_pair & sp, socket_commands)
- {
- send_(sp.first, *sp.second);
- }
-
- BOOST_FOREACH(const sock_pair & sp, socket_commands)
- {
- recv_ok_reply_(sp.first);
- }
- }
-
- void mset( const string_pair_vector & key_value_pairs )
- {
- std::map< int, boost::optional<makecmd> > socket_commands;
-
- for(size_t i=0; i < key_value_pairs.size(); i++)
- {
- const string_type & key = key_value_pairs[i].first;
- const string_type & value = key_value_pairs[i].second;
-
- int socket = get_socket(key);
- boost::optional<makecmd> & cmd = socket_commands[socket];
- if(!cmd)
- cmd = makecmd("MSET");
- *cmd << key << value;
- }
-
- typedef std::pair< int, boost::optional<makecmd> > sock_pair;
- BOOST_FOREACH(const sock_pair & sp, socket_commands)
- {
- send_(sp.first, *sp.second);
- }
- BOOST_FOREACH(const sock_pair & sp, socket_commands)
- {
- recv_ok_reply_(sp.first);
- }
- }
- private:
- struct msetex_data
- {
- boost::optional<makecmd> mset_cmd;
- std::string expire_cmds;
- size_t count;
- };
- public:
- void msetex( const string_pair_vector & key_value_pairs, int_type seconds )
- {
- std::map< int, msetex_data > socket_commands;
-
- for(size_t i=0; i < key_value_pairs.size(); i++)
- {
- const string_type & key = key_value_pairs[i].first;
- const string_type & value = key_value_pairs[i].second;
-
- int socket = get_socket(key);
- msetex_data & dat = socket_commands[socket];
- boost::optional<makecmd> & cmd = dat.mset_cmd;
- if(!cmd)
- {
- cmd = makecmd("MSET");
- dat.count = 0;
- }
- *cmd << key << value;
- std::string & expire_cmds = dat.expire_cmds;
- expire_cmds += makecmd("EXPIRE") << key << seconds;
- dat.count++;
- }
-
- typedef std::pair< int, msetex_data > sock_pair;
- BOOST_FOREACH(const sock_pair & sp, socket_commands)
- {
- std::string cmds = *sp.second.mset_cmd;
- cmds += sp.second.expire_cmds;
- send_(sp.first, cmds);
- }
-
- BOOST_FOREACH(const sock_pair & sp, socket_commands)
- {
- recv_ok_reply_(sp.first);
- for(size_t i= 0; i < sp.second.count; i++)
- recv_int_ok_reply_(sp.first);
-
- }
- }
-
- string_type get(const string_type & key)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("GET") << key);
- return recv_bulk_reply_(socket);
- }
-
- string_type getset(const string_type & key, const string_type & value)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("GETSET") << key << value);
- return recv_bulk_reply_(socket);
- }
- private:
- struct connection_keys
- {
- boost::optional<makecmd> cmd;
- /// Gives the position of the value in the original array
- std::vector<size_t> indices;
- };
- public:
- void exec(command & cmd)
- {
- int socket = get_socket(cmd.hash_key_);
- send_( socket, cmd.request_ );
- cmd.set_reply( recv_generic_reply_(socket) );
- }
- void exec(std::vector<command> & commands)
- {
- std::map< int, std::string > socket_commands;
-
- for(size_t i=0; i < commands.size(); i++)
- {
- int socket = get_socket( commands[i].hash_key_ );
- socket_commands[socket] += commands[i].request_;
- }
-
- typedef std::pair< int, std::string > sock_pair;
- BOOST_FOREACH(const sock_pair & sp, socket_commands)
- {
- send_(sp.first, sp.second);
- }
-
- for(size_t i=0; i < commands.size(); i++)
- {
- int socket = get_socket( commands[i].hash_key_ );
- commands[i].set_reply( recv_generic_reply_(socket) );
- }
- }
-
- void exec_transaction(std::vector<command> & commands)
- {
- int cmd_socket = -1;
- std::string cmd_str = makecmd("MULTI");
-
- for(size_t i=0; i < commands.size(); i++)
- {
- int socket = get_socket( commands[i].hash_key_ );
- if( cmd_socket == -1 )
- cmd_socket = socket;
- else if( cmd_socket != socket )
- throw std::runtime_error("calls in transaction map to different server!");
-
- cmd_str += commands[i].request_;
- }
- cmd_str += makecmd("EXEC");
-
- send_(cmd_socket, cmd_str);
- recv_ok_reply_(cmd_socket); // MULTI => +OK
- for(size_t i=0; i < commands.size(); i++)
- {
- std::string resp = recv_single_line_reply_(cmd_socket);
- if( resp != "QUEUED" )
- throw std::runtime_error("invalid state (expected 'QUEUED' in transaction but got '" + resp + "')");
- }
- if( read_line(cmd_socket)[0] != REDIS_PREFIX_MULTI_BULK_REPLY )
- throw std::runtime_error("EXEC does not return a multi bulk reply");
-
- for(size_t i=0; i < commands.size(); i++)
- {
- commands[i].set_reply( recv_generic_reply_(cmd_socket) );
- }
- }
-
- void mget(const string_vector & keys, string_vector & out)
- {
- out = string_vector( keys.size() );
- std::map< int, connection_keys > socket_commands;
-
- for(size_t i=0; i < keys.size(); i++)
- {
- int socket = get_socket(keys[i]);
- connection_keys & con_keys = socket_commands[socket];
- boost::optional<makecmd> & cmd = con_keys.cmd;
- if(!cmd)
- cmd = makecmd("MGET");
- *cmd << keys[i];
- con_keys.indices.push_back(i);
- }
-
- typedef std::pair< int, connection_keys > sock_pair;
- BOOST_FOREACH(const sock_pair & sp, socket_commands)
- {
- send_(sp.first, *sp.second.cmd);
- }
-
- BOOST_FOREACH(const sock_pair & sp, socket_commands)
- {
- const connection_keys & con_keys = sp.second;
- string_vector cur_out;
- recv_multi_bulk_reply_(sp.first, cur_out);
-
- for(size_t i=0; i < cur_out.size(); i++)
- out[con_keys.indices[i]] = cur_out[i];
- }
- }
-
- bool setnx(const string_type & key,
- const string_type & value)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("SETNX") << key << value);
- return recv_int_reply_(socket) == 1;
- }
-
- bool msetnx( const string_vector & keys, const string_vector & values )
- {
- assert( keys.size() == values.size() );
-
- std::map< int, boost::optional<makecmd> > socket_commands;
-
- for(size_t i=0; i < keys.size(); i++)
- {
- int socket = get_socket(keys);
- boost::optional<makecmd> & cmd = socket_commands[socket];
- if(!cmd)
- cmd = makecmd("MSETNX");
- *cmd << keys[i] << values[i];
- }
- if( socket_commands.size() > 1 )
- throw std::runtime_error("feature is not available in cluster mode");
-
- typedef std::pair< int, boost::optional<makecmd> > sock_pair;
- BOOST_FOREACH(const sock_pair & sp, socket_commands)
- {
- send_(sp.first, *sp.second);
- recv_ok_reply_(sp.first);
- }
- }
-
- bool msetnx( const string_pair_vector & key_value_pairs )
- {
- std::map< int, boost::optional<makecmd> > socket_commands;
-
- for(size_t i=0; i < key_value_pairs.size(); i++)
- {
- int socket = get_socket(keys);
- boost::optional<makecmd> & cmd = socket_commands[socket];
- if(!cmd)
- cmd = makecmd("MSETNX");
- *cmd << key_value_pairs[i].first << key_value_pairs[i].second;
- }
-
- if( socket_commands.size() > 1 )
- throw std::runtime_error("feature is not available in cluster mode");
- typedef std::pair< int, boost::optional<makecmd> > sock_pair;
- BOOST_FOREACH(const sock_pair & sp, socket_commands)
- {
- send_(sp.first, *sp.second);
- recv_ok_reply_(sp.first);
- }
- }
-
- void setex(const string_type & key, const string_type & value, unsigned int secs)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("SETEX") << key << secs << value);
- recv_ok_reply_(socket);
- }
-
- size_t append(const string_type & key, const string_type & value)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("APPEND") << key << value);
- int res = recv_int_reply_(socket);
- if(res < 0)
- throw protocol_error("expected value size");
-
- assert( static_cast<size_t>(res) >= value.size() );
- return static_cast<size_t>(res);
- }
-
- string_type substr(const string_type & key, int start, int end)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("SUBSTR") << key << start << end);
- return recv_bulk_reply_(socket);
- }
-
- int_type incr(const string_type & key)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("INCR") << key);
- return recv_int_reply_(socket);
- }
- template<typename INT_TYPE>
- INT_TYPE incr(const string_type & key)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("INCR") << key);
- return recv_int_reply_<INT_TYPE>(socket);
- }
-
- int_type incrby(const string_type & key, int_type by)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("INCRBY") << key << by);
- return recv_int_reply_(socket);
- }
-
- template<typename INT_TYPE>
- INT_TYPE incrby(const string_type & key, INT_TYPE by)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("INCRBY") << key << by);
- return recv_int_reply_<INT_TYPE>(socket);
- }
-
- int_type decr(const string_type & key)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("DECR") << key);
- return recv_int_reply_(socket);
- }
-
- template<typename INT_TYPE>
- INT_TYPE decr(const string_type & key)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("DECR") << key);
- return recv_int_reply_<INT_TYPE>(socket);
- }
-
- int_type decrby(const string_type & key, int_type by)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("DECRBY") << key << by);
- return recv_int_reply_(socket);
- }
-
- template<typename INT_TYPE>
- INT_TYPE decrby(const string_type & key, INT_TYPE by)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("DECRBY") << key << by);
- return recv_int_reply_<INT_TYPE>(socket);
- }
-
- bool exists(const string_type & key)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("EXISTS") << key);
- return recv_int_reply_(socket) == 1;
- }
-
- bool del(const string_type & key)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("DEL") << key);
- return recv_int_reply_(socket) != 0;
- }
- template<typename ITERATOR>
- bool del(ITERATOR begin, ITERATOR end)
- {
- std::map<int, string_vector> sock_key_map;
- while( begin != end )
- {
- string_type key = *begin++;
- sock_key_map[ get_socket(key) ].push_back(key);
- }
- typedef std::pair<const int, string_vector> sock_key_pair;
- BOOST_FOREACH(const sock_key_pair & p, sock_key_map)
- {
- send_(p.first, makecmd("DEL") << p.second);
- }
- int_type res = false;
- BOOST_FOREACH(const sock_key_pair & p, sock_key_map)
- {
- res += recv_int_reply_(p.first);
- }
- return res;
- }
-
- datatype type(const string_type & key)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("TYPE") << key);
- std::string response = recv_single_line_reply_(socket);
-
- if(response == "none") return datatype_none;
- if(response == "string") return datatype_string;
- if(response == "list") return datatype_list;
- if(response == "set") return datatype_set;
- if(response == "zset") return datatype_zset;
- if(response == "hash") return datatype_hash;
- #ifndef NDEBUG
- std::cerr << "Got unknown datatype name: " << response << std::endl;
- #endif // NDEBUG
-
- return datatype_unknown;
- }
-
- int_type keys(const string_type & pattern, string_vector & out)
- {
- BOOST_FOREACH(const connection_data & con, connections_)
- {
- send_(con.socket, makecmd("KEYS") << pattern);
- }
- int_type res = 0;
-
- BOOST_FOREACH(const connection_data & con, connections_)
- {
- res += recv_multi_bulk_reply_(con.socket, out);
- }
-
- return res;
- }
-
- string_type randomkey()
- {
- int socket = connections_[0].socket;
- if( connections_.size() > 1 )
- {
- /*
- * Select a random server if there are more then one
- */
-
- boost::mt19937 gen;
- boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time();
- boost::posix_time::ptime epoch( boost::gregorian::date(1970, 1, 1) );
- gen.seed( (now-epoch).total_seconds() );
-
- boost::uniform_int<> dist(0, connections_.size()-1);
- boost::variate_generator< boost::mt19937&, boost::uniform_int<> > die(gen, dist);
- socket = connections_[die()].socket;
- }
-
- send_(socket, makecmd("RANDOMKEY") );
- return recv_bulk_reply_(socket);
- }
-
- /**
- * @warning Not cluster save (the old name and the new one must be on the same redis server)
- */
- void rename(const string_type & old_name, const string_type & new_name)
- {
- int source_socket = get_socket(old_name);
- int destin_socket = get_socket(new_name);
- if( source_socket != destin_socket )
- {
- switch( type(old_name) )
- {
- case datatype_none: // key doesn't exist
- return;
- case datatype_string:
- set(new_name, get(old_name));
- break;
- case datatype_list:
- {
- string_vector content;
- lrange(old_name, 0, -1, content);
- del(new_name);
- BOOST_FOREACH(const string_type & val, content)
- {
- rpush(new_name, val);
- }
- break;
- }
- case datatype_set:
- {
- string_set content;
- smembers(old_name, content);
- del(new_name);
- BOOST_FOREACH(const string_type & val, content)
- {
- sadd(new_name, val);
- }
- break;
- }
- case datatype_zset:
- case datatype_hash:
- case datatype_unknown:
- default:
- throw std::runtime_error("renaming is not supported for this datatype in cluster mode");
- }
- del(old_name);
- return;
- }
- send_(source_socket, makecmd("RENAME") << old_name << new_name);
- recv_ok_reply_(source_socket);
- }
-
- /**
- * @warning Not cluster save (the old name and the new one must be on the same redis server)
- */
- bool renamenx(const string_type & old_name, const string_type & new_name)
- {
- int source_socket = get_socket(old_name);
- int destin_socket = get_socket(new_name);
-
- if( source_socket != destin_socket )
- {
- if( exists(new_name) )
- return false;
- rename(old_name, new_name);
- return true;
- }
-
- send_(source_socket, makecmd("RENAMENX") << old_name << new_name);
- return recv_int_reply_(source_socket) == 1;
- }
- /**
- * @returns the number of keys in the currently selected database. In cluster mode the number
- * of keys in all currently selected databases is returned.
- */
- int_type dbsize()
- {
- int_type val = 0;
-
- BOOST_FOREACH(const connection_data & con, connections_)
- {
- send_(con.socket, makecmd("DBSIZE"));
- }
-
- BOOST_FOREACH(const connection_data & con, connections_)
- {
- val += recv_int_reply_(con.socket);
- }
-
- return val;
- }
- /**
- * @returns the number of keys in the currently selected database with the given connection.
- */
- int_type dbsize(const connection_data & con)
- {
- send_(con.socket, makecmd("DBSIZE"));
- return recv_int_reply_(con.socket);
- }
-
- void expire(const string_type & key, unsigned int secs)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("EXPIRE") << key << secs);
- recv_int_ok_reply_(socket);
- }
-
- int ttl(const string_type & key)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("TTL") << key);
- return recv_int_reply_(socket);
- }
-
- int_type rpush(const string_type & key,
- const string_type & value)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("RPUSH") << key << value);
- return recv_int_reply_(socket);
- }
-
- int_type lpush(const string_type & key,
- const string_type & value)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("LPUSH") << key << value);
- return recv_int_reply_(socket);
- }
-
- int_type llen(const string_type & key)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("LLEN") << key);
- return recv_int_reply_(socket);
- }
-
- int_type lrange(const string_type & key,
- int_type start,
- int_type end,
- string_vector & out)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("LRANGE") << key << start << end);
- return recv_multi_bulk_reply_(socket, out);
- }
-
- void ltrim(const string_type & key,
- int_type start,
- int_type end)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("LTRIM") << key << start << end);
- recv_ok_reply_(socket);
- }
-
- string_type lindex(const string_type & key,
- int_type index)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("LINDEX") << key << index);
- return recv_bulk_reply_(socket);
- }
-
- void lset(const string_type & key, int_type index, const string_type & value)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("LSET") << key << index << value);
- recv_ok_reply_(socket);
- }
-
- int_type lrem(const string_type & key, int_type count, const string_type & value)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("LREM") << key << count << value);
- return recv_int_reply_(socket);
- }
-
- string_type lpop(const string_type & key)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("LPOP") << key);
- return recv_bulk_reply_(socket);
- }
-
- string_type rpop(const string_type & key)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("RPOP") << key);
- return recv_bulk_reply_(socket);
- }
- /**
- * @warning Not cluster save (all keys must be on the same redis server)
- */
- string_pair blpop(const string_vector & keys, int_type timeout_seconds = 0)
- {
- int socket = get_socket(keys);
- if(socket == -1)
- // How to do in cluster mode? Is reinserting of to much poped values a solution?
- throw std::runtime_error("feature is not available in cluster mode");
-
- send_(socket, makecmd("BLPOP") << keys << timeout_seconds);
- string_vector sv;
- try
- {
- recv_multi_bulk_reply_(socket, sv);
- }
- catch(key_error & e)
- {
- assert(timeout_seconds > 0);
- throw timeout_error("could not pop value in time");
- }
- if(sv.size() == 2)
- return make_pair( sv[0], sv[1] );
- else
- return make_pair( "", missing_value() );
- }
- string_type blpop(const string_type & key, int_type timeout_seconds = 0)
- {
- int socket = get_socket(key);
- if(socket == -1)
- // How to do in cluster mode? Is reinserting of to much poped values a solution?
- throw std::runtime_error("feature is not available in cluster mode");
-
- send_(socket, makecmd("BLPOP") << key << timeout_seconds);
- string_vector sv;
- try
- {
- recv_multi_bulk_reply_(socket, sv);
- }
- catch(key_error & e)
- {
- assert(timeout_seconds > 0);
- return missing_value(); // should we throw a timeout_error?
- // we set a timeout so we expect that this can happen
- }
- if(sv.size() == 2)
- {
- assert(key == sv[0]);
- return sv[1];
- }
- else
- return missing_value();
- }
-
- /**
- * @warning Not cluster save (all keys must be on the same redis server)
- */
- string_pair brpop(const string_vector & keys, int_type timeout_seconds)
- {
- int socket = get_socket(keys);
- makecmd m("BRPOP");
- for(size_t i=0; i < keys.size(); i++)
- m << keys[i];
- m << timeout_seconds;
- send_(socket, m);
- string_vector sv;
- try
- {
- recv_multi_bulk_reply_(socket, sv);
- }
- catch(key_error & e)
- {
- assert(timeout_seconds > 0);
- return missing_value(); // should we throw a timeout_error?
- // we set a timeout so we expect that this can happen
- }
- if(sv.size() == 2)
- return make_pair( sv[0], sv[1] );
- else
- return make_pair( "", missing_value );
- }
-
- string_type brpop(const string_type & key, int_type timeout_seconds)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("BRPOP") << key << timeout_seconds);
- string_vector sv;
- try
- {
- recv_multi_bulk_reply_(socket, sv);
- }
- catch(key_error & e)
- {
- assert(timeout_seconds > 0);
- return missing_value(); // should we throw a timeout_error?
- // we set a timeout so we expect that this can happen
- }
- if(sv.size() == 2)
- {
- assert(key == sv[0]);
- return sv[1];
- }
- else
- return missing_value();
- }
-
- bool sadd(const string_type & key,
- const string_type & value)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("SADD") << key << value);
- return recv_int_reply_(socket) == 1;
- }
- template<typename ITERATOR>
- int_type sadd(const string_type & key, ITERATOR begin, ITERATOR end)
- {
- int socket = get_socket(key);
- std::string commands;
- size_t count = 0;
-
- while(begin != end)
- {
- string_type val = *begin++;
- commands += makecmd("SADD") << key << val;
- count++;
- }
-
- send_(socket, commands);
- int_type res = 0;
- for(size_t i=0; i < count; i++)
- {
- res += recv_int_reply_(socket);
- }
- return res;
- }
-
- void srem(const string_type & key,
- const string_type & value)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("SREM") << key << value);
- recv_int_ok_reply_(socket);
- }
-
- string_type spop(const string_type & key)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("SPOP") << key);
- return recv_bulk_reply_(socket);
- }
-
- void smove(const string_type & srckey, const string_type & dstkey, const string_type & member)
- {
- int src_socket = get_socket(srckey);
- int dst_socket = get_socket(dstkey);
- if(dst_socket != src_socket)
- {
- srem(srckey, member);
- sadd(dstkey, member);
- return;
- }
-
- send_(src_socket, makecmd("SMOVE") << srckey << dstkey << member);
- recv_int_ok_reply_(src_socket);
- }
-
- int_type scard(const string_type & key)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("SCARD") << key);
- return recv_int_reply_(socket);
- }
-
- bool sismember(const string_type & key,
- const string_type & value)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("SISMEMBER") << key << value);
- return recv_int_reply_(socket) == 1;
- }
- /**
- * @returns the intersection between the Sets stored at key1, key2, ..., keyN
- * @warning Not cluster save (all keys must be on the same redis server)
- */
- int_type sinter(const string_vector & keys, string_set & out)
- {
- std::map<int, string_vector> per_server;
- BOOST_FOREACH(const string_type & key, keys)
- {
- per_server[get_socket(key)].push_back(key);
- }
- size_t i = 0;
- typedef std::pair<const int, string_vector> per_server_pair;
- BOOST_FOREACH(const per_server_pair & p, per_server)
- {
- send_(p.first, makecmd("SINTER") << p.second);
- }
-
- BOOST_FOREACH(const per_server_pair & p, per_server)
- {
- string_set cur;
- recv_multi_bulk_reply_(p.first, cur);
- if(i > 0)
- {
- string_set prev = out;
- out.clear();
-
- std::set_intersection(prev.begin(), prev.end(), cur.begin(), cur.end(), std::inserter(out, out.end()));
- }
- else
- {
- out = cur;
- }
- i++;
- }
-
- return out.size();
- }
- /**
- * @warning Not cluster save (all keys must be on the same redis server)
- */
- int_type sinterstore(const string_type & dstkey, const string_vector & keys)
- {
- int socket = get_socket(dstkey);
- int source_sockets = get_socket(keys);
- if(socket != source_sockets)
- {
- std::cerr << 0 << std::endl;
- string_set content;
- std::cerr << 1 << std::endl;
- sinter(keys, content);
- std::cerr << 2 << std::endl;
- del(dstkey);
- std::cerr << 3 << std::endl;
- BOOST_FOREACH(const string_type & val, content)
- {
- std::cerr << 4 << std::endl;
- sadd(dstkey, val);
- }
- std::cerr << 5 << std::endl;
- return content.size();
- }
-
- send_(socket, makecmd("SINTERSTORE") << dstkey << keys);
- return recv_int_reply_(socket);
- }
-
- int_type sunion(const string_vector & keys, string_set & out)
- {
- int socket = get_socket(keys);
- if(socket == -1)
- {
- std::map< int, boost::optional<makecmd> > per_socket_keys;
- BOOST_FOREACH(const string_type & key, keys)
- {
- boost::optional<makecmd> & optCmd = per_socket_keys[ get_socket(key) ];
- if( !optCmd )
- optCmd = makecmd("SUNION");
- *optCmd << key;
- }
- typedef std::pair< const int, boost::optional<makecmd> > per_sock_pair;
- BOOST_FOREACH(const per_sock_pair & p, per_socket_keys)
- {
- send_(p.first, *p.second);
- }
-
- BOOST_FOREACH(const per_sock_pair & p, per_socket_keys)
- {
- recv_multi_bulk_reply_(p.first, out);
- }
- return out.size();
- }
-
- send_(socket, makecmd("SUNION") << keys);
- return recv_multi_bulk_reply_(socket, out);
- }
-
- int_type sunionstore(const string_type & dstkey,
- const string_vector & keys)
- {
- int socket = get_socket(dstkey);
- int source_sockets = get_socket(keys);
- if(socket != source_sockets)
- {
- string_set content;
- sunion(keys, content);
- del(dstkey);
- return sadd(dstkey, content.begin(), content.end());
- }
-
- send_(socket, makecmd("SUNIONSTORE") << dstkey << keys);
- return recv_int_reply_(socket);
- }
-
- int_type sdiff(const string_vector & keys, string_set & out)
- {
- int socket = get_socket(keys);
- send_(socket, makecmd("SDIFF") << keys);
- return recv_multi_bulk_reply_(socket, out);
- }
-
- int_type sdiffstore(const string_type & dstkey, const string_vector & keys)
- {
- int socket = get_socket(dstkey);
- int source_sockets = get_socket(keys);
- if(socket != source_sockets)
- throw std::runtime_error("not available in cluster mode");
-
- send_(socket, makecmd("SDIFFSTORE") << dstkey << keys);
- return recv_int_reply_(socket);
- }
-
- int_type smembers(const string_type & key, string_set & out)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("SMEMBERS") << key);
- return recv_multi_bulk_reply_(socket, out);
- }
-
- string_type srandmember(const string_type & key)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("SPOP") << key);
- return recv_bulk_reply_(socket);
- }
-
- void zadd(const string_type & key, double score, const string_type & member)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("ZADD") << key << score << member);
- recv_int_ok_reply_(socket);
- }
-
- void zadd(const string_type & key, const string_score_pair & value)
- {
- zadd(key, value.second, value.first);
- }
-
- void zrem(const string_type & key, const string_type & member)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("ZREM") << key << member);
- recv_int_ok_reply_(socket);
- }
-
- double zincrby(const string_type & key, const string_type & member, double increment)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("ZINCRBY") << key << increment << member);
- return boost::lexical_cast<double>( recv_bulk_reply_(socket) );
- }
-
- int_type zrank(const string_type & key, const string_type & member)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("ZRANK") << key << member);
- return recv_int_reply_(socket);
- }
-
- int_type zrevrank(const string_type & key, const string_type & value)
- {
- int socket = get_socket(key);
- send_(socket, makecmd("ZREVRANK") << key << value);
- return boost::lexical_cast<int_type>( recv_int_reply_(socket) );
- }
-
- void zrange(const string_type & key, int_type start, int_type end, string_vector & out)
- {
- int socket = get_socket(key);
- send_( socket, makecmd("ZRANGE") << key << start << end );
- recv_multi_bulk_reply_(socket, out);
- }
- private:
- void convert(const string_vector & in, string_score_vector & out)
- {
- assert( in.size() % 2 == 0 );
- for(size_t i=0; i < in.size(); i += 2)
- {
- const std::string & value = in[i];
- const std::string & str_score = in[i+1];
- double score = boost::lexical_cast<double>(str_score);
- out.push_back( make_pair(value, score) );
- }
- }
-
- public:
-
- void zrange(const string_type & key, int_type start, int_type end, string_score_vector & out)
- {
- int socket = get_socket(key);
- send_( socket, makecmd("ZRANGE") << key << start << end << "WITHSCORES" );
- string_vector res;
- recv_multi_bulk_reply_(socket, res);
- convert(res, out);
- }
-
- void zrevrange(const string_type & key, int_type start, int_type end, string_vector & out)
- {
- int socket = get_socket(key);
- send_( socket, makecmd("ZREVRANGE") << key << start << end );
- recv_multi_bulk_reply_(socket, out);
- }
-
- void zrevrange(const string_type & key, int_type start, int_type end, string_score_vector & out)
- {
- int socket = get_socket(key);
- send_( socket, makecmd("ZREVRANGE") << key << start << end << "WITHSCORES" );
- string_vector res;
- recv_multi_bulk_reply_(socket, res);
- convert(res, out);
- }
- protected:
- void zrangebyscore_base(bool withscores, const string_type & key, double min, double max, string_vector & out, int_type offset, int_type max_count, int range_modification)
- {
- int socket = get_socket(key);
- std::string min_str, max_str;
- if( range_modification & exclude_min )
- min_str = "(";
- if( range_modification & exclude_max )
- max_str = "(";
-
- min_str += boost::lexical_cast<std::string>(min);
- max_str += boost::lexical_cast<std::string>(max);
-
- makecmd m("ZRANGEBYSCORE");
- m << key << min_str << max_str;
-
- if(max_count != -1 || offset > 0)
- {
- std::cerr << "Adding limit: " << offset << " " << max_count << std::endl;
- m << "LIMIT" << offset << max_count;
- }
-
- send_(socket, m);
- recv_multi_bulk_reply_(socket, out);
- }
-
- public:
- void zrangebyscore(const string_type & key, double min, double max, string_vector & out, int_type offset = 0, int_type max_count = -1, int range_modification = 0)
- {
- zrangebyscore_base(false, key, min, max, out, offset, max_count, range_modification);
- }
-
- void zrangebyscore(const string_type & key, double min, double max, string_score_vector & out, int_type offset = 0, int_type max_count = -1, int range_modification = 0)
- {
- string_vector res;
- zrangebyscore_base(true, key, min, max, res, offset, max_count, range_modification);
- convert(res, out);
- }
-
- int_type zcount(const string_type & key, double min, double max, int range_modification = 0)
- {
- int socket = get_socket(key);
- std::string min_str, max_str;
- if( range_modification & exclude_min )
- min_str = "(";
- if( range_modification & exclude_max )
- max_str = "(";
-
- min_str += boost::lexical_cast<std::string>(min);
- max_str += boost::lexical_cast<std::string>(max);
-
- send_(socket, makecmd("ZCOUNT") << key << min_str << max_str);
- return recv_int_reply_(socket);
- }
-
- int_type zremrangebyrank( const string_type & key, int_type start, int_type end )
- {
- int socket = get_socket(key);
- send_(socket, makecmd("ZREMRANGEBYRANK") << key << start << end);
- return recv_int_reply_(socket);
- }
-
- int_type zremrangebyscore( const string_type& key, double min, double max )
- {
- int socket = get_socket(key);
- send_(socket, makecmd("ZREMRANGEBYSCORE") << key << min << max);
- return recv_int_reply_(socket);
- }
-
- int_type zcard( const string_type & key )
- {
- int socket = get_socket(key);
- send_(socket, makecmd("ZCARD") << key);
- return recv_int_reply_(socket);
- }
-
- double zscore( const string_type& key, const string_type& element )
- {
- int socket = get_socket(key);
- send_(socket, makecmd("ZSCORE") << key << element);
- return boost::lexical_cast<double>( recv_bulk_reply_(socket) );
- }
-
- int_type zunionstore( const string_type & dstkey, const string_vector & keys, const std::vector<double> & weights = std::vector<double>(), aggregate_type aggragate = aggregate_sum )
- {
- int dst_socket = get_socket(dstkey);
- int socket = get_socket(keys);
- if(socket != dst_socket)
- throw std::runtime_error("feature is not available in cluster mode");
-
- makecmd m("ZUNIONSTORE");
- m << dstkey << keys.size() << keys;
-
- if( weights.size() > 0 )
- {
- assert(keys.size() == weights.size());
- m << "WEIGHTS" << weights;
- }
-
- m << "AGGREGATE";
- switch(aggragate)
- {
- case aggregate_sum:
- m << "SUM";
- break;
- case aggregate_min:
- m << "MIN";
- break;
- case aggregate_max:
- m << "MAX";
- break;
- default:
- assert(false);
- }
-
- send_(socket, m);
- return re…
Large files files are truncated, but you can click here to view the full file