mirror of
https://github.com/redis/redis.git
synced 2026-05-28 04:02:46 -04:00
zslUpdateScore equal-score fast path
This commit is contained in:
parent
ba1a4b2c8f
commit
783d4e28d3
2 changed files with 44 additions and 6 deletions
26
src/t_zset.c
26
src/t_zset.c
|
|
@ -389,17 +389,31 @@ static void zslDelete(zskiplist *zsl, zskiplistNode *node) {
|
|||
zslFreeNode(zsl, node);
|
||||
}
|
||||
|
||||
/* Returns true if node would still be strictly between its level-0 neighbors
|
||||
* after changing its score. The sorted-set order is (score, element), so equal
|
||||
* scores still need the lexicographic tie-breaker. */
|
||||
static int zslNodeCanKeepPosition(zskiplistNode *node, double newscore) {
|
||||
sds ele = zslGetNodeElement(node);
|
||||
zskiplistNode *prev = node->backward;
|
||||
zskiplistNode *next = node->level[0].forward;
|
||||
|
||||
if (prev != NULL && zslCompareWithNode(newscore, ele, prev) <= 0)
|
||||
return 0;
|
||||
if (next != NULL && zslCompareWithNode(newscore, ele, next) >= 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Update the score of an element inside the sorted set skiplist.
|
||||
* If the new score would keep the node in its current position, updates in-place and returns NULL.
|
||||
* Otherwise, unlinks the node, updates score, reinserts at correct position, and returns node.
|
||||
* Anyway, the node pointer stays the same (no dict update needed). */
|
||||
* If the new score would keep the node in its current position, update it
|
||||
* in-place. Otherwise, unlink the node, update the score, and reinsert it at
|
||||
* the correct position. The node pointer stays the same (no dict update
|
||||
* needed). */
|
||||
static void zslUpdateScore(zskiplist *zsl, zskiplistNode *node, double newscore) {
|
||||
/* Fast path: if the node, after the score update, would be still exactly
|
||||
* at the same position, we can just update the score without
|
||||
* actually removing and re-inserting the element in the skiplist. */
|
||||
if ((node->backward == NULL || node->backward->score < newscore) &&
|
||||
(node->level[0].forward == NULL || node->level[0].forward->score > newscore))
|
||||
{
|
||||
if (zslNodeCanKeepPosition(node, newscore)) {
|
||||
node->score = newscore;
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,6 +127,30 @@ start_server {tags {"zset"}} {
|
|||
assert_equal {y x z} [r zrange ztmp 0 -1]
|
||||
}
|
||||
|
||||
test "ZSET score update with equal-score neighbor - $encoding" {
|
||||
r del ztmp
|
||||
r zadd ztmp 1 a 2 b 3 c
|
||||
r zadd ztmp 3 b
|
||||
assert_equal {a b c} [r zrange ztmp 0 -1]
|
||||
assert_equal {a 1 b 3 c 3} [r zrange ztmp 0 -1 withscores]
|
||||
|
||||
r zadd ztmp 1 b
|
||||
assert_equal {a b c} [r zrange ztmp 0 -1]
|
||||
assert_equal {a 1 b 1 c 3} [r zrange ztmp 0 -1 withscores]
|
||||
|
||||
r del ztmp
|
||||
r zadd ztmp 1 a 2 c 3 b
|
||||
r zadd ztmp 3 c
|
||||
assert_equal {a b c} [r zrange ztmp 0 -1]
|
||||
assert_equal {a 1 b 3 c 3} [r zrange ztmp 0 -1 withscores]
|
||||
|
||||
r del ztmp
|
||||
r zadd ztmp 1 b 2 a 3 c
|
||||
r zadd ztmp 1 a
|
||||
assert_equal {a b c} [r zrange ztmp 0 -1]
|
||||
assert_equal {a 1 b 1 c 3} [r zrange ztmp 0 -1 withscores]
|
||||
}
|
||||
|
||||
test "ZSET element can't be set to NaN with ZADD - $encoding" {
|
||||
assert_error "*not*float*" {r zadd myzset nan abc}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue