summaryrefslogtreecommitdiffstats
path: root/tests/unit/moduleapi/async_rm_call.tcl
blob: 1bf12de2377c351a6a2e0b4e21ec278aa274966a (plain)
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
set testmodule [file normalize tests/modules/blockedclient.so]
set testmodule2 [file normalize tests/modules/postnotifications.so]
set testmodule3 [file normalize tests/modules/blockonkeys.so]

start_server {tags {"modules"}} {
    r module load $testmodule

    test {Locked GIL acquisition from async RM_Call} {
        assert_equal {OK} [r do_rm_call_async acquire_gil]
    }

    test "Blpop on async RM_Call fire and forget" {
        assert_equal {Blocked} [r do_rm_call_fire_and_forget blpop l 0]
        r lpush l a
        assert_equal {0} [r llen l]
    }

    test "Blpop on threaded async RM_Call" {
        set rd [redis_deferring_client]

        $rd do_rm_call_async_on_thread blpop l 0
        wait_for_blocked_clients_count 1
        r lpush l a
        assert_equal [$rd read] {l a}
        wait_for_blocked_clients_count 0
        $rd close
    }

    foreach cmd {do_rm_call_async do_rm_call_async_script_mode } {

        test "Blpop on async RM_Call using $cmd" {
            set rd [redis_deferring_client]

            $rd $cmd blpop l 0
            wait_for_blocked_clients_count 1
            r lpush l a
            assert_equal [$rd read] {l a}
            wait_for_blocked_clients_count 0
            $rd close
        }

        test "Brpop on async RM_Call using $cmd" {
            set rd [redis_deferring_client]

            $rd $cmd brpop l 0
            wait_for_blocked_clients_count 1
            r lpush l a
            assert_equal [$rd read] {l a}
            wait_for_blocked_clients_count 0
            $rd close
        }

        test "Brpoplpush on async RM_Call using $cmd" {
            set rd [redis_deferring_client]

            $rd $cmd brpoplpush l1 l2 0
            wait_for_blocked_clients_count 1
            r lpush l1 a
            assert_equal [$rd read] {a}
            wait_for_blocked_clients_count 0
            $rd close
            r lpop l2
        } {a}

        test "Blmove on async RM_Call using $cmd" {
            set rd [redis_deferring_client]

            $rd $cmd blmove l1 l2 LEFT LEFT 0
            wait_for_blocked_clients_count 1
            r lpush l1 a
            assert_equal [$rd read] {a}
            wait_for_blocked_clients_count 0
            $rd close
            r lpop l2
        } {a}

        test "Bzpopmin on async RM_Call using $cmd" {
            set rd [redis_deferring_client]

            $rd $cmd bzpopmin s 0
            wait_for_blocked_clients_count 1
            r zadd s 10 foo
            assert_equal [$rd read] {s foo 10}
            wait_for_blocked_clients_count 0
            $rd close
        }

        test "Bzpopmax on async RM_Call using $cmd" {
            set rd [redis_deferring_client]

            $rd $cmd bzpopmax s 0
            wait_for_blocked_clients_count 1
            r zadd s 10 foo
            assert_equal [$rd read] {s foo 10}
            wait_for_blocked_clients_count 0
            $rd close
        }
    }

    test {Nested async RM_Call} {
        set rd [redis_deferring_client]

        $rd do_rm_call_async do_rm_call_async do_rm_call_async do_rm_call_async blpop l 0
        wait_for_blocked_clients_count 1
        r lpush l a
        assert_equal [$rd read] {l a}
        wait_for_blocked_clients_count 0
        $rd close
    }

    test {Test multiple async RM_Call waiting on the same event} {
        set rd1 [redis_deferring_client]
        set rd2 [redis_deferring_client]

        $rd1 do_rm_call_async do_rm_call_async do_rm_call_async do_rm_call_async blpop l 0
        $rd2 do_rm_call_async do_rm_call_async do_rm_call_async do_rm_call_async blpop l 0
        wait_for_blocked_clients_count 2
        r lpush l element element
        assert_equal [$rd1 read] {l element}
        assert_equal [$rd2 read] {l element}
        wait_for_blocked_clients_count 0
        $rd1 close
        $rd2 close
    }

    test {async RM_Call calls RM_Call} {
        assert_equal {PONG} [r do_rm_call_async do_rm_call ping]
    }

    test {async RM_Call calls background RM_Call calls RM_Call} {
        assert_equal {PONG} [r do_rm_call_async do_bg_rm_call do_rm_call ping]
    }

    test {async RM_Call calls background RM_Call calls RM_Call calls async RM_Call} {
        assert_equal {PONG} [r do_rm_call_async do_bg_rm_call do_rm_call do_rm_call_async ping]
    }

    test {async RM_Call inside async RM_Call callback} {
        set rd [redis_deferring_client]
        $rd wait_and_do_rm_call blpop l 0
        wait_for_blocked_clients_count 1

        start_server {} {
            test "Connect a replica to the master instance" {
                r slaveof [srv -1 host] [srv -1 port]
                wait_for_condition 50 100 {
                    [s role] eq {slave} &&
                    [string match {*master_link_status:up*} [r info replication]]
                } else {
                    fail "Can't turn the instance into a replica"
                }
            }

            assert_equal {1} [r -1 lpush l a]
            assert_equal [$rd read] {l a}
        }

        wait_for_blocked_clients_count 0
        $rd close
    }

    test {Become replica while having async RM_Call running} {
        r flushall
        set rd [redis_deferring_client]
        $rd do_rm_call_async blpop l 0
        wait_for_blocked_clients_count 1

        #become a replica of a not existing redis
        r replicaof localhost 30000

        catch {[$rd read]} e
        assert_match {UNBLOCKED force unblock from blocking operation*} $e
        wait_for_blocked_clients_count 0

        r replicaof no one

        r lpush l 1
        # make sure the async rm_call was aborted
        assert_equal [r llen l] {1}
        $rd close
    }

    test {Pipeline with blocking RM_Call} {
        r flushall
        set rd [redis_deferring_client]
        set buf ""
        append buf "do_rm_call_async blpop l 0\r\n"
        append buf "ping\r\n"
        $rd write $buf
        $rd flush
        wait_for_blocked_clients_count 1

        # release the blocked client
        r lpush l 1

        assert_equal [$rd read] {l 1}
        assert_equal [$rd read] {PONG}

        wait_for_blocked_clients_count 0
        $rd close
    }

    test {blocking RM_Call abort} {
        r flushall
        set rd [redis_deferring_client]
        
        $rd client id
        set client_id [$rd read]

        $rd do_rm_call_async blpop l 0
        wait_for_blocked_clients_count 1

        r client kill ID $client_id
        assert_error {*error reading reply*} {$rd read}

        wait_for_blocked_clients_count 0

        r lpush l 1
        # make sure the async rm_call was aborted
        assert_equal [r llen l] {1}
        $rd close
    }
}

