diff --git a/src/t_list.c b/src/t_list.c index c705f8e81f7..1740b8d0269 100644 --- a/src/t_list.c +++ b/src/t_list.c @@ -604,12 +604,16 @@ void lsetCommand(client *c) { if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK)) return; - listTypeTryConversionAppend(o, c->argv, 3, 3, NULL, NULL); + int old_encoding = o->encoding; if (listTypeReplaceAtIndex(o, index, value)) { - /* We might replace a big item with a small one or vice versa, but we've - * already handled the growing case in listTypeTryConversionAppend() - * above, so here we just need to try the conversion for shrinking. */ - listTypeTryConversion(o, LIST_CONV_SHRINKING, NULL, NULL); + /* LSET replaces an existing element, so conversion decisions must be + * based on the final list state rather than treating the new value as + * an appended element. A listpack may need to grow into a quicklist, + * while a quicklist may shrink into a listpack. */ + if (old_encoding == OBJ_ENCODING_LISTPACK) + listTypeTryConversion(o, LIST_CONV_GROWING, NULL, NULL); + else + listTypeTryConversion(o, LIST_CONV_SHRINKING, NULL, NULL); signalModifiedKey(c, c->db, c->argv[1]); notifyKeyspaceEvent(NOTIFY_LIST, "lset", c->argv[1], c->db->id); server.dirty++; diff --git a/tests/unit/type/list.tcl b/tests/unit/type/list.tcl index 375737de24a..ba6b3bc24c5 100644 --- a/tests/unit/type/list.tcl +++ b/tests/unit/type/list.tcl @@ -2100,6 +2100,26 @@ foreach {pop} {BLPOP BLMPOP_RIGHT} { } foreach {max_lp_size large} "3 $largevalue(listpack) -1 $largevalue(quicklist)" { + test "List LSET keeps listpack encoding when replacement stays within limits - $max_lp_size" { + set origin_conf [config_get_set list-max-listpack-size $max_lp_size] + + create_listpack lst "a b c" + r LSET lst 0 x + assert_encoding listpack lst + + r config set list-max-listpack-size $origin_conf + } + + test "List LSET converts listpack when replacement exceeds safety limit - $max_lp_size" { + set origin_conf [config_get_set list-max-listpack-size $max_lp_size] + + create_listpack lst "a" + r LSET lst 0 [string repeat x 9000] + assert_encoding quicklist lst + + r config set list-max-listpack-size $origin_conf + } + test "List listpack -> quicklist encoding conversion" { set origin_conf [config_get_set list-max-listpack-size $max_lp_size] @@ -2113,10 +2133,15 @@ foreach {pop} {BLPOP BLMPOP_RIGHT} { r LINSERT lst after b $large assert_encoding quicklist lst - # LSET + # LSET replaces an element, so it does not exceed count limits. + # Size-based limits can still force conversion for large values. create_listpack lst "a b c" r LSET lst 0 $large - assert_encoding quicklist lst + if {$max_lp_size == 3} { + assert_encoding listpack lst + } else { + assert_encoding quicklist lst + } # LMOVE create_quicklist lsrc{t} "a b c $large"