Skip to content

Conversation

@guybe7
Copy link
Collaborator

@guybe7 guybe7 commented Sep 22, 2022

The use case is a module that wants to implement a blocking command on a key that necessarily exists and wants to unblock the client in case the key is deleted (much like what we implemented for XREADGROUP in #10306)

New module API:

  • RedisModule_BlockClientOnKeysWithFlags

Flags:

  • REDISMODULE_BLOCK_UNBLOCK_NONE
  • REDISMODULE_BLOCK_UNBLOCK_DELETED

Detailed description of code changes

blocked.c:

  1. Both module and stream functions are called whether the key exists or not, regardless of its type. We do that in order to allow modules/stream to unblock the client in case the key is no longer present or has changed type (the behavior for streams didn't change, just code that moved into serveClientsBlockedOnStreamKey)
  2. Make sure afterCommand is called in serveClientsBlockedOnKeyByModule, in order to propagate actions from moduleTryServeClientBlockedOnKey.
  3. handleClientsBlockedOnKeys: call propagatePendingCommands directly after lookupKeyReadWithFlags to prevent a possible lazy-expire DEL from being mixed with any command propagated by the preceding functions.
  4. blockForKeys: Caller can specifiy that it wants to be awakened if key is deleted.
    Minor optimizations (use dictAddRaw).
  5. signalKeyAsReady became signalKeyAsReadyLogic which can take a boolean in case the key is deleted. It will only signal if there's at least one client that awaits key deletion (to save calls to handleClientsBlockedOnKeys).
    Minor optimizations (use dictAddRaw)

db.c:

  1. scanDatabaseForDeletedStreams is now scanDatabaseForDeletedKeys and will signalKeyAsReady for any key that was removed from the database or changed type. It is the responsibility of the code in blocked.c to ignore or act on deleted/type-changed keys.
  2. Use the new signalDeletedKeyAsReady where needed

blockedonkey.c + tcl:

  1. Added test of new capabilities (FSL.BPOPGT now requires the key to exist in order to work)

The use case is a module that wants to implement a blocking command on a key
that necessarily exists, and want to unblock the client in case the key is
deleted (much like what we implemnted for XREADGROUP in redis#10306)

blocked.c:
1. Both module and stream functions are called whether the key exists or not,
   and regardless of its type.
   We do that in order to allow modules/stream to unblock the client in case the
   key is no longer present or has changed type (the behavior for streams didn't
   change, just code that moved into serveClientsBlockedOnStreamKey)
2. Make sure afterCommand is called in serveClientsBlockedOnKeyByModule, in
   order to propagate actions from moduleTryServeClientBlockedOnKey.
3. handleClientsBlockedOnKeys: call propagatePendingCommands directly after
   lookupKeyReadWithFlags in order to prevent a possible lazy-expire DEL to be
   mized with any command propagated by the preceding functions.

db.c:
1. scanDatabaseForDeletedStreams is now scanDatabaseForDeletedKeys and will
   signalKeyAsReady for any key the was removed from the database or changed
   type.
   It is the responsibilty of the code in blocked.c to ignore or to act on
   deleted/type-changed keys.

module.c, redismodule.h:
1. new API RM_SignalKeyAsReadyByDbId to be used when RedisModuleCtx is not
   provided.

blockedonkey.c + tcl:
1. Added test of new capabilites (FSL.BPOPGT now requires the key to exist in
   order to work)
Copy link
Member

@oranagra oranagra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly LGTM.
other than paranoia about hidden bugs and edge cases.
let's get more feedback.
@MeirShpilraien @yossigo @soloestoy

@ranshid
Copy link
Contributor

ranshid commented Oct 12, 2022

@guybe7 I went shortly through the change (will make a deeper attempt later) but do we also plan to change the dbOverwrite?
Currently it will only trigger stream types. I read the top comment:

Both module and stream functions are called whether the key exists or not, regardless of its type. We do that in order to allow modules/stream to unblock the client in case the key is no longer present or has changed type (the behavior for streams didn't change, just code that moved into serveClientsBlockedOnStreamKey)

So basically in case of type change due to swapdb the module will be unblocked, but in case of overwrite it will not? (Or I missed something :) )

blocked.c:
1. blockForKeys: Caller can specifiy that it wants to be awakened if key is deleted.
   Minor optimizations (use dictAddRaw).
2. signalKeyAsReady became signalKeyAsReadyLogic which can take a boolean in
   case the key is deleted. It will only signal if there's at least one client
   that awaits key deletion (to save calls to handleClientsBlockedOnKeys).
   Minor optimizations (use dictAddRaw)

db.c:
1. Use the new signalDeletedKeyAsReady where needed

module.c, redismodule.h:
1. Add new API RedisModule_BlockClientOnKeysWithFlags so that module can mimic
   the XREADGROUP behvaiour
@guybe7
Copy link
Collaborator Author

guybe7 commented Oct 12, 2022

@ranshid you are right, please review again (i implemented the mechanism discussed in #11012 (comment))

Copy link
Member

@oranagra oranagra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly LGTM

@oranagra oranagra added release-notes indication that this issue needs to be mentioned in the release notes approval-needed Waiting for core team approval to be merged state:major-decision Requires core team consensus labels Oct 16, 2022
@oranagra
Copy link
Member

@redis/core-team please approve

Copy link
Member

@itamarhaber itamarhaber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API looks valid, not a CR

Copy link
Contributor

@madolson madolson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional comments. API LGTM. Did not do a full review.

@oranagra
Copy link
Member

approved in a core-team meeting

@oranagra oranagra merged commit b57fd01 into redis:unstable Oct 18, 2022
ranshid added a commit to ranshid/redis that referenced this pull request Oct 24, 2022
1. On PR redis#11310 there was a change in the way clients blocked on
   stream/module keys might react in case of key no longer exist.
   However the logic also introduced an optimization to avoid trigger
   deketed keys in case no client was blocked with key delet condition.
   This might be a problem in case several clients are blocked on the
   key but with different types.
   In order to fix that I have added a counter to the blocking_keys_unblock_on_nokey
   so that the entry will be removed also in case all the clients
   who would like to be triggered on nokey were unblocked.

2. Added statistics to the info "Clients" section to report the:
   number of blocking keys
   number of blocking keys which have at least 1 client which would like
   to be unblocked on nokey.

3. added test for the scenarion described in '1'
madolson pushed a commit to madolson/redis that referenced this pull request Apr 19, 2023
…1310)

The use case is a module that wants to implement a blocking command on a key that
necessarily exists and wants to unblock the client in case the key is deleted (much like
what we implemented for XREADGROUP in redis#10306)

New module API:
* RedisModule_BlockClientOnKeysWithFlags

Flags:
* REDISMODULE_BLOCK_UNBLOCK_NONE
* REDISMODULE_BLOCK_UNBLOCK_DELETED

### Detailed description of code changes

blocked.c:
1. Both module and stream functions are called whether the key exists or not, regardless of
  its type. We do that in order to allow modules/stream to unblock the client in case the key
  is no longer present or has changed type (the behavior for streams didn't change, just code
  that moved into serveClientsBlockedOnStreamKey)
2. Make sure afterCommand is called in serveClientsBlockedOnKeyByModule, in order to propagate
  actions from moduleTryServeClientBlockedOnKey.
3. handleClientsBlockedOnKeys: call propagatePendingCommands directly after lookupKeyReadWithFlags
  to prevent a possible lazy-expire DEL from being mixed with any command propagated by the
  preceding functions.
4. blockForKeys: Caller can specifiy that it wants to be awakened if key is deleted.
   Minor optimizations (use dictAddRaw).
5. signalKeyAsReady became signalKeyAsReadyLogic which can take a boolean in case the key is deleted.
  It will only signal if there's at least one client that awaits key deletion (to save calls to
  handleClientsBlockedOnKeys).
  Minor optimizations (use dictAddRaw)

db.c:
1. scanDatabaseForDeletedStreams is now scanDatabaseForDeletedKeys and will signalKeyAsReady
  for any key that was removed from the database or changed type. It is the responsibility of the code
  in blocked.c to ignore or act on deleted/type-changed keys.
2. Use the new signalDeletedKeyAsReady where needed

blockedonkey.c + tcl:
1. Added test of new capabilities (FSL.BPOPGT now requires the key to exist in order to work)
enjoy-binbin pushed a commit to enjoy-binbin/redis that referenced this pull request Jul 31, 2023
…1310)

The use case is a module that wants to implement a blocking command on a key that
necessarily exists and wants to unblock the client in case the key is deleted (much like
what we implemented for XREADGROUP in redis#10306)

New module API:
* RedisModule_BlockClientOnKeysWithFlags

Flags:
* REDISMODULE_BLOCK_UNBLOCK_NONE
* REDISMODULE_BLOCK_UNBLOCK_DELETED

### Detailed description of code changes

blocked.c:
1. Both module and stream functions are called whether the key exists or not, regardless of
  its type. We do that in order to allow modules/stream to unblock the client in case the key
  is no longer present or has changed type (the behavior for streams didn't change, just code
  that moved into serveClientsBlockedOnStreamKey)
2. Make sure afterCommand is called in serveClientsBlockedOnKeyByModule, in order to propagate
  actions from moduleTryServeClientBlockedOnKey.
3. handleClientsBlockedOnKeys: call propagatePendingCommands directly after lookupKeyReadWithFlags
  to prevent a possible lazy-expire DEL from being mixed with any command propagated by the
  preceding functions.
4. blockForKeys: Caller can specifiy that it wants to be awakened if key is deleted.
   Minor optimizations (use dictAddRaw).
5. signalKeyAsReady became signalKeyAsReadyLogic which can take a boolean in case the key is deleted.
  It will only signal if there's at least one client that awaits key deletion (to save calls to
  handleClientsBlockedOnKeys).
  Minor optimizations (use dictAddRaw)

db.c:
1. scanDatabaseForDeletedStreams is now scanDatabaseForDeletedKeys and will signalKeyAsReady
  for any key that was removed from the database or changed type. It is the responsibility of the code
  in blocked.c to ignore or act on deleted/type-changed keys.
2. Use the new signalDeletedKeyAsReady where needed

blockedonkey.c + tcl:
1. Added test of new capabilities (FSL.BPOPGT now requires the key to exist in order to work)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approval-needed Waiting for core team approval to be merged release-notes indication that this issue needs to be mentioned in the release notes state:major-decision Requires core team consensus

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants