aboutsummaryrefslogtreecommitdiff
path: root/examples/redis-unstable/tests/unit/pubsub.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'examples/redis-unstable/tests/unit/pubsub.tcl')
-rw-r--r--examples/redis-unstable/tests/unit/pubsub.tcl1016
1 files changed, 0 insertions, 1016 deletions
diff --git a/examples/redis-unstable/tests/unit/pubsub.tcl b/examples/redis-unstable/tests/unit/pubsub.tcl
deleted file mode 100644
index 24f779f..0000000
--- a/examples/redis-unstable/tests/unit/pubsub.tcl
+++ /dev/null
@@ -1,1016 +0,0 @@
1start_server {tags {"pubsub network"}} {
2 if {$::singledb} {
3 set db 0
4 } else {
5 set db 9
6 }
7
8 foreach resp {2 3} {
9 set rd1 [redis_deferring_client]
10 if {[lsearch $::denytags "resp3"] >= 0} {
11 if {$resp == 3} {continue}
12 } elseif {$::force_resp3} {
13 if {$resp == 2} {continue}
14 }
15
16 $rd1 hello $resp
17 $rd1 read
18
19 test "Pub/Sub PING on RESP$resp" {
20 subscribe $rd1 somechannel
21 # While subscribed to non-zero channels PING works in Pub/Sub mode.
22 $rd1 ping
23 $rd1 ping "foo"
24 # In RESP3, the SUBSCRIBEd client can issue any command and get a reply, so the PINGs are standard
25 # In RESP2, only a handful of commands are allowed after a client is SUBSCRIBED (PING is one of them).
26 # For some reason, the reply in that case is an array with two elements: "pong" and argv[1] or an empty string
27 # God knows why. Done in commit 2264b981
28 if {$resp == 3} {
29 assert_equal {PONG} [$rd1 read]
30 assert_equal {foo} [$rd1 read]
31 } else {
32 assert_equal {pong {}} [$rd1 read]
33 assert_equal {pong foo} [$rd1 read]
34 }
35 unsubscribe $rd1 somechannel
36 # Now we are unsubscribed, PING should just return PONG.
37 $rd1 ping
38 assert_equal {PONG} [$rd1 read]
39
40 }
41 $rd1 close
42 }
43
44 test "PUBLISH/SUBSCRIBE basics" {
45 set rd1 [redis_deferring_client]
46
47 # subscribe to two channels
48 assert_equal {1 2} [subscribe $rd1 {chan1 chan2}]
49 assert_equal 1 [r publish chan1 hello]
50 assert_equal 1 [r publish chan2 world]
51 assert_equal {message chan1 hello} [$rd1 read]
52 assert_equal {message chan2 world} [$rd1 read]
53
54 # unsubscribe from one of the channels
55 unsubscribe $rd1 {chan1}
56 assert_equal 0 [r publish chan1 hello]
57 assert_equal 1 [r publish chan2 world]
58 assert_equal {message chan2 world} [$rd1 read]
59
60 # unsubscribe from the remaining channel
61 unsubscribe $rd1 {chan2}
62 assert_equal 0 [r publish chan1 hello]
63 assert_equal 0 [r publish chan2 world]
64
65 # clean up clients
66 $rd1 close
67 }
68
69 test "PUBLISH/SUBSCRIBE with two clients" {
70 set rd1 [redis_deferring_client]
71 set rd2 [redis_deferring_client]
72
73 assert_equal {1} [subscribe $rd1 {chan1}]
74 assert_equal {1} [subscribe $rd2 {chan1}]
75 assert_equal 2 [r publish chan1 hello]
76 assert_equal {message chan1 hello} [$rd1 read]
77 assert_equal {message chan1 hello} [$rd2 read]
78
79 # clean up clients
80 $rd1 close
81 $rd2 close
82 }
83
84 test "PUBLISH/SUBSCRIBE after UNSUBSCRIBE without arguments" {
85 set rd1 [redis_deferring_client]
86 assert_equal {1 2 3} [subscribe $rd1 {chan1 chan2 chan3}]
87 unsubscribe $rd1
88 wait_for_condition 100 10 {
89 [regexp {cmd=unsubscribe} [r client list]] eq 1
90 } else {
91 fail "unsubscribe did not arrive"
92 }
93 assert_equal 0 [r publish chan1 hello]
94 assert_equal 0 [r publish chan2 hello]
95 assert_equal 0 [r publish chan3 hello]
96
97 # clean up clients
98 $rd1 close
99 }
100
101 test "SUBSCRIBE to one channel more than once" {
102 set rd1 [redis_deferring_client]
103 assert_equal {1 1 1} [subscribe $rd1 {chan1 chan1 chan1}]
104 assert_equal 1 [r publish chan1 hello]
105 assert_equal {message chan1 hello} [$rd1 read]
106
107 # clean up clients
108 $rd1 close
109 }
110
111 test "UNSUBSCRIBE from non-subscribed channels" {
112 set rd1 [redis_deferring_client]
113 assert_equal {0 0 0} [unsubscribe $rd1 {foo bar quux}]
114
115 # clean up clients
116 $rd1 close
117 }
118
119 test "PUBLISH/PSUBSCRIBE basics" {
120 set rd1 [redis_deferring_client]
121
122 # subscribe to two patterns
123 assert_equal {1 2} [psubscribe $rd1 {foo.* bar.*}]
124 assert_equal 1 [r publish foo.1 hello]
125 assert_equal 1 [r publish bar.1 hello]
126 assert_equal 0 [r publish foo1 hello]
127 assert_equal 0 [r publish barfoo.1 hello]
128 assert_equal 0 [r publish qux.1 hello]
129 assert_equal {pmessage foo.* foo.1 hello} [$rd1 read]
130 assert_equal {pmessage bar.* bar.1 hello} [$rd1 read]
131
132 # unsubscribe from one of the patterns
133 assert_equal {1} [punsubscribe $rd1 {foo.*}]
134 assert_equal 0 [r publish foo.1 hello]
135 assert_equal 1 [r publish bar.1 hello]
136 assert_equal {pmessage bar.* bar.1 hello} [$rd1 read]
137
138 # unsubscribe from the remaining pattern
139 assert_equal {0} [punsubscribe $rd1 {bar.*}]
140 assert_equal 0 [r publish foo.1 hello]
141 assert_equal 0 [r publish bar.1 hello]
142
143 # clean up clients
144 $rd1 close
145 }
146
147 test "PUBLISH/PSUBSCRIBE with two clients" {
148 set rd1 [redis_deferring_client]
149 set rd2 [redis_deferring_client]
150
151 assert_equal {1} [psubscribe $rd1 {chan.*}]
152 assert_equal {1} [psubscribe $rd2 {chan.*}]
153 assert_equal 2 [r publish chan.foo hello]
154 assert_equal {pmessage chan.* chan.foo hello} [$rd1 read]
155 assert_equal {pmessage chan.* chan.foo hello} [$rd2 read]
156
157 # clean up clients
158 $rd1 close
159 $rd2 close
160 }
161
162 test "PUBLISH/PSUBSCRIBE after PUNSUBSCRIBE without arguments" {
163 set rd1 [redis_deferring_client]
164 assert_equal {1 2 3} [psubscribe $rd1 {chan1.* chan2.* chan3.*}]
165 punsubscribe $rd1
166 wait_for_condition 100 10 {
167 [regexp {cmd=punsubscribe} [r client list]] eq 1
168 } else {
169 fail "punsubscribe did not arrive"
170 }
171 assert_equal 0 [r publish chan1.hi hello]
172 assert_equal 0 [r publish chan2.hi hello]
173 assert_equal 0 [r publish chan3.hi hello]
174
175 # clean up clients
176 $rd1 close
177 }
178
179 test "PubSub messages with CLIENT REPLY OFF" {
180 set rd [redis_deferring_client]
181 $rd hello 3
182 $rd read ;# Discard the hello reply
183
184 # Test that the subscribe/psubscribe notification is ok
185 $rd client reply off
186 assert_equal {1} [subscribe $rd channel]
187 assert_equal {2} [psubscribe $rd ch*]
188
189 # Test that the publish notification is ok
190 $rd client reply off
191 assert_equal 2 [r publish channel hello]
192 assert_equal {message channel hello} [$rd read]
193 assert_equal {pmessage ch* channel hello} [$rd read]
194
195 # Test that the unsubscribe/punsubscribe notification is ok
196 $rd client reply off
197 assert_equal {1} [unsubscribe $rd channel]
198 assert_equal {0} [punsubscribe $rd ch*]
199
200 $rd close
201 } {0} {resp3}
202
203 test "PUNSUBSCRIBE from non-subscribed channels" {
204 set rd1 [redis_deferring_client]
205 assert_equal {0 0 0} [punsubscribe $rd1 {foo.* bar.* quux.*}]
206
207 # clean up clients
208 $rd1 close
209 }
210
211 test "NUMSUB returns numbers, not strings (#1561)" {
212 r pubsub numsub abc def
213 } {abc 0 def 0}
214
215 test "NUMPATs returns the number of unique patterns" {
216 set rd1 [redis_deferring_client]
217 set rd2 [redis_deferring_client]
218
219 # Three unique patterns and one that overlaps
220 psubscribe $rd1 "foo*"
221 psubscribe $rd2 "foo*"
222 psubscribe $rd1 "bar*"
223 psubscribe $rd2 "baz*"
224
225 set patterns [r pubsub numpat]
226
227 # clean up clients
228 punsubscribe $rd1
229 punsubscribe $rd2
230 assert_equal 3 $patterns
231 $rd1 close
232 $rd2 close
233 }
234
235 test "Mix SUBSCRIBE and PSUBSCRIBE" {
236 set rd1 [redis_deferring_client]
237 assert_equal {1} [subscribe $rd1 {foo.bar}]
238 assert_equal {2} [psubscribe $rd1 {foo.*}]
239
240 assert_equal 2 [r publish foo.bar hello]
241 assert_equal {message foo.bar hello} [$rd1 read]
242 assert_equal {pmessage foo.* foo.bar hello} [$rd1 read]
243
244 # clean up clients
245 $rd1 close
246 }
247
248 test "PUNSUBSCRIBE and UNSUBSCRIBE should always reply" {
249 # Make sure we are not subscribed to any channel at all.
250 r punsubscribe
251 r unsubscribe
252 # Now check if the commands still reply correctly.
253 set reply1 [r punsubscribe]
254 set reply2 [r unsubscribe]
255 concat $reply1 $reply2
256 } {punsubscribe {} 0 unsubscribe {} 0}
257
258 ### Keyspace events notification tests
259
260 test "Keyspace notifications: we receive keyspace notifications" {
261 r config set notify-keyspace-events KA
262 set rd1 [redis_deferring_client]
263 $rd1 CLIENT REPLY OFF ;# Make sure it works even if replies are silenced
264 assert_equal {1} [psubscribe $rd1 *]
265 r set foo bar
266 assert_equal "pmessage * __keyspace@${db}__:foo set" [$rd1 read]
267 $rd1 close
268 }
269
270 test "Keyspace notifications: we receive keyevent notifications" {
271 r config set notify-keyspace-events EA
272 r del foo
273 set rd1 [redis_deferring_client]
274 $rd1 CLIENT REPLY SKIP ;# Make sure it works even if replies are silenced
275 assert_equal {1} [psubscribe $rd1 *]
276 r set foo bar
277 assert_equal "pmessage * __keyevent@${db}__:set foo" [$rd1 read]
278 $rd1 close
279 }
280
281 test "Keyspace notifications: we can receive both kind of events" {
282 r config set notify-keyspace-events KEA
283 r del foo
284 set rd1 [redis_deferring_client]
285 $rd1 CLIENT REPLY ON ;# Just coverage
286 assert_equal {OK} [$rd1 read]
287 assert_equal {1} [psubscribe $rd1 *]
288 r set foo bar
289 assert_equal "pmessage * __keyspace@${db}__:foo set" [$rd1 read]
290 assert_equal "pmessage * __keyevent@${db}__:set foo" [$rd1 read]
291 $rd1 close
292 }
293
294 test "Keyspace notifications: we are able to mask events" {
295 r config set notify-keyspace-events KEl
296 r del mylist
297 set rd1 [redis_deferring_client]
298 assert_equal {1} [psubscribe $rd1 *]
299 r set foo bar
300 r lpush mylist a
301 # No notification for set, because only list commands are enabled.
302 assert_equal "pmessage * __keyspace@${db}__:mylist lpush" [$rd1 read]
303 assert_equal "pmessage * __keyevent@${db}__:lpush mylist" [$rd1 read]
304 $rd1 close
305 }
306
307 test "Keyspace notifications: general events test" {
308 r config set notify-keyspace-events KEg
309 set rd1 [redis_deferring_client]
310 assert_equal {1} [psubscribe $rd1 *]
311 r set foo bar
312 r expire foo 1
313 r del foo
314 assert_equal "pmessage * __keyspace@${db}__:foo expire" [$rd1 read]
315 assert_equal "pmessage * __keyevent@${db}__:expire foo" [$rd1 read]
316 assert_equal "pmessage * __keyspace@${db}__:foo del" [$rd1 read]
317 assert_equal "pmessage * __keyevent@${db}__:del foo" [$rd1 read]
318 $rd1 close
319 }
320
321 test "Keyspace notifications: list events test" {
322 r config set notify-keyspace-events KEl
323 r del mylist
324 set rd1 [redis_deferring_client]
325 assert_equal {1} [psubscribe $rd1 *]
326 r lpush mylist a
327 r rpush mylist a
328 r rpop mylist
329 assert_equal "pmessage * __keyspace@${db}__:mylist lpush" [$rd1 read]
330 assert_equal "pmessage * __keyevent@${db}__:lpush mylist" [$rd1 read]
331 assert_equal "pmessage * __keyspace@${db}__:mylist rpush" [$rd1 read]
332 assert_equal "pmessage * __keyevent@${db}__:rpush mylist" [$rd1 read]
333 assert_equal "pmessage * __keyspace@${db}__:mylist rpop" [$rd1 read]
334 assert_equal "pmessage * __keyevent@${db}__:rpop mylist" [$rd1 read]
335 $rd1 close
336 }
337
338 test "Keyspace notifications: set events test" {
339 r config set notify-keyspace-events Ks
340 r del myset
341 set rd1 [redis_deferring_client]
342 assert_equal {1} [psubscribe $rd1 *]
343 r sadd myset a b c d
344 r srem myset x
345 r sadd myset x y z
346 r srem myset x
347 assert_equal "pmessage * __keyspace@${db}__:myset sadd" [$rd1 read]
348 assert_equal "pmessage * __keyspace@${db}__:myset sadd" [$rd1 read]
349 assert_equal "pmessage * __keyspace@${db}__:myset srem" [$rd1 read]
350 $rd1 close
351 }
352
353 test "Keyspace notifications: zset events test" {
354 r config set notify-keyspace-events Kz
355 r del myzset
356 set rd1 [redis_deferring_client]
357 assert_equal {1} [psubscribe $rd1 *]
358 r zadd myzset 1 a 2 b
359 r zrem myzset x
360 r zadd myzset 3 x 4 y 5 z
361 r zrem myzset x
362 assert_equal "pmessage * __keyspace@${db}__:myzset zadd" [$rd1 read]
363 assert_equal "pmessage * __keyspace@${db}__:myzset zadd" [$rd1 read]
364 assert_equal "pmessage * __keyspace@${db}__:myzset zrem" [$rd1 read]
365 $rd1 close
366 }
367
368 foreach {type max_lp_entries} {listpackex 512 hashtable 0} {
369 test "Keyspace notifications: hash events test ($type)" {
370 r config set hash-max-listpack-entries $max_lp_entries
371 r config set notify-keyspace-events Khg
372 r del myhash
373 set rd1 [redis_deferring_client]
374 assert_equal {1} [psubscribe $rd1 *]
375 r hmset myhash yes 1 no 0 f1 1 f2 2 f3_hdel 3
376 r hincrby myhash yes 10
377 r hexpire myhash 999999 FIELDS 1 yes
378 r hexpireat myhash [expr {[clock seconds] + 999999}] NX FIELDS 1 no
379 r hpexpire myhash 999999 FIELDS 1 yes
380 r hpersist myhash FIELDS 1 yes
381 r hpexpire myhash 0 FIELDS 1 yes
382 assert_encoding $type myhash
383 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
384 assert_equal "pmessage * __keyspace@${db}__:myhash hincrby" [$rd1 read]
385 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
386 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
387 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
388 assert_equal "pmessage * __keyspace@${db}__:myhash hpersist" [$rd1 read]
389 assert_equal "pmessage * __keyspace@${db}__:myhash hdel" [$rd1 read]
390
391 # Test that we will get `hexpired` notification when
392 # a hash field is removed by active expire.
393 r hpexpire myhash 10 FIELDS 1 no
394 after 100 ;# Wait for active expire
395 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
396 assert_equal "pmessage * __keyspace@${db}__:myhash hexpired" [$rd1 read]
397
398 # Test that when a field with TTL is deleted by commands like hdel without
399 # updating the global DS, active expire will not send a notification.
400 r hpexpire myhash 100 FIELDS 1 f3_hdel
401 r hdel myhash f3_hdel
402 after 200 ;# Wait for active expire
403 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
404 assert_equal "pmessage * __keyspace@${db}__:myhash hdel" [$rd1 read]
405
406 # Test that we will get `hexpired` notification when
407 # a hash field is removed by lazy expire.
408 r debug set-active-expire 0
409 r hpexpire myhash 10 FIELDS 2 f1 f2
410 after 20
411 r hmget myhash f1 f2 ;# Trigger lazy expire
412 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
413 # We should get only one `hexpired` notification even two fields was expired.
414 assert_equal "pmessage * __keyspace@${db}__:myhash hexpired" [$rd1 read]
415 # We should get a `del` notification after all fields were expired.
416 assert_equal "pmessage * __keyspace@${db}__:myhash del" [$rd1 read]
417 r debug set-active-expire 1
418
419
420 # Test HSETEX, HGETEX and HGETDEL notifications
421 r hsetex myhash FIELDS 3 f4 v4 f5 v5 f6 v6
422 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
423
424 # hgetex sets ttl in past
425 r hgetex myhash PX 0 FIELDS 1 f4
426 assert_equal "pmessage * __keyspace@${db}__:myhash hdel" [$rd1 read]
427
428 # hgetex sets ttl
429 r hgetex myhash EXAT [expr {[clock seconds] + 999999}] FIELDS 1 f5
430 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
431
432 # hgetex persists field
433 r hgetex myhash PERSIST FIELDS 1 f5
434 assert_equal "pmessage * __keyspace@${db}__:myhash hpersist" [$rd1 read]
435
436 # hgetex sets expiry for one field and lazy expiry deletes another field
437 # (KSN should be 1-hexpired 2-hexpire)
438 r debug set-active-expire 0
439 r hsetex myhash PX 1 FIELDS 1 f5 v5
440 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
441 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
442 after 10
443 r hgetex myhash EX 100 FIELDS 2 f5 f6
444 assert_equal "pmessage * __keyspace@${db}__:myhash hexpired" [$rd1 read]
445 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
446
447 # hgetex lazy expiry deletes the only field and the key
448 # (KSN should be 1-hexpired 2-del)
449 r hsetex myhash PX 1 FIELDS 2 f5 v5 f6 v6
450 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
451 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
452 after 10
453 r hgetex myhash FIELDS 2 f5 f6
454 assert_equal "pmessage * __keyspace@${db}__:myhash hexpired" [$rd1 read]
455 assert_equal "pmessage * __keyspace@${db}__:myhash del" [$rd1 read]
456 r debug set-active-expire 1
457
458 # hgetex sets an expired ttl for the only field and deletes the key
459 # (KSN should be 1-hdel 2-del)
460 r hsetex myhash EX 100 FIELDS 1 f5 v5
461 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
462 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
463 after 10
464 r hgetex myhash PX 0 FIELDS 1 f5
465 assert_equal "pmessage * __keyspace@${db}__:myhash hdel" [$rd1 read]
466 assert_equal "pmessage * __keyspace@${db}__:myhash del" [$rd1 read]
467
468 r hsetex myhash FIELDS 2 f5 v5 f6 v6
469 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
470
471 # hgetdel deletes a field
472 r hgetdel myhash FIELDS 1 f5
473 assert_equal "pmessage * __keyspace@${db}__:myhash hdel" [$rd1 read]
474
475 # hsetex sets field and expiry time
476 r hsetex myhash EXAT [expr {[clock seconds] + 999999}] FIELDS 1 f6 v6
477 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
478 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
479
480 # hsetex sets field and ttl in the past
481 r hsetex myhash PX 0 FIELDS 1 f6 v6
482 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
483 assert_equal "pmessage * __keyspace@${db}__:myhash hdel" [$rd1 read]
484 assert_equal "pmessage * __keyspace@${db}__:myhash del" [$rd1 read]
485
486 # Test that we will get `hexpired` notification when a hash field is
487 # removed by lazy expire using hgetdel command
488 r debug set-active-expire 0
489 r hsetex myhash PX 10 FIELDS 1 f1 v1
490 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
491 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
492
493 # Set another field
494 r hsetex myhash FIELDS 1 f2 v2
495 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
496 # Wait until field expires
497 after 20
498 r hgetdel myhash FIELDS 1 f1
499 assert_equal "pmessage * __keyspace@${db}__:myhash hexpired" [$rd1 read]
500 # Get and delete the only field
501 r hgetdel myhash FIELDS 1 f2
502 assert_equal "pmessage * __keyspace@${db}__:myhash hdel" [$rd1 read]
503 assert_equal "pmessage * __keyspace@${db}__:myhash del" [$rd1 read]
504
505 # HGETDEL deletes one field and the other field is lazily expired
506 # (KSN should be 1-hexpired 2-hdel)
507 r hsetex myhash FIELDS 2 f1 v1 f2 v2
508 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
509 r hsetex myhash PX 1 FIELDS 1 f3 v3
510 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
511 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
512 after 10
513 r hgetdel myhash FIELDS 2 f1 f3
514 assert_equal "pmessage * __keyspace@${db}__:myhash hexpired" [$rd1 read]
515 assert_equal "pmessage * __keyspace@${db}__:myhash hdel" [$rd1 read]
516
517 # HGETDEL, deletes one field and the last field lazily expires
518 # (KSN should be 1-hexpired 2-hdel 3-del)
519 r hsetex myhash FIELDS 1 f1 v1
520 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
521 r hsetex myhash PX 1 FIELDS 1 f2 v2
522 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
523 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
524 after 10
525 r hgetdel myhash FIELDS 2 f1 f2
526 assert_equal "pmessage * __keyspace@${db}__:myhash hexpired" [$rd1 read]
527 assert_equal "pmessage * __keyspace@${db}__:myhash hdel" [$rd1 read]
528 assert_equal "pmessage * __keyspace@${db}__:myhash del" [$rd1 read]
529 r debug set-active-expire 1
530
531 $rd1 close
532 } {0} {needs:debug}
533 } ;# foreach
534
535 test "Keyspace notifications: stream events test" {
536 r config set notify-keyspace-events Kt
537 r del mystream
538 set rd1 [redis_deferring_client]
539 assert_equal {1} [psubscribe $rd1 *]
540 r xgroup create mystream mygroup $ mkstream
541 r xgroup createconsumer mystream mygroup Bob
542 set id [r xadd mystream 1 field1 A]
543 r xreadgroup group mygroup Alice STREAMS mystream >
544 r xclaim mystream mygroup Mike 0 $id force
545 # Not notify because of "Lee" not exists.
546 r xgroup delconsumer mystream mygroup Lee
547 # Not notify because of "Bob" exists.
548 r xautoclaim mystream mygroup Bob 0 $id
549 r xgroup delconsumer mystream mygroup Bob
550 assert_equal "pmessage * __keyspace@${db}__:mystream xgroup-create" [$rd1 read]
551 assert_equal "pmessage * __keyspace@${db}__:mystream xgroup-createconsumer" [$rd1 read]
552 assert_equal "pmessage * __keyspace@${db}__:mystream xadd" [$rd1 read]
553 assert_equal "pmessage * __keyspace@${db}__:mystream xgroup-createconsumer" [$rd1 read]
554 assert_equal "pmessage * __keyspace@${db}__:mystream xgroup-createconsumer" [$rd1 read]
555 assert_equal "pmessage * __keyspace@${db}__:mystream xgroup-delconsumer" [$rd1 read]
556 $rd1 close
557 }
558
559 test "Keyspace notifications:FXX/FNX with HSETEX cmd" {
560 r config set notify-keyspace-events Khxg
561 r del myhash
562 set rd1 [redis_deferring_client]
563 assert_equal {1} [psubscribe $rd1 *]
564 r debug set-active-expire 0
565
566 # FXX on logically expired field
567 r hset myhash f v
568 r hset myhash f2 v
569 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
570 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
571 r hpexpire myhash 10 FIELDS 1 f
572 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
573 after 15
574 assert_equal [r HSETEX myhash FXX PX 10 FIELDS 1 f v] 0
575 assert_equal "pmessage * __keyspace@${db}__:myhash hexpired" [$rd1 read]
576 r hdel myhash f2
577 assert_equal "pmessage * __keyspace@${db}__:myhash hdel" [$rd1 read]
578 assert_equal 0 [r exists myhash]
579 assert_equal "pmessage * __keyspace@${db}__:myhash del" [$rd1 read]
580
581 # FXX with past expiry
582 r HSET myhash f1 v1
583 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
584 set past [expr {[clock seconds] - 2}]
585 assert_equal [r hsetex myhash FXX EXAT $past FIELDS 1 f1 v1] 1
586 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
587 assert_equal "pmessage * __keyspace@${db}__:myhash hdel" [$rd1 read]
588 assert_equal "pmessage * __keyspace@${db}__:myhash del" [$rd1 read]
589
590 # FXX overwrite + full key expiry
591 r hset myhash f v
592 r hset myhash f2 v
593 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
594 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
595 r hpexpire myhash 10 FIELDS 1 f
596 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
597 after 15
598 set past [expr {[clock milliseconds] - 5000}]
599 assert_equal [r hsetex myhash FXX PXAT $past FIELDS 1 f v] 0
600 assert_equal "pmessage * __keyspace@${db}__:myhash hexpired" [$rd1 read]
601 r hpexpire myhash 10 FIELDS 1 f2
602 after 15
603 r hget myhash f2
604 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
605 assert_equal "pmessage * __keyspace@${db}__:myhash hexpired" [$rd1 read]
606 assert_equal "pmessage * __keyspace@${db}__:myhash del" [$rd1 read]
607
608 # FNX on logically expired field
609 r del myhash
610 r hset myhash f v
611 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
612 r hpexpire myhash 10 FIELDS 1 f
613 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
614 after 15
615 assert_equal [r HSETEX myhash FNX PX 1000 FIELDS 1 f v] 1
616 assert_equal "pmessage * __keyspace@${db}__:myhash hexpired" [$rd1 read]
617 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
618 assert_equal "pmessage * __keyspace@${db}__:myhash hexpire" [$rd1 read]
619
620 # FNX with past expiry
621 r del myhash
622 r hset myhash f v
623 assert_equal "pmessage * __keyspace@${db}__:myhash del" [$rd1 read]
624 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
625 set past [expr {[clock seconds] - 2}]
626 assert_equal [r hsetex myhash FNX EXAT $past FIELDS 1 f1 v1] 1
627 # f1 is created and immediately expired
628 assert_equal "pmessage * __keyspace@${db}__:myhash hset" [$rd1 read]
629 assert_equal "pmessage * __keyspace@${db}__:myhash hdel" [$rd1 read]
630
631 r debug set-active-expire 1
632 $rd1 close
633 } {0} {needs:debug}
634
635 test "Keyspace notifications: expired events (triggered expire)" {
636 r config set notify-keyspace-events Ex
637 r del foo
638 set rd1 [redis_deferring_client]
639 assert_equal {1} [psubscribe $rd1 *]
640 r psetex foo 100 1
641 wait_for_condition 50 100 {
642 [r exists foo] == 0
643 } else {
644 fail "Key does not expire?!"
645 }
646 assert_equal "pmessage * __keyevent@${db}__:expired foo" [$rd1 read]
647 $rd1 close
648 }
649
650 test "Keyspace notifications: expired events (background expire)" {
651 r config set notify-keyspace-events Ex
652 r del foo
653 set rd1 [redis_deferring_client]
654 assert_equal {1} [psubscribe $rd1 *]
655 r psetex foo 100 1
656 assert_equal "pmessage * __keyevent@${db}__:expired foo" [$rd1 read]
657 $rd1 close
658 }
659
660 test "Keyspace notifications: evicted events" {
661 r config set notify-keyspace-events Ee
662 r config set maxmemory-policy allkeys-lru
663 r flushdb
664 set rd1 [redis_deferring_client]
665 assert_equal {1} [psubscribe $rd1 *]
666 r set foo bar
667 r config set maxmemory 1
668 assert_equal "pmessage * __keyevent@${db}__:evicted foo" [$rd1 read]
669 r config set maxmemory 0
670 $rd1 close
671 r config set maxmemory-policy noeviction
672 } {OK} {needs:config-maxmemory}
673
674 test "Keyspace notifications: test CONFIG GET/SET of event flags" {
675 r config set notify-keyspace-events gKE
676 assert_equal {gKE} [lindex [r config get notify-keyspace-events] 1]
677 r config set notify-keyspace-events {$lshzxeKE}
678 assert_equal {$lshzxeKE} [lindex [r config get notify-keyspace-events] 1]
679 r config set notify-keyspace-events KA
680 assert_equal {AK} [lindex [r config get notify-keyspace-events] 1]
681 r config set notify-keyspace-events EA
682 assert_equal {AE} [lindex [r config get notify-keyspace-events] 1]
683 }
684
685 test "Keyspace notifications: new key test" {
686 r config set notify-keyspace-events En
687 set rd1 [redis_deferring_client]
688 assert_equal {1} [psubscribe $rd1 *]
689 r set foo bar
690 # second set of foo should not cause a 'new' event
691 r set foo baz
692 r set bar bar
693 assert_equal "pmessage * __keyevent@${db}__:new foo" [$rd1 read]
694 assert_equal "pmessage * __keyevent@${db}__:new bar" [$rd1 read]
695 $rd1 close
696 }
697
698 ### overwritten and type_changed events
699
700 test "Keyspace notifications: overwritten events - string to string" {
701 r config set notify-keyspace-events Eo
702 r del foo
703 set rd1 [redis_deferring_client]
704 assert_equal {1} [psubscribe $rd1 *]
705
706 # First set - should not trigger overwritten (new key)
707 r set foo bar
708
709 # Second set - should trigger overwritten (same type)
710 r set foo baz
711
712 assert_equal "pmessage * __keyevent@${db}__:overwritten foo" [$rd1 read]
713 $rd1 close
714 }
715
716 test "Keyspace notifications: type_changed events - hash to string" {
717 r config set notify-keyspace-events Ec
718 r del testkey
719 set rd1 [redis_deferring_client]
720 assert_equal {1} [psubscribe $rd1 *]
721
722 # Set as hash first
723 r hset testkey field "hash_value"
724
725 # Change to string - should trigger type_changed
726 r set testkey "string_value"
727
728 assert_equal "pmessage * __keyevent@${db}__:type_changed testkey" [$rd1 read]
729 $rd1 close
730 }
731
732 test "Keyspace notifications: both overwritten and type_changed events" {
733 r config set notify-keyspace-events Eoc
734 r del testkey3
735 set rd1 [redis_deferring_client]
736 assert_equal {1} [psubscribe $rd1 *]
737
738 # Set as hash first
739 r hset testkey3 field "hash_value"
740
741 # Change to string - should trigger both overwritten and type_changed
742 r set testkey3 "string_value"
743
744 assert_equal "pmessage * __keyevent@${db}__:overwritten testkey3" [$rd1 read]
745 assert_equal "pmessage * __keyevent@${db}__:type_changed testkey3" [$rd1 read]
746
747 $rd1 close
748 }
749
750 test "Keyspace notifications: configuration flags work correctly" {
751 # Test that 'o' flag enables override notifications
752 r config set notify-keyspace-events o
753 set config [r config get notify-keyspace-events]
754 assert {[lindex $config 1] eq "o"}
755
756 # Test that 'c' flag enables type_changed notifications
757 r config set notify-keyspace-events c
758 set config [r config get notify-keyspace-events]
759 assert {[lindex $config 1] eq "c"}
760
761 # Test that both flags can be combined
762 r config set notify-keyspace-events oc
763 set config [r config get notify-keyspace-events]
764 assert {[lindex $config 1] eq "oc"}
765 }
766
767 ### RESTORE command tests for type_changed KSN types
768
769 test "Keyspace notifications: RESTORE REPLACE different type - restore, overwritten and type_changed events" {
770 r config set notify-keyspace-events Egoc
771 r del restore_test_key3
772
773 # Create a string value and dump it (do this before subscribing)
774 r set temp_key "string_value"
775 set dump_data [r dump temp_key]
776 r del temp_key
777
778 set rd1 [redis_deferring_client]
779 assert_equal {1} [psubscribe $rd1 *]
780
781 # Create initial hash key
782 r hset restore_test_key3 field "hash_value"
783
784 # Restore with REPLACE - should emit restore, overwritten and type_changed events
785 r restore restore_test_key3 0 $dump_data REPLACE
786
787 assert_equal "pmessage * __keyevent@${db}__:restore restore_test_key3" [$rd1 read]
788 assert_equal "pmessage * __keyevent@${db}__:overwritten restore_test_key3" [$rd1 read]
789 assert_equal "pmessage * __keyevent@${db}__:type_changed restore_test_key3" [$rd1 read]
790
791 $rd1 close
792 }
793
794 ### SET command tests for overwritten and type_changed KSN types
795
796 test "Keyspace notifications: SET on existing string key - overwritten event" {
797 r config set notify-keyspace-events EAo
798 r del set_test_key1
799 set rd1 [redis_deferring_client]
800 assert_equal {1} [psubscribe $rd1 *]
801
802 # Create initial string key
803 r set set_test_key1 "initial_value"
804 assert_equal "pmessage * __keyevent@${db}__:set set_test_key1" [$rd1 read]
805
806 # Set new value on existing string key - should emit overwritten event
807 r set set_test_key1 "new_value"
808
809 assert_equal "pmessage * __keyevent@${db}__:overwritten set_test_key1" [$rd1 read]
810 assert_equal "pmessage * __keyevent@${db}__:set set_test_key1" [$rd1 read]
811
812 $rd1 close
813 }
814
815 test "Keyspace notifications: setKey on existing different type key - overwritten and type_changed events" {
816 r config set notify-keyspace-events Eoc
817
818 set rd1 [redis_deferring_client]
819 assert_equal {1} [psubscribe $rd1 *]
820
821 r flushdb
822 r hset set_test_key2 field "hash_value"
823 r set set_test_key2 "string_value"
824 assert_equal "pmessage * __keyevent@${db}__:overwritten set_test_key2" [$rd1 read]
825 assert_equal "pmessage * __keyevent@${db}__:type_changed set_test_key2" [$rd1 read]
826
827 # overwritten and type_changed events should be emitted for any->any
828 # type conversion that uses the setKey command
829 r flushdb
830 r lpush l{t} 1 2 3
831 r sadd s1{t} "A"
832 r sadd s2{t} "B"
833 r sunionstore l{t} s1{t} s2{t}
834 assert_equal "pmessage * __keyevent@${db}__:overwritten l{t}" [$rd1 read]
835 assert_equal "pmessage * __keyevent@${db}__:type_changed l{t}" [$rd1 read]
836
837 r flushdb
838 r sadd s1{t} "A"
839 r set x{t} "\x0f"
840 r set y{t} "\xff"
841 r bitop and s1{t} x{t} y{t}
842 assert_equal "pmessage * __keyevent@${db}__:overwritten s1{t}" [$rd1 read]
843 assert_equal "pmessage * __keyevent@${db}__:type_changed s1{t}" [$rd1 read]
844
845 $rd1 close
846 }
847
848 test "Keyspace notifications: overwritten and type_changed events for RENAME and COPY commands" {
849 r config set notify-keyspace-events Eoc
850
851 set rd1 [redis_deferring_client]
852 assert_equal {1} [psubscribe $rd1 *]
853
854 # test COPY events
855 r flushdb
856 r hset hs{t} 1 2 3 4
857 r lpush l{t} 1 2 3 4
858 r copy hs{t} l{t} replace
859
860 assert_equal "pmessage * __keyevent@${db}__:overwritten l{t}" [$rd1 read]
861 assert_equal "pmessage * __keyevent@${db}__:type_changed l{t}" [$rd1 read]
862
863 # test rename RENAME events
864 r flushdb
865 r hset hs{t} field "hash_value"
866 r sadd x{t} 1 2 3
867 r rename x{t} hs{t}
868
869 assert_equal "pmessage * __keyevent@${db}__:overwritten hs{t}" [$rd1 read]
870 assert_equal "pmessage * __keyevent@${db}__:type_changed hs{t}" [$rd1 read]
871
872 $rd1 close
873 }
874
875 test "Keyspace notifications: overwritten and type_changed for *STORE* commands" {
876 r config set notify-keyspace-events Eoc
877
878 set rd1 [redis_deferring_client]
879 assert_equal {1} [psubscribe $rd1 *]
880
881 r flushdb
882 r set x{t} x
883
884 # SORT
885 r lpush l{t} 4 3 2 1
886 r sort l{t} store x{t}
887 assert_equal "pmessage * __keyevent@${db}__:overwritten x{t}" [$rd1 read]
888 assert_equal "pmessage * __keyevent@${db}__:type_changed x{t}" [$rd1 read]
889
890 # SDIFFSTORE
891 r sadd s1{t} a b c d
892 r sadd s2{t} b e f
893 r sdiffstore x{t} s1{t} s2{t}
894 assert_equal "pmessage * __keyevent@${db}__:overwritten x{t}" [$rd1 read]
895 assert_equal "pmessage * __keyevent@${db}__:type_changed x{t}" [$rd1 read]
896
897 # SINTERSTORE
898 r set d1{t} x
899 r sinterstore d1{t} s1{t} s2{t}
900 assert_equal "pmessage * __keyevent@${db}__:overwritten d1{t}" [$rd1 read]
901 assert_equal "pmessage * __keyevent@${db}__:type_changed d1{t}" [$rd1 read]
902
903 # SUNIONSTORE
904 r set d2{t} x
905 r sunionstore d2{t} s1{t} s2{t}
906 assert_equal "pmessage * __keyevent@${db}__:overwritten d2{t}" [$rd1 read]
907 assert_equal "pmessage * __keyevent@${db}__:type_changed d2{t}" [$rd1 read]
908
909 # ZUNIONSTORE
910 r set d3{t} x
911 r zadd z1{t} 1 a 2 b
912 r zadd z2{t} 3 c 4 d
913 r zunionstore d3{t} 2 z1{t} z2{t}
914 assert_equal "pmessage * __keyevent@${db}__:overwritten d3{t}" [$rd1 read]
915 assert_equal "pmessage * __keyevent@${db}__:type_changed d3{t}" [$rd1 read]
916
917 # ZINTERSTORE
918 r set d4{t} x
919 r zadd z2{t} 2 a
920 r zinterstore d4{t} 2 z1{t} z2{t}
921 assert_equal "pmessage * __keyevent@${db}__:overwritten d4{t}" [$rd1 read]
922 assert_equal "pmessage * __keyevent@${db}__:type_changed d4{t}" [$rd1 read]
923
924 # ZDIFFSTORE
925 r set d5{t} x
926 r zdiffstore d5{t} 2 z1{t} z2{t}
927 assert_equal "pmessage * __keyevent@${db}__:overwritten d5{t}" [$rd1 read]
928 assert_equal "pmessage * __keyevent@${db}__:type_changed d5{t}" [$rd1 read]
929
930 # ZRANGESTORE
931 r set d6{t} x
932 r zadd zsrc{t} 1 a 2 b 3 c 4 d
933 r zrangestore d6{t} zsrc{t} 1 2
934 assert_equal "pmessage * __keyevent@${db}__:overwritten d6{t}" [$rd1 read]
935 assert_equal "pmessage * __keyevent@${db}__:type_changed d6{t}" [$rd1 read]
936
937 # GEORADIUS with STORE
938 r set d7{t} x
939 r geoadd geo{t} 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
940 r georadius geo{t} 15 37 200 km store d7{t}
941 assert_equal "pmessage * __keyevent@${db}__:overwritten d7{t}" [$rd1 read]
942 assert_equal "pmessage * __keyevent@${db}__:type_changed d7{t}" [$rd1 read]
943
944 # GEORADIUS with STOREDIST
945 r set d8{t} x
946 r georadius geo{t} 15 37 200 km storedist d8{t}
947 assert_equal "pmessage * __keyevent@${db}__:overwritten d8{t}" [$rd1 read]
948 assert_equal "pmessage * __keyevent@${db}__:type_changed d8{t}" [$rd1 read]
949
950 # GEOSEARCHSTORE
951 r set d9{t} x
952 r geosearchstore d9{t} geo{t} fromlonlat 15 37 byradius 200 km
953 assert_equal "pmessage * __keyevent@${db}__:overwritten d9{t}" [$rd1 read]
954 assert_equal "pmessage * __keyevent@${db}__:type_changed d9{t}" [$rd1 read]
955
956 # GEOSEARCHSTORE with STOREDIST
957 r set d10{t} x
958 r geosearchstore d10{t} geo{t} fromlonlat 15 37 byradius 200 km storedist
959 assert_equal "pmessage * __keyevent@${db}__:overwritten d10{t}" [$rd1 read]
960 assert_equal "pmessage * __keyevent@${db}__:type_changed d10{t}" [$rd1 read]
961
962 $rd1 close
963 }
964
965 test "publish to self inside multi" {
966 r hello 3
967 r subscribe foo
968 r multi
969 r ping abc
970 r publish foo bar
971 r publish foo vaz
972 r ping def
973 assert_equal [r exec] {abc 1 1 def}
974 assert_equal [r read] {message foo bar}
975 assert_equal [r read] {message foo vaz}
976 } {} {resp3}
977
978 test "publish to self inside script" {
979 r hello 3
980 r subscribe foo
981 set res [r eval {
982 redis.call("ping","abc")
983 redis.call("publish","foo","bar")
984 redis.call("publish","foo","vaz")
985 redis.call("ping","def")
986 return "bla"} 0]
987 assert_equal $res {bla}
988 assert_equal [r read] {message foo bar}
989 assert_equal [r read] {message foo vaz}
990 } {} {resp3}
991
992 test "unsubscribe inside multi, and publish to self" {
993 r hello 3
994
995 # Note: SUBSCRIBE and UNSUBSCRIBE with multiple channels in the same command,
996 # breaks the multi response, see https://github.com/redis/redis/issues/12207
997 # this is just a temporary sanity test to detect unintended breakage.
998
999 # subscribe for 3 channels actually emits 3 "responses"
1000 assert_equal "subscribe foo 1" [r subscribe foo bar baz]
1001 assert_equal "subscribe bar 2" [r read]
1002 assert_equal "subscribe baz 3" [r read]
1003
1004 r multi
1005 r ping abc
1006 r unsubscribe bar
1007 r unsubscribe baz
1008 r ping def
1009 assert_equal [r exec] {abc {unsubscribe bar 2} {unsubscribe baz 1} def}
1010
1011 # published message comes after the publish command's response.
1012 assert_equal [r publish foo vaz] {1}
1013 assert_equal [r read] {message foo vaz}
1014 } {} {resp3}
1015
1016}