#define REDIS_CONFIGLINE_MAX 1024
#define REDIS_OBJFREELIST_MAX 1000000 /* Max number of objects to cache */
#define REDIS_MAX_SYNC_TIME 60 /* Slave can't take more to sync */
-#define REDIS_EXPIRELOOKUPS_PER_CRON 10 /* try to expire 10 keys/loop */
+#define REDIS_EXPIRELOOKUPS_PER_CRON 10 /* lookup 10 expires per loop */
#define REDIS_MAX_WRITE_PER_EVENT (1024*64)
#define REDIS_REQUEST_MAX_SIZE (1024*1024*256) /* max bytes in inline command */
char *requirepass;
int shareobjects;
int rdbcompression;
+ int activerehashing;
/* Replication related */
int isslave;
char *masterauth;
static void zrankCommand(redisClient *c);
static void zrevrankCommand(redisClient *c);
static void hsetCommand(redisClient *c);
-static void hmsetCommand(redisClient *c);
static void hgetCommand(redisClient *c);
+static void hmsetCommand(redisClient *c);
+static void hmgetCommand(redisClient *c);
static void hdelCommand(redisClient *c);
static void hlenCommand(redisClient *c);
static void zremrangebyrankCommand(redisClient *c);
{"zrank",zrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
{"zrevrank",zrevrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
{"hset",hsetCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"hget",hgetCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
{"hmset",hmsetCommand,-4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+ {"hmget",hmgetCommand,-3,REDIS_CMD_BULK,NULL,1,1,1},
{"hincrby",hincrbyCommand,4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
- {"hget",hgetCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
{"hdel",hdelCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
{"hlen",hlenCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
{"hkeys",hkeysCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
int j;
for (j = 0; j < server.dbnum; j++) {
- if (htNeedsResize(server.db[j].dict)) {
- redisLog(REDIS_VERBOSE,"The hash table %d is too sparse, resize it...",j);
+ if (htNeedsResize(server.db[j].dict))
dictResize(server.db[j].dict);
- redisLog(REDIS_VERBOSE,"Hash table %d resized.",j);
- }
if (htNeedsResize(server.db[j].expires))
dictResize(server.db[j].expires);
}
}
+/* Our hash table implementation performs rehashing incrementally while
+ * we write/read from the hash table. Still if the server is idle, the hash
+ * table will use two tables for a long time. So we try to use 1 millisecond
+ * of CPU time at every serverCron() loop in order to rehash some key. */
+static void incrementallyRehash(void) {
+ int j;
+
+ for (j = 0; j < server.dbnum; j++) {
+ if (dictIsRehashing(server.db[j].dict)) {
+ dictRehashMilliseconds(server.db[j].dict,1);
+ break; /* already used our millisecond for this loop... */
+ }
+ }
+}
+
/* A background saving child (BGSAVE) terminated its work. Handle this. */
void backgroundSaveDoneHandler(int statloc) {
int exitcode = WEXITSTATUS(statloc);
* if we resize the HT while there is the saving child at work actually
* a lot of memory movements in the parent will cause a lot of pages
* copied. */
- if (server.bgsavechildpid == -1 && server.bgrewritechildpid == -1 &&
- !(loops % 10))
- {
- tryResizeHashTables();
+ if (server.bgsavechildpid == -1 && server.bgrewritechildpid == -1) {
+ if (!(loops % 10)) tryResizeHashTables();
+ if (server.activerehashing) incrementallyRehash();
}
/* Show information about connected clients */
server.requirepass = NULL;
server.shareobjects = 0;
server.rdbcompression = 1;
+ server.activerehashing = 1;
server.maxclients = 0;
server.blpop_blocked_clients = 0;
server.maxmemory = 0;
char buf[REDIS_CONFIGLINE_MAX+1], *err = NULL;
int linenum = 0;
sds line = NULL;
- char *errormsg = "Fatal error, can't open config file '%s'";
- char *errorbuf = zmalloc(sizeof(char)*(strlen(errormsg)+strlen(filename)));
- sprintf(errorbuf, errormsg, filename);
if (filename[0] == '-' && filename[1] == '\0')
fp = stdin;
else {
if ((fp = fopen(filename,"r")) == NULL) {
- redisLog(REDIS_WARNING, errorbuf);
+ redisLog(REDIS_WARNING, "Fatal error, can't open config file '%s'", filename);
exit(1);
}
}
if ((server.rdbcompression = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
+ } else if (!strcasecmp(argv[0],"activerehashing") && argc == 2) {
+ if ((server.activerehashing = yesnotoi(argv[1])) == -1) {
+ err = "argument must be 'yes' or 'no'"; goto loaderr;
+ }
} else if (!strcasecmp(argv[0],"daemonize") && argc == 2) {
if ((server.daemonize = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
/* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */
- if (dictSize(c->pubsub_channels) > 0 &&
+ if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0)
+ &&
cmd->proc != subscribeCommand && cmd->proc != unsubscribeCommand &&
cmd->proc != psubscribeCommand && cmd->proc != punsubscribeCommand) {
addReplySds(c,sdsnew("-ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context\r\n"));
}
}
+static void hmgetCommand(redisClient *c) {
+ int i;
+
+ robj *o = lookupKeyRead(c->db, c->argv[1]);
+ if (o == NULL) {
+ addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-2));
+ for (i = 2; i < c->argc; i++) {
+ addReply(c,shared.nullbulk);
+ }
+ return;
+ } else {
+ if (o->type != REDIS_HASH) {
+ addReply(c,shared.wrongtypeerr);
+ return;
+ }
+ }
+
+ addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-2));
+ if (o->encoding == REDIS_ENCODING_ZIPMAP) {
+ unsigned char *zm = o->ptr;
+ unsigned char *v;
+ unsigned int vlen;
+ robj *field;
+
+ for (i = 2; i < c->argc; i++) {
+ field = getDecodedObject(c->argv[i]);
+ if (zipmapGet(zm,field->ptr,sdslen(field->ptr),&v,&vlen)) {
+ addReplySds(c,sdscatprintf(sdsempty(),"$%u\r\n", vlen));
+ addReplySds(c,sdsnewlen(v,vlen));
+ addReply(c,shared.crlf);
+ } else {
+ addReply(c,shared.nullbulk);
+ }
+ decrRefCount(field);
+ }
+ } else {
+ dictEntry *de;
+
+ for (i = 2; i < c->argc; i++) {
+ de = dictFind(o->ptr,c->argv[i]);
+ if (de != NULL) {
+ addReplyBulk(c,(robj*)dictGetEntryVal(de));
+ } else {
+ addReply(c,shared.nullbulk);
+ }
+ }
+ }
+}
+
static void hdelCommand(redisClient *c) {
robj *o;
int deleted = 0;
addReply(c,shared.czero);
return;
}
- if (seconds < 0) {
+ if (seconds <= 0) {
if (deleteKey(c->db,key)) server.dirty++;
addReply(c, shared.cone);
return;