X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/5de9ad7c85fb114be03fe158bd5e102515dc5d5b..bae2c7ec6b2a65218b6c5b3d6f434325ef58edc6:/redis.c?ds=inline diff --git a/redis.c b/redis.c index 91411088..b45b8618 100644 --- a/redis.c +++ b/redis.c @@ -127,6 +127,10 @@ #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 @@ -677,6 +681,7 @@ static void zrankCommand(redisClient *c); 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); @@ -740,6 +745,7 @@ static struct redisCommand cmdTable[] = { {"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}, @@ -5926,6 +5932,20 @@ static void hsetCommand(redisClient *c) { 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]); @@ -5934,7 +5954,16 @@ static void hsetCommand(redisClient *c) { 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 { @@ -5989,6 +6018,31 @@ static void hgetCommand(redisClient *c) { } } +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; @@ -6418,6 +6472,9 @@ static sds genRedisInfoString(void) { 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(), @@ -6438,6 +6495,8 @@ static sds genRedisInfoString(void) { "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, @@ -6457,6 +6516,8 @@ static sds genRedisInfoString(void) { 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" ); @@ -7551,7 +7612,7 @@ fmterr: } /* Write an object into a file in the bulk format $\r\n\r\n */ -static int fwriteBulk(FILE *fp, robj *obj) { +static int fwriteBulkObject(FILE *fp, robj *obj) { char buf[128]; int decrrc = 0; @@ -7576,6 +7637,18 @@ err: return 0; } +/* Write binary-safe string into a file in the bulkformat + * $\r\n\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 $\r\n\r\n */ static int fwriteBulkDouble(FILE *fp, double d) { char buf[128], dbuf[128]; @@ -7658,8 +7731,8 @@ static int rewriteAppendOnlyFile(char *filename) { 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; @@ -7672,8 +7745,8 @@ static int rewriteAppendOnlyFile(char *filename) { 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 */ @@ -7686,8 +7759,8 @@ static int rewriteAppendOnlyFile(char *filename) { 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) { @@ -7702,11 +7775,43 @@ static int rewriteAppendOnlyFile(char *filename) { 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); } @@ -7716,7 +7821,7 @@ static int rewriteAppendOnlyFile(char *filename) { /* 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); @@ -8885,11 +8990,20 @@ static void debugCommand(redisClient *c) { 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 "