Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] RedisModule_UnblockClient behaviour does not match the documentation #7879

Closed
manast opened this issue Oct 2, 2020 · 7 comments · Fixed by #7903
Closed

[BUG] RedisModule_UnblockClient behaviour does not match the documentation #7879

manast opened this issue Oct 2, 2020 · 7 comments · Fixed by #7903
Assignees

Comments

@manast
Copy link

manast commented Oct 2, 2020

Describe the bug
sorry if this text is a bit confusing, it has been many hours of debugging until I realized this may be an issue with redis.

The documentation for RedisModule_UnblockClient is either incorrect or something does not add up in how to use the function when blocking a client with RedisModule_BlockClientOnKeys.

Due to issue #7878 I am using the function above as a workaround for unblocking the client. In this case the client is unblocked as expected, and this time as the documentation states (confusingly since it first talks about reply callback then clarifies that it is the timeout callback) it calls the Timeout callback instead of the reply callback (https://redis.io/topics/modules-api-ref)

Note 2: when we unblock a client that is blocked for keys using the API RedisModule_BlockClientOnKeys(),
the privdata argument here is not used, and the reply callback is called with
the privdata pointer that was passed when blocking the client.

Unblocking a client that was blocked for keys using this API will still require
the client to get some reply, so the function will use the "timeout" handler in
order to do so.

The problem is that the signature of the timeout callback is this one (in zig):

fn TimeoutReplyCallback(ctx: ?*RedisModuleCtx, argv: [*c]?*RedisModuleString, argc: c_int) callconv(.C) c_int

There is no privdata argument in the signature, so that must be a mismatch with the documentation. But the biggest problem is that if you try to get the privdata with

    const pData: *ReplyData = @ptrCast(*ReplyData, RedisModule_GetBlockedClientPrivateData.?(ctx));

pData will always be NULL. Therefore it becomes impossible to access the privdata in this scenario.

I am missing something?

@manast manast changed the title [BUG] RedisModule_UnblockClient not hoonori [BUG] RedisModule_UnblockClient behaviour does not match the documentation Oct 2, 2020
@guybe7
Copy link
Collaborator

guybe7 commented Oct 12, 2020

hello @manast
there are two different mechanisms for blocking a client:

  1. "regular" blocking: the module blocks a client, does some processing in a different thread and unblocks the client using RM_UnblockClient
  2. "key" blocking: the module blocks a client pending a specific state of a key. when the key reaches some desired state RM_SignalKeyAsReady should be called and the client is unblocked

in the first case, when you block the client by calling RM_BlockClient (which doesn't have a privdata argument). when you unblock using RM_UnblockClient you provide a privdata which is accessible in the reply_callback via RedisModule_GetBlockedClientPrivateData

in the second case you call RM_BlockClientOnKeys and you may provide a privdata. if you choose to unblock the client with RM_UnblockClient (which also takes a privdata) none of the privdata is used and the client is released with the timeout_callback (the logic is that if you chose to unblock a blocked-on-keys-client with RM_UnblockClient instead of RM_SIgnalKeyAsReady it means that something is wrong)

general comment: the privdata (both the one provided in RM_UnblockClient and optionally in RM_BlockClientOnKeys) is not accessible in the timeout_callback (only in the reply_callback and disconnect_callback)

so yes, there is a problem with the docs. the section you sent should be corrected to:

Note 2: when we unblock a client that is blocked for keys using the API RedisModule_BlockClientOnKeys(),
the privdata argument here is not used.
Unblocking a client that was blocked for keys using this API will still require
the client to get some reply, so the function will use the "timeout" handler in
order to do so. Please note that both the privdata provided here and the one provided to
RedisModule_BlockClientOnKeys are not accessible from the timeout_callback.

we should change the doc of RedisModule_GetBlockedClientPrivateData:

Get the private data set by RedisModule_UnblockClient()
Note: This API will return NULL if called from within the timeout_callback.

@manast
Copy link
Author

manast commented Oct 12, 2020

yeah, basically I knew already all you wrote above after many tests and investigations. The problem is that as I show in #7880 RM_SignalKeyAsReady does not work when called from a timer callback or from a thread (probably the timer callback is called from a thread too it is my guessing). So I am forced to use RM_UnblockClient and of course is a bit inconvenient not having access to the privdata anymore.

@guybe7
Copy link
Collaborator

guybe7 commented Oct 12, 2020

it seems to me the "problem" here is that there's no access to privdata from the timeout callback (after #7880 is cleared that won't be a problem for you as you will not need to call RM_UnblockClient any more)

