4

I want to put a std::map<std::string, std::string> into redis server with hiredis. Since the API only allows formatted strings to be passed to redisCommand, I'm unable to store the map via a single command. I've tried using pipelines, but that is slower than HMSET and is therefore not applicable to performance restrains I'm in.

Anyone knows of any direct or indirect methods to pass a variant sized map via hiredis?

Klaus
  • 2,460
  • 1
  • 21
  • 23

2 Answers2

4

You are supposed to use the "Argv" flavors of redisCommand:

int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);

You need to build two arrays (pointers and sizes) before they can be called.

Something like this should work (untested):

void hmset( redisContext *c, const string &key, const map<string,string> &m )
{
  vector<const char *> argv;
  vector<size_t> argvlen;

  static char cmd[] = "HMSET";
  argv.push_back( cmd );
  argvlen.push_back( sizeof(cmd)-1 );

  argv.push_back( key.c_str() );
  argvlen.push_back( key.size() );

  map<string,string>::const_iterator i;
  for ( i=m.begin(); i!=m.end(); ++i )
  {
    argv.push_back( i->first.c_str() );
    argvlen.push_back( i->first.size() );
    argv.push_back( i->second.c_str() );
    argvlen.push_back( i->second.size() );
  }

  void *r = redisCommandArgv(c, argv.size(), &(argv[0]), &(argvlen[0]) );
  if ( !r )
    throw runtime_error( "Redis error" );
  freeReplyObject( r );
}

Note that if your map contains a lot of items, it is a wrong idea to push it to Redis in one single command. Past N=100-1000 items, variadic commands should be split (in batches of N items) and pipelined. Keep in mind that Redis is single-threaded. When a huge command is executed, nothing else is executed. Plus, you can hit the limit of the communication buffer.

Didier Spezia
  • 70,911
  • 12
  • 189
  • 154
  • 2
    That's it. I recommend everyone who gets to this answer; to double check notes mentioned at the end of answer since they are as important as the answer itself. – Klaus Aug 16 '15 at 19:27
  • I tried it and can confirm that your solution works out of the box. – Aftab Naveed Jul 19 '18 at 06:57
3

This is a late answer, however, with redis-plus-plus you can easily put a std::map<std::string, std::string> into Redis.

Disclaimer: I'm the author of this Redis client library. If you have any problem with this client, feel free to let me know. If you like it, also feel free to star it :)

Sample Code:

Redis redis("tcp://127.0.0.1:6379");
std::map<std::string, std::string> m = {std::make_pair("k1", "v1"), std::make_pair("k2", "v2")};
redis.hmset("hash-key", m.begin(), m.end());

Check the doc for detail.

for_stack
  • 21,012
  • 4
  • 35
  • 48