multi.tcl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. start_server {tags {"multi"}} {
  2. test {MUTLI / EXEC basics} {
  3. r del mylist
  4. r rpush mylist a
  5. r rpush mylist b
  6. r rpush mylist c
  7. r multi
  8. set v1 [r lrange mylist 0 -1]
  9. set v2 [r ping]
  10. set v3 [r exec]
  11. list $v1 $v2 $v3
  12. } {QUEUED QUEUED {{a b c} PONG}}
  13. test {DISCARD} {
  14. r del mylist
  15. r rpush mylist a
  16. r rpush mylist b
  17. r rpush mylist c
  18. r multi
  19. set v1 [r del mylist]
  20. set v2 [r discard]
  21. set v3 [r lrange mylist 0 -1]
  22. list $v1 $v2 $v3
  23. } {QUEUED OK {a b c}}
  24. test {Nested MULTI are not allowed} {
  25. set err {}
  26. r multi
  27. catch {[r multi]} err
  28. r exec
  29. set _ $err
  30. } {*ERR MULTI*}
  31. test {MULTI where commands alter argc/argv} {
  32. r sadd myset a
  33. r multi
  34. r spop myset
  35. list [r exec] [r exists myset]
  36. } {a 0}
  37. test {WATCH inside MULTI is not allowed} {
  38. set err {}
  39. r multi
  40. catch {[r watch x]} err
  41. r exec
  42. set _ $err
  43. } {*ERR WATCH*}
  44. test {EXEC fails if there are errors while queueing commands #1} {
  45. r del foo1 foo2
  46. r multi
  47. r set foo1 bar1
  48. catch {r non-existing-command}
  49. r set foo2 bar2
  50. catch {r exec} e
  51. assert_match {EXECABORT*} $e
  52. list [r exists foo1] [r exists foo2]
  53. } {0 0}
  54. test {EXEC fails if there are errors while queueing commands #2} {
  55. set rd [redis_deferring_client]
  56. r del foo1 foo2
  57. r multi
  58. r set foo1 bar1
  59. $rd config set maxmemory 1
  60. assert {[$rd read] eq {OK}}
  61. catch {r lpush mylist myvalue}
  62. $rd config set maxmemory 0
  63. assert {[$rd read] eq {OK}}
  64. r set foo2 bar2
  65. catch {r exec} e
  66. assert_match {EXECABORT*} $e
  67. $rd close
  68. list [r exists foo1] [r exists foo2]
  69. } {0 0}
  70. test {If EXEC aborts, the client MULTI state is cleared} {
  71. r del foo1 foo2
  72. r multi
  73. r set foo1 bar1
  74. catch {r non-existing-command}
  75. r set foo2 bar2
  76. catch {r exec} e
  77. assert_match {EXECABORT*} $e
  78. r ping
  79. } {PONG}
  80. test {EXEC works on WATCHed key not modified} {
  81. r watch x y z
  82. r watch k
  83. r multi
  84. r ping
  85. r exec
  86. } {PONG}
  87. test {EXEC fail on WATCHed key modified (1 key of 1 watched)} {
  88. r set x 30
  89. r watch x
  90. r set x 40
  91. r multi
  92. r ping
  93. r exec
  94. } {}
  95. test {EXEC fail on WATCHed key modified (1 key of 5 watched)} {
  96. r set x 30
  97. r watch a b x k z
  98. r set x 40
  99. r multi
  100. r ping
  101. r exec
  102. } {}
  103. test {EXEC fail on WATCHed key modified by SORT with STORE even if the result is empty} {
  104. r flushdb
  105. r lpush foo bar
  106. r watch foo
  107. r sort emptylist store foo
  108. r multi
  109. r ping
  110. r exec
  111. } {}
  112. test {After successful EXEC key is no longer watched} {
  113. r set x 30
  114. r watch x
  115. r multi
  116. r ping
  117. r exec
  118. r set x 40
  119. r multi
  120. r ping
  121. r exec
  122. } {PONG}
  123. test {After failed EXEC key is no longer watched} {
  124. r set x 30
  125. r watch x
  126. r set x 40
  127. r multi
  128. r ping
  129. r exec
  130. r set x 40
  131. r multi
  132. r ping
  133. r exec
  134. } {PONG}
  135. test {It is possible to UNWATCH} {
  136. r set x 30
  137. r watch x
  138. r set x 40
  139. r unwatch
  140. r multi
  141. r ping
  142. r exec
  143. } {PONG}
  144. test {UNWATCH when there is nothing watched works as expected} {
  145. r unwatch
  146. } {OK}
  147. test {FLUSHALL is able to touch the watched keys} {
  148. r set x 30
  149. r watch x
  150. r flushall
  151. r multi
  152. r ping
  153. r exec
  154. } {}
  155. test {FLUSHALL does not touch non affected keys} {
  156. r del x
  157. r watch x
  158. r flushall
  159. r multi
  160. r ping
  161. r exec
  162. } {PONG}
  163. test {FLUSHDB is able to touch the watched keys} {
  164. r set x 30
  165. r watch x
  166. r flushdb
  167. r multi
  168. r ping
  169. r exec
  170. } {}
  171. test {FLUSHDB does not touch non affected keys} {
  172. r del x
  173. r watch x
  174. r flushdb
  175. r multi
  176. r ping
  177. r exec
  178. } {PONG}
  179. test {WATCH is able to remember the DB a key belongs to} {
  180. r select 5
  181. r set x 30
  182. r watch x
  183. r select 1
  184. r set x 10
  185. r select 5
  186. r multi
  187. r ping
  188. set res [r exec]
  189. # Restore original DB
  190. r select 9
  191. set res
  192. } {PONG}
  193. test {WATCH will consider touched keys target of EXPIRE} {
  194. r del x
  195. r set x foo
  196. r watch x
  197. r expire x 10
  198. r multi
  199. r ping
  200. r exec
  201. } {}
  202. test {WATCH will not consider touched expired keys} {
  203. r del x
  204. r set x foo
  205. r expire x 1
  206. r watch x
  207. after 1100
  208. r multi
  209. r ping
  210. r exec
  211. } {PONG}
  212. test {DISCARD should clear the WATCH dirty flag on the client} {
  213. r watch x
  214. r set x 10
  215. r multi
  216. r discard
  217. r multi
  218. r incr x
  219. r exec
  220. } {11}
  221. test {DISCARD should UNWATCH all the keys} {
  222. r watch x
  223. r set x 10
  224. r multi
  225. r discard
  226. r set x 10
  227. r multi
  228. r incr x
  229. r exec
  230. } {11}
  231. test {MULTI / EXEC is propagated correctly (single write command)} {
  232. set repl [attach_to_replication_stream]
  233. r multi
  234. r set foo bar
  235. r exec
  236. assert_replication_stream $repl {
  237. {select *}
  238. {multi}
  239. {set foo bar}
  240. {exec}
  241. }
  242. close_replication_stream $repl
  243. }
  244. test {MULTI / EXEC is propagated correctly (empty transaction)} {
  245. set repl [attach_to_replication_stream]
  246. r multi
  247. r exec
  248. r set foo bar
  249. assert_replication_stream $repl {
  250. {select *}
  251. {set foo bar}
  252. }
  253. close_replication_stream $repl
  254. }
  255. test {MULTI / EXEC is propagated correctly (read-only commands)} {
  256. r set foo value1
  257. set repl [attach_to_replication_stream]
  258. r multi
  259. r get foo
  260. r exec
  261. r set foo value2
  262. assert_replication_stream $repl {
  263. {select *}
  264. {set foo value2}
  265. }
  266. close_replication_stream $repl
  267. }
  268. test {MULTI / EXEC is propagated correctly (write command, no effect)} {
  269. r del bar foo bar
  270. set repl [attach_to_replication_stream]
  271. r multi
  272. r del foo
  273. r exec
  274. assert_replication_stream $repl {
  275. {select *}
  276. {multi}
  277. {exec}
  278. }
  279. close_replication_stream $repl
  280. }
  281. test {DISCARD should not fail during OOM} {
  282. set rd [redis_deferring_client]
  283. $rd config set maxmemory 1
  284. assert {[$rd read] eq {OK}}
  285. r multi
  286. catch {r set x 1} e
  287. assert_match {OOM*} $e
  288. r discard
  289. $rd config set maxmemory 0
  290. assert {[$rd read] eq {OK}}
  291. $rd close
  292. r ping
  293. } {PONG}
  294. test {MULTI and script timeout} {
  295. # check that if MULTI arrives during timeout, it is either refused, or
  296. # allowed to pass, and we don't end up executing half of the transaction
  297. set rd1 [redis_deferring_client]
  298. set r2 [redis_client]
  299. r config set lua-time-limit 10
  300. r set xx 1
  301. $rd1 eval {while true do end} 0
  302. after 200
  303. catch { $r2 multi; } e
  304. catch { $r2 incr xx; } e
  305. r script kill
  306. after 200 ; # Give some time to Lua to call the hook again...
  307. catch { $r2 incr xx; } e
  308. catch { $r2 exec; } e
  309. assert_match {EXECABORT*previous errors*} $e
  310. set xx [r get xx]
  311. # make sure that either the whole transcation passed or none of it (we actually expect none)
  312. assert { $xx == 1 || $xx == 3}
  313. # check that the connection is no longer in multi state
  314. set pong [$r2 ping asdf]
  315. assert_equal $pong "asdf"
  316. $rd1 close; $r2 close
  317. }
  318. test {EXEC and script timeout} {
  319. # check that if EXEC arrives during timeout, we don't end up executing
  320. # half of the transaction, and also that we exit the multi state
  321. set rd1 [redis_deferring_client]
  322. set r2 [redis_client]
  323. r config set lua-time-limit 10
  324. r set xx 1
  325. catch { $r2 multi; } e
  326. catch { $r2 incr xx; } e
  327. $rd1 eval {while true do end} 0
  328. after 200
  329. catch { $r2 incr xx; } e
  330. catch { $r2 exec; } e
  331. assert_match {EXECABORT*BUSY*} $e
  332. r script kill
  333. after 200 ; # Give some time to Lua to call the hook again...
  334. set xx [r get xx]
  335. # make sure that either the whole transcation passed or none of it (we actually expect none)
  336. assert { $xx == 1 || $xx == 3}
  337. # check that the connection is no longer in multi state
  338. set pong [$r2 ping asdf]
  339. assert_equal $pong "asdf"
  340. $rd1 close; $r2 close
  341. }
  342. test {MULTI-EXEC body and script timeout} {
  343. # check that we don't run an imcomplete transaction due to some commands
  344. # arriving during busy script
  345. set rd1 [redis_deferring_client]
  346. set r2 [redis_client]
  347. r config set lua-time-limit 10
  348. r set xx 1
  349. catch { $r2 multi; } e
  350. catch { $r2 incr xx; } e
  351. $rd1 eval {while true do end} 0
  352. after 200
  353. catch { $r2 incr xx; } e
  354. r script kill
  355. after 200 ; # Give some time to Lua to call the hook again...
  356. catch { $r2 exec; } e
  357. assert_match {EXECABORT*previous errors*} $e
  358. set xx [r get xx]
  359. # make sure that either the whole transcation passed or none of it (we actually expect none)
  360. assert { $xx == 1 || $xx == 3}
  361. # check that the connection is no longer in multi state
  362. set pong [$r2 ping asdf]
  363. assert_equal $pong "asdf"
  364. $rd1 close; $r2 close
  365. }
  366. test {just EXEC and script timeout} {
  367. # check that if EXEC arrives during timeout, we don't end up executing
  368. # actual commands during busy script, and also that we exit the multi state
  369. set rd1 [redis_deferring_client]
  370. set r2 [redis_client]
  371. r config set lua-time-limit 10
  372. r set xx 1
  373. catch { $r2 multi; } e
  374. catch { $r2 incr xx; } e
  375. $rd1 eval {while true do end} 0
  376. after 200
  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 we didn't execute the transaction
  383. assert { $xx == 1}
  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 {exec with write commands and state change} {
  390. # check that exec that contains write commands fails if server state changed since they were queued
  391. set r1 [redis_client]
  392. r set xx 1
  393. r multi
  394. r incr xx
  395. $r1 config set min-replicas-to-write 2
  396. catch {r exec} e
  397. assert_match {*EXECABORT*NOREPLICAS*} $e
  398. set xx [r get xx]
  399. # make sure that the INCR wasn't executed
  400. assert { $xx == 1}
  401. $r1 config set min-replicas-to-write 0
  402. $r1 close;
  403. }
  404. test {exec with read commands and stale replica state change} {
  405. # check that exec that contains read commands fails if server state changed since they were queued
  406. r config set replica-serve-stale-data no
  407. set r1 [redis_client]
  408. r set xx 1
  409. # check that GET is disallowed on stale replica, even if the replica becomes stale only after queuing.
  410. r multi
  411. r get xx
  412. $r1 replicaof localhsot 0
  413. catch {r exec} e
  414. assert_match {*EXECABORT*MASTERDOWN*} $e
  415. # check that PING is allowed
  416. r multi
  417. r ping
  418. $r1 replicaof localhsot 0
  419. set pong [r exec]
  420. assert {$pong == "PONG"}
  421. # check that when replica is not stale, GET is allowed
  422. # while we're at it, let's check that multi is allowed on stale replica too
  423. r multi
  424. $r1 replicaof no one
  425. r get xx
  426. set xx [r exec]
  427. # make sure that the INCR was executed
  428. assert { $xx == 1 }
  429. $r1 close;
  430. }
  431. }