TBH i don't really see an advantage on accessing privdata in the timeout_callback... after all, it's just a timeout, why would you need privdata? @oranagra thoughts?

@oranagra
Copy link
Member

I think it's always better if any callback would get a privdata.
question is if we can do that easily, and without breaking backwards compatibility.

@guybe7
Copy link
Collaborator

guybe7 commented Oct 12, 2020

yes it's very simple, we just need to assign ctx->blocked_privdata before calling timeout_callback

@manast
Copy link
Author

manast commented Oct 12, 2020

TBH i don't really see an advantage on accessing privdata in the timeout_callback... after all, it's just a timeout, why would you need privdata? @oranagra thoughts?

Normally it is a timeout but when you call UnblockClient manually even though it calls Timeout it has been called before the actual timeout time has passed. What is a bit confusing to me is why UnblockClient has a different behaviour when using RedisModule_BlockClientOnKeys than when used with RM_BlockClient.

@guybe7
Copy link
Collaborator

guybe7 commented Oct 12, 2020

the reason is that under normal circumstances a client that was blocked with BlockOnKeys should be released with SignalKeyAsReady and not by UnblockClient.

when you use BlockClient you don't provide privdata; when you unblock it you do (this is usually the case for threads: the worker thread has some information that should be in the reply of the unblocked client).

when you use BlockClientOnKeys you provide a privdata (usually has to do with the target state of the key); when you unblock it (hopefully with SignalKeyAsReady) you don't provide privdata.

using UnblockClient for a client blocked on keys is undesired and the only reason it exists is so that the module can do a proper cleanup in case it is unloaded etc.

i know it's a bit confusing but just remember the pairs BlockClient/UnblockClient and BlockClientOnKeys/SignalKeyAsReady

guybe7 added a commit to guybe7/redis that referenced this issue Oct 12, 2020
Fix redis#7879 redis#7880

1. Fix some comments
2. Make sure blocked-on-keys client privdata is accessible
   from withing the timeout callback
3. Handle blocked clients in beforeSleep - In case a key
   becomses "ready" outside of processCommand
guybe7 added a commit to guybe7/redis that referenced this issue Oct 12, 2020
Fix redis#7879 redis#7880

1. Fix some comments
2. Make sure blocked-on-keys client privdata is accessible
   from withing the timeout callback
3. Handle blocked clients in beforeSleep - In case a key
   becomses "ready" outside of processCommand
oranagra pushed a commit that referenced this issue Oct 12, 2020
- Clarify some documentation comments
- Make sure blocked-on-keys client privdata is accessible
  from withing the timeout callback
- Handle blocked clients in beforeSleep - In case a key
  becomes "ready" outside of processCommand

See #7879 #7880
oranagra pushed a commit to oranagra/redis that referenced this issue Oct 26, 2020
- Clarify some documentation comments
- Make sure blocked-on-keys client privdata is accessible
  from withing the timeout callback
- Handle blocked clients in beforeSleep - In case a key
  becomes "ready" outside of processCommand

See redis#7879 redis#7880

(cherry picked from commit addf47d)
oranagra pushed a commit to oranagra/redis that referenced this issue Oct 26, 2020
- Clarify some documentation comments
- Make sure blocked-on-keys client privdata is accessible
  from withing the timeout callback
- Handle blocked clients in beforeSleep - In case a key
  becomes "ready" outside of processCommand

See redis#7879 redis#7880

(cherry picked from commit addf47d)
oranagra pushed a commit that referenced this issue Oct 27, 2020
- Clarify some documentation comments
- Make sure blocked-on-keys client privdata is accessible
  from withing the timeout callback
- Handle blocked clients in beforeSleep - In case a key
  becomes "ready" outside of processCommand

See #7879 #7880

(cherry picked from commit addf47d)
JackieXie168 pushed a commit to JackieXie168/redis that referenced this issue Nov 4, 2020
- Clarify some documentation comments
- Make sure blocked-on-keys client privdata is accessible
  from withing the timeout callback
- Handle blocked clients in beforeSleep - In case a key
  becomes "ready" outside of processCommand

See redis#7879 redis#7880
jschmieg pushed a commit to memKeyDB/memKeyDB that referenced this issue Nov 6, 2020
- Clarify some documentation comments
- Make sure blocked-on-keys client privdata is accessible
  from withing the timeout callback
- Handle blocked clients in beforeSleep - In case a key
  becomes "ready" outside of processCommand

See redis#7879 redis#7880

(cherry picked from commit addf47d)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants