1start_server {tags {"scripting"}} { 2 test {EVAL - Does Lua interpreter replies to our requests?} { 3 r eval {return 'hello'} 0 4 } {hello} 5 6 test {EVAL - Lua integer -> Redis protocol type conversion} { 7 r eval {return 100.5} 0 8 } {100} 9 10 test {EVAL - Lua string -> Redis protocol type conversion} { 11 r eval {return 'hello world'} 0 12 } {hello world} 13 14 test {EVAL - Lua true boolean -> Redis protocol type conversion} { 15 r eval {return true} 0 16 } {1} 17 18 test {EVAL - Lua false boolean -> Redis protocol type conversion} { 19 r eval {return false} 0 20 } {} 21 22 test {EVAL - Lua status code reply -> Redis protocol type conversion} { 23 r eval {return {ok='fine'}} 0 24 } {fine} 25 26 test {EVAL - Lua error reply -> Redis protocol type conversion} { 27 catch { 28 r eval {return {err='this is an error'}} 0 29 } e 30 set _ $e 31 } {this is an error} 32 33 test {EVAL - Lua table -> Redis protocol type conversion} { 34 r eval {return {1,2,3,'ciao',{1,2}}} 0 35 } {1 2 3 ciao {1 2}} 36 37 test {EVAL - Are the KEYS and ARGV arrays populated correctly?} { 38 r eval {return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}} 2 a b c d 39 } {a b c d} 40 41 test {EVAL - is Lua able to call Redis API?} { 42 r set mykey myval 43 r eval {return redis.call('get',KEYS[1])} 1 mykey 44 } {myval} 45 46 test {EVALSHA - Can we call a SHA1 if already defined?} { 47 r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey 48 } {myval} 49 50 test {EVALSHA - Can we call a SHA1 in uppercase?} { 51 r evalsha FD758D1589D044DD850A6F05D52F2EEFD27F033F 1 mykey 52 } {myval} 53 54 test {EVALSHA - Do we get an error on invalid SHA1?} { 55 catch {r evalsha NotValidShaSUM 0} e 56 set _ $e 57 } {NOSCRIPT*} 58 59 test {EVALSHA - Do we get an error on non defined SHA1?} { 60 catch {r evalsha ffd632c7d33e571e9f24556ebed26c3479a87130 0} e 61 set _ $e 62 } {NOSCRIPT*} 63 64 test {EVAL - Redis integer -> Lua type conversion} { 65 r set x 0 66 r eval { 67 local foo = redis.pcall('incr',KEYS[1]) 68 return {type(foo),foo} 69 } 1 x 70 } {number 1} 71 72 test {EVAL - Redis bulk -> Lua type conversion} { 73 r set mykey myval 74 r eval { 75 local foo = redis.pcall('get',KEYS[1]) 76 return {type(foo),foo} 77 } 1 mykey 78 } {string myval} 79 80 test {EVAL - Redis multi bulk -> Lua type conversion} { 81 r del mylist 82 r rpush mylist a 83 r rpush mylist b 84 r rpush mylist c 85 r eval { 86 local foo = redis.pcall('lrange',KEYS[1],0,-1) 87 return {type(foo),foo[1],foo[2],foo[3],# foo} 88 } 1 mylist 89 } {table a b c 3} 90 91 test {EVAL - Redis status reply -> Lua type conversion} { 92 r eval { 93 local foo = redis.pcall('set',KEYS[1],'myval') 94 return {type(foo),foo['ok']} 95 } 1 mykey 96 } {table OK} 97 98 test {EVAL - Redis error reply -> Lua type conversion} { 99 r set mykey myval 100 r eval { 101 local foo = redis.pcall('incr',KEYS[1]) 102 return {type(foo),foo['err']} 103 } 1 mykey 104 } {table {ERR value is not an integer or out of range}} 105 106 test {EVAL - Redis nil bulk reply -> Lua type conversion} { 107 r del mykey 108 r eval { 109 local foo = redis.pcall('get',KEYS[1]) 110 return {type(foo),foo == false} 111 } 1 mykey 112 } {boolean 1} 113 114 test {EVAL - Is the Lua client using the currently selected DB?} { 115 r set mykey "this is DB 9" 116 r select 10 117 r set mykey "this is DB 10" 118 r eval {return redis.pcall('get',KEYS[1])} 1 mykey 119 } {this is DB 10} 120 121 test {EVAL - SELECT inside Lua should not affect the caller} { 122 # here we DB 10 is selected 123 r set mykey "original value" 124 r eval {return redis.pcall('select','9')} 0 125 set res [r get mykey] 126 r select 9 127 set res 128 } {original value} 129 130 if 0 { 131 test {EVAL - Script can't run more than configured time limit} { 132 r config set lua-time-limit 1 133 catch { 134 r eval { 135 local i = 0 136 while true do i=i+1 end 137 } 0 138 } e 139 set _ $e 140 } {*execution time*} 141 } 142 143 test {EVAL - Scripts can't run certain commands} { 144 set e {} 145 catch {r eval {return redis.pcall('blpop','x',0)} 0} e 146 set e 147 } {*not allowed*} 148 149 test {EVAL - Scripts can't run certain commands} { 150 set e {} 151 catch { 152 r eval "redis.pcall('randomkey'); return redis.pcall('set','x','ciao')" 0 153 } e 154 set e 155 } {*not allowed after*} 156 157 test {EVAL - No arguments to redis.call/pcall is considered an error} { 158 set e {} 159 catch {r eval {return redis.call()} 0} e 160 set e 161 } {*one argument*} 162 163 test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} { 164 set e {} 165 catch { 166 r eval "redis.call('nosuchcommand')" 0 167 } e 168 set e 169 } {*Unknown Redis*} 170 171 test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} { 172 set e {} 173 catch { 174 r eval "redis.call('get','a','b','c')" 0 175 } e 176 set e 177 } {*number of args*} 178 179 test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} { 180 set e {} 181 r set foo bar 182 catch { 183 r eval {redis.call('lpush',KEYS[1],'val')} 1 foo 184 } e 185 set e 186 } {*against a key*} 187 188 test {EVAL - JSON numeric decoding} { 189 # We must return the table as a string because otherwise 190 # Redis converts floats to ints and we get 0 and 1023 instead 191 # of 0.0003 and 1023.2 as the parsed output. 192 r eval {return 193 table.concat( 194 cjson.decode( 195 "[0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10]"), " ") 196 } 0 197 } {0 -5000 -1 0.0003 1023.2 0} 198 199 test {EVAL - JSON string decoding} { 200 r eval {local decoded = cjson.decode('{"keya": "a", "keyb": "b"}') 201 return {decoded.keya, decoded.keyb} 202 } 0 203 } {a b} 204 205 test {EVAL - cmsgpack can pack double?} { 206 r eval {local encoded = cmsgpack.pack(0.1) 207 local h = "" 208 for i = 1, #encoded do 209 h = h .. string.format("%02x",string.byte(encoded,i)) 210 end 211 return h 212 } 0 213 } {cb3fb999999999999a} 214 215 test {EVAL - cmsgpack can pack negative int64?} { 216 r eval {local encoded = cmsgpack.pack(-1099511627776) 217 local h = "" 218 for i = 1, #encoded do 219 h = h .. string.format("%02x",string.byte(encoded,i)) 220 end 221 return h 222 } 0 223 } {d3ffffff0000000000} 224 225 test {EVAL - cmsgpack can pack and unpack circular references?} { 226 r eval {local a = {x=nil,y=5} 227 local b = {x=a} 228 a['x'] = b 229 local encoded = cmsgpack.pack(a) 230 local h = "" 231 -- cmsgpack encodes to a depth of 16, but can't encode 232 -- references, so the encoded object has a deep copy recusive 233 -- depth of 16. 234 for i = 1, #encoded do 235 h = h .. string.format("%02x",string.byte(encoded,i)) 236 end 237 -- when unpacked, re.x.x != re because the unpack creates 238 -- individual tables down to a depth of 16. 239 -- (that's why the encoded output is so large) 240 local re = cmsgpack.unpack(encoded) 241 assert(re) 242 assert(re.x) 243 assert(re.x.x.y == re.y) 244 assert(re.x.x.x.x.y == re.y) 245 assert(re.x.x.x.x.x.x.y == re.y) 246 assert(re.x.x.x.x.x.x.x.x.x.x.y == re.y) 247 -- maximum working depth: 248 assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.y == re.y) 249 -- now the last x would be b above and has no y 250 assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x) 251 -- so, the final x.x is at the depth limit and was assigned nil 252 assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x == nil) 253 return {h, re.x.x.x.x.x.x.x.x.y == re.y, re.y == 5} 254 } 0 255 } {82a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a178c0 1 1} 256 257 test {EVAL - Numerical sanity check from bitop} { 258 r eval {assert(0x7fffffff == 2147483647, "broken hex literals"); 259 assert(0xffffffff == -1 or 0xffffffff == 2^32-1, 260 "broken hex literals"); 261 assert(tostring(-1) == "-1", "broken tostring()"); 262 assert(tostring(0xffffffff) == "-1" or 263 tostring(0xffffffff) == "4294967295", 264 "broken tostring()") 265 } 0 266 } {} 267 268 test {EVAL - Verify minimal bitop functionality} { 269 r eval {assert(bit.tobit(1) == 1); 270 assert(bit.band(1) == 1); 271 assert(bit.bxor(1,2) == 3); 272 assert(bit.bor(1,2,4,8,16,32,64,128) == 255) 273 } 0 274 } {} 275 276 test {EVAL - Able to parse trailing comments} { 277 r eval {return 'hello' --trailing comment} 0 278 } {hello} 279 280 test {SCRIPTING FLUSH - is able to clear the scripts cache?} { 281 r set mykey myval 282 set v [r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey] 283 assert_equal $v myval 284 set e "" 285 r script flush 286 catch {r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey} e 287 set e 288 } {NOSCRIPT*} 289 290 test {SCRIPT EXISTS - can detect already defined scripts?} { 291 r eval "return 1+1" 0 292 r script exists a27e7e8a43702b7046d4f6a7ccf5b60cef6b9bd9 a27e7e8a43702b7046d4f6a7ccf5b60cef6b9bda 293 } {1 0} 294 295 test {SCRIPT LOAD - is able to register scripts in the scripting cache} { 296 list \ 297 [r script load "return 'loaded'"] \ 298 [r evalsha b534286061d4b9e4026607613b95c06c06015ae8 0] 299 } {b534286061d4b9e4026607613b95c06c06015ae8 loaded} 300 301 test "In the context of Lua the output of random commands gets ordered" { 302 r del myset 303 r sadd myset a b c d e f g h i l m n o p q r s t u v z aa aaa azz 304 r eval {return redis.call('smembers',KEYS[1])} 1 myset 305 } {a aa aaa azz b c d e f g h i l m n o p q r s t u v z} 306 307 test "SORT is normally not alpha re-ordered for the scripting engine" { 308 r del myset 309 r sadd myset 1 2 3 4 10 310 r eval {return redis.call('sort',KEYS[1],'desc')} 1 myset 311 } {10 4 3 2 1} 312 313 test "SORT BY <constant> output gets ordered for scripting" { 314 r del myset 315 r sadd myset a b c d e f g h i l m n o p q r s t u v z aa aaa azz 316 r eval {return redis.call('sort',KEYS[1],'by','_')} 1 myset 317 } {a aa aaa azz b c d e f g h i l m n o p q r s t u v z} 318 319 test "SORT BY <constant> with GET gets ordered for scripting" { 320 r del myset 321 r sadd myset a b c 322 r eval {return redis.call('sort',KEYS[1],'by','_','get','#','get','_:*')} 1 myset 323 } {a {} b {} c {}} 324 325 test "redis.sha1hex() implementation" { 326 list [r eval {return redis.sha1hex('')} 0] \ 327 [r eval {return redis.sha1hex('Pizza & Mandolino')} 0] 328 } {da39a3ee5e6b4b0d3255bfef95601890afd80709 74822d82031af7493c20eefa13bd07ec4fada82f} 329 330 test {Globals protection reading an undeclared global variable} { 331 catch {r eval {return a} 0} e 332 set e 333 } {*ERR*attempted to access unexisting global*} 334 335 test {Globals protection setting an undeclared global*} { 336 catch {r eval {a=10} 0} e 337 set e 338 } {*ERR*attempted to create global*} 339 340 test {Test an example script DECR_IF_GT} { 341 set decr_if_gt { 342 local current 343 344 current = redis.call('get',KEYS[1]) 345 if not current then return nil end 346 if current > ARGV[1] then 347 return redis.call('decr',KEYS[1]) 348 else 349 return redis.call('get',KEYS[1]) 350 end 351 } 352 r set foo 5 353 set res {} 354 lappend res [r eval $decr_if_gt 1 foo 2] 355 lappend res [r eval $decr_if_gt 1 foo 2] 356 lappend res [r eval $decr_if_gt 1 foo 2] 357 lappend res [r eval $decr_if_gt 1 foo 2] 358 lappend res [r eval $decr_if_gt 1 foo 2] 359 set res 360 } {4 3 2 2 2} 361 362 test {Scripting engine resets PRNG at every script execution} { 363 set rand1 [r eval {return tostring(math.random())} 0] 364 set rand2 [r eval {return tostring(math.random())} 0] 365 assert_equal $rand1 $rand2 366 } 367 368 test {Scripting engine PRNG can be seeded correctly} { 369 set rand1 [r eval { 370 math.randomseed(ARGV[1]); return tostring(math.random()) 371 } 0 10] 372 set rand2 [r eval { 373 math.randomseed(ARGV[1]); return tostring(math.random()) 374 } 0 10] 375 set rand3 [r eval { 376 math.randomseed(ARGV[1]); return tostring(math.random()) 377 } 0 20] 378 assert_equal $rand1 $rand2 379 assert {$rand2 ne $rand3} 380 } 381 382 test {EVAL does not leak in the Lua stack} { 383 r set x 0 384 # Use a non blocking client to speedup the loop. 385 set rd [redis_deferring_client] 386 for {set j 0} {$j < 10000} {incr j} { 387 $rd eval {return redis.call("incr",KEYS[1])} 1 x 388 } 389 for {set j 0} {$j < 10000} {incr j} { 390 $rd read 391 } 392 assert {[s used_memory_lua] < 1024*100} 393 $rd close 394 r get x 395 } {10000} 396 397 test {EVAL processes writes from AOF in read-only slaves} { 398 r flushall 399 r config set appendonly yes 400 r eval {redis.call("set",KEYS[1],"100")} 1 foo 401 r eval {redis.call("incr",KEYS[1])} 1 foo 402 r eval {redis.call("incr",KEYS[1])} 1 foo 403 wait_for_condition 50 100 { 404 [s aof_rewrite_in_progress] == 0 405 } else { 406 fail "AOF rewrite can't complete after CONFIG SET appendonly yes." 407 } 408 r config set slave-read-only yes 409 r slaveof 127.0.0.1 0 410 r debug loadaof 411 set res [r get foo] 412 r slaveof no one 413 set res 414 } {102} 415 416 test {We can call scripts rewriting client->argv from Lua} { 417 r del myset 418 r sadd myset a b c 419 r mset a 1 b 2 c 3 d 4 420 assert {[r spop myset] ne {}} 421 assert {[r spop myset 1] ne {}} 422 assert {[r spop myset] ne {}} 423 assert {[r mget a b c d] eq {1 2 3 4}} 424 assert {[r spop myset] eq {}} 425 } 426 427 test {Call Redis command with many args from Lua (issue #1764)} { 428 r eval { 429 local i 430 local x={} 431 redis.call('del','mylist') 432 for i=1,100 do 433 table.insert(x,i) 434 end 435 redis.call('rpush','mylist',unpack(x)) 436 return redis.call('lrange','mylist',0,-1) 437 } 0 438 } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100} 439 440 test {Number conversion precision test (issue #1118)} { 441 r eval { 442 local value = 9007199254740991 443 redis.call("set","foo",value) 444 return redis.call("get","foo") 445 } 0 446 } {9007199254740991} 447 448 test {String containing number precision test (regression of issue #1118)} { 449 r eval { 450 redis.call("set", "key", "12039611435714932082") 451 return redis.call("get", "key") 452 } 0 453 } {12039611435714932082} 454 455 test {Verify negative arg count is error instead of crash (issue #1842)} { 456 catch { r eval { return "hello" } -12 } e 457 set e 458 } {ERR Number of keys can't be negative} 459 460 test {Correct handling of reused argv (issue #1939)} { 461 r eval { 462 for i = 0, 10 do 463 redis.call('SET', 'a', '1') 464 redis.call('MGET', 'a', 'b', 'c') 465 redis.call('EXPIRE', 'a', 0) 466 redis.call('GET', 'a') 467 redis.call('MGET', 'a', 'b', 'c') 468 end 469 } 0 470 } 471 472 test {Functions in the Redis namespace are able to report errors} { 473 catch { 474 r eval { 475 redis.sha1hex() 476 } 0 477 } e 478 set e 479 } {*wrong number*} 480} 481 482# Start a new server since the last test in this stanza will kill the 483# instance at all. 484start_server {tags {"scripting"}} { 485 test {Timedout read-only scripts can be killed by SCRIPT KILL} { 486 set rd [redis_deferring_client] 487 r config set lua-time-limit 10 488 $rd eval {while true do end} 0 489 after 200 490 catch {r ping} e 491 assert_match {BUSY*} $e 492 r script kill 493 after 200 ; # Give some time to Lua to call the hook again... 494 assert_equal [r ping] "PONG" 495 } 496 497 test {Timedout script link is still usable after Lua returns} { 498 r config set lua-time-limit 10 499 r eval {for i=1,100000 do redis.call('ping') end return 'ok'} 0 500 r ping 501 } {PONG} 502 503 test {Timedout scripts that modified data can't be killed by SCRIPT KILL} { 504 set rd [redis_deferring_client] 505 r config set lua-time-limit 10 506 $rd eval {redis.call('set',KEYS[1],'y'); while true do end} 1 x 507 after 200 508 catch {r ping} e 509 assert_match {BUSY*} $e 510 catch {r script kill} e 511 assert_match {UNKILLABLE*} $e 512 catch {r ping} e 513 assert_match {BUSY*} $e 514 } 515 516 # Note: keep this test at the end of this server stanza because it 517 # kills the server. 518 test {SHUTDOWN NOSAVE can kill a timedout script anyway} { 519 # The server sould be still unresponding to normal commands. 520 catch {r ping} e 521 assert_match {BUSY*} $e 522 catch {r shutdown nosave} 523 # Make sure the server was killed 524 catch {set rd [redis_deferring_client]} e 525 assert_match {*connection refused*} $e 526 } 527} 528 529foreach cmdrepl {0 1} { 530 start_server {tags {"scripting repl"}} { 531 start_server {} { 532 if {$cmdrepl == 1} { 533 set rt "(commmands replication)" 534 } else { 535 set rt "(scripts replication)" 536 r debug lua-always-replicate-commands 1 537 } 538 539 test "Before the slave connects we issue two EVAL commands $rt" { 540 # One with an error, but still executing a command. 541 # SHA is: 67164fc43fa971f76fd1aaeeaf60c1c178d25876 542 catch { 543 r eval {redis.call('incr',KEYS[1]); redis.call('nonexisting')} 1 x 544 } 545 # One command is correct: 546 # SHA is: 6f5ade10a69975e903c6d07b10ea44c6382381a5 547 r eval {return redis.call('incr',KEYS[1])} 1 x 548 } {2} 549 550 test "Connect a slave to the master instance $rt" { 551 r -1 slaveof [srv 0 host] [srv 0 port] 552 wait_for_condition 50 100 { 553 [s -1 role] eq {slave} && 554 [string match {*master_link_status:up*} [r -1 info replication]] 555 } else { 556 fail "Can't turn the instance into a slave" 557 } 558 } 559 560 test "Now use EVALSHA against the master, with both SHAs $rt" { 561 # The server should replicate successful and unsuccessful 562 # commands as EVAL instead of EVALSHA. 563 catch { 564 r evalsha 67164fc43fa971f76fd1aaeeaf60c1c178d25876 1 x 565 } 566 r evalsha 6f5ade10a69975e903c6d07b10ea44c6382381a5 1 x 567 } {4} 568 569 test "If EVALSHA was replicated as EVAL, 'x' should be '4' $rt" { 570 wait_for_condition 50 100 { 571 [r -1 get x] eq {4} 572 } else { 573 fail "Expected 4 in x, but value is '[r -1 get x]'" 574 } 575 } 576 577 test "Replication of script multiple pushes to list with BLPOP $rt" { 578 set rd [redis_deferring_client] 579 $rd brpop a 0 580 r eval { 581 redis.call("lpush",KEYS[1],"1"); 582 redis.call("lpush",KEYS[1],"2"); 583 } 1 a 584 set res [$rd read] 585 $rd close 586 wait_for_condition 50 100 { 587 [r -1 lrange a 0 -1] eq [r lrange a 0 -1] 588 } else { 589 fail "Expected list 'a' in slave and master to be the same, but they are respectively '[r -1 lrange a 0 -1]' and '[r lrange a 0 -1]'" 590 } 591 set res 592 } {a 1} 593 594 test "EVALSHA replication when first call is readonly $rt" { 595 r del x 596 r eval {if tonumber(ARGV[1]) > 0 then redis.call('incr', KEYS[1]) end} 1 x 0 597 r evalsha 6e0e2745aa546d0b50b801a20983b70710aef3ce 1 x 0 598 r evalsha 6e0e2745aa546d0b50b801a20983b70710aef3ce 1 x 1 599 wait_for_condition 50 100 { 600 [r -1 get x] eq {1} 601 } else { 602 fail "Expected 1 in x, but value is '[r -1 get x]'" 603 } 604 } 605 606 test "Lua scripts using SELECT are replicated correctly $rt" { 607 r eval { 608 redis.call("set","foo1","bar1") 609 redis.call("select","10") 610 redis.call("incr","x") 611 redis.call("select","11") 612 redis.call("incr","z") 613 } 0 614 r eval { 615 redis.call("set","foo1","bar1") 616 redis.call("select","10") 617 redis.call("incr","x") 618 redis.call("select","11") 619 redis.call("incr","z") 620 } 0 621 wait_for_condition 50 100 { 622 [r -1 debug digest] eq [r debug digest] 623 } else { 624 fail "Master-Slave desync after Lua script using SELECT." 625 } 626 } 627 } 628 } 629} 630 631start_server {tags {"scripting repl"}} { 632 start_server {overrides {appendonly yes}} { 633 test "Connect a slave to the master instance" { 634 r -1 slaveof [srv 0 host] [srv 0 port] 635 wait_for_condition 50 100 { 636 [s -1 role] eq {slave} && 637 [string match {*master_link_status:up*} [r -1 info replication]] 638 } else { 639 fail "Can't turn the instance into a slave" 640 } 641 } 642 643 test "Redis.replicate_commands() must be issued before any write" { 644 r eval { 645 redis.call('set','foo','bar'); 646 return redis.replicate_commands(); 647 } 0 648 } {} 649 650 test "Redis.replicate_commands() must be issued before any write (2)" { 651 r eval { 652 return redis.replicate_commands(); 653 } 0 654 } {1} 655 656 test "Redis.set_repl() must be issued after replicate_commands()" { 657 catch { 658 r eval { 659 redis.set_repl(redis.REPL_ALL); 660 } 0 661 } e 662 set e 663 } {*only after turning on*} 664 665 test "Redis.set_repl() don't accept invalid values" { 666 catch { 667 r eval { 668 redis.replicate_commands(); 669 redis.set_repl(12345); 670 } 0 671 } e 672 set e 673 } {*Invalid*flags*} 674 675 test "Test selective replication of certain Redis commands from Lua" { 676 r del a b c d 677 r eval { 678 redis.replicate_commands(); 679 redis.call('set','a','1'); 680 redis.set_repl(redis.REPL_NONE); 681 redis.call('set','b','2'); 682 redis.set_repl(redis.REPL_AOF); 683 redis.call('set','c','3'); 684 redis.set_repl(redis.REPL_ALL); 685 redis.call('set','d','4'); 686 } 0 687 688 wait_for_condition 50 100 { 689 [r -1 mget a b c d] eq {1 {} {} 4} 690 } else { 691 fail "Only a and c should be replicated to slave" 692 } 693 694 # Master should have everything right now 695 assert {[r mget a b c d] eq {1 2 3 4}} 696 697 # After an AOF reload only a, c and d should exist 698 r debug loadaof 699 700 assert {[r mget a b c d] eq {1 {} 3 4}} 701 } 702 703 test "PRNG is seeded randomly for command replication" { 704 set a [ 705 r eval { 706 redis.replicate_commands(); 707 return math.random()*100000; 708 } 0 709 ] 710 set b [ 711 r eval { 712 redis.replicate_commands(); 713 return math.random()*100000; 714 } 0 715 ] 716 assert {$a ne $b} 717 } 718 719 test "Using side effects is not a problem with command replication" { 720 r eval { 721 redis.replicate_commands(); 722 redis.call('set','time',redis.call('time')[1]) 723 } 0 724 725 assert {[r get time] ne {}} 726 727 wait_for_condition 50 100 { 728 [r get time] eq [r -1 get time] 729 } else { 730 fail "Time key does not match between master and slave" 731 } 732 } 733 } 734} 735 736