Generated with:
local r = require "lredis.cqueues".connect_tcp()
r:call("multi")
r:call("subscribe", "test")
r:call("publish", "test", "a message")
r:call("unsubscribe", "test")
r:call("exec")
What goes over the socket:
connect(127.0.0.1/[127.0.0.1]:6379): ready
write(127.0.0.1/[127.0.0.1]:6379): sent 15 bytes
000000 2a 31 0d 0a 24 35 0d 0a 6d 75 6c 74 69 0d 0a |*1..$5..multi.. |
read(127.0.0.1/[127.0.0.1]:6379): rcvd 5 bytes
000000 2b 4f 4b 0d 0a |+OK.. |
write(127.0.0.1/[127.0.0.1]:6379): sent 29 bytes
000000 2a 32 0d 0a 24 39 0d 0a 73 75 62 73 63 72 69 62 |*2..$9..subscrib|
000010 65 0d 0a 24 34 0d 0a 74 65 73 74 0d 0a |e..$4..test.. |
read(127.0.0.1/[127.0.0.1]:6379): rcvd 9 bytes
000000 2b 51 55 45 55 45 44 0d 0a |+QUEUED.. |
write(127.0.0.1/[127.0.0.1]:6379): sent 42 bytes
000000 2a 33 0d 0a 24 37 0d 0a 70 75 62 6c 69 73 68 0d |*3..$7..publish.|
000010 0a 24 34 0d 0a 74 65 73 74 0d 0a 24 39 0d 0a 61 |.$4..test..$9..a|
000020 20 6d 65 73 73 61 67 65 0d 0a |.message.. |
read(127.0.0.1/[127.0.0.1]:6379): rcvd 9 bytes
000000 2b 51 55 45 55 45 44 0d 0a |+QUEUED.. |
write(127.0.0.1/[127.0.0.1]:6379): sent 32 bytes
000000 2a 32 0d 0a 24 31 31 0d 0a 75 6e 73 75 62 73 63 |*2..$11..unsubsc|
000010 72 69 62 65 0d 0a 24 34 0d 0a 74 65 73 74 0d 0a |ribe..$4..test..|
read(127.0.0.1/[127.0.0.1]:6379): rcvd 9 bytes
000000 2b 51 55 45 55 45 44 0d 0a |+QUEUED.. |
write(127.0.0.1/[127.0.0.1]:6379): sent 14 bytes
000000 2a 31 0d 0a 24 34 0d 0a 65 78 65 63 0d 0a |*1..$4..exec.. |
read(127.0.0.1/[127.0.0.1]:6379): rcvd 119 bytes
000000 2a 33 0d 0a 2a 33 0d 0a 24 39 0d 0a 73 75 62 73 |*3..*3..$9..subs|
000010 63 72 69 62 65 0d 0a 24 34 0d 0a 74 65 73 74 0d |cribe..$4..test.|
000020 0a 3a 31 0d 0a 2a 33 0d 0a 24 37 0d 0a 6d 65 73 |.:1..*3..$7..mes|
000030 73 61 67 65 0d 0a 24 34 0d 0a 74 65 73 74 0d 0a |sage..$4..test..|
000040 24 39 0d 0a 61 20 6d 65 73 73 61 67 65 0d 0a 3a |$9..a.message..:|
000050 31 0d 0a 2a 33 0d 0a 24 31 31 0d 0a 75 6e 73 75 |1..*3..$11..unsu|
000060 62 73 63 72 69 62 65 0d 0a 24 34 0d 0a 74 65 73 |bscribe..$4..tes|
000070 74 0d 0a 3a 30 0d 0a |t..:0.. |
You'll notice that the exec response returned is 3 elements long:
- first element is {"subscribe", "test", 1}
- second element is {"message", "test", "a message"}
- third element is 1 (probably the result from the publish?)
After that the server sends a 3 element array with only one element: {"unsubscribe", "test", 0}
Comment From: badboy
Sounds good to me. You're just tripping over the unusual behaviour of pubsub inside a transaction.
What happens:
1. You get the response to your subscribe: the usual message with type, channel and number of subscriptions
2. You get the published message from your own publish call. Published messages are queued immediately on publish
3. You get the response of your publish call: the number of clients that received the message, which is only your own
4. You get the message as a result of your unsubscribe with type, the channel and the number of subscriptions left
Normally only a few selected commands are allowed in subscribed mode, but because this is only checked on receive of a command, which is inside a transaction for you and thus only queued, not executed, Redis will happily execute it on EXEC (at which point it does not recheck your client's status)
Comment From: daurnimator
It sounds like one of the following should happen:
- when publish sends the message, the 'length' of the EXEC result needs to be incremented.
- a publish to yourself while in a transaction should be an error
- a subscribe while in a transaction should be an error
Comment From: badboy
Ah, indeed the initial multi-bulk response length is of course wrong.
1. You do not know whether or not a publish in the queue generates a message
2. That might be possible, but I didn't check the code there.
3. I bet some people are relying on it :D
All in all: we can't stop everyting bad from the user.
Comment From: antirez
Hello, it looks sensible to deny *SUBSCRIBE inside transactions starting from Redis 3.2 RC2. Do you agree?
Comment From: daurnimator
Hello, it looks sensible to deny *SUBSCRIBE inside transactions starting from Redis 3.2 RC2. Do you agree?
No objections here. It already is an error for e.g. lua scripts inside redis.
Comment From: daurnimator
Btw, what postgres does is that LISTEN only takes effect at time of commit: http://www.postgresql.org/docs/current/static/sql-listen.html
LISTEN takes effect at transaction commit. If LISTEN or UNLISTEN is executed within a transaction that later rolls back, the set of notification channels being listened to is unchanged
Comment From: itamarhaber
Erroring makes it easier to reason about compare to delaying the subscribe until exec (no offense to pgsql ;)).
On Mon, Apr 11, 2016 at 7:20 AM, daurnimator notifications@github.com wrote:
Btw, what postgres does is that LISTEN only takes effect at time of commit: http://www.postgresql.org/docs/current/static/sql-listen.html
LISTEN takes effect at transaction commit. If LISTEN or UNLISTEN is executed within a transaction that later rolls back, the set of notification channels being listened to is unchanged
— You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub https://github.com/antirez/redis/issues/2967#issuecomment-208158707