1start_server { 2 tags {"set"} 3 overrides { 4 "set-max-intset-entries" 512 5 } 6} { 7 proc create_set {key entries} { 8 r del $key 9 foreach entry $entries { r sadd $key $entry } 10 } 11 12 test {SADD, SCARD, SISMEMBER, SMEMBERS basics - regular set} { 13 create_set myset {foo} 14 assert_encoding hashtable myset 15 assert_equal 1 [r sadd myset bar] 16 assert_equal 0 [r sadd myset bar] 17 assert_equal 2 [r scard myset] 18 assert_equal 1 [r sismember myset foo] 19 assert_equal 1 [r sismember myset bar] 20 assert_equal 0 [r sismember myset bla] 21 assert_equal {bar foo} [lsort [r smembers myset]] 22 } 23 24 test {SADD, SCARD, SISMEMBER, SMEMBERS basics - intset} { 25 create_set myset {17} 26 assert_encoding intset myset 27 assert_equal 1 [r sadd myset 16] 28 assert_equal 0 [r sadd myset 16] 29 assert_equal 2 [r scard myset] 30 assert_equal 1 [r sismember myset 16] 31 assert_equal 1 [r sismember myset 17] 32 assert_equal 0 [r sismember myset 18] 33 assert_equal {16 17} [lsort [r smembers myset]] 34 } 35 36 test {SADD against non set} { 37 r lpush mylist foo 38 assert_error WRONGTYPE* {r sadd mylist bar} 39 } 40 41 test "SADD a non-integer against an intset" { 42 create_set myset {1 2 3} 43 assert_encoding intset myset 44 assert_equal 1 [r sadd myset a] 45 assert_encoding hashtable myset 46 } 47 48 test "SADD an integer larger than 64 bits" { 49 create_set myset {213244124402402314402033402} 50 assert_encoding hashtable myset 51 assert_equal 1 [r sismember myset 213244124402402314402033402] 52 } 53 54 test "SADD overflows the maximum allowed integers in an intset" { 55 r del myset 56 for {set i 0} {$i < 512} {incr i} { r sadd myset $i } 57 assert_encoding intset myset 58 assert_equal 1 [r sadd myset 512] 59 assert_encoding hashtable myset 60 } 61 62 test {Variadic SADD} { 63 r del myset 64 assert_equal 3 [r sadd myset a b c] 65 assert_equal 2 [r sadd myset A a b c B] 66 assert_equal [lsort {A a b c B}] [lsort [r smembers myset]] 67 } 68 69 test "Set encoding after DEBUG RELOAD" { 70 r del myintset myhashset mylargeintset 71 for {set i 0} {$i < 100} {incr i} { r sadd myintset $i } 72 for {set i 0} {$i < 1280} {incr i} { r sadd mylargeintset $i } 73 for {set i 0} {$i < 256} {incr i} { r sadd myhashset [format "i%03d" $i] } 74 assert_encoding intset myintset 75 assert_encoding hashtable mylargeintset 76 assert_encoding hashtable myhashset 77 78 r debug reload 79 assert_encoding intset myintset 80 assert_encoding hashtable mylargeintset 81 assert_encoding hashtable myhashset 82 } 83 84 test {SREM basics - regular set} { 85 create_set myset {foo bar ciao} 86 assert_encoding hashtable myset 87 assert_equal 0 [r srem myset qux] 88 assert_equal 1 [r srem myset foo] 89 assert_equal {bar ciao} [lsort [r smembers myset]] 90 } 91 92 test {SREM basics - intset} { 93 create_set myset {3 4 5} 94 assert_encoding intset myset 95 assert_equal 0 [r srem myset 6] 96 assert_equal 1 [r srem myset 4] 97 assert_equal {3 5} [lsort [r smembers myset]] 98 } 99 100 test {SREM with multiple arguments} { 101 r del myset 102 r sadd myset a b c d 103 assert_equal 0 [r srem myset k k k] 104 assert_equal 2 [r srem myset b d x y] 105 lsort [r smembers myset] 106 } {a c} 107 108 test {SREM variadic version with more args needed to destroy the key} { 109 r del myset 110 r sadd myset 1 2 3 111 r srem myset 1 2 3 4 5 6 7 8 112 } {3} 113 114 foreach {type} {hashtable intset} { 115 for {set i 1} {$i <= 5} {incr i} { 116 r del [format "set%d" $i] 117 } 118 for {set i 0} {$i < 200} {incr i} { 119 r sadd set1 $i 120 r sadd set2 [expr $i+195] 121 } 122 foreach i {199 195 1000 2000} { 123 r sadd set3 $i 124 } 125 for {set i 5} {$i < 200} {incr i} { 126 r sadd set4 $i 127 } 128 r sadd set5 0 129 130 # To make sure the sets are encoded as the type we are testing -- also 131 # when the VM is enabled and the values may be swapped in and out 132 # while the tests are running -- an extra element is added to every 133 # set that determines its encoding. 134 set large 200 135 if {$type eq "hashtable"} { 136 set large foo 137 } 138 139 for {set i 1} {$i <= 5} {incr i} { 140 r sadd [format "set%d" $i] $large 141 } 142 143 test "Generated sets must be encoded as $type" { 144 for {set i 1} {$i <= 5} {incr i} { 145 assert_encoding $type [format "set%d" $i] 146 } 147 } 148 149 test "SINTER with two sets - $type" { 150 assert_equal [list 195 196 197 198 199 $large] [lsort [r sinter set1 set2]] 151 } 152 153 test "SINTERSTORE with two sets - $type" { 154 r sinterstore setres set1 set2 155 assert_encoding $type setres 156 assert_equal [list 195 196 197 198 199 $large] [lsort [r smembers setres]] 157 } 158 159 test "SINTERSTORE with two sets, after a DEBUG RELOAD - $type" { 160 r debug reload 161 r sinterstore setres set1 set2 162 assert_encoding $type setres 163 assert_equal [list 195 196 197 198 199 $large] [lsort [r smembers setres]] 164 } 165 166 test "SUNION with two sets - $type" { 167 set expected [lsort -uniq "[r smembers set1] [r smembers set2]"] 168 assert_equal $expected [lsort [r sunion set1 set2]] 169 } 170 171 test "SUNIONSTORE with two sets - $type" { 172 r sunionstore setres set1 set2 173 assert_encoding $type setres 174 set expected [lsort -uniq "[r smembers set1] [r smembers set2]"] 175 assert_equal $expected [lsort [r smembers setres]] 176 } 177 178 test "SINTER against three sets - $type" { 179 assert_equal [list 195 199 $large] [lsort [r sinter set1 set2 set3]] 180 } 181 182 test "SINTERSTORE with three sets - $type" { 183 r sinterstore setres set1 set2 set3 184 assert_equal [list 195 199 $large] [lsort [r smembers setres]] 185 } 186 187 test "SUNION with non existing keys - $type" { 188 set expected [lsort -uniq "[r smembers set1] [r smembers set2]"] 189 assert_equal $expected [lsort [r sunion nokey1 set1 set2 nokey2]] 190 } 191 192 test "SDIFF with two sets - $type" { 193 assert_equal {0 1 2 3 4} [lsort [r sdiff set1 set4]] 194 } 195 196 test "SDIFF with three sets - $type" { 197 assert_equal {1 2 3 4} [lsort [r sdiff set1 set4 set5]] 198 } 199 200 test "SDIFFSTORE with three sets - $type" { 201 r sdiffstore setres set1 set4 set5 202 # When we start with intsets, we should always end with intsets. 203 if {$type eq {intset}} { 204 assert_encoding intset setres 205 } 206 assert_equal {1 2 3 4} [lsort [r smembers setres]] 207 } 208 } 209 210 test "SDIFF with first set empty" { 211 r del set1 set2 set3 212 r sadd set2 1 2 3 4 213 r sadd set3 a b c d 214 r sdiff set1 set2 set3 215 } {} 216 217 test "SDIFF with same set two times" { 218 r del set1 219 r sadd set1 a b c 1 2 3 4 5 6 220 r sdiff set1 set1 221 } {} 222 223 test "SDIFF fuzzing" { 224 for {set j 0} {$j < 100} {incr j} { 225 unset -nocomplain s 226 array set s {} 227 set args {} 228 set num_sets [expr {[randomInt 10]+1}] 229 for {set i 0} {$i < $num_sets} {incr i} { 230 set num_elements [randomInt 100] 231 r del set_$i 232 lappend args set_$i 233 while {$num_elements} { 234 set ele [randomValue] 235 r sadd set_$i $ele 236 if {$i == 0} { 237 set s($ele) x 238 } else { 239 unset -nocomplain s($ele) 240 } 241 incr num_elements -1 242 } 243 } 244 set result [lsort [r sdiff {*}$args]] 245 assert_equal $result [lsort [array names s]] 246 } 247 } 248 249 test "SINTER against non-set should throw error" { 250 r set key1 x 251 assert_error "WRONGTYPE*" {r sinter key1 noset} 252 } 253 254 test "SUNION against non-set should throw error" { 255 r set key1 x 256 assert_error "WRONGTYPE*" {r sunion key1 noset} 257 } 258 259 test "SINTER should handle non existing key as empty" { 260 r del set1 set2 set3 261 r sadd set1 a b c 262 r sadd set2 b c d 263 r sinter set1 set2 set3 264 } {} 265 266 test "SINTER with same integer elements but different encoding" { 267 r del set1 set2 268 r sadd set1 1 2 3 269 r sadd set2 1 2 3 a 270 r srem set2 a 271 assert_encoding intset set1 272 assert_encoding hashtable set2 273 lsort [r sinter set1 set2] 274 } {1 2 3} 275 276 test "SINTERSTORE against non existing keys should delete dstkey" { 277 r set setres xxx 278 assert_equal 0 [r sinterstore setres foo111 bar222] 279 assert_equal 0 [r exists setres] 280 } 281 282 test "SUNIONSTORE against non existing keys should delete dstkey" { 283 r set setres xxx 284 assert_equal 0 [r sunionstore setres foo111 bar222] 285 assert_equal 0 [r exists setres] 286 } 287 288 foreach {type contents} {hashtable {a b c} intset {1 2 3}} { 289 test "SPOP basics - $type" { 290 create_set myset $contents 291 assert_encoding $type myset 292 assert_equal $contents [lsort [list [r spop myset] [r spop myset] [r spop myset]]] 293 assert_equal 0 [r scard myset] 294 } 295 296 test "SPOP with <count>=1 - $type" { 297 create_set myset $contents 298 assert_encoding $type myset 299 assert_equal $contents [lsort [list [r spop myset 1] [r spop myset 1] [r spop myset 1]]] 300 assert_equal 0 [r scard myset] 301 } 302 303 test "SRANDMEMBER - $type" { 304 create_set myset $contents 305 unset -nocomplain myset 306 array set myset {} 307 for {set i 0} {$i < 100} {incr i} { 308 set myset([r srandmember myset]) 1 309 } 310 assert_equal $contents [lsort [array names myset]] 311 } 312 } 313 314 foreach {type contents} { 315 hashtable {a b c d e f g h i j k l m n o p q r s t u v w x y z} 316 intset {1 10 11 12 13 14 15 16 17 18 19 2 20 21 22 23 24 25 26 3 4 5 6 7 8 9} 317 } { 318 test "SPOP with <count>" { 319 create_set myset $contents 320 assert_encoding $type myset 321 assert_equal $contents [lsort [concat [r spop myset 11] [r spop myset 9] [r spop myset 0] [r spop myset 4] [r spop myset 1] [r spop myset 0] [r spop myset 1] [r spop myset 0]]] 322 assert_equal 0 [r scard myset] 323 } 324 } 325 326 # As seen in intsetRandomMembers 327 test "SPOP using integers, testing Knuth's and Floyd's algorithm" { 328 create_set myset {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20} 329 assert_encoding intset myset 330 assert_equal 20 [r scard myset] 331 r spop myset 1 332 assert_equal 19 [r scard myset] 333 r spop myset 2 334 assert_equal 17 [r scard myset] 335 r spop myset 3 336 assert_equal 14 [r scard myset] 337 r spop myset 10 338 assert_equal 4 [r scard myset] 339 r spop myset 10 340 assert_equal 0 [r scard myset] 341 r spop myset 1 342 assert_equal 0 [r scard myset] 343 } {} 344 345 test "SPOP using integers with Knuth's algorithm" { 346 r spop nonexisting_key 100 347 } {} 348 349 test "SPOP new implementation: code path #1" { 350 set content {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20} 351 create_set myset $content 352 set res [r spop myset 30] 353 assert {[lsort $content] eq [lsort $res]} 354 } 355 356 test "SPOP new implementation: code path #2" { 357 set content {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20} 358 create_set myset $content 359 set res [r spop myset 2] 360 assert {[llength $res] == 2} 361 assert {[r scard myset] == 18} 362 set union [concat [r smembers myset] $res] 363 assert {[lsort $union] eq [lsort $content]} 364 } 365 366 test "SPOP new implementation: code path #3" { 367 set content {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20} 368 create_set myset $content 369 set res [r spop myset 18] 370 assert {[llength $res] == 18} 371 assert {[r scard myset] == 2} 372 set union [concat [r smembers myset] $res] 373 assert {[lsort $union] eq [lsort $content]} 374 } 375 376 test "SRANDMEMBER with <count> against non existing key" { 377 r srandmember nonexisting_key 100 378 } {} 379 380 foreach {type contents} { 381 hashtable { 382 1 5 10 50 125 50000 33959417 4775547 65434162 383 12098459 427716 483706 2726473884 72615637475 384 MARY PATRICIA LINDA BARBARA ELIZABETH JENNIFER MARIA 385 SUSAN MARGARET DOROTHY LISA NANCY KAREN BETTY HELEN 386 SANDRA DONNA CAROL RUTH SHARON MICHELLE LAURA SARAH 387 KIMBERLY DEBORAH JESSICA SHIRLEY CYNTHIA ANGELA MELISSA 388 BRENDA AMY ANNA REBECCA VIRGINIA KATHLEEN 389 } 390 intset { 391 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 392 20 21 22 23 24 25 26 27 28 29 393 30 31 32 33 34 35 36 37 38 39 394 40 41 42 43 44 45 46 47 48 49 395 } 396 } { 397 test "SRANDMEMBER with <count> - $type" { 398 create_set myset $contents 399 unset -nocomplain myset 400 array set myset {} 401 foreach ele [r smembers myset] { 402 set myset($ele) 1 403 } 404 assert_equal [lsort $contents] [lsort [array names myset]] 405 406 # Make sure that a count of 0 is handled correctly. 407 assert_equal [r srandmember myset 0] {} 408 409 # We'll stress different parts of the code, see the implementation 410 # of SRANDMEMBER for more information, but basically there are 411 # four different code paths. 412 # 413 # PATH 1: Use negative count. 414 # 415 # 1) Check that it returns repeated elements. 416 set res [r srandmember myset -100] 417 assert_equal [llength $res] 100 418 419 # 2) Check that all the elements actually belong to the 420 # original set. 421 foreach ele $res { 422 assert {[info exists myset($ele)]} 423 } 424 425 # 3) Check that eventually all the elements are returned. 426 unset -nocomplain auxset 427 set iterations 1000 428 while {$iterations != 0} { 429 incr iterations -1 430 set res [r srandmember myset -10] 431 foreach ele $res { 432 set auxset($ele) 1 433 } 434 if {[lsort [array names myset]] eq 435 [lsort [array names auxset]]} { 436 break; 437 } 438 } 439 assert {$iterations != 0} 440 441 # PATH 2: positive count (unique behavior) with requested size 442 # equal or greater than set size. 443 foreach size {50 100} { 444 set res [r srandmember myset $size] 445 assert_equal [llength $res] 50 446 assert_equal [lsort $res] [lsort [array names myset]] 447 } 448 449 # PATH 3: Ask almost as elements as there are in the set. 450 # In this case the implementation will duplicate the original 451 # set and will remove random elements up to the requested size. 452 # 453 # PATH 4: Ask a number of elements definitely smaller than 454 # the set size. 455 # 456 # We can test both the code paths just changing the size but 457 # using the same code. 458 459 foreach size {45 5} { 460 set res [r srandmember myset $size] 461 assert_equal [llength $res] $size 462 463 # 1) Check that all the elements actually belong to the 464 # original set. 465 foreach ele $res { 466 assert {[info exists myset($ele)]} 467 } 468 469 # 2) Check that eventually all the elements are returned. 470 unset -nocomplain auxset 471 set iterations 1000 472 while {$iterations != 0} { 473 incr iterations -1 474 set res [r srandmember myset -10] 475 foreach ele $res { 476 set auxset($ele) 1 477 } 478 if {[lsort [array names myset]] eq 479 [lsort [array names auxset]]} { 480 break; 481 } 482 } 483 assert {$iterations != 0} 484 } 485 } 486 } 487 488 proc setup_move {} { 489 r del myset3 myset4 490 create_set myset1 {1 a b} 491 create_set myset2 {2 3 4} 492 assert_encoding hashtable myset1 493 assert_encoding intset myset2 494 } 495 496 test "SMOVE basics - from regular set to intset" { 497 # move a non-integer element to an intset should convert encoding 498 setup_move 499 assert_equal 1 [r smove myset1 myset2 a] 500 assert_equal {1 b} [lsort [r smembers myset1]] 501 assert_equal {2 3 4 a} [lsort [r smembers myset2]] 502 assert_encoding hashtable myset2 503 504 # move an integer element should not convert the encoding 505 setup_move 506 assert_equal 1 [r smove myset1 myset2 1] 507 assert_equal {a b} [lsort [r smembers myset1]] 508 assert_equal {1 2 3 4} [lsort [r smembers myset2]] 509 assert_encoding intset myset2 510 } 511 512 test "SMOVE basics - from intset to regular set" { 513 setup_move 514 assert_equal 1 [r smove myset2 myset1 2] 515 assert_equal {1 2 a b} [lsort [r smembers myset1]] 516 assert_equal {3 4} [lsort [r smembers myset2]] 517 } 518 519 test "SMOVE non existing key" { 520 setup_move 521 assert_equal 0 [r smove myset1 myset2 foo] 522 assert_equal 0 [r smove myset1 myset1 foo] 523 assert_equal {1 a b} [lsort [r smembers myset1]] 524 assert_equal {2 3 4} [lsort [r smembers myset2]] 525 } 526 527 test "SMOVE non existing src set" { 528 setup_move 529 assert_equal 0 [r smove noset myset2 foo] 530 assert_equal {2 3 4} [lsort [r smembers myset2]] 531 } 532 533 test "SMOVE from regular set to non existing destination set" { 534 setup_move 535 assert_equal 1 [r smove myset1 myset3 a] 536 assert_equal {1 b} [lsort [r smembers myset1]] 537 assert_equal {a} [lsort [r smembers myset3]] 538 assert_encoding hashtable myset3 539 } 540 541 test "SMOVE from intset to non existing destination set" { 542 setup_move 543 assert_equal 1 [r smove myset2 myset3 2] 544 assert_equal {3 4} [lsort [r smembers myset2]] 545 assert_equal {2} [lsort [r smembers myset3]] 546 assert_encoding intset myset3 547 } 548 549 test "SMOVE wrong src key type" { 550 r set x 10 551 assert_error "WRONGTYPE*" {r smove x myset2 foo} 552 } 553 554 test "SMOVE wrong dst key type" { 555 r set x 10 556 assert_error "WRONGTYPE*" {r smove myset2 x foo} 557 } 558 559 test "SMOVE with identical source and destination" { 560 r del set 561 r sadd set a b c 562 r smove set set b 563 lsort [r smembers set] 564 } {a b c} 565 566 tags {slow} { 567 test {intsets implementation stress testing} { 568 for {set j 0} {$j < 20} {incr j} { 569 unset -nocomplain s 570 array set s {} 571 r del s 572 set len [randomInt 1024] 573 for {set i 0} {$i < $len} {incr i} { 574 randpath { 575 set data [randomInt 65536] 576 } { 577 set data [randomInt 4294967296] 578 } { 579 set data [randomInt 18446744073709551616] 580 } 581 set s($data) {} 582 r sadd s $data 583 } 584 assert_equal [lsort [r smembers s]] [lsort [array names s]] 585 set len [array size s] 586 for {set i 0} {$i < $len} {incr i} { 587 set e [r spop s] 588 if {![info exists s($e)]} { 589 puts "Can't find '$e' on local array" 590 puts "Local array: [lsort [r smembers s]]" 591 puts "Remote array: [lsort [array names s]]" 592 error "exception" 593 } 594 array unset s $e 595 } 596 assert_equal [r scard s] 0 597 assert_equal [array size s] 0 598 } 599 } 600 } 601} 602