RedisReply RedisCon::pipeliningSingle(const std::vector<std::string> command) { std::vector<const char*> argv; argv.reserve(command.size()); std::vector<size_t> argvlen; argvlen.reserve(command.size()); for (std::vector<std::string>::const_iterator it = command.begin(); it != command.end(); ++it) { argv.push_back(it->c_str()); argvlen.push_back(it->size()); } int err = redisAppendCommandArgv(connect_.get(), static_cast<int>(command.size()), argv.data(), argvlen.data()); if (err != REDIS_OK) { throw redis_transport_except(connect_->errstr); } redisReply *r; err = redisGetReply(connect_.get(), reinterpret_cast<void**>(&r)); RedisReply reply(r); if (err != REDIS_OK) { throw redis_transport_except(connect_->errstr); } return std::move(reply); }
static VALUE connection_write(VALUE self, VALUE command) { redisParentContext *pc; int argc; char **argv = NULL; size_t *alen = NULL; int i; /* Commands should be an array of commands, where each command * is an array of string arguments. */ if (TYPE(command) != T_ARRAY) rb_raise(rb_eArgError,"%s","not an array"); Data_Get_Struct(self,redisParentContext,pc); if (!pc->context) rb_raise(rb_eRuntimeError,"%s","not connected"); argc = (int)RARRAY_LEN(command); argv = malloc(argc*sizeof(char*)); alen = malloc(argc*sizeof(size_t)); for (i = 0; i < argc; i++) { /* Replace arguments in the arguments array to prevent their string * equivalents to be garbage collected before this loop is done. */ VALUE entry = rb_obj_as_string(rb_ary_entry(command, i)); rb_ary_store(command, i, entry); argv[i] = RSTRING_PTR(entry); alen[i] = RSTRING_LEN(entry); } redisAppendCommandArgv(pc->context,argc,(const char**)argv,alen); free(argv); free(alen); return Qnil; }
static int cliSendCommand(int argc, char **argv, int repeat) { char *command = argv[0]; size_t *argvlen; int j, output_raw; if (context == NULL) { printf("Not connected, please use: connect <host> <port>\n"); return REDIS_OK; } output_raw = !strcasecmp(command,"info"); if (!strcasecmp(command,"help") || !strcasecmp(command,"?")) { cliOutputHelp(--argc, ++argv); return REDIS_OK; } if (!strcasecmp(command,"shutdown")) config.shutdown = 1; if (!strcasecmp(command,"monitor")) config.monitor_mode = 1; if (!strcasecmp(command,"subscribe") || !strcasecmp(command,"psubscribe")) config.pubsub_mode = 1; #ifdef ALCHEMY_DATABASE DXDB_cliSendCommand(&argc, argv); #endif /* Setup argument length */ argvlen = malloc(argc*sizeof(size_t)); for (j = 0; j < argc; j++) argvlen[j] = sdslen(argv[j]); while(repeat--) { redisAppendCommandArgv(context,argc,(const char**)argv,argvlen); while (config.monitor_mode) { if (cliReadReply(output_raw) != REDIS_OK) exit(1); fflush(stdout); } if (config.pubsub_mode) { if (!config.raw_output) printf("Reading messages... (press Ctrl-C to quit)\n"); while (1) { if (cliReadReply(output_raw) != REDIS_OK) exit(1); } } if (cliReadReply(output_raw) != REDIS_OK) { free(argvlen); return REDIS_ERR; } else { /* Store database number when SELECT was successfully executed. */ if (!strcasecmp(command,"select") && argc == 2) { config.dbnum = atoi(argv[1]); cliRefreshPrompt(); } } } free(argvlen); return REDIS_OK; }
int db_redis_append_command_argv(km_redis_con_t *con, redis_key_t *query, int queue) { char **argv = NULL; int ret, argc; print_query(query); if (queue > 0 && db_redis_push_query(con, query) != 0) { LM_ERR("Failed to queue redis command\n"); return -1; } argc = db_redis_key_list2arr(query, &argv); if (argc < 0) { LM_ERR("Failed to allocate memory for query array\n"); return -1; } LM_DBG("query has %d args\n", argc); ret = redisAppendCommandArgv(con->con, argc, (const char**)argv, NULL); // this should actually never happen, because if all replies // are properly consumed for the previous command, it won't send // out a new query until redisGetReply is called if (con->con->err == REDIS_ERR_EOF) { if (db_redis_connect(con) != 0) { LM_ERR("Failed to reconnect to redis db\n"); pkg_free(argv); if (con->con) { redisFree(con->con); con->con = NULL; } return ret; } ret = redisAppendCommandArgv(con->con, argc, (const char**)argv, NULL); } pkg_free(argv); if (!con->con->err) { con->append_counter++; } return ret; }
static int lconn_append_command(lua_State * L) { redisContext * pContext = check_connection(L, 1); const char * argv[LUAHIREDIS_MAXARGS]; size_t argvlen[LUAHIREDIS_MAXARGS]; int nargs = load_args(L, pContext, 2, argv, argvlen); redisAppendCommandArgv(pContext, nargs, argv, argvlen); return 0; }
static int cliSendCommand(int argc, char **argv, int repeat) { char *command = argv[0]; size_t *argvlen; int j, output_raw; if (context == NULL) { printf("Not connected, please use: connect <host> <port>\n"); return REDIS_OK; } output_raw = !strcasecmp(command,"info"); if (!strcasecmp(command,"help") || !strcasecmp(command,"?")) { cliOutputHelp(--argc, ++argv); return REDIS_OK; } if (!strcasecmp(command,"shutdown")) config.shutdown = 1; if (!strcasecmp(command,"monitor")) config.monitor_mode = 1; if (!strcasecmp(command,"subscribe") || !strcasecmp(command,"psubscribe")) config.pubsub_mode = 1; /* Setup argument length */ argvlen = malloc(argc*sizeof(size_t)); for (j = 0; j < argc; j++) argvlen[j] = sdslen(argv[j]); while(repeat--) { redisAppendCommandArgv(context,argc,(const char**)argv,argvlen); while (config.monitor_mode) { if (cliReadReply(output_raw) != REDIS_OK) exit(1); fflush(stdout); } if (config.pubsub_mode) { if (!config.raw_output) printf("Reading messages... (press Ctrl-C to quit)\n"); while (1) { if (cliReadReply(output_raw) != REDIS_OK) exit(1); } } if (cliReadReply(output_raw) != REDIS_OK) return REDIS_ERR; } return REDIS_OK; }
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) { if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK) return NULL; return __redisBlockForReply(c); }
/** Change the state of a connection to READONLY execute a command and switch to READWRITE * * @param[out] status_out Where to write the status from the command. * @param[out] reply_out Where to write the reply associated with the highest priority status. * @param[in] request The current request. * @param[in] conn to issue commands with. * @param[in] argc Redis command argument count. * @param[in] argv Redis command arguments. * @return * - 0 success. * - -1 normal failure. * - -2 failure that may leave the connection in a READONLY state. */ static int redis_command_read_only(fr_redis_rcode_t *status_out, redisReply **reply_out, REQUEST *request, fr_redis_conn_t *conn, int argc, char const **argv) { bool maybe_more = false; redisReply *reply; fr_redis_rcode_t status; *reply_out = NULL; redisAppendCommand(conn->handle, "READONLY"); redisAppendCommandArgv(conn->handle, argc, argv, NULL); redisAppendCommand(conn->handle, "READWRITE"); /* * Process the response for READONLY */ reply = NULL; /* Doesn't set reply to NULL on error *sigh* */ if (redisGetReply(conn->handle, (void **)&reply) == REDIS_OK) maybe_more = true; status = fr_redis_command_status(conn, reply); if (status != REDIS_RCODE_SUCCESS) { REDEBUG("Setting READONLY failed"); *reply_out = reply; *status_out = status; if (maybe_more) { if (redisGetReply(conn->handle, (void **)&reply) != REDIS_OK) return -1; fr_redis_reply_free(reply); if (redisGetReply(conn->handle, (void **)&reply) != REDIS_OK) return -1; fr_redis_reply_free(reply); } return -1; } fr_redis_reply_free(reply); /* * Process the response for the command */ reply = NULL; if (redisGetReply(conn->handle, (void **)&reply) == REDIS_OK) maybe_more = true; status = fr_redis_command_status(conn, reply); if (status != REDIS_RCODE_SUCCESS) { *reply_out = reply; *status_out = status; if (maybe_more) { if (redisGetReply(conn->handle, (void **)&reply) != REDIS_OK) return -1; fr_redis_reply_free(reply); } return -1; } *reply_out = reply; *status_out = status; /* * Process the response for READWRITE */ reply = NULL; status = fr_redis_command_status(conn, reply); if ((redisGetReply(conn->handle, (void **)&reply) != REDIS_OK) || (status != REDIS_RCODE_SUCCESS)) { REDEBUG("Setting READWRITE failed"); fr_redis_reply_free(*reply_out); *reply_out = reply; *status_out = status; return -2; } return 0; }
/** Insert a new entry into the data store * * @copydetails cache_entry_insert_t */ static cache_status_t cache_entry_insert(UNUSED rlm_cache_config_t const *config, void *driver_inst, REQUEST *request, UNUSED void *handle, const rlm_cache_entry_t *c) { rlm_cache_redis_t *driver = driver_inst; TALLOC_CTX *pool; vp_map_t *map; fr_redis_conn_t *conn; fr_redis_cluster_state_t state; fr_redis_rcode_t status; redisReply *reply = NULL; int s_ret; static char const command[] = "RPUSH"; char const **argv; size_t *argv_len; char const **argv_p; size_t *argv_len_p; int pipelined = 0; /* How many commands pending in the pipeline */ redisReply *replies[5]; /* Should have the same number of elements as pipelined commands */ size_t reply_num = 0, i; char *p; int cnt; vp_tmpl_t expires_value; vp_map_t expires = { .op = T_OP_SET, .lhs = &driver->expires_attr, .rhs = &expires_value, }; vp_tmpl_t created_value; vp_map_t created = { .op = T_OP_SET, .lhs = &driver->created_attr, .rhs = &created_value, .next = &expires }; /* * Encode the entry created date */ tmpl_init(&created_value, TMPL_TYPE_DATA, "<TEMP>", 6, T_BARE_WORD); created_value.tmpl_data_type = PW_TYPE_DATE; created_value.tmpl_data_length = sizeof(created_value.tmpl_data_value.date); created_value.tmpl_data_value.date = c->created; /* * Encode the entry expiry time * * Although Redis objects expire on their own, we still need this * to ignore entries that were created before the last epoch. */ tmpl_init(&expires_value, TMPL_TYPE_DATA, "<TEMP>", 6, T_BARE_WORD); expires_value.tmpl_data_type = PW_TYPE_DATE; expires_value.tmpl_data_length = sizeof(expires_value.tmpl_data_value.date); expires_value.tmpl_data_value.date = c->expires; expires.next = c->maps; /* Head of the list */ for (cnt = 0, map = &created; map; cnt++, map = map->next); /* * The majority of serialized entries should be under 1k. * * @todo We should really calculate this using some sort of moving average. */ pool = talloc_pool(request, 1024); if (!pool) return CACHE_ERROR; argv_p = argv = talloc_array(pool, char const *, (cnt * 3) + 2); /* pair = 3 + cmd + key */ argv_len_p = argv_len = talloc_array(pool, size_t, (cnt * 3) + 2); /* pair = 3 + cmd + key */ *argv_p++ = command; *argv_len_p++ = sizeof(command) - 1; *argv_p++ = (char const *)c->key; *argv_len_p++ = c->key_len; /* * Add the maps to the command string in reverse order */ for (map = &created; map; map = map->next) { if (fr_redis_tuple_from_map(pool, argv_p, argv_len_p, map) < 0) { REDEBUG("Failed encoding map as Redis K/V pair"); talloc_free(pool); return CACHE_ERROR; } argv_p += 3; argv_len_p += 3; } RDEBUG3("Pipelining commands"); RINDENT(); for (s_ret = fr_redis_cluster_state_init(&state, &conn, driver->cluster, request, c->key, c->key_len, false); s_ret == REDIS_RCODE_TRY_AGAIN; /* Continue */ s_ret = fr_redis_cluster_state_next(&state, &conn, driver->cluster, request, status, &reply)) { /* * Start the transaction, as we need to set an expiry time too. */ if (c->expires > 0) { RDEBUG3("MULTI"); if (redisAppendCommand(conn->handle, "MULTI") != REDIS_OK) { append_error: REXDENT(); RERROR("Failed appending Redis command to output buffer: %s", conn->handle->errstr); talloc_free(pool); return CACHE_ERROR; } pipelined++; } if (RDEBUG_ENABLED3) { p = fr_asprint(request, (char const *)c->key, c->key_len, '\0'); RDEBUG3("DEL \"%s\"", p); talloc_free(p); } if (redisAppendCommand(conn->handle, "DEL %b", c->key, c->key_len) != REDIS_OK) goto append_error; pipelined++; if (RDEBUG_ENABLED3) { RDEBUG3("argv command"); RINDENT(); for (i = 0; i < talloc_array_length(argv); i++) { p = fr_asprint(request, argv[i], argv_len[i], '\0'); RDEBUG3("%s", p); talloc_free(p); } REXDENT(); } redisAppendCommandArgv(conn->handle, talloc_array_length(argv), argv, argv_len); pipelined++; /* * Set the expiry time and close out the transaction. */ if (c->expires > 0) { if (RDEBUG_ENABLED3) { p = fr_asprint(request, (char const *)c->key, c->key_len, '\"'); RDEBUG3("EXPIREAT \"%s\" %li", p, (long)c->expires); talloc_free(p); } if (redisAppendCommand(conn->handle, "EXPIREAT %b %i", c->key, c->key_len, c->expires) != REDIS_OK) goto append_error; pipelined++; RDEBUG3("EXEC"); if (redisAppendCommand(conn->handle, "EXEC") != REDIS_OK) goto append_error; pipelined++; } REXDENT(); reply_num = fr_redis_pipeline_result(&status, replies, sizeof(replies) / sizeof(*replies), conn, pipelined); reply = replies[0]; } talloc_free(pool); if (s_ret != REDIS_RCODE_SUCCESS) { RERROR("Failed inserting entry"); return CACHE_ERROR; } RDEBUG3("Command results"); RINDENT(); for (i = 0; i < reply_num; i++) { fr_redis_reply_print(L_DBG_LVL_3, replies[i], request, i); fr_redis_reply_free(replies[i]); } REXDENT(); return CACHE_OK; } /** Call delete the cache entry from redis * * @copydetails cache_entry_expire_t */ static cache_status_t cache_entry_expire(UNUSED rlm_cache_config_t const *config, void *driver_inst, REQUEST *request, UNUSED void *handle, uint8_t const *key, size_t key_len) { rlm_cache_redis_t *driver = driver_inst; fr_redis_cluster_state_t state; fr_redis_conn_t *conn; fr_redis_rcode_t status; redisReply *reply = NULL; int s_ret; for (s_ret = fr_redis_cluster_state_init(&state, &conn, driver->cluster, request, key, key_len, false); s_ret == REDIS_RCODE_TRY_AGAIN; /* Continue */ s_ret = fr_redis_cluster_state_next(&state, &conn, driver->cluster, request, status, &reply)) { reply = redisCommand(conn->handle, "DEL %b", key, key_len); status = fr_redis_command_status(conn, reply); } if (s_ret != REDIS_RCODE_SUCCESS) { RERROR("Failed expiring entry"); fr_redis_reply_free(reply); return CACHE_ERROR; } rad_assert(reply); /* clang scan */ if (reply->type == REDIS_REPLY_INTEGER) { fr_redis_reply_free(reply); if (reply->integer) return CACHE_OK; /* Affected */ return CACHE_MISS; } REDEBUG("Bad result type, expected integer, got %s", fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>")); fr_redis_reply_free(reply); return CACHE_ERROR; } extern cache_driver_t rlm_cache_redis; cache_driver_t rlm_cache_redis = { .name = "rlm_cache_redis", .instantiate = mod_instantiate, .inst_size = sizeof(rlm_cache_redis_t), .free = cache_entry_free, .find = cache_entry_find, .insert = cache_entry_insert, .expire = cache_entry_expire, };
static int cliSendCommand(int argc, char **argv, int repeat) { char *command = argv[0]; size_t *argvlen; int j, output_raw; if (!strcasecmp(command,"help") || !strcasecmp(command,"?")) { cliOutputHelp(--argc, ++argv); return REDIS_OK; } if (context == NULL) return REDIS_ERR; output_raw = 0; if (!strcasecmp(command,"info") || (argc == 2 && !strcasecmp(command,"cluster") && (!strcasecmp(argv[1],"nodes") || !strcasecmp(argv[1],"info"))) || (argc == 2 && !strcasecmp(command,"client") && !strcasecmp(argv[1],"list"))) { output_raw = 1; } if (!strcasecmp(command,"shutdown")) config.shutdown = 1; if (!strcasecmp(command,"monitor")) config.monitor_mode = 1; if (!strcasecmp(command,"subscribe") || !strcasecmp(command,"psubscribe")) config.pubsub_mode = 1; /* Setup argument length */ argvlen = malloc(argc*sizeof(size_t)); for (j = 0; j < argc; j++) argvlen[j] = sdslen(argv[j]); while(repeat--) { redisAppendCommandArgv(context,argc,(const char**)argv,argvlen); while (config.monitor_mode) { if (cliReadReply(output_raw) != REDIS_OK) exit(1); fflush(stdout); } if (config.pubsub_mode) { if (config.output != OUTPUT_RAW) printf("Reading messages... (press Ctrl-C to quit)\n"); while (1) { if (cliReadReply(output_raw) != REDIS_OK) exit(1); } } if (cliReadReply(output_raw) != REDIS_OK) { free(argvlen); return REDIS_ERR; } else { /* Store database number when SELECT was successfully executed. */ if (!strcasecmp(command,"select") && argc == 2) { config.dbnum = atoi(argv[1]); cliRefreshPrompt(); } } if (config.interval) usleep(config.interval); fflush(stdout); /* Make it grep friendly */ } free(argvlen); return REDIS_OK; }
int main(int argc, char *argv[]) { int rc=-1, i, opt, n=10000; void *reply; while ( (opt = getopt(argc, argv, "v+n:s:p:")) != -1) { switch (opt) { case 'v': verbose++; break; case 'n': n=atoi(optarg); break; case 's': redis_host=strdup(optarg); break; case 'p': redis_port=atoi(optarg); break; default: usage(argv[0]); break; } } /* connect to redis */ redisContext *c = redisConnect(redis_host,redis_port); if (c && c->err) { fprintf(stderr,"redisConnect: %s\n", c->errstr); goto done; } struct timeval tv_a, tv_b; char big_str[100]; for(i=0; i < sizeof(big_str); i++) big_str[i] = 'a'+(i%10); big_str[sizeof(big_str)-1] = '\0'; /************************************************************* * big string push ************************************************************/ gettimeofday(&tv_a,NULL); for(i=0; i < n; i++) { reply = redisCommand(c, "LPUSH list %s", big_str); if (reply) freeReplyObject(reply); else fprintf(stderr,"redisCommand: %s\n", c->errstr); } gettimeofday(&tv_b,NULL); print_result(n,"big string push", tv_a, tv_b); reply = redisCommand(c, "DEL list"); if (reply) freeReplyObject(reply); else fprintf(stderr,"redisCommand: %s\n", c->errstr); /************************************************************* * big string push/trim ************************************************************/ gettimeofday(&tv_a,NULL); for(i=0; i < n; i++) { reply = redisCommand(c, "LPUSH list %s", big_str); if (reply) freeReplyObject(reply); else fprintf(stderr,"redisCommand: %s\n", c->errstr); reply = redisCommand(c, "LTRIM list 0 %u", n/2); if (reply) freeReplyObject(reply); else fprintf(stderr,"redisCommand: %s\n", c->errstr); } gettimeofday(&tv_b,NULL); print_result(n,"big string push/trim", tv_a, tv_b); reply = redisCommand(c, "DEL list"); if (reply) freeReplyObject(reply); else fprintf(stderr,"redisCommand: %s\n", c->errstr); /************************************************************* * pipelined big string push/trim ************************************************************/ gettimeofday(&tv_a,NULL); for(i=0; i < n; i++) { redisAppendCommand(c, "LPUSH list %s", big_str); redisAppendCommand(c, "LTRIM list 0 %u", n/2); redisGetReply(c, &reply); if (reply) freeReplyObject(reply); else fprintf(stderr,"redisCommand: %s\n", c->errstr); redisGetReply(c, &reply); if (reply) freeReplyObject(reply); else fprintf(stderr,"redisCommand: %s\n", c->errstr); } gettimeofday(&tv_b,NULL); print_result(n,"pipelined big string push/trim", tv_a, tv_b); reply = redisCommand(c, "DEL list"); if (reply) freeReplyObject(reply); else fprintf(stderr,"redisCommand: %s\n", c->errstr); /************************************************************* * pipelined big string push/trim with precomputed arg len ************************************************************/ char half_n[10]; sprintf(half_n, "%u", n/2); size_t half_n_len = strlen(half_n); gettimeofday(&tv_a,NULL); for(i=0; i < n; i++) { const char *cmds[] = { "LPUSH", "list", big_str }; size_t len[] = { 5, 4, sizeof(big_str) }; redisAppendCommandArgv(c, 3, cmds, len); const char *cmds2[] = { "LTRIM", "list", "0", half_n}; size_t len2[] = { 5, 4, 1, half_n_len }; redisAppendCommandArgv(c, 4, cmds2, len2); redisGetReply(c, &reply); if (reply) freeReplyObject(reply); else fprintf(stderr,"redisCommand: %s\n", c->errstr); redisGetReply(c, &reply); if (reply) freeReplyObject(reply); else fprintf(stderr,"redisCommand: %s\n", c->errstr); } gettimeofday(&tv_b,NULL); print_result(n,"pipelined big string push/trim precomputed arg len", tv_a, tv_b); /* reply = redisCommand(c, "DEL list"); if (reply) freeReplyObject(reply); else fprintf(stderr,"redisCommand: %s\n", c->errstr); */ done: return rc; }
u32 rddRedisInsert(redisContext*rd,char*rdd) { rddRedisDelete(rd,rdd); const char * const redisSetCmd[5] = { "SET", "RPUSH", "SADD", "ZADD", "HMSET" }; u32 nb=0; char **keys = rddGetAllKeys(rdd,&nb); for(u32 c=0; c<nb; c++) { struct keynfo k; keyGetNfo(keys[c],&k); int argnb = (*k.nb) + 2; const char ** cargv = (const char**) malloc( argnb*(sizeof(char*)) ); size_t *cargl = (size_t*)malloc( argnb*(sizeof(size_t*)) ); cargv[0] = redisSetCmd[*k.type]; cargl[0] = strlen(cargv[0]); cargv[1] = (const char *)k.name; cargl[1] = strlen(cargv[1]); if(*k.type == 3) // zset, need invert args... { for(u32 n=0; n<(*(k.nb)); n+=2) { cargv[n+2] = (const char *)(k.data[n+1]); // score cargl[n+2] = k.sizes[n+1]; cargv[n+3] = (const char *)(k.data[n]); // data cargl[n+3] = k.sizes[n]; } } else { for(u32 n=0; n<(*(k.nb)); n++) { cargv[n+2] = (const char *)(k.data[n]); cargl[n+2] = k.sizes[n]; } } redisAppendCommandArgv(rd, argnb, cargv, cargl); free(k.data); free(cargv); free(cargl); } for(u32 c=0; c<nb; c++) { struct keynfo k; keyGetNfo(keys[c],&k); time_t now = time(NULL); if(*k.ttl != 0) { u32 ttl = *k.ttl - now; redisCommand(rd,"EXPIRE %s %u",k.name,ttl); } free(k.data); } free(keys); redisReply * reply; for(u32 c=0; c<nb; c++) { redisGetReply(rd,(void**)&reply); freeReplyObject(reply); } return 1; }