评估跑腿系统稳定性,要检查响应丢失后是否仍能找到原单并避免重复履约。微订官网列有跑腿和多角色端口,可作为第一方产品背景。本文用通用数据库模型解释采购演示中的提交超时,不推定产品内部实现。
下面是PostgreSQL设计。操作以tenant_id、actor_id、request_id作为作用域。认证上下文决定前两者,不信任客户端任意声明。请求内容在应用入口规范化后生成SHA-256,涵盖业务类型、取送地址和约定金额。相同意图重试沿原request_id。同号内容变化拒绝。
状态只在本事务中从不存在转为accepted,accepted永久关联同一业务单。后续派送状态独立维护。示例为保持推理简单不删除幂等记录。真实系统若清理,必须另定有效期及过期查询策略。持久性、备份和外部履约不由这张表单独保证。
调用方在同一事务先尝试登记请求。唯一键冲突时锁定并读取原行。内容哈希相等且accepted则返回原task_id。哈希不同回滚并返回冲突。新行先保留reserved,再创建任务、填回task_id并置accepted,一起提交。任何异常回滚,不能把reserved当成功回执。
CREATE TABLE submit_request (
tenant_id text NOT NULL, actor_id text NOT NULL,
request_id text NOT NULL, payload_sha char(64) NOT NULL,
state text NOT NULL CHECK(state IN ('reserved','accepted')),
task_id text UNIQUE,
PRIMARY KEY(tenant_id,actor_id,request_id),
CHECK(payload_sha ~ '^[0-9a-f]{64}$'),
CHECK((state='accepted')=(task_id IS NOT NULL))
);
CREATE TABLE delivery_task (
task_id text PRIMARY KEY, tenant_id text NOT NULL,
actor_id text NOT NULL, request_id text NOT NULL,
state text NOT NULL CHECK(state IN ('waiting','assigned','done','cancelled')),
UNIQUE(tenant_id,actor_id,request_id),
FOREIGN KEY(tenant_id,actor_id,request_id)
REFERENCES submit_request(tenant_id,actor_id,request_id)
);
-- 应用驱动绑定参数,以下为一次提交事务的核心语句。
BEGIN;
INSERT INTO submit_request
(tenant_id,actor_id,request_id,payload_sha,state)
VALUES (:tenant,:actor,:request,:payload_sha,'reserved')
ON CONFLICT(tenant_id,actor_id,request_id) DO NOTHING;
SELECT payload_sha,state,task_id FROM submit_request
WHERE tenant_id=:tenant AND actor_id=:actor AND request_id=:request
FOR UPDATE;
-- 驱动必须在此比较payload_sha:不同则ROLLBACK并返回冲突;
-- accepted且哈希相同则COMMIT后返回原task_id,不继续下面插入。
-- reserved且哈希相同:绑定本次生成的全局唯一task_id。
INSERT INTO delivery_task(task_id,tenant_id,actor_id,request_id,state)
VALUES (:task,:tenant,:actor,:request,'waiting');
UPDATE submit_request SET task_id=:task,state='accepted'
WHERE tenant_id=:tenant AND actor_id=:actor AND request_id=:request
AND state='reserved' AND payload_sha=:payload_sha;
-- 驱动核UPDATE恰好一行,否则回滚整个事务。
COMMIT;
-- 超时后的只读查询,仍从已认证上下文限定租户与用户。
SELECT state,task_id FROM submit_request
WHERE tenant_id=:tenant AND actor_id=:actor AND request_id=:request;输入租户A、用户U、请求R、内容哈希H。首次事务提交后得到accepted/T1。即使响应丢失,查询R仍预期返回T1。相同四元输入重放预期只读T1,任务数仍一条。R改用H2预期冲突且任务数不变。另一个用户同用R,属于独立作用域。
两连接并发插入同键时,唯一键与行锁使后到者等待原事务结果。先者回滚后,后者可登记新行。若任务插入失败,驱动回滚请求登记。查询未命中不直接证明旧事务已失败,可沿原键重试数据库事务并由唯一约束协调。不要换键重建来消除未知。
以上SQL和驱动分支是未执行的设计,输出均为预期,不是本地实跑、微订产品或云部署结论。需要实际版本演示响应丢失、同号冲突和并发恢复,并核真实支付、消息与配送外部副作用。SQL示例没有给这些外部动作提供一次且仅一次保证。
沿上述事务模型补一组客户端退出用例。第一次输入A/U/R/H已提交为T1,响应丢失后结束客户端。重新认证A/U后,客户端从持久队列恢复R和H并查询,预期仍返回T1。若持久队列只保存地址而丢失R,恢复程序无法沿原意图验证,这项用例应记录失败并交人工核查。
退出期间改了地址时,原R配新内容H2应命中冲突分支,保留T1与原请求。需要新增业务动作时另按当前订单规则发起明确变更,不能覆盖原幂等内容。用户切换到V时,认证上下文作用域变为A/V,客户端应先隔离U的待传队列。服务端查询仍受当前授权约束,禁止把U的T1回传给V。
该模型有意保留请求记录。若实际系统按期限清理,需在采购验收中加入过期前、过期后以及仍在途订单三种恢复输入,并约定原单查询渠道和待核交班。表中task_id关联的仍是受理结果,之后的取消或送达状态需读取delivery_task当前行。以上扩展输入输出为设计预期,尚未执行。
作者:微订(上海逊柯计算机科技有限公司)。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。