#define REDIS_ENCODING_ZIPMAP 2 /* Encoded as zipmap */
#define REDIS_ENCODING_HT 3 /* Encoded as an hash table */
+static char* strencoding[] = {
+ "raw", "int", "zipmap", "hashtable"
+};
+
/* Object types only used for dumping to disk */
#define REDIS_EXPIRETIME 253
#define REDIS_SELECTDB 254
static void zrevrankCommand(redisClient *c);
static void hsetCommand(redisClient *c);
static void hgetCommand(redisClient *c);
+static void hdelCommand(redisClient *c);
static void zremrangebyrankCommand(redisClient *c);
static void zunionCommand(redisClient *c);
static void zinterCommand(redisClient *c);
{"zrevrank",zrevrankCommand,3,REDIS_CMD_INLINE,1,1,1},
{"hset",hsetCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,1,1},
{"hget",hgetCommand,3,REDIS_CMD_BULK,1,1,1},
+ {"hdel",hdelCommand,3,REDIS_CMD_BULK,1,1,1},
{"incrby",incrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,1,1,1},
{"decrby",decrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,1,1,1},
{"getset",getsetCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,1,1},
return;
}
}
+ /* We want to convert the zipmap into an hash table right now if the
+ * entry to be added is too big. Note that we check if the object
+ * is integer encoded before to try fetching the length in the test below.
+ * This is because integers are small, but currently stringObjectLen()
+ * performs a slow conversion: not worth it. */
+ if (o->encoding == REDIS_ENCODING_ZIPMAP &&
+ ((c->argv[2]->encoding == REDIS_ENCODING_RAW &&
+ sdslen(c->argv[2]->ptr) > server.hash_max_zipmap_value) ||
+ (c->argv[3]->encoding == REDIS_ENCODING_RAW &&
+ sdslen(c->argv[3]->ptr) > server.hash_max_zipmap_value)))
+ {
+ convertToRealHash(o);
+ }
+
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
unsigned char *zm = o->ptr;
robj *valobj = getDecodedObject(c->argv[3]);
valobj->ptr,sdslen(valobj->ptr),&update);
decrRefCount(valobj);
o->ptr = zm;
+
+ /* And here there is the second check for hash conversion...
+ * we want to do it only if the operation was not just an update as
+ * zipmapLen() is O(N). */
+ if (!update && zipmapLen(zm) > server.hash_max_zipmap_entries)
+ convertToRealHash(o);
} else {
+ tryObjectEncoding(c->argv[2]);
+ /* note that c->argv[3] is already encoded, as the latest arg
+ * of a bulk command is always integer encoded if possible. */
if (dictAdd(o->ptr,c->argv[2],c->argv[3]) == DICT_OK) {
incrRefCount(c->argv[2]);
} else {
}
}
+static void hdelCommand(redisClient *c) {
+ robj *o = lookupKeyRead(c->db,c->argv[1]);
+
+ if (o == NULL) {
+ addReply(c,shared.czero);
+ return;
+ } else {
+ int deleted = 0;
+
+ if (o->type != REDIS_HASH) {
+ addReply(c,shared.wrongtypeerr);
+ return;
+ }
+
+ if (o->encoding == REDIS_ENCODING_ZIPMAP) {
+ o->ptr = zipmapDel((unsigned char*) o->ptr,
+ (unsigned char*) c->argv[2]->ptr,
+ sdslen(c->argv[2]->ptr), &deleted);
+ } else {
+ deleted = dictDelete((dict*)o->ptr,c->argv[2]) == DICT_OK;
+ }
+ addReply(c,deleted ? shared.cone : shared.czero);
+ }
+}
+
static void convertToRealHash(robj *o) {
unsigned char *key, *val, *p, *zm = o->ptr;
unsigned int klen, vlen;
time_t uptime = time(NULL)-server.stat_starttime;
int j;
char hmem[64];
+
+ server.hash_max_zipmap_entries = REDIS_HASH_MAX_ZIPMAP_ENTRIES;
+ server.hash_max_zipmap_value = REDIS_HASH_MAX_ZIPMAP_VALUE;
bytesToHuman(hmem,zmalloc_used_memory());
info = sdscatprintf(sdsempty(),
"bgrewriteaof_in_progress:%d\r\n"
"total_connections_received:%lld\r\n"
"total_commands_processed:%lld\r\n"
+ "hash_max_zipmap_entries:%ld\r\n"
+ "hash_max_zipmap_value:%ld\r\n"
"vm_enabled:%d\r\n"
"role:%s\r\n"
,REDIS_VERSION,
server.bgrewritechildpid != -1,
server.stat_numconnections,
server.stat_numcommands,
+ server.hash_max_zipmap_entries,
+ server.hash_max_zipmap_value,
server.vm_enabled != 0,
server.masterhost == NULL ? "master" : "slave"
);
}
/* Write an object into a file in the bulk format $<count>\r\n<payload>\r\n */
-static int fwriteBulk(FILE *fp, robj *obj) {
+static int fwriteBulkObject(FILE *fp, robj *obj) {
char buf[128];
int decrrc = 0;
return 0;
}
+/* Write binary-safe string into a file in the bulkformat
+ * $<count>\r\n<payload>\r\n */
+static int fwriteBulkString(FILE *fp, char *s, unsigned long len) {
+ char buf[128];
+
+ snprintf(buf,sizeof(buf),"$%ld\r\n",(unsigned long)len);
+ if (fwrite(buf,strlen(buf),1,fp) == 0) return 0;
+ if (len && fwrite(s,len,1,fp) == 0) return 0;
+ if (fwrite("\r\n",2,1,fp) == 0) return 0;
+ return 1;
+}
+
/* Write a double value in bulk format $<count>\r\n<payload>\r\n */
static int fwriteBulkDouble(FILE *fp, double d) {
char buf[128], dbuf[128];
char cmd[]="*3\r\n$3\r\nSET\r\n";
if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
/* Key and value */
- if (fwriteBulk(fp,key) == 0) goto werr;
- if (fwriteBulk(fp,o) == 0) goto werr;
+ if (fwriteBulkObject(fp,key) == 0) goto werr;
+ if (fwriteBulkObject(fp,o) == 0) goto werr;
} else if (o->type == REDIS_LIST) {
/* Emit the RPUSHes needed to rebuild the list */
list *list = o->ptr;
robj *eleobj = listNodeValue(ln);
if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
- if (fwriteBulk(fp,key) == 0) goto werr;
- if (fwriteBulk(fp,eleobj) == 0) goto werr;
+ if (fwriteBulkObject(fp,key) == 0) goto werr;
+ if (fwriteBulkObject(fp,eleobj) == 0) goto werr;
}
} else if (o->type == REDIS_SET) {
/* Emit the SADDs needed to rebuild the set */
robj *eleobj = dictGetEntryKey(de);
if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
- if (fwriteBulk(fp,key) == 0) goto werr;
- if (fwriteBulk(fp,eleobj) == 0) goto werr;
+ if (fwriteBulkObject(fp,key) == 0) goto werr;
+ if (fwriteBulkObject(fp,eleobj) == 0) goto werr;
}
dictReleaseIterator(di);
} else if (o->type == REDIS_ZSET) {
double *score = dictGetEntryVal(de);
if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
- if (fwriteBulk(fp,key) == 0) goto werr;
+ if (fwriteBulkObject(fp,key) == 0) goto werr;
if (fwriteBulkDouble(fp,*score) == 0) goto werr;
- if (fwriteBulk(fp,eleobj) == 0) goto werr;
+ if (fwriteBulkObject(fp,eleobj) == 0) goto werr;
}
dictReleaseIterator(di);
+ } else if (o->type == REDIS_HASH) {
+ char cmd[]="*4\r\n$4\r\nHSET\r\n";
+
+ /* Emit the HSETs needed to rebuild the hash */
+ if (o->encoding == REDIS_ENCODING_ZIPMAP) {
+ unsigned char *p = zipmapRewind(o->ptr);
+ unsigned char *field, *val;
+ unsigned int flen, vlen;
+
+ while((p = zipmapNext(p,&field,&flen,&val,&vlen)) != NULL) {
+ if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
+ if (fwriteBulkObject(fp,key) == 0) goto werr;
+ if (fwriteBulkString(fp,(char*)field,flen) == -1)
+ return -1;
+ if (fwriteBulkString(fp,(char*)val,vlen) == -1)
+ return -1;
+ }
+ } else {
+ dictIterator *di = dictGetIterator(o->ptr);
+ dictEntry *de;
+
+ while((de = dictNext(di)) != NULL) {
+ robj *field = dictGetEntryKey(de);
+ robj *val = dictGetEntryVal(de);
+
+ if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
+ if (fwriteBulkObject(fp,key) == 0) goto werr;
+ if (fwriteBulkObject(fp,field) == -1) return -1;
+ if (fwriteBulkObject(fp,val) == -1) return -1;
+ }
+ dictReleaseIterator(di);
+ }
} else {
redisAssert(0 != 0);
}
/* If this key is already expired skip it */
if (expiretime < now) continue;
if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
- if (fwriteBulk(fp,key) == 0) goto werr;
+ if (fwriteBulkObject(fp,key) == 0) goto werr;
if (fwriteBulkLong(fp,expiretime) == 0) goto werr;
}
if (swapped) decrRefCount(o);
val = dictGetEntryVal(de);
if (!server.vm_enabled || (key->storage == REDIS_VM_MEMORY ||
key->storage == REDIS_VM_SWAPPING)) {
+ char *strenc;
+ char buf[128];
+
+ if (val->encoding < (sizeof(strencoding)/sizeof(char*))) {
+ strenc = strencoding[val->encoding];
+ } else {
+ snprintf(buf,64,"unknown encoding %d\n", val->encoding);
+ strenc = buf;
+ }
addReplySds(c,sdscatprintf(sdsempty(),
"+Key at:%p refcount:%d, value at:%p refcount:%d "
- "encoding:%d serializedlength:%lld\r\n",
+ "encoding:%s serializedlength:%lld\r\n",
(void*)key, key->refcount, (void*)val, val->refcount,
- val->encoding, (long long) rdbSavedObjectLen(val,NULL)));
+ strenc, (long long) rdbSavedObjectLen(val,NULL)));
} else {
addReplySds(c,sdscatprintf(sdsempty(),
"+Key at:%p refcount:%d, value swapped at: page %llu "