跳转到主要内容
内核为所有正在运行的插件提供了一个共享的键值存储。 插件可以使用它来存储必须通过内核读取或更新的小段状态,例如缓存值、插件状态或与其他插件共享的数据。 KV 值是原始字节。内核不会为其指定数据格式,因此您可以将值编码为 JSON、文本、二进制结构或插件能够理解的任何其他格式。

存储模型

该存储采用两级结构:
例如:
KV 存储属于内核,而不是单个插件实例。所有连接到同一内核的插件都使用同一个存储。 插件命名空间是一种组织约定,而非权限边界。在当前实现中,除非另一个插件的命名空间是只读的(当前仅有sys),否则插件可以读取和写入该命名空间。 按照惯例,使用插件拥有的命名空间,该命名空间通常由插件名称派生而来。如果多个插件有意共享一个命名空间,请记录它们约定使用的键名和值格式。

操作

操作由 Protobuf 主机契约定义。有关共享传输的定义,请参阅Protobuf

读取值

读取请求包含: 响应包含一个 found 布尔值和一个 value 字节序列。缺少键并非错误:foundfalse,且不返回任何值。 读取值不会创建其命名空间或键。

写入值

写入请求包含相同的 namespacekey 字段,以及: 写入现有键会替换其值。如果写入操作被拒绝, 例如命名空间为只读时,该操作将返回错误字符串。

删除值

删除请求包含 namespacekey。删除现有键会移除其值。 删除不存在的键会成功,且不会更改存储。 如果删除操作被拒绝,包括尝试从只读命名空间中删除, 该操作将返回错误字符串。

列出键

列表请求包含一个 namespace 字段:
  • namespace 非空时,响应包含当前该命名空间中存在的键。
  • namespace 为空时,响应包含当前存储中所有命名空间的名称。
返回的名称按内核排序。列表不会返回值。

生命周期和持久化

内核在创建插件管理器时会创建 KV 存储。该存储位于内存中:
  • 停止或重启插件不会清除其 KV 值;
  • 加载同一插件的新版本不会清除其 KV 值;并且
  • 重启内核会创建一个新的空存储并清除所有值。
因此,KV 不是持久数据库。当数据必须在内核重启后仍然存在时, 请将其持久化到其他位置,并将 KV 视为运行时状态或共享缓存。

并发性和值所有权

内核会同步各个键值 (KV) 操作。当多个插件同时调用时, 读取、写入、删除和列表操作都是安全的。 不存在事务或比较交换操作。 诸如“读取、修改、写入”之类的操作序列对于其他插件而言并非原子操作。 如果多个插件更新同一个键,则需要协调所有权或设计该值, 以确保丢失的更新不会破坏应用程序状态。 内核在存储和读取字节值时会进行复制。 写入后修改字节缓冲区不会改变存储的值, 修改返回的缓冲区也不会改变存储的值。

sys 命名空间

sys 是一个由内核管理的只读命名空间。插件可以读取和列出它, 但通过插件宿主边界进行的写入和删除操作会返回错误。 内核使用此命名空间来管理宿主状态,包括插件注册表信息。 不要写入 sys,也不要依赖未记录的系统键,除非内核文档明确定义了它们。

后端行为

WASM 和 gRPC 插件使用相同的键值对 (KV) 约定:
  • WASM 插件通过生成的主机绑定访问存储;以及
  • gRPC 插件通过内核的主机回调通道访问存储。
两种后端的存储语义相同。后端改变的是请求如何跨越插件边界,而不是内核如何存储或返回值。

推荐用法

使用可预测的命名空间和键方案:
保持值足够小,以便于调用路径,并在其他组件使用它们之前定义它们的编码。 在解码值之前检查 found 结果处理写入和删除错误,并避免将成功的写入视为持久化。