键空间触发器

基于键空间通知执行 JavaScript 函数

Redis 开源 Redis Enterprise Software Redis Cloud Redis 开源 Redis Enterprise for Kubernetes 客户端

键空间触发器允许您注册一个函数,该函数将在数据库中发生事件时执行。 大多数事件由命令调用触发,但有两个特殊事件可以独立于命令发生

  1. 已过期:当密钥从数据库中过期时会触发此事件。
  2. 已驱逐:当密钥从数据库中被驱逐时会触发此事件。

有关支持事件的完整列表,请参阅Redis 键空间通知页面

要注册键空间触发器,您需要在加载库时使用 redis.registerKeySpaceTrigger API。 以下示例演示了如何注册一个数据库触发器,该触发器在哈希键被修改时添加“最后更新”字段

#!js api_version=1.0 name=myFirstLibrary

redis.registerKeySpaceTrigger("consumer", "", function(client, data){
    if (client.call("type", data.key) != "hash") {
        // key is not a has, do not touch it.
        return;
    }
    // get the current time in ms
    var curr_time = client.call("time")[0];
    // set '__last_updated__' with the current time value
    client.call('hset', data.key, '__last_updated__', curr_time);
});

参数说明

  • consumer:消费者名称。
  • prefix :触发器应触发的键前缀。
  • callback:要调用的回调函数,遵循与同步和异步调用相同的规则。 回调仅在主分片上调用。

运行示例

127.0.0.1:6379> hset h x 1
(integer) 1
127.0.0.1:6379> hgetall h
1) "x"
2) "1"
3) "__last_updated__"
4) "1658390831"
127.0.0.1:6379> hincrby h x 1
(integer) 2
127.0.0.1:6379> hgetall h
1) "x"
2) "2"
3) "__last_updated__"
4) "1658390910"

传递给消费者回调的 data 参数采用以下格式

{
    "event": "<the event name that fired the trigger>",
    "key": "<key name that the event was fired on as String>",
    "key_raw": "<key name that the event was fired on as ArrayBuffer>"
}

请注意,仅当密钥可以解码为 JS String 时才提供 key 字段,否则该值为 null

我们可以使用 TFUNCTION LIST 命令显示触发器信息

127.0.0.1:6379> TFUNCTION list vvv
1)  1) "engine"
    2) "js"
    3) "api_version"
    4) "1.0"
    5) "name"
    6) "foo"
    7) "pending_jobs"
    8) (integer) 0
    9) "user"
    10) "default"
    11) "functions"
   12) (empty array)
   13) "keyspace_triggers"
   14) (empty array)
   15) "stream_triggers"
   16) 1)  1) "name"
           2) "consumer"
           3) "num_triggered"
           4) (integer) 2
           5) "num_finished"
           6) (integer) 2
           7) "num_success"
           8) (integer) 1
           9) "num_failed"
          10) (integer) 1
          11) "last_error"
          12) "TypeError: redis.call is not a function"
          13) "last_exection_time"
          14) (integer) 0
          15) "total_exection_time"
          16) (integer) 0
          17) "avg_exection_time"
          18) "0"

触发器保证

如果传递给触发器的回调函数是一个 JS 函数(不是协程),则保证回调将与导致触发器的操作原子地并行调用;这意味着所有客户端只有在回调完成后才能看到数据。 此外,保证回调的效果将在 multi/exec 块中与触发触发器的命令一起复制到副本和 AOF 中。

如果回调是协程,它将在后台执行,并且无法保证它将在何处或是否执行。 保证与 同步和异步调用中描述的相同。

升级

使用 TFUNCTION LOAD 命令的 REPLACE 选项升级现有触发器代码时,可以修改所有触发器参数。

高级用法

对于大多数用例,registerKeySpaceTrigger API 已经足够。 但是在某些用例中,您可能需要更好地保证触发器何时被触发。 让我们看下面的例子

#!js api_version=1.0 name=myFirstLibrary

redis.registerKeySpaceTrigger("consumer", "", function(client, data){
    if (client.call("type", data.key) != "hash") {
        // key is not a has, do not touch it.
        return;
    }
    var name = client.call('hget', data.key, 'name');
    client.call('incr', `name_${name}`);
});

每当哈希键被更改时,上面的示例将从哈希中读取字段 name,例如 tom,并增加键 name_tom 的值。 (注意:此函数在集群上无法正常工作。我们需要在键的名称上使用 {},以确保我们写入到位于当前分片上的键。为了简单起见,我们暂时忽略集群问题)。运行该函数将给出以下结果

127.0.0.1:6379> hset x name tom
(integer) 1
127.0.0.1:6379> hset x name jerry
(integer) 0
127.0.0.1:6379> get name_tom
"1"
127.0.0.1:6379> get name_jerry
"1"

我们可以看到键 name_tom 递增了一次,键 name_jerry 递增了一次。 如果我们用 multi/exec 包装 hset 调用,我们会得到相同的结果吗?

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> hset x name tom
QUEUED
127.0.0.1:6379(TX)> hset x name jerry
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 1
2) (integer) 0
127.0.0.1:6379> get name_tom
(nil)
127.0.0.1:6379> get name_jerry
"2"

刚才发生了什么? name_jerry 递增了两次,而 name_tom 根本没有递增。 发生这种情况是因为,在 multi/exec 或 Lua 函数的情况下,通知在事务结束时触发,因此所有客户端都会收到写入的最后一个值(即 jerry)的通知。

为了修复代码,并且仍然在 multi/exec 上获得预期的结果。 触发器和函数允许您指定一个可选的回调,该回调将在通知发生时(而不是在事务结束时)精确运行。 此回调的约束是它只能读取数据,而不能执行任何写入操作。 新代码如下

#!js api_version=1.0 name=lib

redis.registerKeySpaceTrigger("consumer", "", function(client, data){
    if (data.name !== undefined) {
        client.call('incr', `name_${data.name}`);
    }
},{
    onTriggerFired: (client, data) => {
        if (client.call("type", data.key) != "hash") {
            // key is not a has, do not touch it.
            return;
        }
        data.name = client.call('hget', data.key, 'name');
    }
});

上面的代码给触发器提供了一个可选的函数参数,onTriggerFired。该函数将在键更改后立即触发,并允许我们读取键的内容。我们将内容添加到data参数中,该参数将传递给实际的触发函数,该函数可以写入数据。上面的代码按预期工作。

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> hset x name tom
QUEUED
127.0.0.1:6379(TX)> hset x name jerry
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 1
2) (integer) 0
127.0.0.1:6379> get name_tom
"1"
127.0.0.1:6379> get name_jerry
"1"
为此页评分
返回顶部 ↑