Unverified Commit 05aed4ca authored by Filipe Oliveira (Redis)'s avatar Filipe Oliveira (Redis) Committed by GitHub
Browse files

Optimize SET/INCR*/DECR*/SETRANGE/APPEND by reducing duplicate computation (#13505)



- Avoid addReplyLongLong (which converts back to string) the value we
already have as a robj, by using addReplyProto + addReply
- Avoid doing dbFind Twice for the same dictEntry on
INCR*/DECR*/SETRANGE/APPEND commands.
- Avoid multiple sdslen calls with the same input on setrangeCommand and
appendCommand
- Introduce setKeyWithDictEntry, which is like setKey(), but accepts an
optional dictEntry input: Avoids the second dictFind in SET command

---------
Co-authored-by: default avatardebing.sun <debing.sun@redis.com>
parent de7f2f87
...@@ -49,6 +49,9 @@ void updateLFU(robj *val) { ...@@ -49,6 +49,9 @@ void updateLFU(robj *val) {
* found in the specified DB. This function implements the functionality of * found in the specified DB. This function implements the functionality of
* lookupKeyRead(), lookupKeyWrite() and their ...WithFlags() variants. * lookupKeyRead(), lookupKeyWrite() and their ...WithFlags() variants.
* *
* 'deref' is an optional output dictEntry reference argument, to get the
* associated dictEntry* of the key in case the key is found.
*
* Side-effects of calling this function: * Side-effects of calling this function:
* *
* 1. A key gets expired if it reached it's TTL. * 1. A key gets expired if it reached it's TTL.
...@@ -72,7 +75,7 @@ void updateLFU(robj *val) { ...@@ -72,7 +75,7 @@ void updateLFU(robj *val) {
* Even if the key expiry is master-driven, we can correctly report a key is * Even if the key expiry is master-driven, we can correctly report a key is
* expired on replicas even if the master is lagging expiring our key via DELs * expired on replicas even if the master is lagging expiring our key via DELs
* in the replication link. */ * in the replication link. */
robj *lookupKey(redisDb *db, robj *key, int flags) { robj *lookupKey(redisDb *db, robj *key, int flags, dictEntry **deref) {
dictEntry *de = dbFind(db, key->ptr); dictEntry *de = dbFind(db, key->ptr);
robj *val = NULL; robj *val = NULL;
if (de) { if (de) {
...@@ -123,6 +126,7 @@ robj *lookupKey(redisDb *db, robj *key, int flags) { ...@@ -123,6 +126,7 @@ robj *lookupKey(redisDb *db, robj *key, int flags) {
/* TODO: Use separate misses stats and notify event for WRITE */ /* TODO: Use separate misses stats and notify event for WRITE */
} }
if (val && deref) *deref = de;
return val; return val;
} }
...@@ -137,7 +141,7 @@ robj *lookupKey(redisDb *db, robj *key, int flags) { ...@@ -137,7 +141,7 @@ robj *lookupKey(redisDb *db, robj *key, int flags) {
* the key. */ * the key. */
robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) { robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
serverAssert(!(flags & LOOKUP_WRITE)); serverAssert(!(flags & LOOKUP_WRITE));
return lookupKey(db, key, flags); return lookupKey(db, key, flags, NULL);
} }
/* Like lookupKeyReadWithFlags(), but does not use any flag, which is the /* Like lookupKeyReadWithFlags(), but does not use any flag, which is the
...@@ -153,13 +157,20 @@ robj *lookupKeyRead(redisDb *db, robj *key) { ...@@ -153,13 +157,20 @@ robj *lookupKeyRead(redisDb *db, robj *key) {
* Returns the linked value object if the key exists or NULL if the key * Returns the linked value object if the key exists or NULL if the key
* does not exist in the specified DB. */ * does not exist in the specified DB. */
robj *lookupKeyWriteWithFlags(redisDb *db, robj *key, int flags) { robj *lookupKeyWriteWithFlags(redisDb *db, robj *key, int flags) {
return lookupKey(db, key, flags | LOOKUP_WRITE); return lookupKey(db, key, flags | LOOKUP_WRITE, NULL);
} }
robj *lookupKeyWrite(redisDb *db, robj *key) { robj *lookupKeyWrite(redisDb *db, robj *key) {
return lookupKeyWriteWithFlags(db, key, LOOKUP_NONE); return lookupKeyWriteWithFlags(db, key, LOOKUP_NONE);
} }
/* Like lookupKeyWrite(), but accepts an optional dictEntry input,
* which can be used if we already have one, thus saving the dbFind call.
*/
robj *lookupKeyWriteWithDictEntry(redisDb *db, robj *key, dictEntry **deref) {
return lookupKey(db, key, LOOKUP_NONE | LOOKUP_WRITE, deref);
}
robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply) { robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply) {
robj *o = lookupKeyRead(c->db, key); robj *o = lookupKeyRead(c->db, key);
if (!o) addReplyOrErrorObject(c, reply); if (!o) addReplyOrErrorObject(c, reply);
...@@ -294,6 +305,14 @@ void dbReplaceValue(redisDb *db, robj *key, robj *val) { ...@@ -294,6 +305,14 @@ void dbReplaceValue(redisDb *db, robj *key, robj *val) {
dbSetValue(db, key, val, 0, NULL); dbSetValue(db, key, val, 0, NULL);
} }
/* Replace an existing key with a new value, we just replace value and don't
* emit any events.
* The dictEntry input is optional, can be used if we already have one.
*/
void dbReplaceValueWithDictEntry(redisDb *db, robj *key, robj *val, dictEntry *de) {
dbSetValue(db, key, val, 0, de);
}
/* High level Set operation. This function can be used in order to set /* High level Set operation. This function can be used in order to set
* a key, whatever it was existing or not, to a new object. * a key, whatever it was existing or not, to a new object.
* *
...@@ -308,6 +327,12 @@ void dbReplaceValue(redisDb *db, robj *key, robj *val) { ...@@ -308,6 +327,12 @@ void dbReplaceValue(redisDb *db, robj *key, robj *val) {
* The client 'c' argument may be set to NULL if the operation is performed * The client 'c' argument may be set to NULL if the operation is performed
* in a context where there is no clear client performing the operation. */ * in a context where there is no clear client performing the operation. */
void setKey(client *c, redisDb *db, robj *key, robj *val, int flags) { void setKey(client *c, redisDb *db, robj *key, robj *val, int flags) {
setKeyWithDictEntry(c,db,key,val,flags,NULL);
}
/* Like setKey(), but accepts an optional dictEntry input,
* which can be used if we already have one, thus saving the dictFind call. */
void setKeyWithDictEntry(client *c, redisDb *db, robj *key, robj *val, int flags, dictEntry *de) {
int keyfound = 0; int keyfound = 0;
if (flags & SETKEY_ALREADY_EXIST) if (flags & SETKEY_ALREADY_EXIST)
...@@ -322,7 +347,7 @@ void setKey(client *c, redisDb *db, robj *key, robj *val, int flags) { ...@@ -322,7 +347,7 @@ void setKey(client *c, redisDb *db, robj *key, robj *val, int flags) {
} else if (keyfound<0) { } else if (keyfound<0) {
dbAddInternal(db,key,val,1); dbAddInternal(db,key,val,1);
} else { } else {
dbSetValue(db,key,val,1,NULL); dbSetValue(db,key,val,1,de);
} }
incrRefCount(val); incrRefCount(val);
if (!(flags & SETKEY_KEEPTTL)) removeExpire(db,key); if (!(flags & SETKEY_KEEPTTL)) removeExpire(db,key);
...@@ -452,12 +477,18 @@ int dbDelete(redisDb *db, robj *key) { ...@@ -452,12 +477,18 @@ int dbDelete(redisDb *db, robj *key) {
* using an sdscat() call to append some data, or anything else. * using an sdscat() call to append some data, or anything else.
*/ */
robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o) { robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o) {
return dbUnshareStringValueWithDictEntry(db,key,o,NULL);
}
/* Like dbUnshareStringValue(), but accepts a optional dictEntry,
* which can be used if we already have one, thus saving the dbFind call. */
robj *dbUnshareStringValueWithDictEntry(redisDb *db, robj *key, robj *o, dictEntry *de) {
serverAssert(o->type == OBJ_STRING); serverAssert(o->type == OBJ_STRING);
if (o->refcount != 1 || o->encoding != OBJ_ENCODING_RAW) { if (o->refcount != 1 || o->encoding != OBJ_ENCODING_RAW) {
robj *decoded = getDecodedObject(o); robj *decoded = getDecodedObject(o);
o = createRawStringObject(decoded->ptr, sdslen(decoded->ptr)); o = createRawStringObject(decoded->ptr, sdslen(decoded->ptr));
decrRefCount(decoded); decrRefCount(decoded);
dbReplaceValue(db,key,o); dbReplaceValueWithDictEntry(db,key,o,de);
} }
return o; return o;
} }
...@@ -1828,16 +1859,23 @@ int removeExpire(redisDb *db, robj *key) { ...@@ -1828,16 +1859,23 @@ int removeExpire(redisDb *db, robj *key) {
return kvstoreDictDelete(db->expires, getKeySlot(key->ptr), key->ptr) == DICT_OK; return kvstoreDictDelete(db->expires, getKeySlot(key->ptr), key->ptr) == DICT_OK;
} }
/* Set an expire to the specified key. If the expire is set in the context /* Set an expire to the specified key. If the expire is set in the context
* of an user calling a command 'c' is the client, otherwise 'c' is set * of an user calling a command 'c' is the client, otherwise 'c' is set
* to NULL. The 'when' parameter is the absolute unix time in milliseconds * to NULL. The 'when' parameter is the absolute unix time in milliseconds
* after which the key will no longer be considered valid. */ * after which the key will no longer be considered valid. */
void setExpire(client *c, redisDb *db, robj *key, long long when) { void setExpire(client *c, redisDb *db, robj *key, long long when) {
dictEntry *kde, *de, *existing; setExpireWithDictEntry(c,db,key,when,NULL);
}
/* Like setExpire(), but accepts an optional dictEntry input,
* which can be used if we already have one, thus saving the kvstoreDictFind call. */
void setExpireWithDictEntry(client *c, redisDb *db, robj *key, long long when, dictEntry *kde) {
dictEntry *de, *existing;
/* Reuse the sds from the main dict in the expire dict */ /* Reuse the sds from the main dict in the expire dict */
int slot = getKeySlot(key->ptr); int slot = getKeySlot(key->ptr);
kde = kvstoreDictFind(db->keys, slot, key->ptr); if (!kde) kde = kvstoreDictFind(db->keys, slot, key->ptr);
serverAssertWithInfo(NULL,key,kde != NULL); serverAssertWithInfo(NULL,key,kde != NULL);
de = kvstoreDictAddRaw(db->expires, slot, dictGetKey(kde), &existing); de = kvstoreDictAddRaw(db->expires, slot, dictGetKey(kde), &existing);
if (existing) { if (existing) {
......
...@@ -970,6 +970,12 @@ void addReplyLongLong(client *c, long long ll) { ...@@ -970,6 +970,12 @@ void addReplyLongLong(client *c, long long ll) {
} }
} }
void addReplyLongLongFromStr(client *c, robj *str) {
addReplyProto(c,":",1);
addReply(c,str);
addReplyProto(c,"\r\n",2);
}
void addReplyAggregateLen(client *c, long length, int prefix) { void addReplyAggregateLen(client *c, long length, int prefix) {
serverAssert(length >= 0); serverAssert(length >= 0);
if (prepareClientToWrite(c) != C_OK) return; if (prepareClientToWrite(c) != C_OK) return;
......
...@@ -2631,6 +2631,7 @@ void addReplyDouble(client *c, double d); ...@@ -2631,6 +2631,7 @@ void addReplyDouble(client *c, double d);
void addReplyBigNum(client *c, const char *num, size_t len); void addReplyBigNum(client *c, const char *num, size_t len);
void addReplyHumanLongDouble(client *c, long double d); void addReplyHumanLongDouble(client *c, long double d);
void addReplyLongLong(client *c, long long ll); void addReplyLongLong(client *c, long long ll);
void addReplyLongLongFromStr(client *c, robj* str);
void addReplyArrayLen(client *c, long length); void addReplyArrayLen(client *c, long length);
void addReplyMapLen(client *c, long length); void addReplyMapLen(client *c, long length);
void addReplySetLen(client *c, long length); void addReplySetLen(client *c, long length);
...@@ -3366,10 +3367,12 @@ void propagateDeletion(redisDb *db, robj *key, int lazy); ...@@ -3366,10 +3367,12 @@ void propagateDeletion(redisDb *db, robj *key, int lazy);
int keyIsExpired(redisDb *db, robj *key); int keyIsExpired(redisDb *db, robj *key);
long long getExpire(redisDb *db, robj *key); long long getExpire(redisDb *db, robj *key);
void setExpire(client *c, redisDb *db, robj *key, long long when); void setExpire(client *c, redisDb *db, robj *key, long long when);
void setExpireWithDictEntry(client *c, redisDb *db, robj *key, long long when, dictEntry *kde);
int checkAlreadyExpired(long long when); int checkAlreadyExpired(long long when);
int parseExtendedExpireArgumentsOrReply(client *c, int *flags); int parseExtendedExpireArgumentsOrReply(client *c, int *flags);
robj *lookupKeyRead(redisDb *db, robj *key); robj *lookupKeyRead(redisDb *db, robj *key);
robj *lookupKeyWrite(redisDb *db, robj *key); robj *lookupKeyWrite(redisDb *db, robj *key);
robj *lookupKeyWriteWithDictEntry(redisDb *db, robj *key, dictEntry **deref);
robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply); robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply);
robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply); robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply);
robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags); robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags);
...@@ -3389,6 +3392,7 @@ int objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle, ...@@ -3389,6 +3392,7 @@ int objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
dictEntry *dbAdd(redisDb *db, robj *key, robj *val); dictEntry *dbAdd(redisDb *db, robj *key, robj *val);
int dbAddRDBLoad(redisDb *db, sds key, robj *val); int dbAddRDBLoad(redisDb *db, sds key, robj *val);
void dbReplaceValue(redisDb *db, robj *key, robj *val); void dbReplaceValue(redisDb *db, robj *key, robj *val);
void dbReplaceValueWithDictEntry(redisDb *db, robj *key, robj *val, dictEntry *de);
#define SETKEY_KEEPTTL 1 #define SETKEY_KEEPTTL 1
#define SETKEY_NO_SIGNAL 2 #define SETKEY_NO_SIGNAL 2
...@@ -3396,11 +3400,14 @@ void dbReplaceValue(redisDb *db, robj *key, robj *val); ...@@ -3396,11 +3400,14 @@ void dbReplaceValue(redisDb *db, robj *key, robj *val);
#define SETKEY_DOESNT_EXIST 8 #define SETKEY_DOESNT_EXIST 8
#define SETKEY_ADD_OR_UPDATE 16 /* Key most likely doesn't exists */ #define SETKEY_ADD_OR_UPDATE 16 /* Key most likely doesn't exists */
void setKey(client *c, redisDb *db, robj *key, robj *val, int flags); void setKey(client *c, redisDb *db, robj *key, robj *val, int flags);
void setKeyWithDictEntry(client *c, redisDb *db, robj *key, robj *val, int flags, dictEntry *de);
robj *dbRandomKey(redisDb *db); robj *dbRandomKey(redisDb *db);
int dbGenericDelete(redisDb *db, robj *key, int async, int flags); int dbGenericDelete(redisDb *db, robj *key, int async, int flags);
int dbSyncDelete(redisDb *db, robj *key); int dbSyncDelete(redisDb *db, robj *key);
int dbDelete(redisDb *db, robj *key); int dbDelete(redisDb *db, robj *key);
robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o); robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o);
robj *dbUnshareStringValueWithDictEntry(redisDb *db, robj *key, robj *o, dictEntry *de);
#define EMPTYDB_NO_FLAGS 0 /* No flags. */ #define EMPTYDB_NO_FLAGS 0 /* No flags. */
#define EMPTYDB_ASYNC (1<<0) /* Reclaim memory in another thread. */ #define EMPTYDB_ASYNC (1<<0) /* Reclaim memory in another thread. */
......
...@@ -73,7 +73,8 @@ void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, ...@@ -73,7 +73,8 @@ void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire,
if (getGenericCommand(c) == C_ERR) return; if (getGenericCommand(c) == C_ERR) return;
} }
found = (lookupKeyWrite(c->db,key) != NULL); dictEntry *de = NULL;
found = (lookupKeyWriteWithDictEntry(c->db,key,&de) != NULL);
if ((flags & OBJ_SET_NX && found) || if ((flags & OBJ_SET_NX && found) ||
(flags & OBJ_SET_XX && !found)) (flags & OBJ_SET_XX && !found))
...@@ -88,12 +89,12 @@ void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, ...@@ -88,12 +89,12 @@ void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire,
setkey_flags |= ((flags & OBJ_KEEPTTL) || expire) ? SETKEY_KEEPTTL : 0; setkey_flags |= ((flags & OBJ_KEEPTTL) || expire) ? SETKEY_KEEPTTL : 0;
setkey_flags |= found ? SETKEY_ALREADY_EXIST : SETKEY_DOESNT_EXIST; setkey_flags |= found ? SETKEY_ALREADY_EXIST : SETKEY_DOESNT_EXIST;
setKey(c,c->db,key,val,setkey_flags); setKeyWithDictEntry(c,c->db,key,val,setkey_flags,de);
server.dirty++; server.dirty++;
notifyKeyspaceEvent(NOTIFY_STRING,"set",key,c->db->id); notifyKeyspaceEvent(NOTIFY_STRING,"set",key,c->db->id);
if (expire) { if (expire) {
setExpire(c,c->db,key,milliseconds); setExpireWithDictEntry(c,c->db,key,milliseconds,de);
/* Propagate as SET Key Value PXAT millisecond-timestamp if there is /* Propagate as SET Key Value PXAT millisecond-timestamp if there is
* EX/PX/EXAT flag. */ * EX/PX/EXAT flag. */
if (!(flags & OBJ_PXAT)) { if (!(flags & OBJ_PXAT)) {
...@@ -422,6 +423,7 @@ void setrangeCommand(client *c) { ...@@ -422,6 +423,7 @@ void setrangeCommand(client *c) {
robj *o; robj *o;
long offset; long offset;
sds value = c->argv[3]->ptr; sds value = c->argv[3]->ptr;
const size_t value_len = sdslen(value);
if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != C_OK) if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != C_OK)
return; return;
...@@ -431,19 +433,20 @@ void setrangeCommand(client *c) { ...@@ -431,19 +433,20 @@ void setrangeCommand(client *c) {
return; return;
} }
o = lookupKeyWrite(c->db,c->argv[1]); dictEntry *de;
o = lookupKeyWriteWithDictEntry(c->db,c->argv[1],&de);
if (o == NULL) { if (o == NULL) {
/* Return 0 when setting nothing on a non-existing string */ /* Return 0 when setting nothing on a non-existing string */
if (sdslen(value) == 0) { if (value_len == 0) {
addReply(c,shared.czero); addReply(c,shared.czero);
return; return;
} }
/* Return when the resulting string exceeds allowed size */ /* Return when the resulting string exceeds allowed size */
if (checkStringLength(c,offset,sdslen(value)) != C_OK) if (checkStringLength(c,offset,value_len) != C_OK)
return; return;
o = createObject(OBJ_STRING,sdsnewlen(NULL, offset+sdslen(value))); o = createObject(OBJ_STRING,sdsnewlen(NULL, offset+value_len));
dbAdd(c->db,c->argv[1],o); dbAdd(c->db,c->argv[1],o);
} else { } else {
size_t olen; size_t olen;
...@@ -454,22 +457,22 @@ void setrangeCommand(client *c) { ...@@ -454,22 +457,22 @@ void setrangeCommand(client *c) {
/* Return existing string length when setting nothing */ /* Return existing string length when setting nothing */
olen = stringObjectLen(o); olen = stringObjectLen(o);
if (sdslen(value) == 0) { if (value_len == 0) {
addReplyLongLong(c,olen); addReplyLongLong(c,olen);
return; return;
} }
/* Return when the resulting string exceeds allowed size */ /* Return when the resulting string exceeds allowed size */
if (checkStringLength(c,offset,sdslen(value)) != C_OK) if (checkStringLength(c,offset,value_len) != C_OK)
return; return;
/* Create a copy when the object is shared or encoded. */ /* Create a copy when the object is shared or encoded. */
o = dbUnshareStringValue(c->db,c->argv[1],o); o = dbUnshareStringValueWithDictEntry(c->db,c->argv[1],o,de);
} }
if (sdslen(value) > 0) { if (value_len > 0) {
o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value)); o->ptr = sdsgrowzero(o->ptr,offset+value_len);
memcpy((char*)o->ptr+offset,value,sdslen(value)); memcpy((char*)o->ptr+offset,value,value_len);
signalModifiedKey(c,c->db,c->argv[1]); signalModifiedKey(c,c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING, notifyKeyspaceEvent(NOTIFY_STRING,
"setrange",c->argv[1],c->db->id); "setrange",c->argv[1],c->db->id);
...@@ -571,8 +574,8 @@ void msetnxCommand(client *c) { ...@@ -571,8 +574,8 @@ void msetnxCommand(client *c) {
void incrDecrCommand(client *c, long long incr) { void incrDecrCommand(client *c, long long incr) {
long long value, oldvalue; long long value, oldvalue;
robj *o, *new; robj *o, *new;
dictEntry *de;
o = lookupKeyWrite(c->db,c->argv[1]); o = lookupKeyWriteWithDictEntry(c->db,c->argv[1],&de);
if (checkType(c,o,OBJ_STRING)) return; if (checkType(c,o,OBJ_STRING)) return;
if (getLongLongFromObjectOrReply(c,o,&value,NULL) != C_OK) return; if (getLongLongFromObjectOrReply(c,o,&value,NULL) != C_OK) return;
...@@ -593,7 +596,7 @@ void incrDecrCommand(client *c, long long incr) { ...@@ -593,7 +596,7 @@ void incrDecrCommand(client *c, long long incr) {
} else { } else {
new = createStringObjectFromLongLongForValue(value); new = createStringObjectFromLongLongForValue(value);
if (o) { if (o) {
dbReplaceValue(c->db,c->argv[1],new); dbReplaceValueWithDictEntry(c->db,c->argv[1],new,de);
} else { } else {
dbAdd(c->db,c->argv[1],new); dbAdd(c->db,c->argv[1],new);
} }
...@@ -601,7 +604,7 @@ void incrDecrCommand(client *c, long long incr) { ...@@ -601,7 +604,7 @@ void incrDecrCommand(client *c, long long incr) {
signalModifiedKey(c,c->db,c->argv[1]); signalModifiedKey(c,c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,"incrby",c->argv[1],c->db->id); notifyKeyspaceEvent(NOTIFY_STRING,"incrby",c->argv[1],c->db->id);
server.dirty++; server.dirty++;
addReplyLongLong(c, value); addReplyLongLongFromStr(c,new);
} }
void incrCommand(client *c) { void incrCommand(client *c) {
...@@ -635,7 +638,8 @@ void incrbyfloatCommand(client *c) { ...@@ -635,7 +638,8 @@ void incrbyfloatCommand(client *c) {
long double incr, value; long double incr, value;
robj *o, *new; robj *o, *new;
o = lookupKeyWrite(c->db,c->argv[1]); dictEntry *de;
o = lookupKeyWriteWithDictEntry(c->db,c->argv[1],&de);
if (checkType(c,o,OBJ_STRING)) return; if (checkType(c,o,OBJ_STRING)) return;
if (getLongDoubleFromObjectOrReply(c,o,&value,NULL) != C_OK || if (getLongDoubleFromObjectOrReply(c,o,&value,NULL) != C_OK ||
getLongDoubleFromObjectOrReply(c,c->argv[2],&incr,NULL) != C_OK) getLongDoubleFromObjectOrReply(c,c->argv[2],&incr,NULL) != C_OK)
...@@ -648,7 +652,7 @@ void incrbyfloatCommand(client *c) { ...@@ -648,7 +652,7 @@ void incrbyfloatCommand(client *c) {
} }
new = createStringObjectFromLongDouble(value,1); new = createStringObjectFromLongDouble(value,1);
if (o) if (o)
dbReplaceValue(c->db,c->argv[1],new); dbReplaceValueWithDictEntry(c->db,c->argv[1],new,de);
else else
dbAdd(c->db,c->argv[1],new); dbAdd(c->db,c->argv[1],new);
signalModifiedKey(c,c->db,c->argv[1]); signalModifiedKey(c,c->db,c->argv[1]);
...@@ -668,7 +672,8 @@ void appendCommand(client *c) { ...@@ -668,7 +672,8 @@ void appendCommand(client *c) {
size_t totlen; size_t totlen;
robj *o, *append; robj *o, *append;
o = lookupKeyWrite(c->db,c->argv[1]); dictEntry *de;
o = lookupKeyWriteWithDictEntry(c->db,c->argv[1],&de);
if (o == NULL) { if (o == NULL) {
/* Create the key */ /* Create the key */
c->argv[2] = tryObjectEncoding(c->argv[2]); c->argv[2] = tryObjectEncoding(c->argv[2]);
...@@ -682,12 +687,13 @@ void appendCommand(client *c) { ...@@ -682,12 +687,13 @@ void appendCommand(client *c) {
/* "append" is an argument, so always an sds */ /* "append" is an argument, so always an sds */
append = c->argv[2]; append = c->argv[2];
if (checkStringLength(c,stringObjectLen(o),sdslen(append->ptr)) != C_OK) const size_t append_len = sdslen(append->ptr);
if (checkStringLength(c,stringObjectLen(o),append_len) != C_OK)
return; return;
/* Append the value */ /* Append the value */
o = dbUnshareStringValue(c->db,c->argv[1],o); o = dbUnshareStringValueWithDictEntry(c->db,c->argv[1],o,de);
o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr)); o->ptr = sdscatlen(o->ptr,append->ptr,append_len);
totlen = sdslen(o->ptr); totlen = sdslen(o->ptr);
} }
signalModifiedKey(c,c->db,c->argv[1]); signalModifiedKey(c,c->db,c->argv[1]);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment