跳转到正文

错误与结果核对 ​

先区分“请求被明确拒绝”“仍在执行”“结果无法确认”。对游戏写入,超时和 unknown 都不能推出“没有执行”,也不能据此换键重试。

操作回执 ​

await ctx.operations.get(operation_id) 和 wait(...) 返回普通字典:

text
OperationReceipt = {
  request_id: str,
  operation_id: str,
  state: "accepted" | "dispatching" | "running" |
         "succeeded" | "failed" | "cancelled" | "unknown",
  expires_at: str,
  result: ActionResult | None,
  can_retry: false
}
状态处理方式
accepted、dispatching、running保存原 ID,继续查询;不要重复提交新动作。
succeeded读取证据和具体结果,再判断是否满足业务目标。
failed读取原因与可能的部分效果;修正问题不等于可以直接重放。
cancelled查看部分效果和取消边界,不假设世界已经回滚。
unknown保留原意图、ID、幂等键和期限,停止自动重试,核对实际状态。

终态 result 至少含相同的 state,可选字段有 evidence、reason、result、partial_effects、recovery_required。字段缺失时不要自行填出成功证据。can_retry 为 false,不是通用重试建议。

python
receipt = await ctx.operations.get(operation_id)
if receipt["state"] == "unknown":
    ctx.log.warning("动作结果未确认,需核对原收据",
                    extra={"operation_id": operation_id})
elif receipt["state"] == "succeeded":
    evidence = (receipt.get("result") or {}).get("evidence")
    ctx.log.info("动作已完成,证据=%s", evidence,
                 extra={"operation_id": operation_id})

此片段只报告状态,不在错误分支重新发起动作。evidence 的含义取决于操作;服务器命令回应不能证明玩家看见画面或业务全部达成。

SDK 异常 ​

异常及导入模块应保留的信息与处理
IPCRejected,neomega_runtime.managedcode、request_id、retryable;明确的 Host 拒绝,按具体原因修正。
OperationSubmissionTimeout / OperationSubmissionError,neomega_runtime.operations.intent;提交是否被接受不确定,保留原请求,不生成新键。
OperationWaitTimeout,neomega_runtime.operations.operation_id;只是等待到期,原动作未因此取消,查询同一 ID。
OperationCancellationUncertain,neomega_runtime.operations.operation_id;取消回应不确定,查询原操作。
CommitUncertain,neomega_runtime.storage.intent;数据库提交回应丢失或无效,核对原 commit_id。
ServiceUncertain / ServiceWaitTimeout,neomega_runtime.services原 .intent / .call_id;在线提供方可能仍执行,不重新调用。
ServiceFailed / ServiceRejected,neomega_runtime.services.receipt、.call_id,后者还有 .code;区分调用失败与预期业务拒绝。
BusinessConflict,neomega_runtime.business同业务 ID 对应不同请求;核对原业务记录,不换 ID 绕过。
ProtocolError,neomega_runtime.http协议结构或字段无效;是否可能已有副作用取决于发生在准备、提交还是查询阶段。
IPCError,neomega_runtime.managed底层连接或运行时错误;不能仅凭断线认定动作未执行。

IPCRejected.retryable 是 Host 的拒绝信息,不是允许重放有副作用调用的通行证。服务回执受 generation 与内存保留边界约束,详见在线服务。

本地参数校验还可能抛出 ValueError、TypeError,配置使用 ConfigError,密钥使用 SecretError,玩家匹配使用 PlayerNotFound 或 AmbiguousPlayer。这些都需要按发生位置处理,不能统一转换成“发奖失败,请再试一次”。

事务回应丢失 ​

tx.prepare() 得到 CommitIntent,其中含固定的 commit_id、状态修改和动作意图。save 成功时业务状态已提交,动作仍有各自终态;不确定时可以先查询原提交:

python
from neomega_runtime.storage import CommitUncertain

try:
    committed = await tx.save()
except CommitUncertain as error:
    original_intent = error.intent
    commit_id = original_intent.payload()["commit_id"]
    # 保留 intent,且不要把可能含敏感业务数据的完整正文写入日志。
    committed = await ctx.storage.receipt(commit_id)
    if committed is None:
        raise  # 当前没有确认回执;不重新执行交易。

receipt 返回 None 只表示这次查询未找到回执,不证明并发中的旧提交永远不会完成。连接已经断开时,立即查询也可能失败;应由恢复流程保留原请求,待连接可用后核对。内存变量不会跨进程重启保存,交易插件要提前设计持久业务标识和未决状态。

ctx.storage.submit(original_intent) 是显式重交原请求的接口,不是自动恢复策略。只有核对结果及对应业务规则允许时才使用,不能重新计算奖励、生成新 ID 或更换期限来绕过冲突。取消事务等待时异常也可能携带 .intent,同样要保留。

明确的版本冲突 ​

业务事务遇到 IPCRejected.code == "revision_conflict",说明该次提交的状态版本条件不符。重新读取状态并重新审查业务前提;不要把所有 IPCRejected 都归为可重试冲突。SDK 的简单存储便捷方法已对这一种冲突提供有限重试,见存储说明。

配置和权限分别有自己的 revision。配置冲突需要合并新配置;权限冲突需要重新审阅当前制品的申请。不能只替换版本号继续提交之前的变更。

排查入口 ​

现象下一步
插件无法启动执行 doctor NAME,核对配置、权限确认、事件来源及 Host 版本。
能启动但没有聊天事件核对 manifest 订阅、实际事件来源和目标实例;用玩家聊天测试。
重载后仍是旧行为reload 只重启已安装制品;新源码需 update-plan/update,再核对实际制品摘要与运行日志。
配置已保存但没生效检查 saved、applied、active_revision 及重载日志。
操作没看到效果用原 operation_id 查看终态、证据、部分效果;再核对实际游戏状态。

反馈问题时提供框架与 SDK 版本、插件 ID/版本、最小代码、复现步骤、脱敏错误、发生时间及原 operation_id / commit_id。不要提供管理令牌、密钥、完整私有配置或未经脱敏的玩家数据。

Bus 错误 ​

provider_busy、unavailable、contract_changed 等调用错误不能自动改用另一个提供者重试 service。循环、过深和无效因果分别拒绝为 cycle_rejected、depth_exceeded、invalid_cause。持久动作接纳不明先保留原请求及已知 ID,查询原回执;unknown 不换 key、session 或 cause 重放。见Bus。

Scope 与远程会话恢复 ​

1.1.0 的 Scope把业务结果与清理结果分别保留。ScopeError 带有已知句柄、原 request_id、business_error 和 cleanup_error;run 的 ScopeResult 同样保留两种错误。只看到回调返回,不能判断 Scope 已经完成关闭。

关闭返回后检查 handle.state;只有 closed 表示关闭完成。仍未关闭时保留 Scope ID、operation_ids 与 pending_operation_ids,通过 scopes.read 和原动作回执核对。申请回应丢失时使用原 request_id 查询,不能创建新申请后再次执行业务。

EP 重连会建立新会话,不接管旧 Scope。原会话的动作可能仍在执行;新连接和过期租约都不能证明资源已可安全复用。Host 继续协调已接纳动作,插件按远程 EP 规则核对原回执,unknown 仍不自动重放。

Neomega · 使用、开发与参考文档