start_server {tags {"modules"}} {
    r module load $testmodule

    test {Test basic replication stream on unblock handler} {
        r flushall
        set repl [attach_to_replication_stream]

        set rd [redis_deferring_client]

        $rd do_rm_call_async blpop l 0
        wait_for_blocked_clients_count 1
        r lpush l a
        assert_equal [$rd read] {l a}

        assert_replication_stream $repl {
            {select *}
            {lpush l a}
            {lpop l}
        }
        close_replication_stream $repl

        wait_for_blocked_clients_count 0
        $rd close
    }

    test {Test unblock handler are executed as a unit} {
        r flushall
        set repl [attach_to_replication_stream]

        set rd [redis_deferring_client]

        $rd blpop_and_set_multiple_keys l x 1 y 2
        wait_for_blocked_clients_count 1
        r lpush l a
        assert_equal [$rd read] {OK}

        assert_replication_stream $repl {
            {select *}
            {lpush l a}
            {multi}
            {lpop l}
            {set x 1}
            {set y 2}
            {exec}
        }
        close_replication_stream $repl

        wait_for_blocked_clients_count 0
        $rd close
    }

    test {Test no propagation of blocking command} {
        r flushall
        set repl [attach_to_replication_stream]

        set rd [redis_deferring_client]

        $rd do_rm_call_async_no_replicate blpop l 0
        wait_for_blocked_clients_count 1
        r lpush l a
        assert_equal [$rd read] {l a}

        # make sure the lpop are not replicated
        r set x 1

        assert_replication_stream $repl {
            {select *}
            {lpush l a}
            {set x 1}
        }
        close_replication_stream $repl

        wait_for_blocked_clients_count 0
        $rd close
    }
}

