From 3feff43534f846a2ae71c159339e4c671f3d6dce Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Mon, 16 Sep 2024 10:44:33 +0300 Subject: [PATCH 01/38] Valkey Triggers RFC Signed-off-by: Ran Shidlansik --- Triggers.md | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 Triggers.md diff --git a/Triggers.md b/Triggers.md new file mode 100644 index 0000000..a903d42 --- /dev/null +++ b/Triggers.md @@ -0,0 +1,233 @@ +--- +RFC: +Status: +--- + +# Valkey Triggers RFC + +## Abstract +Valkey triggers are persistent procedures which are executed in response to some keyspace event. +This will allow users to localize triggering actions on the server side reducing the need to monitor and react to events on the application side. +Today [Valkey keyspace Notifications](https://valkey.io/topics/notifications/) offer the ability to publish events to dedicated pub/sub channels. +Valkey triggers add the ability to place extending logic on such keyspace events. +We propose to extend the existing [Valkey Functions](https://valkey.io/topics/functions-intro/) with the ability to define trigger function call on specific keyspace event. +As being integrated in the Valkey Functions infrastructure, Triggers are persisted as part of the user data. + +## Motivation + +1. Localized actions. Today different application use cases rely on [Valkey keyspace Notifications](https://valkey.io/topics/notifications/) in order to manage the database. For example, consider a + case were a key is referenced in different places (like SortedSet and lists). In order to support TTL logic a cleanup needs to be made when the key is evicted to remove it from the different lists or sets. It is possible to have the application listen for eviction events and perform the cleanup, however this would require the application to rely on non-persistent subscribe connections, implement application side logic and extra network hops, which can cause the cleanup to be operated long after the key was evicted. + For example lets take a commonly used pattern of scheduled tasks. + In such cases user usually register tasks in a ZSET with the matching executing time as the score. + Usually what is being done is setting a puller job in the application side which periodically reads all items in the set which has score smaller than the current timestamp, and issue an eval/fcall back to redis with the relevant task to run. + This pattern introduce some waste as the application needs to maintain a puller job and perform the roundtrip back to the application in order to execute the scheduled operations. + With Valkey triggers this can be achieved without the need to place a puller job by the user. + In order to achieve that we can use: + - ZSET z for for holding scheduled tasks + - Key k to manage the next task execution time + + The external application code will only have to add tasks to the ZSET **z** with score matching their required execution time. + 1. once the task is added to **z** a trigger code will be executed which will take the minimal score from the ZSET **z**, and apply the diff to the current time to the TTL of key **k**. + 2. once key **k** has expired, a trigger will be executed which will remove and execute all the tasks from the zset **z** + + Here is an example library code to schedule tasks to be executed after/every several seconds + + ![image](https://user-images.githubusercontent.com/88133677/225652613-aa85beee-05f1-4c5e-abe7-1d334b0e882f.png) + + So in order to use them the user can simply register operations to be triggered: + + ``` + fcall schedule 0 'redis.call("PUBLISH", "scheduled", "this is a msg from the past")' "3" + ``` + Will cause publish of a msg after 3 seconds from the time the fcall was processed. + + ``` + fcall every 0 "3" 'redis.call("PUBLISH", "scheduled", "this is an annoying msg")' + ``` + Will cause the specified message to be published every 3 seconds. + Note that the same concept can be used to implement HASH members eviction! + +2. Flexibility of extensibility. There are some cases were application needs to extend the logic of server side operations. In some of the cases it might be problematic making the change on the application + side, either because of the risk to deploy the application part or since the operation is not triggered by the application (eg evictions, expirations etc...). + For example, consider the current implementation of [Valkey keyspace Notifications](https://valkey.io/topics/notifications/). The existing mechanism relies on non-persistent pub/sub notifications. + In order to persist the notifications it is possible to write them into a sorted set. + Triggers can provide this ability in a very straightforward way. For example, consider the following function library: + + ![image](https://user-images.githubusercontent.com/88133677/225616988-b9bb8730-59c1-478b-ae21-ece3b8b3a617.png) + + In this implementation we will capture each keyspace event and place the relevant key name and event on a dedicated stream called + "keyevent-notifications". + + ``` + > set a b + OK + > lpush mylist f + (integer) 1 + > xread streams keyevent-notifications 0 + 1) 1) "keyevent-notifications" + 2) 1) 1) "1678974201461-0" + 2) 1) "a" + 2) "new" + 2) 1) "1678974201461-1" + 2) 1) "a" + 2) "set" + 3) 1) "1678974210181-0" + 2) 1) "mylist" + 2) "new" + 4) 1) "1678974210181-1" + 2) 1) "mylist" + 2) "lpush" + ``` + + [Valkey keyspace Notifications](https://valkey.io/topics/notifications/) has another major disadvantage: they are not reported on the cluster-bus. While this might have good reasoning (eg avoid cluster-bud load, duplication of events etc...), this makes Valkey clients almost unable to manage key events listeners on Valkey clusters. Valkey triggers can enable the user to decide to publish keyspace events on the cluster bus. This way the trigger code can make smart decisions about publishing of the event and then user the 'PUBLISH' call in order to make the event propagated to all cluster nodes. + +3. Security. In some cases there is a need to limit the access to the cache. Currently Valkey provides restricting functionality via [Access Control Lists](https://valkey.io/topics/acl/). + However what if there is a need to access restricted parts of the dataset without granting real permissions to clients? + Since a trigger can operate without explicit user request, it can potentially be operated under any defined ACL user which will enable it to operate actions on restricted parts of the dataset. + +### Can't the same be done with modules? +Modules have the ability to build logic placed on top of keyspace events. However Modules provide a much more restrictive form of logic which is much harder to implement and dynamically replace. +By building this new logic inside the redis function library users will have a much more simple way to create stored procedures which are persistent, managed and replicated. + + +## Design and Specification + +### Triggers as part of function libraries +Valkey triggers will be implemented as an extension of the Valkey [Function Libraries](https://valkey.io/topics/functions-intro/) . +A trigger is basically a Valkey function which will be called when some specific keyspace event was issued. +in order to register a specific function as trigger, one will simply have to use the new **"register_trigger"** API which will be available for the different library engines. +For example for the (currently only) LUA engine the registration code will look like: + +![image](https://user-images.githubusercontent.com/88133677/225090299-1cf05508-75e6-43dd-a90f-05ca91dd3a6f.png) + +### Register trigger with named arguments form< + In order to provide some extra arguments to the trigger, the new API will also support the named arguments form. + For example: + +![image](https://user-images.githubusercontent.com/88133677/225531522-d3f66a00-bd90-490b-98c8-11b62c3f3eba.png) + +### The Trigger code block +As stated before, a trigger is basically a specific engine function. +The function synopsis will be identical for all triggers which will get the following parameters: + +**a. key -** the key for which the event was triggered + +**b. event -** a string specifying the specific event (ie. “expire ”, “set”, “move”, “store” etc...) + +**c. db -** the id of the db context for which the event is triggered (note it is not necessarily the current db scope the trigger is executed in, ie. MOVE event). + +For LUA engine based functions, The key will be supplied as the first argument in the keys array (eg KEYS[1]) +The **event** and **db** will be supplied in the ARGS array respectively (event will be provided as ARGS[1] and db as ARGS[2]) + + +### Triggering Events + +Valkey already has a feature to report different [keyspace notifications](https://valkey.io/topics/notifications/) which currently provides the ability to publish keyspace events to registered subscribers. The implementation should introduce the ability to set a trigger function to run on **ANY** event which is defined for [keyspace notifications](https://valkey.io/topics/notifications/). + +### Triggers configuration +By design Triggers will not require ANY configuration in order to work. The Triggers will be part of the loadable function libraries and should be able to run on the target events even if the user **DID NOT** explicitly configured keyspace-notifications. + +### Atomicity and triggers + +All keyspace events are triggered immediately after the operation which triggered them. If we allow the triggers to be executed when the keyspace event has been triggered, +It means allowing a write call performed in the middle of operating another command. Another (technical) issue is related to nested scripts. Currently there is a [protection](https://github.com/valkey-io/valkey/blame/unstable/src/script_lua.c#L891) in Valkey code preventing to execute lua script from a scripts. + +There are 2 design options: + +1. post execution of triggers. Much like was presented for modules with `VM_AddPostNotificationJob` which was introduced to allow modules +perform write operations on keyspace events as part of postExecutionUnitOperations. It is possible to follow the same logic as modules and share the same notification mechanism ie. to operate the trigger operation during the unit operation completion. +This will help prevent breaking atomicity of operations and better follows the way applications are currently reacting to keyspace events. The downside of this option is that since there is no +grantee about the state of the data after the operation is completed. + +2. Allow nested script calls for trigger scripts. This suggestion will require some handling of nested lua run contexts, but during PoC was tested to work. + +### recursive trigger flow or "should trigger trigger trigger?" + +We do not want to reach a nested call of triggers and a potential endless loops. According to this design a trigger execution as a result of some keyspace event is a terminal state. +we could keep some recursion depth counter that will stop triggering once it reaches some depth, but that would cause some unexpected behavior for the library developers, and difficulty debugging cases where triggers where not executed. +Another important aspect to consider is if modules based keyspace events calbacks should be triggered from trigger actions and vice versa. +In the scope of this document I will assume that the trigger based actions **WILL NOT** cause matching module calbacks to be called and that module actions performed during it's callback operation **WILL NOT** cause triggering triggers, but each will be executed only in 1 level of nesting. + +So for example lets say I have a module callback **cb** set on some keyspace event **e1** and a trigger action **t** registered on some event **e2**. +when the event **e1** is issued the **cb** will be executed and will cause event **e2** which will **NOT** trigger **t**. +Also in case **e2** is issued the **t** is being executed and cause event **e1** which will **NOT** trigger **cb**. + +### Scope of a trigger + +Whenever some event triggers an action, we will need this action to be processed in the scope of a specific database and ACL user. + +#### Database context +Since Redis triggers are built on keyspace events, the DB scope will be the same as the keyspace event was issued on. + +#### ACL User scope +By default the scope of the user will by the superuser which will not be limited by any authentication restrictions. +This makes sense as the triggers might be needed in order to operate a managed operation using some commands which should be otherwise restricted. +However in some cases the user might want the trigger to operate under some ACL restrictions. +The problem is that triggers might not operate in the scope of a specific external client (eg expiration events), so we suggest during trigger registration it will be possible to specify +the name of the ACL user to attach to the trigger run context. + +### New Commands +1. `CLIENT TRIGGERS [ON|OFF]` new subcommand. Sometimes it will be required to mute trigger execution in some context (say administrative fix to the dataset) + In order to support it the specific client will be able to mute triggers execution. + Response will always be "O.K". +2. The `FUNCTION LIST` command output will now include a new part named "triggers". +Much like functions the triggers part will describe a per-trigger information: + +``` +"library_name" + "mylib" + "engine" + "LUA" + "functions" + 1) "name" + 2) "myfunc" + 3) "description" + 4) + 5) "flags" + 6) + "triggers" + 1) "name" + 2) "trigger_name" + 3) "event" + 4) "__keyspace@0__:*" + 7) "calls" + 8) "" + 9) "errors" + 10) "" + 11) "total_duration" + 12) +``` + +### Benchmarking (Optional) + +TBD + +### Testing +TBD + +### Observability + +#### general statistics +The "Stats" info section will be added with the following statistics: +**total_trigger_calls -** the number of general trigger calls. this will not be a teardown list of calls per trigger function (this can be taken from the function list command as will be explained later) +**total_trigger_errors -** The total number of errors during trigger execution. (an error is accounted for each time the afterErrorReply is called) +**total_trigger_duration -** The total time spent running trigger related code. + +### Per trigger statistics +The same global statistics will be available on a per-trigger resolution via the `FUNCTION LIST` command: +"calls" - the total number of times the trigger has called +"errors" - the total number of errors issued during this trigger run +"total_duration" - the aggregated total duration time this trigger run + +*** Open question *** +should `CONFIG RESETSTAT` also reset the triggers statistics, or should that only be done when functions lib is reloaded? + +### Debug mechanism + +While Valkey functions and scripts are executed as part of the FCALL/EVAL calls, it is thus possible to report the errors back to the calling client. +Triggers, however, are silently executing which makes it hard for the user to understand why the trigger did not work and what errors occurred during execution of the trigger. +The suggestion here is to enable reporting error msg on a predefined pub/sub channel: +1. All error msgs will be intercepted as deferred errors (much like modules do). +2. After trigger completes run, in case errors were issues, it will update the stats with the num,ber of errors. +3. The trigger will report the errors to a dedicated channel in the following structure: + "__triggers@errors__:" From 037ec1eed7b4b417b80da7729472583bfbe2e24e Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Mon, 16 Sep 2024 11:10:30 +0300 Subject: [PATCH 02/38] refactor the RFC Signed-off-by: Ran Shidlansik --- Triggers.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Triggers.md b/Triggers.md index a903d42..2f1638e 100644 --- a/Triggers.md +++ b/Triggers.md @@ -7,16 +7,15 @@ Status: ## Abstract Valkey triggers are persistent procedures which are executed in response to some keyspace event. -This will allow users to localize triggering actions on the server side reducing the need to monitor and react to events on the application side. Today [Valkey keyspace Notifications](https://valkey.io/topics/notifications/) offer the ability to publish events to dedicated pub/sub channels. -Valkey triggers add the ability to place extending logic on such keyspace events. +Valkey triggers add the ability to place extending logic on such keyspace events and to localize actions on the server side reducing the need to monitor and react to events on the application side. We propose to extend the existing [Valkey Functions](https://valkey.io/topics/functions-intro/) with the ability to define trigger function call on specific keyspace event. As being integrated in the Valkey Functions infrastructure, Triggers are persisted as part of the user data. ## Motivation -1. Localized actions. Today different application use cases rely on [Valkey keyspace Notifications](https://valkey.io/topics/notifications/) in order to manage the database. For example, consider a - case were a key is referenced in different places (like SortedSet and lists). In order to support TTL logic a cleanup needs to be made when the key is evicted to remove it from the different lists or sets. It is possible to have the application listen for eviction events and perform the cleanup, however this would require the application to rely on non-persistent subscribe connections, implement application side logic and extra network hops, which can cause the cleanup to be operated long after the key was evicted. +1. *** Localized actions. *** + Today different application use cases rely on [Valkey keyspace Notifications](https://valkey.io/topics/notifications/) in order to manage the database. For example, consider a case were a key is referenced in different places (like SortedSet and lists). In order to support TTL logic a cleanup needs to be made when the key is evicted to remove it from the different lists or sets. It is possible to have the application listen for eviction events and perform the cleanup, however this would require the application to rely on non-persistent subscribe connections, implement application side logic and extra network hops, which can cause the cleanup to be operated long after the key was evicted. For example lets take a commonly used pattern of scheduled tasks. In such cases user usually register tasks in a ZSET with the matching executing time as the score. Usually what is being done is setting a puller job in the application side which periodically reads all items in the set which has score smaller than the current timestamp, and issue an eval/fcall back to redis with the relevant task to run. @@ -47,7 +46,8 @@ As being integrated in the Valkey Functions infrastructure, Triggers are persist Will cause the specified message to be published every 3 seconds. Note that the same concept can be used to implement HASH members eviction! -2. Flexibility of extensibility. There are some cases were application needs to extend the logic of server side operations. In some of the cases it might be problematic making the change on the application +2. *** Flexibility of extensibility. *** + There are some cases were application needs to extend the logic of server side operations. In some of the cases it might be problematic making the change on the application side, either because of the risk to deploy the application part or since the operation is not triggered by the application (eg evictions, expirations etc...). For example, consider the current implementation of [Valkey keyspace Notifications](https://valkey.io/topics/notifications/). The existing mechanism relies on non-persistent pub/sub notifications. In order to persist the notifications it is possible to write them into a sorted set. @@ -81,7 +81,8 @@ As being integrated in the Valkey Functions infrastructure, Triggers are persist [Valkey keyspace Notifications](https://valkey.io/topics/notifications/) has another major disadvantage: they are not reported on the cluster-bus. While this might have good reasoning (eg avoid cluster-bud load, duplication of events etc...), this makes Valkey clients almost unable to manage key events listeners on Valkey clusters. Valkey triggers can enable the user to decide to publish keyspace events on the cluster bus. This way the trigger code can make smart decisions about publishing of the event and then user the 'PUBLISH' call in order to make the event propagated to all cluster nodes. -3. Security. In some cases there is a need to limit the access to the cache. Currently Valkey provides restricting functionality via [Access Control Lists](https://valkey.io/topics/acl/). +3. *** Security. *** + In some cases there is a need to limit the access to the cache. Currently Valkey provides restricting functionality via [Access Control Lists](https://valkey.io/topics/acl/). However what if there is a need to access restricted parts of the dataset without granting real permissions to clients? Since a trigger can operate without explicit user request, it can potentially be operated under any defined ACL user which will enable it to operate actions on restricted parts of the dataset. From d3db587292531d97e51bbe2cb1c864a68db0587f Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Mon, 16 Sep 2024 11:16:03 +0300 Subject: [PATCH 03/38] more refactor Signed-off-by: Ran Shidlansik --- Triggers.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Triggers.md b/Triggers.md index 2f1638e..dd209a4 100644 --- a/Triggers.md +++ b/Triggers.md @@ -14,7 +14,7 @@ As being integrated in the Valkey Functions infrastructure, Triggers are persist ## Motivation -1. *** Localized actions. *** +1. ***Localized actions.*** Today different application use cases rely on [Valkey keyspace Notifications](https://valkey.io/topics/notifications/) in order to manage the database. For example, consider a case were a key is referenced in different places (like SortedSet and lists). In order to support TTL logic a cleanup needs to be made when the key is evicted to remove it from the different lists or sets. It is possible to have the application listen for eviction events and perform the cleanup, however this would require the application to rely on non-persistent subscribe connections, implement application side logic and extra network hops, which can cause the cleanup to be operated long after the key was evicted. For example lets take a commonly used pattern of scheduled tasks. In such cases user usually register tasks in a ZSET with the matching executing time as the score. @@ -46,7 +46,7 @@ As being integrated in the Valkey Functions infrastructure, Triggers are persist Will cause the specified message to be published every 3 seconds. Note that the same concept can be used to implement HASH members eviction! -2. *** Flexibility of extensibility. *** +2. ***Flexibility of extensibility.*** There are some cases were application needs to extend the logic of server side operations. In some of the cases it might be problematic making the change on the application side, either because of the risk to deploy the application part or since the operation is not triggered by the application (eg evictions, expirations etc...). For example, consider the current implementation of [Valkey keyspace Notifications](https://valkey.io/topics/notifications/). The existing mechanism relies on non-persistent pub/sub notifications. @@ -81,7 +81,7 @@ As being integrated in the Valkey Functions infrastructure, Triggers are persist [Valkey keyspace Notifications](https://valkey.io/topics/notifications/) has another major disadvantage: they are not reported on the cluster-bus. While this might have good reasoning (eg avoid cluster-bud load, duplication of events etc...), this makes Valkey clients almost unable to manage key events listeners on Valkey clusters. Valkey triggers can enable the user to decide to publish keyspace events on the cluster bus. This way the trigger code can make smart decisions about publishing of the event and then user the 'PUBLISH' call in order to make the event propagated to all cluster nodes. -3. *** Security. *** +3. ***Security.*** In some cases there is a need to limit the access to the cache. Currently Valkey provides restricting functionality via [Access Control Lists](https://valkey.io/topics/acl/). However what if there is a need to access restricted parts of the dataset without granting real permissions to clients? Since a trigger can operate without explicit user request, it can potentially be operated under any defined ACL user which will enable it to operate actions on restricted parts of the dataset. @@ -231,4 +231,4 @@ The suggestion here is to enable reporting error msg on a predefined pub/sub cha 1. All error msgs will be intercepted as deferred errors (much like modules do). 2. After trigger completes run, in case errors were issues, it will update the stats with the num,ber of errors. 3. The trigger will report the errors to a dedicated channel in the following structure: - "__triggers@errors__:" + `__triggers@errors__:` From 969c45158e14fe0a25d2e7038119218c9ec1305c Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:31:50 +0300 Subject: [PATCH 04/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index dd209a4..f82fe06 100644 --- a/Triggers.md +++ b/Triggers.md @@ -9,7 +9,7 @@ Status: Valkey triggers are persistent procedures which are executed in response to some keyspace event. Today [Valkey keyspace Notifications](https://valkey.io/topics/notifications/) offer the ability to publish events to dedicated pub/sub channels. Valkey triggers add the ability to place extending logic on such keyspace events and to localize actions on the server side reducing the need to monitor and react to events on the application side. -We propose to extend the existing [Valkey Functions](https://valkey.io/topics/functions-intro/) with the ability to define trigger function call on specific keyspace event. +We propose to extend the existing [Valkey Functions](https://valkey.io/topics/functions-intro/) with the ability to define trigger function calls on specific keyspace events. As being integrated in the Valkey Functions infrastructure, Triggers are persisted as part of the user data. ## Motivation From eb0ffe7345c2a19e8f94cea233826026d98658ba Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:32:02 +0300 Subject: [PATCH 05/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index f82fe06..a856793 100644 --- a/Triggers.md +++ b/Triggers.md @@ -10,7 +10,7 @@ Valkey triggers are persistent procedures which are executed in response to some Today [Valkey keyspace Notifications](https://valkey.io/topics/notifications/) offer the ability to publish events to dedicated pub/sub channels. Valkey triggers add the ability to place extending logic on such keyspace events and to localize actions on the server side reducing the need to monitor and react to events on the application side. We propose to extend the existing [Valkey Functions](https://valkey.io/topics/functions-intro/) with the ability to define trigger function calls on specific keyspace events. -As being integrated in the Valkey Functions infrastructure, Triggers are persisted as part of the user data. +As they are being integrated into the Valkey Functions infrastructure, Triggers are persisted as part of the user data. ## Motivation From 2ee0409f2949e908b479c5de4b31d3f23da548e7 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:32:15 +0300 Subject: [PATCH 06/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index a856793..a9406fa 100644 --- a/Triggers.md +++ b/Triggers.md @@ -15,7 +15,7 @@ As they are being integrated into the Valkey Functions infrastructure, Triggers ## Motivation 1. ***Localized actions.*** - Today different application use cases rely on [Valkey keyspace Notifications](https://valkey.io/topics/notifications/) in order to manage the database. For example, consider a case were a key is referenced in different places (like SortedSet and lists). In order to support TTL logic a cleanup needs to be made when the key is evicted to remove it from the different lists or sets. It is possible to have the application listen for eviction events and perform the cleanup, however this would require the application to rely on non-persistent subscribe connections, implement application side logic and extra network hops, which can cause the cleanup to be operated long after the key was evicted. + Today different application use cases rely on [Valkey keyspace notifications](https://valkey.io/topics/notifications/) in order to manage the database. For example, consider a case were a key is referenced in different places (like SortedSet and lists). In order to support TTL logic a cleanup needs to be made when the key is evicted to remove it from the different lists or sets. It is possible to have the application listen for eviction events and perform the cleanup, however this would require the application to rely on non-persistent subscribe connections, implement application side logic and extra network hops, which can cause the cleanup to be performed long after the key was evicted. For example lets take a commonly used pattern of scheduled tasks. In such cases user usually register tasks in a ZSET with the matching executing time as the score. Usually what is being done is setting a puller job in the application side which periodically reads all items in the set which has score smaller than the current timestamp, and issue an eval/fcall back to redis with the relevant task to run. From 0ee10e81b11913bf67c092d1120da47e8146f190 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:32:28 +0300 Subject: [PATCH 07/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index a9406fa..ba93e05 100644 --- a/Triggers.md +++ b/Triggers.md @@ -17,7 +17,7 @@ As they are being integrated into the Valkey Functions infrastructure, Triggers 1. ***Localized actions.*** Today different application use cases rely on [Valkey keyspace notifications](https://valkey.io/topics/notifications/) in order to manage the database. For example, consider a case were a key is referenced in different places (like SortedSet and lists). In order to support TTL logic a cleanup needs to be made when the key is evicted to remove it from the different lists or sets. It is possible to have the application listen for eviction events and perform the cleanup, however this would require the application to rely on non-persistent subscribe connections, implement application side logic and extra network hops, which can cause the cleanup to be performed long after the key was evicted. For example lets take a commonly used pattern of scheduled tasks. - In such cases user usually register tasks in a ZSET with the matching executing time as the score. + In such cases the user usually registers tasks in a ZSET with the matching execution time as the score. Usually what is being done is setting a puller job in the application side which periodically reads all items in the set which has score smaller than the current timestamp, and issue an eval/fcall back to redis with the relevant task to run. This pattern introduce some waste as the application needs to maintain a puller job and perform the roundtrip back to the application in order to execute the scheduled operations. With Valkey triggers this can be achieved without the need to place a puller job by the user. From 5f6640b95c683b2525b38916b2cfc833256c8555 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:32:45 +0300 Subject: [PATCH 08/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index ba93e05..9440230 100644 --- a/Triggers.md +++ b/Triggers.md @@ -18,7 +18,7 @@ As they are being integrated into the Valkey Functions infrastructure, Triggers Today different application use cases rely on [Valkey keyspace notifications](https://valkey.io/topics/notifications/) in order to manage the database. For example, consider a case were a key is referenced in different places (like SortedSet and lists). In order to support TTL logic a cleanup needs to be made when the key is evicted to remove it from the different lists or sets. It is possible to have the application listen for eviction events and perform the cleanup, however this would require the application to rely on non-persistent subscribe connections, implement application side logic and extra network hops, which can cause the cleanup to be performed long after the key was evicted. For example lets take a commonly used pattern of scheduled tasks. In such cases the user usually registers tasks in a ZSET with the matching execution time as the score. - Usually what is being done is setting a puller job in the application side which periodically reads all items in the set which has score smaller than the current timestamp, and issue an eval/fcall back to redis with the relevant task to run. + Usually what is being done is setting a puller job in the application side which periodically reads all items in the set which have scores smaller than the current timestamp, and issue eval/fcall commands back to Valkey with the relevant tasks to run. This pattern introduce some waste as the application needs to maintain a puller job and perform the roundtrip back to the application in order to execute the scheduled operations. With Valkey triggers this can be achieved without the need to place a puller job by the user. In order to achieve that we can use: From 5aa4d29a4f4a2a91498cb4328eb21b4d23f8639e Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:33:18 +0300 Subject: [PATCH 09/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 9440230..22d9e01 100644 --- a/Triggers.md +++ b/Triggers.md @@ -19,7 +19,7 @@ As they are being integrated into the Valkey Functions infrastructure, Triggers For example lets take a commonly used pattern of scheduled tasks. In such cases the user usually registers tasks in a ZSET with the matching execution time as the score. Usually what is being done is setting a puller job in the application side which periodically reads all items in the set which have scores smaller than the current timestamp, and issue eval/fcall commands back to Valkey with the relevant tasks to run. - This pattern introduce some waste as the application needs to maintain a puller job and perform the roundtrip back to the application in order to execute the scheduled operations. + This pattern introduces some waste as the application needs to maintain a puller job and perform the round trip back to the application in order to execute the scheduled operations. With Valkey triggers this can be achieved without the need to place a puller job by the user. In order to achieve that we can use: - ZSET z for for holding scheduled tasks From 5bab53bd4ee0730c06c632d9c30b798e52f7f702 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:33:30 +0300 Subject: [PATCH 10/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 22d9e01..615cc05 100644 --- a/Triggers.md +++ b/Triggers.md @@ -225,7 +225,7 @@ should `CONFIG RESETSTAT` also reset the triggers statistics, or should that onl ### Debug mechanism -While Valkey functions and scripts are executed as part of the FCALL/EVAL calls, it is thus possible to report the errors back to the calling client. +When Valkey functions and scripts are executed via FCALL/EVAL, it is possible to report the errors back to the calling client. Triggers, however, are silently executing which makes it hard for the user to understand why the trigger did not work and what errors occurred during execution of the trigger. The suggestion here is to enable reporting error msg on a predefined pub/sub channel: 1. All error msgs will be intercepted as deferred errors (much like modules do). From 321b769de53eef4cbec7396dc4d4c0d4bb3d8bf5 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:33:47 +0300 Subject: [PATCH 11/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 615cc05..fc5773f 100644 --- a/Triggers.md +++ b/Triggers.md @@ -20,7 +20,7 @@ As they are being integrated into the Valkey Functions infrastructure, Triggers In such cases the user usually registers tasks in a ZSET with the matching execution time as the score. Usually what is being done is setting a puller job in the application side which periodically reads all items in the set which have scores smaller than the current timestamp, and issue eval/fcall commands back to Valkey with the relevant tasks to run. This pattern introduces some waste as the application needs to maintain a puller job and perform the round trip back to the application in order to execute the scheduled operations. - With Valkey triggers this can be achieved without the need to place a puller job by the user. + With Valkey triggers this can be achieved without the need for a puller job by the user. In order to achieve that we can use: - ZSET z for for holding scheduled tasks - Key k to manage the next task execution time From c37e26a4646a74e3f1822781f80a997c7c604bf3 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:01:30 +0300 Subject: [PATCH 12/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index fc5773f..034cf51 100644 --- a/Triggers.md +++ b/Triggers.md @@ -22,7 +22,7 @@ As they are being integrated into the Valkey Functions infrastructure, Triggers This pattern introduces some waste as the application needs to maintain a puller job and perform the round trip back to the application in order to execute the scheduled operations. With Valkey triggers this can be achieved without the need for a puller job by the user. In order to achieve that we can use: - - ZSET z for for holding scheduled tasks + - ZSET z for holding scheduled tasks - Key k to manage the next task execution time The external application code will only have to add tasks to the ZSET **z** with score matching their required execution time. From cd4cfef17690d4ea97ed8fc09ee298c52db1fe09 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:02:04 +0300 Subject: [PATCH 13/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 034cf51..c016286 100644 --- a/Triggers.md +++ b/Triggers.md @@ -36,7 +36,7 @@ As they are being integrated into the Valkey Functions infrastructure, Triggers So in order to use them the user can simply register operations to be triggered: ``` - fcall schedule 0 'redis.call("PUBLISH", "scheduled", "this is a msg from the past")' "3" + fcall schedule 0 'server.call("PUBLISH", "scheduled", "this is a msg from the past")' "3" ``` Will cause publish of a msg after 3 seconds from the time the fcall was processed. From b905acf097f4447a157a5a2ac1981d3a79009b24 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:07:47 +0300 Subject: [PATCH 14/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index c016286..0a7bca4 100644 --- a/Triggers.md +++ b/Triggers.md @@ -27,7 +27,7 @@ As they are being integrated into the Valkey Functions infrastructure, Triggers The external application code will only have to add tasks to the ZSET **z** with score matching their required execution time. 1. once the task is added to **z** a trigger code will be executed which will take the minimal score from the ZSET **z**, and apply the diff to the current time to the TTL of key **k**. - 2. once key **k** has expired, a trigger will be executed which will remove and execute all the tasks from the zset **z** + 2. once key **k** has expired, a trigger will be executed which will remove and execute the tasks from the zset **z** that have scores lower than the current time. Here is an example library code to schedule tasks to be executed after/every several seconds From 57a0df4684b15d4ead7bc994be10c826ab3582b0 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:14:37 +0300 Subject: [PATCH 15/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 0a7bca4..3b812aa 100644 --- a/Triggers.md +++ b/Triggers.md @@ -38,7 +38,7 @@ As they are being integrated into the Valkey Functions infrastructure, Triggers ``` fcall schedule 0 'server.call("PUBLISH", "scheduled", "this is a msg from the past")' "3" ``` - Will cause publish of a msg after 3 seconds from the time the fcall was processed. + will cause the message to be published 3 seconds after the `fcall` is processed. ``` fcall every 0 "3" 'redis.call("PUBLISH", "scheduled", "this is an annoying msg")' From 3f720ce28e3fe4744d2a2d5285fa9aba895746f5 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:14:47 +0300 Subject: [PATCH 16/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 3b812aa..45e91ee 100644 --- a/Triggers.md +++ b/Triggers.md @@ -43,7 +43,7 @@ As they are being integrated into the Valkey Functions infrastructure, Triggers ``` fcall every 0 "3" 'redis.call("PUBLISH", "scheduled", "this is an annoying msg")' ``` - Will cause the specified message to be published every 3 seconds. + will cause the specified message to be published every 3 seconds. Note that the same concept can be used to implement HASH members eviction! 2. ***Flexibility of extensibility.*** From 432918975ff4d26e8bda3d40d42d8f31c6bdd3f8 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:22:32 +0300 Subject: [PATCH 17/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 45e91ee..2a2fa18 100644 --- a/Triggers.md +++ b/Triggers.md @@ -88,7 +88,7 @@ As they are being integrated into the Valkey Functions infrastructure, Triggers ### Can't the same be done with modules? Modules have the ability to build logic placed on top of keyspace events. However Modules provide a much more restrictive form of logic which is much harder to implement and dynamically replace. -By building this new logic inside the redis function library users will have a much more simple way to create stored procedures which are persistent, managed and replicated. +By building this new logic inside the Valkey function library users will have a much simpler way to create stored procedures which are persistent, managed and replicated. ## Design and Specification From 3273a7f4fea17f9af3e50a9064679c6db0ea832e Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:22:46 +0300 Subject: [PATCH 18/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 2a2fa18..0371222 100644 --- a/Triggers.md +++ b/Triggers.md @@ -95,7 +95,7 @@ By building this new logic inside the Valkey function library users will have a ### Triggers as part of function libraries Valkey triggers will be implemented as an extension of the Valkey [Function Libraries](https://valkey.io/topics/functions-intro/) . -A trigger is basically a Valkey function which will be called when some specific keyspace event was issued. +A trigger is basically a Valkey function which will be called when some specific keyspace event is issued. in order to register a specific function as trigger, one will simply have to use the new **"register_trigger"** API which will be available for the different library engines. For example for the (currently only) LUA engine the registration code will look like: From efd9a2a29a9dfbc03abf1b755681e491e79e45eb Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:22:57 +0300 Subject: [PATCH 19/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 0371222..0850797 100644 --- a/Triggers.md +++ b/Triggers.md @@ -96,7 +96,7 @@ By building this new logic inside the Valkey function library users will have a ### Triggers as part of function libraries Valkey triggers will be implemented as an extension of the Valkey [Function Libraries](https://valkey.io/topics/functions-intro/) . A trigger is basically a Valkey function which will be called when some specific keyspace event is issued. -in order to register a specific function as trigger, one will simply have to use the new **"register_trigger"** API which will be available for the different library engines. +In order to register a specific function as trigger, one will simply have to use the new **"register_trigger"** API which will be available for the different library engines. For example for the (currently only) LUA engine the registration code will look like: ![image](https://user-images.githubusercontent.com/88133677/225090299-1cf05508-75e6-43dd-a90f-05ca91dd3a6f.png) From 4eca10c55e30b0dbcc232f8e5310c8eb17ec55e7 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:25:23 +0300 Subject: [PATCH 20/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 0850797..c06d930 100644 --- a/Triggers.md +++ b/Triggers.md @@ -101,7 +101,7 @@ For example for the (currently only) LUA engine the registration code will look ![image](https://user-images.githubusercontent.com/88133677/225090299-1cf05508-75e6-43dd-a90f-05ca91dd3a6f.png) -### Register trigger with named arguments form< +### Register trigger with named arguments form In order to provide some extra arguments to the trigger, the new API will also support the named arguments form. For example: From cebd67681b1354a0ef74e7400025245d882fbe62 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:25:40 +0300 Subject: [PATCH 21/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index c06d930..eae6b7d 100644 --- a/Triggers.md +++ b/Triggers.md @@ -79,7 +79,7 @@ As they are being integrated into the Valkey Functions infrastructure, Triggers 2) "lpush" ``` - [Valkey keyspace Notifications](https://valkey.io/topics/notifications/) has another major disadvantage: they are not reported on the cluster-bus. While this might have good reasoning (eg avoid cluster-bud load, duplication of events etc...), this makes Valkey clients almost unable to manage key events listeners on Valkey clusters. Valkey triggers can enable the user to decide to publish keyspace events on the cluster bus. This way the trigger code can make smart decisions about publishing of the event and then user the 'PUBLISH' call in order to make the event propagated to all cluster nodes. + [Valkey keyspace notifications](https://valkey.io/topics/notifications/) has another major disadvantage: they are not reported on the cluster-bus. While this might have good reasoning (eg avoid cluster-bus load, duplication of events etc...), this makes Valkey clients almost unable to manage key event listeners on Valkey clusters. Valkey triggers can enable the user to decide to publish keyspace events on the cluster bus. This way the trigger code can make smart decisions about publishing of the event and then use the 'PUBLISH' call in order to propagate the event to all cluster nodes. 3. ***Security.*** In some cases there is a need to limit the access to the cache. Currently Valkey provides restricting functionality via [Access Control Lists](https://valkey.io/topics/acl/). From 5db2bbc6ec20f8dba750a8e2a6e99e653f9a87ad Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:26:03 +0300 Subject: [PATCH 22/38] Update Triggers.md Co-authored-by: Madelyn Olson Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index eae6b7d..b9d6170 100644 --- a/Triggers.md +++ b/Triggers.md @@ -1,5 +1,5 @@ --- -RFC: +RFC: 9 Status: --- From 2ea37a920c403f11f223537ac29c3ab5f50b5755 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:30:57 +0300 Subject: [PATCH 23/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index b9d6170..41b42ec 100644 --- a/Triggers.md +++ b/Triggers.md @@ -117,7 +117,7 @@ The function synopsis will be identical for all triggers which will get the foll **c. db -** the id of the db context for which the event is triggered (note it is not necessarily the current db scope the trigger is executed in, ie. MOVE event). -For LUA engine based functions, The key will be supplied as the first argument in the keys array (eg KEYS[1]) +For LUA engine based functions, the key will be supplied as the first argument in the keys array (i.e. KEYS[1]). The **event** and **db** will be supplied in the ARGS array respectively (event will be provided as ARGS[1] and db as ARGS[2]) From 2868333c75b440c9a05528a4da19494432563b27 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:31:07 +0300 Subject: [PATCH 24/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 41b42ec..6452a4b 100644 --- a/Triggers.md +++ b/Triggers.md @@ -118,7 +118,7 @@ The function synopsis will be identical for all triggers which will get the foll **c. db -** the id of the db context for which the event is triggered (note it is not necessarily the current db scope the trigger is executed in, ie. MOVE event). For LUA engine based functions, the key will be supplied as the first argument in the keys array (i.e. KEYS[1]). -The **event** and **db** will be supplied in the ARGS array respectively (event will be provided as ARGS[1] and db as ARGS[2]) +The **event** and **db** will be supplied in the ARGS array. (event will be ARGS[1] and db will be ARGS[2]). ### Triggering Events From 3becac92f0973aed6809984cfd73b11c6320ad2f Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:31:21 +0300 Subject: [PATCH 25/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 6452a4b..6b54c24 100644 --- a/Triggers.md +++ b/Triggers.md @@ -126,7 +126,7 @@ The **event** and **db** will be supplied in the ARGS array. (event will be ARG Valkey already has a feature to report different [keyspace notifications](https://valkey.io/topics/notifications/) which currently provides the ability to publish keyspace events to registered subscribers. The implementation should introduce the ability to set a trigger function to run on **ANY** event which is defined for [keyspace notifications](https://valkey.io/topics/notifications/). ### Triggers configuration -By design Triggers will not require ANY configuration in order to work. The Triggers will be part of the loadable function libraries and should be able to run on the target events even if the user **DID NOT** explicitly configured keyspace-notifications. +By design Triggers will not require ANY configuration in order to work. The Triggers will be part of the loadable function libraries and should be able to run on the target events even if the user **DID NOT** explicitly configure keyspace-notifications. ### Atomicity and triggers From 0b745020da5c8a6c29ff5b5b0c1fe50df336f3e8 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:31:39 +0300 Subject: [PATCH 26/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 6b54c24..ef6b8d0 100644 --- a/Triggers.md +++ b/Triggers.md @@ -49,7 +49,7 @@ As they are being integrated into the Valkey Functions infrastructure, Triggers 2. ***Flexibility of extensibility.*** There are some cases were application needs to extend the logic of server side operations. In some of the cases it might be problematic making the change on the application side, either because of the risk to deploy the application part or since the operation is not triggered by the application (eg evictions, expirations etc...). - For example, consider the current implementation of [Valkey keyspace Notifications](https://valkey.io/topics/notifications/). The existing mechanism relies on non-persistent pub/sub notifications. + For example, consider the current implementation of [Valkey keyspace notifications](https://valkey.io/topics/notifications/). The existing mechanism relies on non-persistent pub/sub notifications. In order to persist the notifications it is possible to write them into a sorted set. Triggers can provide this ability in a very straightforward way. For example, consider the following function library: From c682a75ceded7e7eff1b981985da27b8169f925c Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:56:00 +0300 Subject: [PATCH 27/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index ef6b8d0..5a905e8 100644 --- a/Triggers.md +++ b/Triggers.md @@ -135,7 +135,7 @@ It means allowing a write call performed in the middle of operating another comm There are 2 design options: -1. post execution of triggers. Much like was presented for modules with `VM_AddPostNotificationJob` which was introduced to allow modules +1. Post execution of triggers. Much like what was provided for modules with `VM_AddPostNotificationJob`, which was introduced to allow modules to perform write operations on keyspace events as part of postExecutionUnitOperations. It is possible to follow the same logic as modules and share the same notification mechanism ie. to operate the trigger operation during the unit operation completion. This will help prevent breaking atomicity of operations and better follows the way applications are currently reacting to keyspace events. The downside of this option is that since there is no grantee about the state of the data after the operation is completed. From 059dede13bd486e626490337b863cc1cd0555d77 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:56:13 +0300 Subject: [PATCH 28/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 5a905e8..e997a06 100644 --- a/Triggers.md +++ b/Triggers.md @@ -136,7 +136,7 @@ It means allowing a write call performed in the middle of operating another comm There are 2 design options: 1. Post execution of triggers. Much like what was provided for modules with `VM_AddPostNotificationJob`, which was introduced to allow modules to -perform write operations on keyspace events as part of postExecutionUnitOperations. It is possible to follow the same logic as modules and share the same notification mechanism ie. to operate the trigger operation during the unit operation completion. +perform write operations on keyspace events as part of postExecutionUnitOperations. It is possible to follow the same logic as modules and share the same notification mechanism ie. to execute the trigger during the unit operation completion. This will help prevent breaking atomicity of operations and better follows the way applications are currently reacting to keyspace events. The downside of this option is that since there is no grantee about the state of the data after the operation is completed. From 4768e3259bea1a952fb2ec0db947c652872adbdd Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:59:23 +0300 Subject: [PATCH 29/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index e997a06..2af0850 100644 --- a/Triggers.md +++ b/Triggers.md @@ -158,7 +158,7 @@ Also in case **e2** is issued the **t** is being executed and cause event **e1** Whenever some event triggers an action, we will need this action to be processed in the scope of a specific database and ACL user. #### Database context -Since Redis triggers are built on keyspace events, the DB scope will be the same as the keyspace event was issued on. +Since Valkey triggers are built on keyspace events, the DB scope will be the same as the keyspace the event was issued on. #### ACL User scope By default the scope of the user will by the superuser which will not be limited by any authentication restrictions. From c9db2e5fdb22a847891239e55958d0ed7ca8c9f2 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:06:49 +0300 Subject: [PATCH 30/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 2af0850..9c53675 100644 --- a/Triggers.md +++ b/Triggers.md @@ -194,7 +194,7 @@ Much like functions the triggers part will describe a per-trigger information: 7) "calls" 8) "" 9) "errors" - 10) "" + 10) "" 11) "total_duration" 12) ``` From d1af248d99a99b67cfee58c278a3f717570d3a39 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:06:58 +0300 Subject: [PATCH 31/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 9c53675..9b33776 100644 --- a/Triggers.md +++ b/Triggers.md @@ -196,7 +196,7 @@ Much like functions the triggers part will describe a per-trigger information: 9) "errors" 10) "" 11) "total_duration" - 12) + 12) ``` ### Benchmarking (Optional) From cbc13b648716e82e4a465ad18c6c186a2e3e1ab4 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:07:08 +0300 Subject: [PATCH 32/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 9b33776..7c5cd3f 100644 --- a/Triggers.md +++ b/Triggers.md @@ -210,7 +210,7 @@ TBD #### general statistics The "Stats" info section will be added with the following statistics: -**total_trigger_calls -** the number of general trigger calls. this will not be a teardown list of calls per trigger function (this can be taken from the function list command as will be explained later) +- **total_trigger_calls -** the number of general trigger calls. this will not be a teardown list of calls per trigger function (this can be taken from the function list command as will be explained later) **total_trigger_errors -** The total number of errors during trigger execution. (an error is accounted for each time the afterErrorReply is called) **total_trigger_duration -** The total time spent running trigger related code. From bb1bddfb773641c15926dee002924d462c8d5265 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:07:16 +0300 Subject: [PATCH 33/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 7c5cd3f..e51bb19 100644 --- a/Triggers.md +++ b/Triggers.md @@ -211,7 +211,7 @@ TBD #### general statistics The "Stats" info section will be added with the following statistics: - **total_trigger_calls -** the number of general trigger calls. this will not be a teardown list of calls per trigger function (this can be taken from the function list command as will be explained later) -**total_trigger_errors -** The total number of errors during trigger execution. (an error is accounted for each time the afterErrorReply is called) +- **total_trigger_errors -** The total number of errors during trigger execution (an error is counted for each time the afterErrorReply is called). **total_trigger_duration -** The total time spent running trigger related code. ### Per trigger statistics From 85d89e704cff902db3c444f89fffcc922b2c4d0a Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:07:25 +0300 Subject: [PATCH 34/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index e51bb19..ea67b4c 100644 --- a/Triggers.md +++ b/Triggers.md @@ -212,7 +212,7 @@ TBD The "Stats" info section will be added with the following statistics: - **total_trigger_calls -** the number of general trigger calls. this will not be a teardown list of calls per trigger function (this can be taken from the function list command as will be explained later) - **total_trigger_errors -** The total number of errors during trigger execution (an error is counted for each time the afterErrorReply is called). -**total_trigger_duration -** The total time spent running trigger related code. +- **total_trigger_duration -** The total time spent running trigger related code. ### Per trigger statistics The same global statistics will be available on a per-trigger resolution via the `FUNCTION LIST` command: From 1c9181e174b198adab5c3a2479caef9585f9264c Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:07:34 +0300 Subject: [PATCH 35/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index ea67b4c..4cf5391 100644 --- a/Triggers.md +++ b/Triggers.md @@ -216,7 +216,7 @@ The "Stats" info section will be added with the following statistics: ### Per trigger statistics The same global statistics will be available on a per-trigger resolution via the `FUNCTION LIST` command: -"calls" - the total number of times the trigger has called +- "calls" - the total number of times the trigger has called "errors" - the total number of errors issued during this trigger run "total_duration" - the aggregated total duration time this trigger run From 8f1bfe255f3ef54a41a4a836b0641e12bbb27c01 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:07:42 +0300 Subject: [PATCH 36/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 4cf5391..d79fc9f 100644 --- a/Triggers.md +++ b/Triggers.md @@ -217,7 +217,7 @@ The "Stats" info section will be added with the following statistics: ### Per trigger statistics The same global statistics will be available on a per-trigger resolution via the `FUNCTION LIST` command: - "calls" - the total number of times the trigger has called -"errors" - the total number of errors issued during this trigger run +- "errors" - the total number of errors issued during this trigger run "total_duration" - the aggregated total duration time this trigger run *** Open question *** From 9fb30ae483170e0632bd246c337ef49b3d98ee47 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:07:51 +0300 Subject: [PATCH 37/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index d79fc9f..04a7066 100644 --- a/Triggers.md +++ b/Triggers.md @@ -218,7 +218,7 @@ The "Stats" info section will be added with the following statistics: The same global statistics will be available on a per-trigger resolution via the `FUNCTION LIST` command: - "calls" - the total number of times the trigger has called - "errors" - the total number of errors issued during this trigger run -"total_duration" - the aggregated total duration time this trigger run +- "total_duration" - the aggregated total duration time this trigger run *** Open question *** should `CONFIG RESETSTAT` also reset the triggers statistics, or should that only be done when functions lib is reloaded? From 057009128cdc7e7bffa0430634cb30b641530936 Mon Sep 17 00:00:00 2001 From: ranshid <88133677+ranshid@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:08:04 +0300 Subject: [PATCH 38/38] Update Triggers.md Co-authored-by: Daniel House @ Seeking Opportunities <76451671+daniel-house@users.noreply.github.com> Signed-off-by: ranshid <88133677+ranshid@users.noreply.github.com> --- Triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triggers.md b/Triggers.md index 04a7066..05ca5b0 100644 --- a/Triggers.md +++ b/Triggers.md @@ -229,6 +229,6 @@ When Valkey functions and scripts are executed via FCALL/EVAL, it is possible to Triggers, however, are silently executing which makes it hard for the user to understand why the trigger did not work and what errors occurred during execution of the trigger. The suggestion here is to enable reporting error msg on a predefined pub/sub channel: 1. All error msgs will be intercepted as deferred errors (much like modules do). -2. After trigger completes run, in case errors were issues, it will update the stats with the num,ber of errors. +2. After trigger completes run, in case errors were issued, it will update the stats with the number of errors. 3. The trigger will report the errors to a dedicated channel in the following structure: `__triggers@errors__:`