短链接TTL设计教训:过期是功能,而非定时任务

短链接TTL不应依赖后台清扫,而应在查询中读时校验过期。
文章通过短链接创建器的实践,指出用后台清扫任务实现TTL会在三处失效:数据库与API服务器的时钟偏移导致过期时间不确定;清扫器依赖进程存活,重启后过期逻辑静默停止,临时链接变成永久占用slug;删除与查找不原子,请求可能通过过期检查后行被清扫器删除而崩溃。正确方案是把过期条件写进查询本身,让数据库成为存活性的唯一真相来源,清扫器降级为惰性回收的垃圾回收器。重定向处理器对“行不存在”和“行已过期”返回相同的410,调用方无需区分。这一模式对AI agent间传递一次性资源尤其重要。
为什么你的短链接过期逻辑会失效?
很多开发者实现短链接的临时性(TTL)时,第一反应是加一个后台清扫任务:每分钟扫表,删掉 expires_at < now() 的行。简单直接,但作者在构建短链接创建器时发现,这个方案在三个地方会悄悄崩溃。

时钟偏移:看不见的竞态
数据库服务器和API服务器的时间差了400毫秒,导致一个60秒TTL的链接可能存活60.4秒,或在59.6秒时就失效——取决于最后是哪个进程碰了它。对人类用户无所谓,但对一个“任务结束后就轮询”的AI agent来说,“仍然有效”和“410 Gone”的差异就是别人工作流里的竞态条件。
时钟偏移(Clock Skew) 指分布式系统中不同机器的本地时钟之间存在差异,通常由硬件晶振漂移、NTP同步延迟或虚拟机调度抖动引起。即使所有节点都启用了NTP,秒级甚至百毫秒级的偏差仍然常见。在TTL场景中,如果过期判断分别发生在API进程和数据库进程,两边各自用本地时间比较expires_at,那么同一行数据在不同时刻可能得到相反的存活结论。更隐蔽的是,这种偏差不是固定值,而是随负载和同步周期波动的。工程上的通用对策是:对时间敏感的判断只信任单一时间源(通常是数据库的now()),或者使用单调时钟(monotonic clock)而非墙上时钟来测量时间间隔。
清扫器依赖进程存活
清扫器只在进程活着时运行。重启机器人,过期逻辑就静默停止。本该短暂的链接变成了永久占用slug命名空间的“钉子户”。作者发现了一个半年前的“临时”链接,仍然解析到一个早已忘记的测试webhook负载。
惰性回收(Lazy Reclamation) 是一种与后台清扫相对的资源清理策略:不主动定期删除过期数据,而是在读取或写入路径上顺带判断并清理。它的核心优势是把“清理”与“访问”解耦——即使清理进程从未运行,正确性也不受影响,只是存储空间暂未释放。惰性回收通常配合“读时校验”使用:读路径负责语义正确性(过期即不可见),写路径或低频后台任务负责物理回收。这种分层让系统在进程崩溃、部署中断或调度器故障时依然保持行为一致,代价是过期行可能在一段时间内继续占用存储和唯一约束(如slug命名空间),因此回收任务仍应存在,但只作为优化而非正确性依赖。
删除与查找不原子
请求可以通过过期检查,然后被调度出去,接着清扫器删掉了行,请求再回来尝试提供重定向时因行缺失而崩溃。修复方式不是事务,而是把过期检查挪进查询本身:
WHERE slug = ? AND (expires_at IS NULL OR expires_at > now())
数据库成为存活性的唯一真相来源。没有清扫器,没有偏差,没有时间窗口。
正确姿势:读时校验 + 惰性回收
现在TTL在读取时强制执行,清扫器只负责回收已经过期的行(它成了垃圾回收器,不是守门人)。重定向处理器把“行不存在”和“行已过期”同等对待——同样的410,同样的响应体。调用方无法区分“从未存在”和“五秒前过期”,这正是短命对象需要的语义。
作者把教训总结为一句话:如果你构建的东西承诺会消失,不要让消失成为进程存活的副作用。让“不存在”成为默认状态,让“存在”成为临时的例外。 现在即使忘记运行清扫器,短链接也会自己过期——作者确实忘过好几次,链接依然做了正确的事。
工具已打包在 x402.freeq.one/tools/shortlink_create.html,支持slug、TTL以及用于统计和删除的管理密钥。
对AI Agent工作流的启示
这个案例的价值不止于短链接。当AI agent之间进行任务交接、传递一次性文档或事件邀请时,“临时性”是核心契约。如果过期依赖一个可能崩溃的后台进程,agent收到的就是不可靠的承诺。把过期检查内化到每次读取中,用数据库的当前时间做判断,才能让“自动消失”成为可依赖的行为。
作者没有使用分布式锁或复杂的事务隔离,而是用一条 WHERE 条件解决了问题——有时候最稳健的设计就是让数据层替你做决定。
相关推荐

uGAP:统一100+非洲地理数据集的AI平台
uGAP是开普敦大学团队开发的非洲地理空间AI平台,统一120余个非洲数据集并提供Python SDK,支持数据发现、处理与模型训练一站式工作流,已用于气旋洪水制图等实战场景。

Cursor能控制你的电脑了:AI代理可驱动桌面应用测试
Cursor 新增桌面控制能力,AI 代理可驱动 iOS 模拟器等桌面应用测试自己构建的功能。本文分析这一更新对开发工作流的影响、技术实现可能路径及可靠性考量。

Vibecoding自研推理引擎:理想丰满,现实骨感
Reddit开发者分享Vibecoding自研推理引擎的真实经历:用WebGL2和GLSL在浏览器跑GGUF Q4模型,却陷入tokenizer调试地狱。本文分析浏览器端LLM推理的工程难点,以及为何直接clone llama.cpp是更理性的选择。