* m: may increase memory usage once called. Don't allow if out of memory.
* a: admin command, like SAVE or SHUTDOWN.
* p: Pub/Sub related command.
- * f: force replication of this command, regarless of server.dirty. */
+ * f: force replication of this command, regarless of server.dirty.
+ * s: command not allowed in scripts.
+ * r: random command. Command is not deterministic, that is, the same command
+ * with the same arguments, with the same key space, may have different
+ * results. For instance SPOP and RANDOMKEY are two random commands. */
struct redisCommand redisCommandTable[] = {
{"get",getCommand,2,"r",0,NULL,1,1,1,0,0},
{"set",setCommand,3,"wm",0,noPreloadGetKeys,1,1,1,0,0},
{"setnx",setnxCommand,3,"wm",0,noPreloadGetKeys,1,1,1,0,0},
{"setex",setexCommand,4,"wm",0,noPreloadGetKeys,2,2,1,0,0},
+ {"psetex",psetexCommand,4,"wm",0,noPreloadGetKeys,2,2,1,0,0},
{"append",appendCommand,3,"wm",0,NULL,1,1,1,0,0},
{"strlen",strlenCommand,2,"r",0,NULL,1,1,1,0,0},
{"del",delCommand,-2,"w",0,noPreloadGetKeys,1,-1,1,0,0},
{"smove",smoveCommand,4,"w",0,NULL,1,2,1,0,0},
{"sismember",sismemberCommand,3,"r",0,NULL,1,1,1,0,0},
{"scard",scardCommand,2,"r",0,NULL,1,1,1,0,0},
- {"spop",spopCommand,2,"w",0,NULL,1,1,1,0,0},
- {"srandmember",srandmemberCommand,2,"r",0,NULL,1,1,1,0,0},
+ {"spop",spopCommand,2,"wRs",0,NULL,1,1,1,0,0},
+ {"srandmember",srandmemberCommand,2,"rR",0,NULL,1,1,1,0,0},
{"sinter",sinterCommand,-2,"r",0,NULL,1,-1,1,0,0},
{"sinterstore",sinterstoreCommand,-3,"wm",0,NULL,2,-1,1,0,0},
{"sunion",sunionCommand,-2,"r",0,NULL,1,-1,1,0,0},
{"getset",getsetCommand,3,"wm",0,NULL,1,1,1,0,0},
{"mset",msetCommand,-3,"wm",0,NULL,1,-1,2,0,0},
{"msetnx",msetnxCommand,-3,"wm",0,NULL,1,-1,2,0,0},
- {"randomkey",randomkeyCommand,1,"r",0,NULL,0,0,0,0,0},
+ {"randomkey",randomkeyCommand,1,"rR",0,NULL,0,0,0,0,0},
{"select",selectCommand,2,"r",0,NULL,0,0,0,0,0},
{"move",moveCommand,3,"w",0,NULL,1,1,1,0,0},
{"rename",renameCommand,3,"w",0,renameGetKeys,1,2,1,0,0},
{"renamenx",renamenxCommand,3,"w",0,renameGetKeys,1,2,1,0,0},
{"expire",expireCommand,3,"w",0,NULL,1,1,1,0,0},
{"expireat",expireatCommand,3,"w",0,NULL,1,1,1,0,0},
+ {"pexpire",pexpireCommand,3,"w",0,NULL,1,1,1,0,0},
+ {"pexpireat",pexpireatCommand,3,"w",0,NULL,1,1,1,0,0},
{"keys",keysCommand,2,"r",0,NULL,0,0,0,0,0},
{"dbsize",dbsizeCommand,1,"r",0,NULL,0,0,0,0,0},
{"auth",authCommand,2,"r",0,NULL,0,0,0,0,0},
{"shutdown",shutdownCommand,1,"ar",0,NULL,0,0,0,0,0},
{"lastsave",lastsaveCommand,1,"r",0,NULL,0,0,0,0,0},
{"type",typeCommand,2,"r",0,NULL,1,1,1,0,0},
- {"multi",multiCommand,1,"r",0,NULL,0,0,0,0,0},
- {"exec",execCommand,1,"wm",0,NULL,0,0,0,0,0},
- {"discard",discardCommand,1,"r",0,NULL,0,0,0,0,0},
- {"sync",syncCommand,1,"ar",0,NULL,0,0,0,0,0},
+ {"multi",multiCommand,1,"rs",0,NULL,0,0,0,0,0},
+ {"exec",execCommand,1,"wms",0,NULL,0,0,0,0,0},
+ {"discard",discardCommand,1,"rs",0,NULL,0,0,0,0,0},
+ {"sync",syncCommand,1,"ars",0,NULL,0,0,0,0,0},
{"flushdb",flushdbCommand,1,"w",0,NULL,0,0,0,0,0},
{"flushall",flushallCommand,1,"w",0,NULL,0,0,0,0,0},
{"sort",sortCommand,-2,"wm",0,NULL,1,1,1,0,0},
{"info",infoCommand,-1,"r",0,NULL,0,0,0,0,0},
- {"monitor",monitorCommand,1,"ar",0,NULL,0,0,0,0,0},
+ {"monitor",monitorCommand,1,"ars",0,NULL,0,0,0,0,0},
{"ttl",ttlCommand,2,"r",0,NULL,1,1,1,0,0},
+ {"pttl",pttlCommand,2,"r",0,NULL,1,1,1,0,0},
{"persist",persistCommand,2,"w",0,NULL,1,1,1,0,0},
- {"slaveof",slaveofCommand,3,"aw",0,NULL,0,0,0,0,0},
+ {"slaveof",slaveofCommand,3,"aws",0,NULL,0,0,0,0,0},
{"debug",debugCommand,-2,"aw",0,NULL,0,0,0,0,0},
{"config",configCommand,-2,"ar",0,NULL,0,0,0,0,0},
- {"subscribe",subscribeCommand,-2,"rp",0,NULL,0,0,0,0,0},
- {"unsubscribe",unsubscribeCommand,-1,"rp",0,NULL,0,0,0,0,0},
- {"psubscribe",psubscribeCommand,-2,"rp",0,NULL,0,0,0,0,0},
- {"punsubscribe",punsubscribeCommand,-1,"rp",0,NULL,0,0,0,0,0},
+ {"subscribe",subscribeCommand,-2,"rps",0,NULL,0,0,0,0,0},
+ {"unsubscribe",unsubscribeCommand,-1,"rps",0,NULL,0,0,0,0,0},
+ {"psubscribe",psubscribeCommand,-2,"rps",0,NULL,0,0,0,0,0},
+ {"punsubscribe",punsubscribeCommand,-1,"rps",0,NULL,0,0,0,0,0},
{"publish",publishCommand,3,"rpf",0,NULL,0,0,0,0,0},
- {"watch",watchCommand,-2,"r",0,noPreloadGetKeys,1,-1,1,0,0},
- {"unwatch",unwatchCommand,1,"r",0,NULL,0,0,0,0,0},
+ {"watch",watchCommand,-2,"rs",0,noPreloadGetKeys,1,-1,1,0,0},
+ {"unwatch",unwatchCommand,1,"rs",0,NULL,0,0,0,0,0},
{"cluster",clusterCommand,-2,"ar",0,NULL,0,0,0,0,0},
- {"restore",restoreCommand,4,"awm",0,NULL,0,0,0,0,0},
+ {"restore",restoreCommand,4,"awm",0,NULL,1,1,1,0,0},
{"migrate",migrateCommand,6,"aw",0,NULL,0,0,0,0,0},
+ {"asking",askingCommand,1,"r",0,NULL,0,0,0,0,0},
{"dump",dumpCommand,2,"ar",0,NULL,0,0,0,0,0},
{"object",objectCommand,-2,"r",0,NULL,0,0,0,0,0},
{"client",clientCommand,-2,"ar",0,NULL,0,0,0,0,0},
- {"eval",evalCommand,-3,"wm",0,zunionInterGetKeys,0,0,0,0,0},
- {"evalsha",evalShaCommand,-3,"wm",0,zunionInterGetKeys,0,0,0,0,0},
- {"slowlog",slowlogCommand,-2,"r",0,NULL,0,0,0,0,0}
+ {"eval",evalCommand,-3,"wms",0,zunionInterGetKeys,0,0,0,0,0},
+ {"evalsha",evalShaCommand,-3,"wms",0,zunionInterGetKeys,0,0,0,0,0},
+ {"slowlog",slowlogCommand,-2,"r",0,NULL,0,0,0,0,0},
+ {"script",scriptCommand,-2,"ras",0,NULL,0,0,0,0,0}
};
/*============================ Utility functions ============================ */
return ust;
}
+/* Return the UNIX time in milliseconds */
+long long mstime(void) {
+ return ustime()/1000;
+}
+
/*====================== Hash table type implementation ==================== */
/* This is an hash table type that uses the SDS dynamic strings libary as
time_t t;
if ((de = dictGetRandomKey(db->expires)) == NULL) break;
- t = (time_t) dictGetEntryVal(de);
+ t = (time_t) dictGetVal(de);
if (now > t) {
- sds key = dictGetEntryKey(de);
+ sds key = dictGetKey(de);
robj *keyobj = createStringObject(key,sdslen(key));
propagateExpire(db,keyobj);
"-NOSCRIPT No matching script. Please use EVAL.\r\n"));
shared.loadingerr = createObject(REDIS_STRING,sdsnew(
"-LOADING Redis is loading the dataset in memory\r\n"));
+ shared.slowscripterr = createObject(REDIS_STRING,sdsnew(
+ "-BUSY Redis is busy running a script. Please wait or stop the server with SHUTDOWN.\r\n"));
shared.space = createObject(REDIS_STRING,sdsnew(" "));
shared.colon = createObject(REDIS_STRING,sdsnew(":"));
shared.plus = createObject(REDIS_STRING,sdsnew("+"));
server.port = REDIS_SERVERPORT;
server.bindaddr = NULL;
server.unixsocket = NULL;
+ server.unixsocketperm = 0;
server.ipfd = -1;
server.sofd = -1;
server.dbnum = REDIS_DEFAULT_DBNUM;
server.requirepass = NULL;
server.rdbcompression = 1;
server.activerehashing = 1;
- server.maxclients = 0;
+ server.maxclients = REDIS_MAX_CLIENTS;
server.bpop_blocked_clients = 0;
server.maxmemory = 0;
server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
server.zset_max_ziplist_entries = REDIS_ZSET_MAX_ZIPLIST_ENTRIES;
server.zset_max_ziplist_value = REDIS_ZSET_MAX_ZIPLIST_VALUE;
server.shutdown_asap = 0;
+ server.repl_ping_slave_period = REDIS_REPL_PING_SLAVE_PERIOD;
+ server.repl_timeout = REDIS_REPL_TIMEOUT;
server.cluster_enabled = 0;
server.cluster.configfile = zstrdup("nodes.conf");
server.lua_time_limit = REDIS_LUA_TIME_LIMIT;
+ server.lua_client = NULL;
+ server.lua_timedout = 0;
updateLRUClock();
resetServerSaveParams();
}
if (server.unixsocket != NULL) {
unlink(server.unixsocket); /* don't care if this fails */
- server.sofd = anetUnixServer(server.neterr,server.unixsocket);
+ server.sofd = anetUnixServer(server.neterr,server.unixsocket,server.unixsocketperm);
if (server.sofd == ANET_ERR) {
redisLog(REDIS_WARNING, "Opening socket: %s", server.neterr);
exit(1);
slowlogInit();
bioInit();
srand(time(NULL)^getpid());
+
+ /* Try to raise the max number of open files accordingly to the
+ * configured max number of clients. Also account for 32 additional
+ * file descriptors as we need a few more for persistence, listening
+ * sockets, log files and so forth. */
+ {
+ rlim_t maxfiles = server.maxclients+32;
+ struct rlimit limit;
+
+ if (maxfiles < 1024) maxfiles = 1024;
+ if (getrlimit(RLIMIT_NOFILE,&limit) == -1) {
+ redisLog(REDIS_WARNING,"Unable to obtain the current NOFILE limit (%s), assuming 1024 and setting the max clients configuration accordingly.",
+ strerror(errno));
+ server.maxclients = 1024-32;
+ } else {
+ rlim_t oldlimit = limit.rlim_cur;
+
+ /* Set the max number of files if the current limit is not enough
+ * for our needs. */
+ if (oldlimit < maxfiles) {
+ limit.rlim_cur = maxfiles;
+ limit.rlim_max = maxfiles;
+ if (setrlimit(RLIMIT_NOFILE,&limit) == -1) {
+ server.maxclients = oldlimit-32;
+ redisLog(REDIS_WARNING,"Unable to set the max number of files limit to %d (%s), setting the max clients configuration to %d.",
+ (int) maxfiles, strerror(errno), (int) server.maxclients);
+ } else {
+ redisLog(REDIS_NOTICE,"Max number of open files set to %d",
+ (int) maxfiles);
+ }
+ }
+ }
+ }
}
/* Populates the Redis Command Table starting from the hard coded list
case 'a': c->flags |= REDIS_CMD_ADMIN; break;
case 'p': c->flags |= REDIS_CMD_PUBSUB; break;
case 'f': c->flags |= REDIS_CMD_FORCE_REPLICATION; break;
+ case 's': c->flags |= REDIS_CMD_NOSCRIPT; break;
+ case 'R': c->flags |= REDIS_CMD_RANDOM; break;
default: redisPanic("Unsupported command flag"); break;
}
f++;
return REDIS_OK;
}
+ /* Lua script too slow? */
+ if (server.lua_timedout && c->cmd->proc != shutdownCommand) {
+ addReply(c, shared.slowscripterr);
+ return REDIS_OK;
+ }
+
/* Exec the command */
if (c->flags & REDIS_MULTI &&
c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
/* Close the listening sockets. Apparently this allows faster restarts. */
if (server.ipfd != -1) close(server.ipfd);
if (server.sofd != -1) close(server.sofd);
+ if (server.unixsocket) {
+ redisLog(REDIS_NOTICE,"Removing the unix socket file.");
+ unlink(server.unixsocket); /* don't care if this fails */
+ }
redisLog(REDIS_WARNING,"Redis is now ready to exit, bye bye...");
return REDIS_OK;
/*================================== Commands =============================== */
void authCommand(redisClient *c) {
- if (!server.requirepass || !strcmp(c->argv[1]->ptr, server.requirepass)) {
+ if (!server.requirepass) {
+ addReplyError(c,"Client sent AUTH, but no password is set");
+ } else if (!strcmp(c->argv[1]->ptr, server.requirepass)) {
c->authenticated = 1;
addReply(c,shared.ok);
} else {
"used_cpu_user:%.2f\r\n"
"used_cpu_sys_children:%.2f\r\n"
"used_cpu_user_children:%.2f\r\n",
- (float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000,
(float)self_ru.ru_stime.tv_sec+(float)self_ru.ru_stime.tv_usec/1000000,
- (float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000,
- (float)c_ru.ru_stime.tv_sec+(float)c_ru.ru_stime.tv_usec/1000000);
+ (float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000,
+ (float)c_ru.ru_stime.tv_sec+(float)c_ru.ru_stime.tv_usec/1000000,
+ (float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000);
}
/* cmdtime */
server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_RANDOM)
{
de = dictGetRandomKey(dict);
- bestkey = dictGetEntryKey(de);
+ bestkey = dictGetKey(de);
}
/* volatile-lru and allkeys-lru policy */
robj *o;
de = dictGetRandomKey(dict);
- thiskey = dictGetEntryKey(de);
+ thiskey = dictGetKey(de);
/* When policy is volatile-lru we need an additonal lookup
* to locate the real key, as dict is set to db->expires. */
if (server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU)
de = dictFind(db->dict, thiskey);
- o = dictGetEntryVal(de);
+ o = dictGetVal(de);
thisval = estimateObjectIdleTime(o);
/* Higher idle time is better candidate for deletion */
long thisval;
de = dictGetRandomKey(dict);
- thiskey = dictGetEntryKey(de);
- thisval = (long) dictGetEntryVal(de);
+ thiskey = dictGetKey(de);
+ thisval = (long) dictGetVal(de);
/* Expire sooner (minor expire unix timestamp) is better
* candidate for deletion */
if (loadAppendOnlyFile(server.appendfilename) == REDIS_OK)
redisLog(REDIS_NOTICE,"DB loaded from append only file: %.3f seconds",(float)(ustime()-start)/1000000);
} else {
- if (rdbLoad(server.dbfilename) == REDIS_OK)
- redisLog(REDIS_NOTICE,"DB loaded from disk: %.3f seconds",(float)(ustime()-start)/1000000);
+ if (rdbLoad(server.dbfilename) == REDIS_OK) {
+ redisLog(REDIS_NOTICE,"DB loaded from disk: %.3f seconds",
+ (float)(ustime()-start)/1000000);
+ } else if (errno != ENOENT) {
+ redisLog(REDIS_WARNING,"Fatal error loading the DB. Exiting.");
+ exit(1);
+ }
}
if (server.ipfd > 0)
redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);