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] Calling RedisModule_SignalKeyAsReady from a timer callback does not trigger the reply callback. #7880

Closed
manast opened this issue Oct 3, 2020 · 8 comments
Assignees

Comments

@manast
Copy link

manast commented Oct 3, 2020

Describe the bug

When using RedisModule_BlockClientOnKeys it should be possible to unblock the client by calling RedisModule_SignalKeyAsReady. However this is not working if the call is performed inside the callback of a timer.
The following code reproduces the issue. Note that for RedisModule_SignalKeyAsReady to work at all, the key we are signalling must exist before we call it as reported here (#7878)

#include "redismodule.h"
#include <stdlib.h>

int BlockedReplyCallback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)  {
    RedisModule_Log(ctx, "warning", "Reply callback!");
    RedisModule_ReplyWithSimpleString(ctx, "We are done!");
    return REDISMODULE_OK;
}

int TimeoutReplyCallback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)  {
    RedisModule_Log(ctx, "warning", "Timed out!");

    RedisModule_ReplyWithSimpleString(ctx, "Timed Out");
    return REDISMODULE_OK;
}

void BlockedFreePrivdata(RedisModuleCtx *ctx, void *privdata) {
    RedisModule_Log(ctx, "warning", "Freeing all priv data");
}

void TimerCallback(RedisModuleCtx *ctx, void *data) {
    RedisModuleString *key = data;
        
    RedisModule_Call(ctx, "RPUSH", "sc", key, "foobar");
    RedisModule_SignalKeyAsReady(ctx, key);
}

int HelloworldRand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {

    RedisModuleBlockedClient* bc = RedisModule_BlockClientOnKeys(ctx, 
            BlockedReplyCallback, 
            TimeoutReplyCallback, 
            BlockedFreePrivdata,
            10000, 
            &argv[1],
            1, 
            NULL);

    RedisModule_HoldString(ctx, argv[1]);
    RedisModule_CreateTimer(ctx, 
                1000, 
                TimerCallback, 
                argv[1]);

    return REDISMODULE_OK;
}

int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
    if (RedisModule_Init(ctx,"helloworld",1,REDISMODULE_APIVER_1)
        == REDISMODULE_ERR) return REDISMODULE_ERR;

    if (RedisModule_CreateCommand(ctx,"bug2",
        HelloworldRand_RedisCommand, "",
        0, 0, 0) == REDISMODULE_ERR)
        return REDISMODULE_ERR;

    return REDISMODULE_OK;
}

To reproduce

Just compile and load that module in redis, call it with for example "bug2 1234". It should create a list named 1234 with one element "foobar" in it. However the command is not unblocked after 1 second.

Expected behavior

The command should unblock one second after running it.

Additional information

This issue is reproducible with Redis 6.0.8.

Some observations. If the call to RPUSH is moved outside of the timer callback, then the command will unblock the first time it is called, but not the second time (as if it detects the LIST was already there and then ignore the signalling).

@guybe7
Copy link
Collaborator

guybe7 commented Oct 12, 2020

@manast it seems you pass argv to RedisModule_CreateTimer but as soon as HelloworldRand_RedisCommand finished argv is not a valid pointer anymore... maybe just pass argv[1] (after retaining it)

let me know if the issue is still happening

@manast
Copy link
Author

manast commented Oct 12, 2020

I updated the test code, it is still happening.

@guybe7
Copy link
Collaborator

guybe7 commented Oct 12, 2020

i still see

RedisModule_CreateTimer(ctx, 
                1000, 
                TimerCallback, 
                argv);

instead of

RedisModule_CreateTimer(ctx, 
                1000, 
                TimerCallback, 
                argv[1]);

@manast
Copy link
Author

manast commented Oct 12, 2020

not anymore :). I just forgot to update that in the issue, in my code it is correct.

@manast
Copy link
Author

manast commented Oct 12, 2020

but as I mentioned, if you push before creating the timer then the call to signalKeysAsReady works, so the pointer must be correct, the problem is when you push from inside the timer AND you call the signalKeyAsReady from inside the timer.

@guybe7
Copy link
Collaborator

guybe7 commented Oct 12, 2020

ok so indeed there's a bug: the function that releases blocked clients (handleClientsBlockedOnKeys) is called only from processCommand (i.e. the assumption is that only a command can cause a key to be "ready" - but that assumption seems to be an over-simplification)

we can either call handleClientsBlockedOnKeys at the end of moduleTimerHandler or every once in a while in serverCron (seems a bit more risky)

@oranagra WDYT?

@oranagra
Copy link
Member

i think this was indeed overlooked, and i guess we should call handleClientsBlockedOnKeys from beforeSleep too.

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
Copy link
Member

handled vie the above mentioned PR.

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

No branches or pull requests

3 participants