tommwq.work/aip

AIP-216 状态

· [tommwq@126.com]
编号 216
原文链接 https://google.aip.dev/216
状态 批准
创建日期 2018-10-01
更新日期 2018-10-01

许多API资源都带有“状态”概念:通常指资源在其生命周期中的位置。例如,虚拟机可能处于配置中、可用、关闭中,或者其他几种可能的情况之一。作业或查询可能处于准备运行、运行中、已完成等。

指南

需要表达状态的资源 应当 使用枚举,枚举 应当 称为 State (或者如果要更具体,以 State 结尾)。如果枚举只用作消息内的域, 应当 将枚举嵌套在其所描述的消息中。

重要 我们使用词汇 State 而非 Status (后者用于HTTP和gRPC状态)。

枚举值

理想情况下,Google API在表达相同的语义概念时,使用同一个词汇。通常会有多个词汇可以表达同一个状态,而我们的客户经常同时使用多个API,如果我们使用统一的词汇,对他们来说更容易。

在高层次上:

  • 可用的资源是 ACTIVE 的(优先于“ready”或“available”等词汇)。
  • 已完成(通常时终止)请求操作的资源,状态使用过去分词(通常以 -ED 结尾),例如 SUCCEEDED (不是“successful”)、 FAILED (不是“failure”)、 DELETEDSUSPENDED 等。
  • 当前正在进行状态变更的资源,状态使用现在分词(通常以 -ING 结尾),例如 RUNNINGCREATINGDELETING 等。此时,状态通常是临时的,会自行转变为另一个状态,无需用户进一步操作。

注意 请记住,只添加对客户有用的状态。没有必要仅仅因为状态在系统内部中存在而发布大量状态,这会给客户带来困惑。每个状态都必须附带一个用例,说明为什么需要它。

只输出域

资源中引用 State 枚举的域 应当 按照AIP-203规定,遵守“只输出域”行为,并记录在文档中。

API 不应 允许通过“更新”方法直接修改(或通过“创建”方法直接设置) State 枚举域, 应当 使用自定义状态转换方法。

这是因为更新方法通常不应具有副作用,直接更新状态意味着可以将状态设置为任何有效值,但状态通常反映资源在生命周期中的进度。

状态转换方法

状态转换方法是一种特殊的自定义方法,负责将状态域从一个枚举值转换到另一个。作为转换工作的一部分,其他域也可能发生变化,例如 update_time 域。方法定义应如下所示:

// 发布书籍。
// 发布后书籍的 state 为 PUBLISHED。
// PublishBook 可以在状态为 DRAFT 的书籍上调用;对于处于不同状态(包括 PUBLISHED)的书籍返回错误。
rpc PublishBook(PublishBookRequest) returns (Book) {
  option (google.api.http) = {
    post: "/v1/{name=publishers/*/books/*}:publish"
    body: "*"
  };
}
  • 方法的名字 应当 是动词后接资源消息名字的单数形式。
  • 请求消息名字 必须 与远程过程调用名字一致,并带有 Request 后缀。
  • 应答消息 应当 是资源本身。
    • 如果远程过程调用是耗时的,应答消息 应当google.longrunning.Operation ,解析为资源本身。
  • HTTP动词 必须POST
  • HTTP URI 必须 使用 : 字符后接自定义动词(如上例中的 :publish )。URI中的动词 必须 与远程过程调用名字中的动词一致。
    • 如果需要分词, 必须 使用 camelCase
  • google.api.http 注解中的 body 子句 必须 是~"*"~。
  • 请求消息中接收资源名字的域 应当 映射到URI路径。
    • 应当 称为 name
    • name应当 是URI路径中唯一的变量。所有其余参数 应当 映射到URI查询参数。
  • 如果不允许请求的状态转换,服务 必须 返回 FAILED_PRECONDITION (HTTP 400)错误。

请求消息应如下所示:

message PublishBookRequest {
  // 要发布的书籍名字。
  // 格式:publishers/{publisher}/books/{book}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "library.googleapis.com/Book"
    }];
}
  • 必须 包含资源名字域, 应当 称为 name
  • 域的注释 应当 记录资源模式。
  • 可以 包含其他域。

附加指南

默认值

每个状态枚举的零值 应当 遵守以下约定:

enum State {
  // 默认值。省略状态时使用此值。
  STATE_UNSPECIFIED = 0;

  // 其他值...
}

资源 不应 向用户提供“未指定”状态,这个值 不应 实际使用。

值唯一性

同一包中的多个顶级枚举 不得 共享相同的值。这是因为C++ protoc代码生成器将顶级枚举值展平到同一个名字空间中。

状态枚举 应当 存在于资源定义内部。

前缀

不需要在每个枚举值上使用 STATE_ 前缀。状态枚举值 不应 以枚举名字作为前缀,默认值 STATE_UNSPECIFIED 除外。

破坏性变更

省流 明确告知用户,状态枚举可能在未来添加新值。向现有枚举添加状态时要谨慎。

尽管向状态枚举添加新状态 可能 破坏现有用户代码,但这不被视为破坏性变更。考虑只有两个值的状态: ACTIVEDELETED 。用户可能会添加代码进行检查 if state == ACTIVE ,并在else情况下简单的假定资源已删除。如果API后来出于其他目的添加了新状态,这份代码将失效。

我们无法从根本上控制这种行为,但API文档 应当 积极鼓励用户在编写代码时假定状态枚举可能在未来添加新值。

API 可以 适时向状态枚举添加新值,并且 被视为破坏性变更。

何时避免使用状态

有时 State 枚举可能不是API的最佳选择,特别是状态只有几个可选值或状态不互斥的时候。

考虑前文只有 ACTIVEDELETED 的状态。此时API可能更适合发布 google.protobuf.Timestamp delete_time 域,告诉用户根据域是否设定来判定删除。

常见状态

以下是常用状态列表。API在决定状态名字时 应当 考虑先例。在先例冲突时 应当 优先考虑局部一致性,而非全局一致性。

静置状态

“静置状态”是在没有用户操作的情况下,预计会无限期维持的生命周期状态。用户可以启动操作,将处于静置状态的资源转变为其他状态(静置或活动)。

  • ACCEPTED
  • ACTIVE
  • CANCELLED
  • DELETED
  • FAILED
  • SUCCEEDED
  • SUSPENDED
  • VERIFIED

活动状态

“活动状态”是通常会自行转变到一个预期静置状态的生命周期状态。

注意 请记住,只发布对客户有用的状态。只有资源在状态下可能维持足够长的时间,活动状态才有价值。如果状态转变是立即的,不需要活动状态。

  • CREATING (通常转变为ACTIVE
  • DELETING (通常转变为DELETED
  • PENDING (通常转变为RUNNING
  • REPAIRING (通常转变为ACTIVE
  • RUNNING (通常转变为SUCCEEDED
  • SUSPENDING (通常转变为SUSPENDED

进一步阅读

  • 关于枚举的一般信息,请参考AIP-126

修订记录

  • 2022-06-02 修改后缀描述,消除多余的“-”。
  • 2020-10-20 添加关于在枚举值前添加枚举名字的指南。
  • 2020-09-02 明确状态在创建时也不可以直接设置。
  • 2019-12-05 修改状态转换方法指南,将应答类型从 必须 降级为 应当
  • 2019-08-16 添加状态转换方法指南。
  • 2019-07-18 添加关于未指定值的明确指南。