multi.tcl 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. proc wait_for_dbsize {size} {
  2. set r2 [redis_client]
  3. wait_for_condition 50 100 {
  4. [$r2 dbsize] == $size
  5. } else {
  6. fail "Target dbsize not reached"
  7. }
  8. $r2 close
  9. }
  10. start_server {tags {"multi"}} {
  11. test {MUTLI / EXEC basics} {
  12. r del mylist
  13. r rpush mylist a
  14. r rpush mylist b
  15. r rpush mylist c
  16. r multi
  17. set v1 [r lrange mylist 0 -1]
  18. set v2 [r ping]
  19. set v3 [r exec]
  20. list $v1 $v2 $v3
  21. } {QUEUED QUEUED {{a b c} PONG}}
  22. test {DISCARD} {
  23. r del mylist
  24. r rpush mylist a
  25. r rpush mylist b
  26. r rpush mylist c
  27. r multi
  28. set v1 [r del mylist]
  29. set v2 [r discard]
  30. set v3 [r lrange mylist 0 -1]
  31. list $v1 $v2 $v3
  32. } {QUEUED OK {a b c}}
  33. test {Nested MULTI are not allowed} {
  34. set err {}
  35. r multi
  36. catch {[r multi]} err
  37. r exec
  38. set _ $err
  39. } {*ERR MULTI*}
  40. test {MULTI where commands alter argc/argv} {
  41. r sadd myset a
  42. r multi
  43. r spop myset
  44. list [r exec] [r exists myset]
  45. } {a 0}
  46. test {WATCH inside MULTI is not allowed} {
  47. set err {}
  48. r multi
  49. catch {[r watch x]} err
  50. r exec
  51. set _ $err
  52. } {*ERR WATCH*}
  53. test {EXEC fails if there are errors while queueing commands #1} {
  54. r del foo1{t} foo2{t}
  55. r multi
  56. r set foo1{t} bar1
  57. catch {r non-existing-command}
  58. r set foo2{t} bar2
  59. catch {r exec} e
  60. assert_match {EXECABORT*} $e
  61. list [r exists foo1{t}] [r exists foo2{t}]
  62. } {0 0}
  63. test {EXEC fails if there are errors while queueing commands #2} {
  64. set rd [redis_deferring_client]
  65. r del foo1{t} foo2{t}
  66. r multi
  67. r set foo1{t} bar1
  68. $rd config set maxmemory 1
  69. assert {[$rd read] eq {OK}}
  70. catch {r lpush mylist{t} myvalue}
  71. $rd config set maxmemory 0
  72. assert {[$rd read] eq {OK}}
  73. r set foo2{t} bar2
  74. catch {r exec} e
  75. assert_match {EXECABORT*} $e
  76. $rd close
  77. list [r exists foo1{t}] [r exists foo2{t}]
  78. } {0 0} {needs:config-maxmemory}
  79. test {If EXEC aborts, the client MULTI state is cleared} {
  80. r del foo1{t} foo2{t}
  81. r multi
  82. r set foo1{t} bar1
  83. catch {r non-existing-command}
  84. r set foo2{t} bar2
  85. catch {r exec} e
  86. assert_match {EXECABORT*} $e
  87. r ping
  88. } {PONG}
  89. test {EXEC works on WATCHed key not modified} {
  90. r watch x{t} y{t} z{t}
  91. r watch k{t}
  92. r multi
  93. r ping
  94. r exec
  95. } {PONG}
  96. test {EXEC fail on WATCHed key modified (1 key of 1 watched)} {
  97. r set x 30
  98. r watch x
  99. r set x 40
  100. r multi
  101. r ping
  102. r exec
  103. } {}
  104. test {EXEC fail on WATCHed key modified (1 key of 5 watched)} {
  105. r set x{t} 30
  106. r watch a{t} b{t} x{t} k{t} z{t}
  107. r set x{t} 40
  108. r multi
  109. r ping
  110. r exec
  111. } {}
  112. test {EXEC fail on WATCHed key modified by SORT with STORE even if the result is empty} {
  113. r flushdb
  114. r lpush foo bar
  115. r watch foo
  116. r sort emptylist store foo
  117. r multi
  118. r ping
  119. r exec
  120. } {} {cluster:skip}
  121. test {EXEC fail on lazy expired WATCHed key} {
  122. r flushall
  123. r debug set-active-expire 0
  124. r del key
  125. r set key 1 px 2
  126. r watch key
  127. after 100
  128. r multi
  129. r incr key
  130. assert_equal [r exec] {}
  131. r debug set-active-expire 1
  132. } {OK} {needs:debug}
  133. test {After successful EXEC key is no longer watched} {
  134. r set x 30
  135. r watch x
  136. r multi
  137. r ping
  138. r exec
  139. r set x 40
  140. r multi
  141. r ping
  142. r exec
  143. } {PONG}
  144. test {After failed EXEC key is no longer watched} {
  145. r set x 30
  146. r watch x
  147. r set x 40
  148. r multi
  149. r ping
  150. r exec
  151. r set x 40
  152. r multi
  153. r ping
  154. r exec
  155. } {PONG}
  156. test {It is possible to UNWATCH} {
  157. r set x 30
  158. r watch x
  159. r set x 40
  160. r unwatch
  161. r multi
  162. r ping
  163. r exec
  164. } {PONG}
  165. test {UNWATCH when there is nothing watched works as expected} {
  166. r unwatch
  167. } {OK}
  168. test {FLUSHALL is able to touch the watched keys} {
  169. r set x 30
  170. r watch x
  171. r flushall
  172. r multi
  173. r ping
  174. r exec
  175. } {}
  176. test {FLUSHALL does not touch non affected keys} {
  177. r del x
  178. r watch x
  179. r flushall
  180. r multi
  181. r ping
  182. r exec
  183. } {PONG}
  184. test {FLUSHDB is able to touch the watched keys} {
  185. r set x 30
  186. r watch x
  187. r flushdb
  188. r multi
  189. r ping
  190. r exec
  191. } {}
  192. test {FLUSHDB does not touch non affected keys} {
  193. r del x
  194. r watch x
  195. r flushdb
  196. r multi
  197. r ping
  198. r exec
  199. } {PONG}
  200. test {SWAPDB is able to touch the watched keys that exist} {
  201. r flushall
  202. r select 0
  203. r set x 30
  204. r watch x ;# make sure x (set to 30) doesn't change (SWAPDB will "delete" it)
  205. r swapdb 0 1
  206. r multi
  207. r ping
  208. r exec
  209. } {} {singledb:skip}
  210. test {SWAPDB is able to touch the watched keys that do not exist} {
  211. r flushall
  212. r select 1
  213. r set x 30
  214. r select 0
  215. r watch x ;# make sure the key x (currently missing) doesn't change (SWAPDB will create it)
  216. r swapdb 0 1
  217. r multi
  218. r ping
  219. r exec
  220. } {} {singledb:skip}
  221. test {WATCH is able to remember the DB a key belongs to} {
  222. r select 5
  223. r set x 30
  224. r watch x
  225. r select 1
  226. r set x 10
  227. r select 5
  228. r multi
  229. r ping
  230. set res [r exec]
  231. # Restore original DB
  232. r select 9
  233. set res
  234. } {PONG} {singledb:skip}
  235. test {WATCH will consider touched keys target of EXPIRE} {
  236. r del x
  237. r set x foo
  238. r watch x
  239. r expire x 10
  240. r multi
  241. r ping
  242. r exec
  243. } {}
  244. test {WATCH will consider touched expired keys} {
  245. r flushall
  246. r del x
  247. r set x foo
  248. r expire x 1
  249. r watch x
  250. # Wait for the keys to expire.
  251. wait_for_dbsize 0
  252. r multi
  253. r ping
  254. r exec
  255. } {}
  256. test {DISCARD should clear the WATCH dirty flag on the client} {
  257. r watch x
  258. r set x 10
  259. r multi
  260. r discard
  261. r multi
  262. r incr x
  263. r exec
  264. } {11}
  265. test {DISCARD should UNWATCH all the keys} {
  266. r watch x
  267. r set x 10
  268. r multi
  269. r discard
  270. r set x 10
  271. r multi
  272. r incr x
  273. r exec
  274. } {11}
  275. test {MULTI / EXEC is propagated correctly (single write command)} {
  276. set repl [attach_to_replication_stream]
  277. r multi
  278. r set foo bar
  279. r exec
  280. assert_replication_stream $repl {
  281. {select *}
  282. {multi}
  283. {set foo bar}
  284. {exec}
  285. }
  286. close_replication_stream $repl
  287. } {} {needs:repl}
  288. test {MULTI / EXEC is propagated correctly (empty transaction)} {
  289. set repl [attach_to_replication_stream]
  290. r multi
  291. r exec
  292. r set foo bar
  293. assert_replication_stream $repl {
  294. {select *}
  295. {set foo bar}
  296. }
  297. close_replication_stream $repl
  298. } {} {needs:repl}
  299. test {MULTI / EXEC is propagated correctly (read-only commands)} {
  300. r set foo value1
  301. set repl [attach_to_replication_stream]
  302. r multi
  303. r get foo
  304. r exec
  305. r set foo value2
  306. assert_replication_stream $repl {
  307. {select *}
  308. {set foo value2}
  309. }
  310. close_replication_stream $repl
  311. } {} {needs:repl}
  312. test {MULTI / EXEC is propagated correctly (write command, no effect)} {
  313. r del bar
  314. r del foo
  315. set repl [attach_to_replication_stream]
  316. r multi
  317. r del foo
  318. r exec
  319. # add another command so that when we see it we know multi-exec wasn't
  320. # propagated
  321. r incr foo
  322. assert_replication_stream $repl {
  323. {select *}
  324. {incr foo}
  325. }
  326. close_replication_stream $repl
  327. } {} {needs:repl}
  328. test {DISCARD should not fail during OOM} {
  329. set rd [redis_deferring_client]
  330. $rd config set maxmemory 1
  331. assert {[$rd read] eq {OK}}
  332. r multi
  333. catch {r set x 1} e
  334. assert_match {OOM*} $e
  335. r discard
  336. $rd config set maxmemory 0
  337. assert {[$rd read] eq {OK}}
  338. $rd close
  339. r ping
  340. } {PONG} {needs:config-maxmemory}
  341. test {MULTI and script timeout} {
  342. # check that if MULTI arrives during timeout, it is either refused, or
  343. # allowed to pass, and we don't end up executing half of the transaction
  344. set rd1 [redis_deferring_client]
  345. set r2 [redis_client]
  346. r config set lua-time-limit 10
  347. r set xx 1
  348. $rd1 eval {while true do end} 0
  349. after 200
  350. catch { $r2 multi; } e
  351. catch { $r2 incr xx; } e
  352. r script kill
  353. after 200 ; # Give some time to Lua to call the hook again...
  354. catch { $r2 incr xx; } e
  355. catch { $r2 exec; } e
  356. assert_match {EXECABORT*previous errors*} $e
  357. set xx [r get xx]
  358. # make sure that either the whole transcation passed or none of it (we actually expect none)
  359. assert { $xx == 1 || $xx == 3}
  360. # check that the connection is no longer in multi state
  361. set pong [$r2 ping asdf]
  362. assert_equal $pong "asdf"
  363. $rd1 close; $r2 close
  364. }
  365. test {EXEC and script timeout} {
  366. # check that if EXEC arrives during timeout, we don't end up executing
  367. # half of the transaction, and also that we exit the multi state
  368. set rd1 [redis_deferring_client]
  369. set r2 [redis_client]
  370. r config set lua-time-limit 10
  371. r set xx 1
  372. catch { $r2 multi; } e
  373. catch { $r2 incr xx; } e
  374. $rd1 eval {while true do end} 0
  375. after 200
  376. catch { $r2 incr xx; } e
  377. catch { $r2 exec; } e
  378. assert_match {EXECABORT*BUSY*} $e
  379. r script kill
  380. after 200 ; # Give some time to Lua to call the hook again...
  381. set xx [r get xx]
  382. # make sure that either the whole transcation passed or none of it (we actually expect none)
  383. assert { $xx == 1 || $xx == 3}
  384. # check that the connection is no longer in multi state
  385. set pong [$r2 ping asdf]
  386. assert_equal $pong "asdf"
  387. $rd1 close; $r2 close
  388. }
  389. test {MULTI-EXEC body and script timeout} {
  390. # check that we don't run an incomplete transaction due to some commands
  391. # arriving during busy script
  392. set rd1 [redis_deferring_client]
  393. set r2 [redis_client]
  394. r config set lua-time-limit 10
  395. r set xx 1
  396. catch { $r2 multi; } e
  397. catch { $r2 incr xx; } e
  398. $rd1 eval {while true do end} 0
  399. after 200
  400. catch { $r2 incr xx; } e
  401. r script kill
  402. after 200 ; # Give some time to Lua to call the hook again...
  403. catch { $r2 exec; } e
  404. assert_match {EXECABORT*previous errors*} $e
  405. set xx [r get xx]
  406. # make sure that either the whole transcation passed or none of it (we actually expect none)
  407. assert { $xx == 1 || $xx == 3}
  408. # check that the connection is no longer in multi state
  409. set pong [$r2 ping asdf]
  410. assert_equal $pong "asdf"
  411. $rd1 close; $r2 close
  412. }
  413. test {just EXEC and script timeout} {
  414. # check that if EXEC arrives during timeout, we don't end up executing
  415. # actual commands during busy script, and also that we exit the multi state
  416. set rd1 [redis_deferring_client]
  417. set r2 [redis_client]
  418. r config set lua-time-limit 10
  419. r set xx 1
  420. catch { $r2 multi; } e
  421. catch { $r2 incr xx; } e
  422. $rd1 eval {while true do end} 0
  423. after 200
  424. catch { $r2 exec; } e
  425. assert_match {EXECABORT*BUSY*} $e
  426. r script kill
  427. after 200 ; # Give some time to Lua to call the hook again...
  428. set xx [r get xx]
  429. # make we didn't execute the transaction
  430. assert { $xx == 1}
  431. # check that the connection is no longer in multi state
  432. set pong [$r2 ping asdf]
  433. assert_equal $pong "asdf"
  434. $rd1 close; $r2 close
  435. }
  436. test {exec with write commands and state change} {
  437. # check that exec that contains write commands fails if server state changed since they were queued
  438. set r1 [redis_client]
  439. r set xx 1
  440. r multi
  441. r incr xx
  442. $r1 config set min-replicas-to-write 2
  443. catch {r exec} e
  444. assert_match {*EXECABORT*NOREPLICAS*} $e
  445. set xx [r get xx]
  446. # make sure that the INCR wasn't executed
  447. assert { $xx == 1}
  448. $r1 config set min-replicas-to-write 0
  449. $r1 close
  450. } {0} {needs:repl}
  451. test {exec with read commands and stale replica state change} {
  452. # check that exec that contains read commands fails if server state changed since they were queued
  453. r config set replica-serve-stale-data no
  454. set r1 [redis_client]
  455. r set xx 1
  456. # check that GET is disallowed on stale replica, even if the replica becomes stale only after queuing.
  457. r multi
  458. r get xx
  459. $r1 replicaof localhsot 0
  460. catch {r exec} e
  461. assert_match {*EXECABORT*MASTERDOWN*} $e
  462. # check that PING is allowed
  463. r multi
  464. r ping
  465. $r1 replicaof localhsot 0
  466. set pong [r exec]
  467. assert {$pong == "PONG"}
  468. # check that when replica is not stale, GET is allowed
  469. # while we're at it, let's check that multi is allowed on stale replica too
  470. r multi
  471. $r1 replicaof no one
  472. r get xx
  473. set xx [r exec]
  474. # make sure that the INCR was executed
  475. assert { $xx == 1 }
  476. $r1 close
  477. } {0} {needs:repl cluster:skip}
  478. test {EXEC with only read commands should not be rejected when OOM} {
  479. set r2 [redis_client]
  480. r set x value
  481. r multi
  482. r get x
  483. r ping
  484. # enforcing OOM
  485. $r2 config set maxmemory 1
  486. # finish the multi transaction with exec
  487. assert { [r exec] == {value PONG} }
  488. # releasing OOM
  489. $r2 config set maxmemory 0
  490. $r2 close
  491. } {0} {needs:config-maxmemory}
  492. test {EXEC with at least one use-memory command should fail} {
  493. set r2 [redis_client]
  494. r multi
  495. r set x 1
  496. r get x
  497. # enforcing OOM
  498. $r2 config set maxmemory 1
  499. # finish the multi transaction with exec
  500. catch {r exec} e
  501. assert_match {EXECABORT*OOM*} $e
  502. # releasing OOM
  503. $r2 config set maxmemory 0
  504. $r2 close
  505. } {0} {needs:config-maxmemory}
  506. test {Blocking commands ignores the timeout} {
  507. r xgroup create s{t} g $ MKSTREAM
  508. set m [r multi]
  509. r blpop empty_list{t} 0
  510. r brpop empty_list{t} 0
  511. r brpoplpush empty_list1{t} empty_list2{t} 0
  512. r blmove empty_list1{t} empty_list2{t} LEFT LEFT 0
  513. r bzpopmin empty_zset{t} 0
  514. r bzpopmax empty_zset{t} 0
  515. r xread BLOCK 0 STREAMS s{t} $
  516. r xreadgroup group g c BLOCK 0 STREAMS s{t} >
  517. set res [r exec]
  518. list $m $res
  519. } {OK {{} {} {} {} {} {} {} {}}}
  520. test {MULTI propagation of PUBLISH} {
  521. set repl [attach_to_replication_stream]
  522. # make sure that PUBLISH inside MULTI is propagated in a transaction
  523. r multi
  524. r publish bla bla
  525. r exec
  526. assert_replication_stream $repl {
  527. {select *}
  528. {multi}
  529. {publish bla bla}
  530. {exec}
  531. }
  532. close_replication_stream $repl
  533. } {} {needs:repl cluster:skip}
  534. test {MULTI propagation of SCRIPT LOAD} {
  535. set repl [attach_to_replication_stream]
  536. # make sure that SCRIPT LOAD inside MULTI is propagated in a transaction
  537. r multi
  538. r script load {redis.call('set', KEYS[1], 'foo')}
  539. set res [r exec]
  540. set sha [lindex $res 0]
  541. assert_replication_stream $repl {
  542. {select *}
  543. {multi}
  544. {script load *}
  545. {exec}
  546. }
  547. close_replication_stream $repl
  548. } {} {needs:repl}
  549. test {MULTI propagation of SCRIPT LOAD} {
  550. set repl [attach_to_replication_stream]
  551. # make sure that EVAL inside MULTI is propagated in a transaction
  552. r config set lua-replicate-commands no
  553. r multi
  554. r eval {redis.call('set', KEYS[1], 'bar')} 1 bar
  555. r exec
  556. assert_replication_stream $repl {
  557. {select *}
  558. {multi}
  559. {eval *}
  560. {exec}
  561. }
  562. close_replication_stream $repl
  563. } {} {needs:repl}
  564. tags {"stream"} {
  565. test {MULTI propagation of XREADGROUP} {
  566. # stream is a special case because it calls propagate() directly for XREADGROUP
  567. set repl [attach_to_replication_stream]
  568. r XADD mystream * foo bar
  569. r XGROUP CREATE mystream mygroup 0
  570. # make sure the XCALIM (propagated by XREADGROUP) is indeed inside MULTI/EXEC
  571. r multi
  572. r XREADGROUP GROUP mygroup consumer1 STREAMS mystream ">"
  573. r exec
  574. assert_replication_stream $repl {
  575. {select *}
  576. {xadd *}
  577. {xgroup CREATE *}
  578. {multi}
  579. {xclaim *}
  580. {exec}
  581. }
  582. close_replication_stream $repl
  583. } {} {needs:repl}
  584. }
  585. }