start_server {tags {"modules"}} {
    r module load $testmodule
    r module load $testmodule2

    test {Test unblock handler are executed as a unit with key space notifications} {
        r flushall
        set repl [attach_to_replication_stream]

        set rd [redis_deferring_client]

        $rd blpop_and_set_multiple_keys l string_foo 1 string_bar 2
        wait_for_blocked_clients_count 1
        r lpush l a
        assert_equal [$rd read] {OK}

        # Explanation of the first multi exec block:
        # {lpop l} - pop the value by our blocking command 'blpop_and_set_multiple_keys'
        # {set string_foo 1} - the action of our blocking command 'blpop_and_set_multiple_keys'
        # {set string_bar 2} - the action of our blocking command 'blpop_and_set_multiple_keys'
        # {incr string_changed{string_foo}} - post notification job that was registered when 'string_foo' changed
        # {incr string_changed{string_bar}} - post notification job that was registered when 'string_bar' changed
        # {incr string_total} - post notification job that was registered when string_changed{string_foo} changed
        # {incr string_total} - post notification job that was registered when string_changed{string_bar} changed
        assert_replication_stream $repl {
            {select *}
            {lpush l a}
            {multi}
            {lpop l}
            {set string_foo 1}
            {set string_bar 2}
            {incr string_changed{string_foo}}
            {incr string_changed{string_bar}}
            {incr string_total}
            {incr string_total}
            {exec}
        }
        close_replication_stream $repl

        wait_for_blocked_clients_count 0
        $rd close
    }

    test {Test unblock handler are executed as a unit with lazy expire} {
        r flushall
        r DEBUG SET-ACTIVE-EXPIRE 0
        set repl [attach_to_replication_stream]

        set rd [redis_deferring_client]

        $rd blpop_and_set_multiple_keys l string_foo 1 string_bar 2
        wait_for_blocked_clients_count 1
        r lpush l a
        assert_equal [$rd read] {OK}

        # set expiration on string_foo
        r pexpire string_foo 1
        after 10

        # now the key should have been expired
        $rd blpop_and_set_multiple_keys l string_foo 1 string_bar 2
        wait_for_blocked_clients_count 1
        r lpush l a
        assert_equal [$rd read] {OK}

        # Explanation of the first multi exec block:
        # {lpop l} - pop the value by our blocking command 'blpop_and_set_multiple_keys'
        # {set string_foo 1} - the action of our blocking command 'blpop_and_set_multiple_keys'
        # {set string_bar 2} - the action of our blocking command 'blpop_and_set_multiple_keys'
        # {incr string_changed{string_foo}} - post notification job that was registered when 'string_foo' changed
        # {incr string_changed{string_bar}} - post notification job that was registered when 'string_bar' changed
        # {incr string_total} - post notification job that was registered when string_changed{string_foo} changed
        # {incr string_total} - post notification job that was registered when string_changed{string_bar} changed
        #
        # Explanation of the second multi exec block:
        # {lpop l} - pop the value by our blocking command 'blpop_and_set_multiple_keys'
        # {del string_foo} - lazy expiration of string_foo when 'blpop_and_set_multiple_keys' tries to write to it. 
        # {set string_foo 1} - the action of our blocking command 'blpop_and_set_multiple_keys'
        # {set string_bar 2} - the action of our blocking command 'blpop_and_set_multiple_keys'
        # {incr expired} - the post notification job, registered after string_foo got expired
        # {incr string_changed{string_foo}} - post notification job triggered when we set string_foo
        # {incr string_changed{string_bar}} - post notification job triggered when we set string_bar
        # {incr string_total} - post notification job triggered when we incr 'string_changed{string_foo}'
        # {incr string_total} - post notification job triggered when we incr 'string_changed{string_bar}'
        assert_replication_stream $repl {
            {select *}
            {lpush l a}
            {multi}
            {lpop l}
            {set string_foo 1}
            {set string_bar 2}
            {incr string_changed{string_foo}}
            {incr string_changed{string_bar}}
            {incr string_total}
            {incr string_total}
            {exec}
            {pexpireat string_foo *}
            {lpush l a}
            {multi}
            {lpop l}
            {del string_foo}
            {set string_foo 1}
            {set string_bar 2}
            {incr expired}
            {incr string_changed{string_foo}}
            {incr string_changed{string_bar}}
            {incr string_total}
            {incr string_total}
            {exec}
        }
        close_replication_stream $repl
        r DEBUG SET-ACTIVE-EXPIRE 1
        
        wait_for_blocked_clients_count 0
        $rd close
    }
}

start_server {tags {"modules"}} {
    r module load $testmodule
    r module load $testmodule3

    test {Test unblock handler on module blocked on keys} {
        set rd [redis_deferring_client]

        r fsl.push l 1
        $rd do_rm_call_async FSL.BPOPGT l 3 0
        wait_for_blocked_clients_count 1
        r fsl.push l 2
        r fsl.push l 3
        r fsl.push l 4
        assert_equal [$rd read] {4}

        wait_for_blocked_clients_count 0
        $rd close
    }
}