diff --git a/src/redis_zset.cc b/src/redis_zset.cc index dbf5bdbf236..4c43968362c 100644 --- a/src/redis_zset.cc +++ b/src/redis_zset.cc @@ -5,6 +5,7 @@ #include #include #include +#include namespace Redis { @@ -28,8 +29,24 @@ rocksdb::Status ZSet::Add(const Slice &user_key, uint8_t flags, std::vectorsize(); i++) { + std::set added_member_keys; + for (int i = static_cast(mscores->size()-1); i >= 0; i--) { InternalKey(ns_key, (*mscores)[i].member, metadata.version).Encode(&member_key); + + // Fix the corner case that adds the same member which may add the score + // column family many times and cause problems in the ZRANGE command. + // + // For example, we add members with `ZADD mykey 1 a 2 a` and `ZRANGE mykey 0 1` + // return only one member(`a`) was expected but got the member `a` twice now. + // + // The root cause of this issue was the score key was composed by member and score, + // so the last one can't overwrite the previous when the score was different. + // A simple workaround was add those members with reversed order and skip the member if has added. + if (added_member_keys.find(member_key) != added_member_keys.end()) { + continue; + } + added_member_keys.insert(member_key); + if (metadata.size > 0) { std::string old_score_bytes; s = db_->Get(rocksdb::ReadOptions(), member_key, &old_score_bytes); diff --git a/tests/tcl/tests/unit/type/zset.tcl b/tests/tcl/tests/unit/type/zset.tcl index 2b037f23642..61ef8e895e7 100644 --- a/tests/tcl/tests/unit/type/zset.tcl +++ b/tests/tcl/tests/unit/type/zset.tcl @@ -24,6 +24,17 @@ start_server {tags {"zset"}} { assert_equal {y x z} [r zrange ztmp 0 -1] } + test "ZSET basic ZADD the same member with different scores - $encoding" { + r del ztmp + assert_equal 1 [r zadd ztmp 10 x 20 x] + assert_equal {x} [r zrange ztmp 0 -1] + assert_equal 20 [r zscore ztmp x] + + assert_equal 2 [r zadd ztmp 30 x 40 y 50 z] + assert_equal {x y z} [r zrange ztmp 0 -1] + assert_equal 30 [r zscore ztmp x] + } + test "ZSET element can't be set to NaN with ZADD - $encoding" { assert_error "*float*" {r zadd myzset nan abc} }