十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

WeKan 持久化作业设计:检查点、租约与幂等重放的重启安全作业契约

WeKan 持久化作业设计:检查点、租约与幂等重放的重启安全作业契约 WeKan 持久化作业设计检查点、租约与幂等重放的重启安全作业契约【免费下载链接】wekanThe Open Source kanban, built with Meteor. GitHub issues/PRs are only for FLOSS Developers, not for support, support is at https://wekan.fi/commercial-support/ . PR source translation to imports/i18n/data/en.i18n.json, other translations at https://app.transifex.com/wekan/wekan项目地址: https://gitcode.com/GitHub_Trending/we/wekan本文基于 WeKan 仓库中 Admin Panel → Problems 下的设计文档 Durable-Operations.md系统讲解 WeKan 如何保证跨进程存活的后台作业——导入、附件迁移、数据库迁移、备份、完整性扫描、定时规则、Webhook 与邮件——在服务端重启、进程崩溃或外部服务限流时从检查点续跑而不是从头再来。读完后你能掌握该契约的完整要素持久化作业记录字段、原子 claim 与可续租的 lease、五步幂等执行顺序、外部服务重试/退避策略以及仓库中已经落地的实现证据Trello 导入作业、SQLite 忙写重试、Recovery 事件审计。一、适用范围与设计目标该设计适用于 WeKan 启动的任何可能比一次 HTTP 请求或一个服务端进程活得更久的工作文档明确列出的范围包括各类导入Trello 及其他看板导入、ZIP/JSON/CSV/Jira/Kanboard/ICS 导入附件/头像迁移、数据库文本迁移、备份/恢复完整性/恢复扫描、定时规则、Webhook、邮件及其他外部服务调用。核心恢复规则一句话概括不是从头再跑一遍而是每完成一个幂等单元就持久化一个检查点checkpoint非正常停止后从检查点继续。这个规则与 Recovery.md 中的失败覆盖表直接联动当后台作业进程退出时过期的持久化 lease 会被回收执行从最后已验证的单元继续并在 Recovery 报告中留下job-reclaimed-after-restart事件及后续结果。文档当前状态为Implementation in progress负责人 xet7。也就是说这是一份契约式设计它先定义了所有持久化作业必须遵守的接口和不变量各具体作业按契约逐步落地——Trello 导入作业与数据库忙写重试已经可见实现见第四节而统一的租约/检查点设施仍在推进中。二、持久化作业契约Durable Job Contract2.1 每条作业必须有完整的数据库记录每个后台作业都对应一条数据库记录至少包含字段说明类型 / 属主 / 租户作业类型、发起者owner、所属租户tenant脱敏的输入引用sanitized input reference输入只以脱敏引用形式保存状态state例如running、paused、failed等当前检查点该作业已验证完成的位置尝试计数器与nextAttemptAt自动尝试已用次数、下一次允许尝试的时间有界的错误历史bounded error history不无限膨胀的错误记录时间戳创建、更新等关键时间点可续租的 lease带属主与过期的执行租约一条关键安全不变量秘密永远只通过服务端配置或加密凭证记录来引用明文 token 绝不复制进作业记录。仓库中已经落地的 models/trelloImportJobs.js 在注释里明确写死了这一点Trello API 的 key/token NEVER stored in this document只保留在运行循环的服务端内存中服务端重启后需要客户端重新提供文件第 12–14 行。这份作业文档保存的是队列、进度、逐板结果和错误日志因此用户可以在 Trello 导入页离开后回来查看进度、在致命 API 错误后恢复或取消并删除已导入的看板重新开始。2.2 原子 claim 与可续租的 leaseWorker 用一次原子的条件更新conditional update来 claim 一条到期的作业lease 带属主 ID 与过期时间工作期间持续续租干净暂停或完成时释放过期 lease 可被替换进程replacement process回收由此保证多个 WeKan 副本replica不可能并发执行同一个单元。这与文档中 Startup 一节的呼应关系是正常 shutdown 主动释放 lease而 SIGKILL、掉电、进程崩溃这类无法主动释放的场景则依赖 lease 过期 幂等重放来兜底。2.3 五步幂等单元执行顺序每个执行单元使用一个稳定的幂等键stable idempotency key并严格按以下顺序执行Claim 或回收作业读取其持久化检查点检查该单元的结果是否已经以它的幂等键存在已存在则跳过执行执行一个有界的工作单元bounded unit of work验证结果然后原子地推进检查点续租、让出yield、claim 下一个单元。崩溃语义由此清晰在第 4 步之前崩溃只会重复执行同一个单元而不是重跑整个作业。而重复执行必须本身是安全的文档给出了四类保证数据库复制按_idupsert导入的看板保留其来源/作业身份source/job identity重复导入不会产生第二份文件传输在校验目标字节一致后才删除源外部请求在服务商支持时携带 idempotency key。2.4 自动尝试耗尽之后达到自动尝试上限后作业保持持久化为paused或failed绝不被丢弃。Admin Panel → Problems → Recovery 记录该作业的操作、检查点、尝试次数、下次重试时间、有界的失败原因以及重启恢复是否回收了它。安全敏感型的拒绝例如 SSRF 拦截则留在 Problems → Security 流中。三、仓库中可对照的实现证据3.1 Trello 导入持久化作业 重试策略的完整落地server/trelloApiImport.js 是外部服务策略的一份具体实现其常量与文档逐条对应有界超时REQUEST_TIMEOUT_MS 30000API 调用用AbortSignal.timeout施加总超时L74、L140可重试状态408、425、429与5xxL157以及网络错误进入同一退避路径非重试的拒绝SSRF guard 的拒绝是裁决而不是小故障——SSRF_GUARD:前缀的错误直接抛出trello-blocked-url不重试L143-L147重试上限MAX_RETRIES 5耗尽后抛出trello-api-rate-limitedL170-L175最小间隔门MIN_REQUEST_GAP_MS 120串行请求之间保持最小间隔使批量导入从一开始就不接近限额这正是文档中provider 级最小间隔门在本实例所有作业上生效的一个实例L62-L86。Retry-After的处理实现见 retryDelayMs优先解析头部的秒数 delta其次解析HTTP date两者都取有效值都不可用才回落到封顶指数退避BASE_BACKOFF_MS(1000) * 2^attempt上限MAX_BACKOFF_MS(30000)。注意实现里对Retry-After也套了Math.min(…, MAX_BACKOFF_MS)封顶这是provider 可延长但不得缩短之外、单实例层面的安全上限。3.2 SQLite 忙写重试带 jitter 的封顶退避server/00retryBusyWrites.js 展示了文档重试使用带 jitter 的指数退避且有配置上限在数据库侧的形态所有集合写insertAsync/updateAsync/upsertAsync/removeAsync经过一个有界重试包装全部参数可用环境变量配置环境变量默认值含义WEKAN_DB_RETRY_ATTEMPTS5含首次在内的总尝试次数WEKAN_DB_RETRY_BASE_MS25退避基数WEKAN_DB_RETRY_MAX_MS400单步延迟上限WEKAN_DB_RETRY_TOTAL_MS2000含首试的总等待上限WEKAN_DB_RETRY_LOG_MS60000重试统计日志的最短周期其中 delayFor 使用完整 jitter步长的 0.5–1.5 倍随机目的是同一把锁交接释放的 N 个写者不会全部同时回来。文件头部注释还解释了为什么重复写是安全的SQLite 单写者模型下SQLITE_BUSY意味着该写根本没有发生而 Meteor 已选好_id撞了唯一索引的重复尝试会被拒绝而非插入两次——这正是文档数据库复制按_idupsert、重放必须安全要求的实例化。3.3 Recovery 证据链JSONL → 集合 → 管理面板作业被回收、恢复动作发生时证据通过三层流转对应文档Problems → Recovery records the operation, checkpoint, attempt, next retry…的要求启动脚本snapferretdb-control、发布包start-wekan.sh、Docker entrypoint每执行一个动作向 SQLite 数据目录的recovery-events.jsonl追加一行server/recovery.js 在Meteor.startup时把比上次导入更新的行批量插入recoveryEvents集合用集合 meta 里的最后导入时间做增量重启不会重复导入整个文件models/recoveryEvents.js 定义了事件类型常量corruption-detected、restore-backup、remigrate、bloat-repaired、manual-required等Recovery 报告 面向管理员按最新优先展示支持搜索与分页。recoveryEvents的 schemaL34-L79包含severityinfo/warning/error、sourceserver/startup/ferretdb/manual、done布尔列、操作者userId/username与ipv4/ipv6——这些字段支撑了 Recovery 表中绿色对勾 / 红色三角 / 黄色垃圾桶的 Done 状态渲染。四、外部服务策略External-Service Policy文档对每一个出站操作定义了统一策略可以归纳为五条4.1 超时与有界响应每个出站操作都有连接超时和总超时且响应体有界。这防止慢连接、挂起的 DNS 或超大响应拖死 worker也防止响应体被意外持久化进作业记录文档 Tests 一节要求负面测试证明无界响应体不会持久化到作业或报告中。4.2 可重试状态的分类可重试HTTP408、425、429、瞬时5xx、DNS/连接重置与超时不可重试认证、鉴权、校验失败以及 SSRF 拒绝。分类的意义在于把权限不足URL 被安全守卫拦截这类裁决verdict与网络抖动区分开。重试一个被拒绝的 SSRF 请求只是把同一个被拦截的请求多执行五次正如 trelloFetch 的注释所述。4.3Retry-After是权威信号当Retry-After是合法的 delta秒数或 HTTP date 时它说了算provider 的速率限制重置头如X-RateLimit-Reset一类只能延长、绝不能缩短该延迟。Trello 导入的 retryDelayMs 实现了 delta 与 HTTP date 两种形态的解析。4.4 先持久化nextAttemptAt再睡眠文档特别强调下一次尝试时间在睡眠之前就已持久化。因此重启不会重置速率限制、不会造成重试风暴retry storm——进程在退避期间死掉醒来时按库里存的nextAttemptAt判断是否到期而不是把计数器清零。此外provider 级的并发门与最小间隔门作用于本实例内的所有作业而不仅仅是单个作业内部Trello 导入里MIN_REQUEST_GAP_MS 120的最小间隔就是这种门的一个已落地实例。五、按作业类型的检查点Operation-Specific Checkpoints文档给出了一张各作业的持久化单元 完成证据表完整继承如下作业持久化单元与完成证据Trello 及其他看板导入一个源看板源 ID 在队列索引前进之前必须恰好映射到一个已导入看板ZIP/JSON/CSV/Jira/Kanboard/ICS 导入已解析的源 一个看板/卡片批次创建出的记录携带作业/来源键job/source key附件/头像迁移一个文件版本删除源之前目标的大小/校验和与元数据必须一致文本数据库迁移按_id排序的一个集合批次目标端 upsert 且证据覆盖检查点备份/恢复一个集合/文件条目暂存的归档/对象与校验和清单最后才发布完整性/恢复扫描一个有界清单批次最后一条稳定的对象键被持久化定时规则触发事件 IDoccurrence ID已记录的事件不会被应用两次Webhook/邮件每个事件每个目的地一条投递记录provider 幂等键或显式的 at-least-once 状态对请求/响应式导出文档单独做了边界说明它们重启后不会恢复 HTTP socket而是走快照读取snapshot read并显式失败将来若增加可下载的异步导出必须遵守本契约。客户端可以安全地重试同步的幂等读取。六、启动与关闭行为6.1 启动时先把过期的runninglease 标记为可回收记录一条job-reclaimed-after-restart的 Recovery 事件然后带 jitter 地渐进启动到期作业避免所有副本同时抢作业造成惊群。对于所需凭证或存储不可用的作业置为paused并给出可操作的actionable原因——文档明确它们不得被错误地标记为 failed 或 completed。6.2 关闭时SIGTERM停止 claim 新工作把检查点写回当前安全边界并在 shutdown 宽限期内释放 leaseSIGKILL、掉电、进程崩溃不依赖任何主动清理依靠 lease 过期 幂等重放恢复。七、测试要求文档规定每个持久化作业必须具备以下测试这是验收一份实现是否真的持久化的清单通用正常完成positive completion、检查点前崩溃、副作用后崩溃、过期 lease 回收、并发 worker 互斥、暂停与取消外部作业附加超时、网络重置、每种可重试状态、非重试的 4xx、合法/非法Retry-After、jitter 边界、尝试耗尽、退避期间重启负面测试证明秘密与无界响应体不会被持久化进作业或报告。仓库中与之配套的现有测试文件可作为参照例如 tests/recoveryPlan.test.cjs、tests/recoveryReportQuery.test.cjs 在 Recovery.md 的 Tests 一节中列出的决策逻辑与报告查询测试而 docs/Features/Admin-Panel/Problems/README.md 也已在Detailed pages表中把本设计标注为 Problems 面板的组成部分Restart-safe jobs, leases, checkpoints, idempotency, external-service retries and recovery reporting。八、小结如何阅读这套契约如果你是运维关注的是恢复语义——重启不会丢工作作业要么从检查点续跑要么以paused/failed留在库里并带 actionable 原因Recovery 面板能看到job-reclaimed-after-restart与每次恢复动作的记录server/recovery.js 的 JSONL 导入、RECOVERY_IN_PROGRESS标记与数据库健康探针共同支撑这一观察面。如果你是开发者新增任何后台作业时照第二节五步顺序实现幂等单元、第四节策略实现外部调用并对照第七节清单补测试已有的 models/trelloImportJobs.js作业文档不存秘密与 server/00retryBusyWrites.js可配置的 jitter 退避、总时长封顶、统计上报是两份可直接比对的实现范本。该设计处于Implementation in progress状态统一的 lease/checkpoint 设施仍在推进中引用本契约实现新作业时请以 Durable-Operations.md 当前版本为准并结合 Recovery.md 的失败覆盖表核对证据链是否完整。【免费下载链接】wekanThe Open Source kanban, built with Meteor. GitHub issues/PRs are only for FLOSS Developers, not for support, support is at https://wekan.fi/commercial-support/ . PR source translation to imports/i18n/data/en.i18n.json, other translations at https://app.transifex.com/wekan/wekan项目地址: https://gitcode.com/GitHub_Trending/we/wekan创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表