后台任务与统计
检查 Queue、Reaction、Cron 和访问统计的执行链,区分消息投递成功与业务完成。
后台任务的问题通常表现为“请求成功了,但结果没有出现”。先确定任务属于哪个队列,再追踪执行记录。不能把所有后台任务都当成同一种消息处理。
一、找到负责这项工作的队列
| Binding | 名称变量 | 当前用途 |
|---|---|---|
ASYNC_POLICY_TASK_QUEUE | ASYNC_POLICY_TASK_QUEUE_NAME | Reaction Event 的异步 Command 执行 |
ASYNC_LOGGER_QUEUE | ASYNC_LOGGER_QUEUE_NAME | 异步日志处理与持久化 |
ANALYTICS_QUEUE | ANALYTICS_QUEUE_NAME | 第一方访问统计消息处理 |
src/entry/index.ts 根据收到的 Queue 名称与这些变量进行精确比较,再分发给对应消费者。
每个队列要检查三处:queues.producers 的目标名、queues.consumers 的名称、vars 中相应的 *_QUEUE_NAME。三者必须一致。CLI 改写项目资源名时也会一起改写相关变量。
npm run doctor 或 npm run doctor:remote 能检查这一配置关系。但真实队列是否存在、消息是否到达、消费者是否报错,仍要看 Cloudflare 的队列与 Worker 日志。
二、有消息,但看不到业务结果
先按下面的边界检查:
产生业务事件 → 保存执行记录 → 发送 Queue 消息
→ Worker 消费者分发 → 执行 Command → 保存执行结果queue.send() 成功只是消息发送成功。消息被确认也不总意味着业务成功,消费者可能在识别无效消息、重试耗尽或需要人工核对时确认消息并记录告警,避免无休止地重复执行。
如果应用的异步日志本身也有问题,应同时查看 Worker 运行日志,不要只依赖后台日志页面。否则日志队列的故障可能把业务队列的报错一起隐藏。
三、查询 Event 和 Command
先通过后台 Reaction 页面或日志定位执行 ID。也可以在业务库 DB 中查询最近的目标事件,下面以一次性付款事件为例:
SELECT id, event_type, event_version, user_id,
created_at, lease_expires_at
FROM event_execution
WHERE event_type = 'payment_succeeded'
ORDER BY created_at DESC
LIMIT 20;再用实际执行 ID 查 Command:
SELECT id, command_key, command_type, mode, status,
attempt_count, max_attempts, sequence,
created_at, updated_at, completed_at
FROM command_execution
WHERE event_execution_id = 'replace_with_event_execution_id'
ORDER BY sequence;这里的内部 Event 执行 ID 不是 Stripe 的 evt_... ID。不要把两者混用,也不要为了查询方便导出全部 payload。
event_execution 没有持久化的 status 字段,Event 的显示状态由 Command 状态计算。查询 event_execution.status 会报缺少字段。
| Command 状态 | 含义 | 排查方向 |
|---|---|---|
pending | 尚未结束,可能在等待执行或重试 | 队列、租约、顺序和重试安排 |
succeeded | Handler 返回成功且结果已保存 | 继续检查需要验证的最终业务效果 |
failed | Handler 返回了业务失败 | 结合 result 中的错误码检查输入和业务条件 |
errored | Handler 抛出异常并结束 | 在受控环境检查 last_error 与异常日志 |
当 Event 不允许失败后继续时,前面的失败可能阻止后续步骤执行。后面仍有 pending 不能直接解释为 Queue 丢消息。
四、分清两层重试
Command 有自己的 maxAttempts 和执行次数,当前定义默认最大尝试次数为 3。失败是否继续尝试,还与错误是否可重试有关。
Cloudflare Queue 有独立的投递次数。模板策略任务消费者的 max_retries 为 3,表示首次投递后最多再投递三次。这个次数不是 Command 的尝试次数,也不能简单把两者相乘作为一定会执行的次数。
修改策略 Queue 的 max_retries 时,还要核对 consumeEventCommandBatch 的 maxRetries 参数。当前入口使用函数默认值 3,消费者用它判断是否已经到最后一次投递。只改 Wrangler 配置可能让判断不一致。
一直提示租约忙碌
Event 使用租约避免多个消费者同时执行。看到 Event execution lease is busy 时,查看当前时间、lease_expires_at 和对应执行日志,确认是不是另一个请求仍在工作。
不要直接清空租约字段强行并发执行。租约持续忙碌并耗尽消息投递后,消费者会记录告警,需要核对实际运行状态,不能假定一直等待就一定自动恢复。
Command outcome needs manual review
这个提示表示 Handler 已经完成,但执行结果无法可靠保存。外部邮件、通知或其他操作可能已经发生,继续投递可能重复产生效果,所以消费者会停止自动重试并告警。
先到对应服务商或业务记录中确认效果,再决定如何恢复执行记录。不要看到数据库里仍是旧状态就立即重跑。
修复后手动重试
模板提供后台 Reaction Command 重试入口。先修复凭据、输入或服务故障,核对前一次是否产生副作用,再通过受权限保护的入口重试。
如果返回 409,查看具体提示。当前入口会拒绝重跑成功的 Command、拒绝处理仍持有有效租约的 Event,并要求先处理阻塞当前步骤的前序 Command。这些检查应按提示处理,不要绕过。
重试会进入所属 Event 的执行流程,应检查该 Event 其他步骤是否也会继续。不要直接把数据库状态改成 pending,也不要把重新投递消息视为对所有故障都安全的恢复方式。
五、Cron 没有执行
当前计划与 src/entry/index.ts 的分发条件如下:
| Cron 表达式 | 任务 |
|---|---|
0 0 * * * | 安排到期的周期权益处理 |
0 1 * * * | 安排通用数据清理 |
30 */6 * * * | 执行统计数据保留策略 |
Cloudflare Cron 使用 UTC,排查时间需换算,参见 Cron Triggers。
入口按表达式字符串判断要执行哪个任务。只修改 wrangler.jsonc 的时间表达式,却没有同步修改源码中的分发条件,可能出现平台触发了 Worker,但没有进入预期任务分支。
先检查平台是否有触发记录,再查执行日志。周期权益和通用清理还会继续产生后台工作,因此有 Cron 记录不代表所有数据处理完成。没有到期权益时也可能不会出现预期的业务变更,应使用确实到期的测试案例验证。
本地打开页面不会自动证明定时入口可用。需要单独测试 scheduled 流程,并在隔离数据上验证会删除记录的清理任务。
六、访问统计没有数据
按浏览器、采集接口、队列、统计数据库、报表的顺序检查:
deploy.analytics.enabled是否开启,页面是否加载统计脚本。- 当前路径是否被排除。默认排除
/dashboard,不要用反复刷新后台页面验证采集。 - 浏览器是否启用 Do Not Track,插件或站点同意策略是否阻止脚本运行。
- 采集请求是否发出,主机、Endpoint 和站点标识是否与当前配置一致。
- 采集接口是否接受该域名、路径和来源,日志是否记录过滤原因。
ANALYTICS_QUEUE是否正确消费,ANALYTICS_DB是否已经迁移。- 报表的站点、日期和筛选条件是否覆盖本次访问。
可以先在统计库检查事件总量,避免查询不必要的访客属性:
SELECT COUNT(*) AS event_count FROM analytics_event;测试时对比操作前后结果即可,不应为了让报表出现数字而直接插入伪造访问记录。
关闭第一方统计后,当前实现会确认并丢弃已经在统计队列中的消息,不会自动为以后重新开启而保存。修改站点 ID、保留策略或采集配置前,应理解对已有数据的影响。
修复后的验证
发起一条可关联的新任务,检查消息、执行记录和最终结果。再验证失败输入或重复消息,确认不会重复发送、重复计费或重复授予权益。统计故障则使用一个未被排除的页面,完整追踪一次采集。