Files
orbit/docs/api/resource-retirement.md

86 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 通用资源退役
适用于长期服务、临时环境和自动化回归。平台不根据应用名称、队列名称或测试前缀自动清理。退役是不可恢复的显式管理动作,保留资源 ID、名称、任务、交付、幂等回执与审计;不是硬删除,也不自动归档或缩小数据库。
## 接口与权限
| 操作 | HTTP | 权限 | Go SDK |
| --- | --- | --- | --- |
| 退役订阅 | `POST /v1/subscriptions/{id}:retire` | `events:manage`,原凭据所有权和队列授权 | `RetireSubscription` |
| 退役队列 | `POST /v1/queues/{id}:retire` | `queues:manage` 和队列授权 | `RetireQueue` |
单管理员通过已登录的控制台管理授权范围;普通 SDK 不能操作其他凭据的订阅。请求需要 Bearer 身份及 `Idempotency-Key`,不接受查询参数,正文:
```json
{"expected_revision":"3","reason":"临时资源已结清,结束使用"}
```
成功 HTTP 200,保持现有 `request_id + data/error` 信封。data 分别为 `{subscription,deduplicated}`、`{queue,deduplicated}`。查询及列表增加可选 `retired_at`;订阅同时增加十进制字符串 `retired_sequence`。旧记录不需要回填。
同键同内容返回原回执,不因状态已经退役而失败;同键不同内容为 `IDEMPOTENCY_CONFLICT`。无明确回执时必须保存并重用原目标、请求和键,不自动生成新键。新命令须携带最新配置 revision。
## 订阅的结清边界
退役事务锁定命名空间结果流的已提交水位,再锁订阅。必须同时满足:
- pending_count=0,实际不存在 PENDING、LEASED 或 EXPIRED 交付。
- 截止水位之前,没有该队列和类型过滤范围内尚未物化的结果;缺失来源或历史保留缺口也拒绝退役。
成功后 enabled=false,记录退役水位并推进 cursor 至该水位,释放一个活动名额。此后新产生的事件不再属于该订阅。管理员应先停止旧生产者并排空消费者;平台无法判断调用方将来是否还会提交任务。
暂停依然占活动名额;每命名空间最多 64 个未退役订阅。已退役记录仍可列表、读取和查交付历史,不能恢复、重置起点或更换原名称。不存在通过忽略未消费事件强制释放名额的接口。
## 队列的依赖保护
队列退役按应用额度、操作身份、队列持锁,与提交、执行和流程推进串行。下列情况返回 `RESOURCE_BUSY` 并说明原因:
- 有未完成或待核查任务、在途执行尝试或未释放执行许可。
- 有启用的定时计划。先暂停计划,已产生任务仍须结清。
- 有固定版本引用该队列的未终结流程,包括暂停、等待、尚未执行到该队列的分支。
- 有未退役订阅,包括暂停订阅。
成功后 paused=true,禁止新任务、新订阅、Worker 注册、计划创建/发布/恢复以及使用该队列启动新流程。已发布流程定义和暂停计划作为历史保留,不能绕过退役恢复新工作。任务与流程历史查询继续可用,原已完成命令的冻结回执不重写。队列名称不回收,重新使用需创建新 ID 和名称。
## SDK 与自动化收尾
使用 `git.yuntuo.hk/k2safe/orbit/sdk/go@v0.1.2`;旧 v0.1.1 消费/提交协议继续兼容,但不包含新方法。当前仓库内可直接从 `sdk/go` 运行示例。
```go
sub, err := client.GetSubscription(ctx, subscriptionID)
if err != nil { return err }
// 实际服务应把 key 和请求持久保存,未知回执重放原内容。
retire := orbit.RetireResourceRequest{
ExpectedRevision: sub.Revision,
Reason: "临时资源已经结清",
}
_, err = client.RetireSubscription(ctx, subscriptionID, stableOperationKey, retire)
if err != nil { return err } // RESOURCE_BUSY 时先排空/核查,不能跳过结果。
queue, err := client.GetQueue(ctx, queueID)
if err != nil { return err }
_, err = client.RetireQueue(ctx, queueID, stableQueueOperationKey,
orbit.RetireResourceRequest{ExpectedRevision: queue.Revision, Reason: "停止使用队列"})
return err
```
建议顺序:停止生产者和计划 → 结清执行与流程 → 消费并确认所有订阅结果 → 退役订阅 → 退役队列。失败的测试可能保留待核查任务,收尾应报告资源 ID 与阻塞原因,不能把测试失败变成丢弃结果的理由。
完整可运行示例:[resource-lifecycle](../../sdk/go/examples/resource-lifecycle/README.md)。示例只操作本次新建空资源;进程异常终止仍可能留下资源,需要按输出 ID 核查,不能无限创建来绕过名额。
## 错误和管理页面
| HTTP / code | 含义 |
| --- | --- |
| 409 `RESOURCE_BUSY` | 尚未结清,message 给出依赖或积压原因;状态不变 |
| 409 `RESOURCE_RETIRED` | 新命令试图复用已退役资源 |
| 409 `STATE_CONFLICT` | 配置版本不匹配 |
| 409 `IDEMPOTENCY_CONFLICT` | 原键对应不同内容 |
| 410 `EVENT_HISTORY_EXPIRED` | 订阅存在无法验证的历史缺口 |
| 401 / 403 / 404 | 保持现有身份、权限和资源隔离语义 |
控制台“结果订阅”详情提供退役入口;“应用接入”提供所选队列退役和活动订阅名额。多队列时明确选择,浏览器只记忆命名空间对应的队列 ID;暂停/退役状态清晰展示。复制配置不会静默换成另一张凭据有权限的队列。
## 部署与保留
先停止旧写节点,再执行迁移 000034,再启动新版本;不要混跑不认识退役状态的旧服务端。现有资源默认未退役,不自动处理任何应用或测试数据。新库基线仍为零外键。历史保留、归档导出继续按现有配置执行;本次退役不引入自动删除机制。