为什么做这个
我在看日语技术文档的时候总会碰到生词。碰到生词的标准流程是:选中 → 右键 → 搜索,或者切到另一个标签页查 Jisho。每次这样做都会打断阅读节奏,查完词还得找回之前读到哪里。
我在看日语技术文档的时候总会碰到生词。碰到生词的标准流程是:选中 → 右键 → 搜索,或者切到另一个标签页查 Jisho。每次这样做都会打断阅读节奏,查完词还得找回之前读到哪里。
我在看日文资料时总有查词的需求。现有的工具要么太重(需要手动划词+查词典),要么太轻(只有汉字读音,没有语法解析)。所以自己做了一个。
在 NestJS 里写 CRUD,每次都是一样的样板代码。Entity 类、DTO 类、五个路由的 Controller、五个方法的 Service。乘以二十个资源,就是一千行看起来一模一样的代码。
nest-faster-crud 是我对更声明式方案的尝试:给实体类加注解,注册一下,框架负责其余的一切。
在 NestJS 里写 CRUD 每次都是一样的样板代码。实体类、DTO 类、带五条路由的 Controller、带五个方法的 Service。乘以二十个资源,你就有了一千行全都长得一样的代码。
nest-faster-crud 是我对更声明式方法的尝试:注解实体类,注册它,框架处理其余的一切。
Leverage OJ 重写完成后,下一个问题是:AI 能不能自主使用这个平台——不只是调 API,而是从头设计一整个游戏?
答案是可以。关键要素只有两个:一份机器可读的协议文档,和一个 MCP 服务器。
我们在后端加了一个公开端点 GET /ai,返回完整的平台上下文纯文本:
从评测引擎到完整 OJ 平台,145+ commits,675 个测试,从零到可水平扩展,一夜完成。
原有的 Leverage OJ 评测方案是"一个 NestJS controller + 多台评测机 client"的长连接架构,问题很明显:
认证这件事,感觉上早就被解决了——直到你接手一个其实没解决好的代码库。开始重写 Leverage OJ 的时候,我发现认证系统其实是三个问题穿了一件外套:一个在 PM2 下会出问题的 Session 配置、一个演变成独立平行宇宙的 ContestUser 认证体系、以及一套差一次配置泄露就会全库密码暴露的哈希方案。
这些问题都不是一眼能看出来的。系统在运行——用户可以登录,Session 能持久化,比赛能正常进行。但"能运行"和"正确"是两回事,看得越仔细,就越能看到一个基于不再成立的假设运转的系统。
提交链路是 Online Judge 的关键路径。用户提交代码,进队列,Worker 取出来,发给评测机,等结果,写回数据库。理论上简单。原版 Leverage 的实现是一个基于 Redis List 的自研队列——问题只在出事的时候才会暴露出来。
这篇文章讲我们为什么要换掉它,换成了什么样,以及一个关于消息投递的边界情况,让我不得不认真思考故障模式。
原代码有一个自研的 Queue<T> 类,底层用 Redis List 操作:
// 原版 queue.ts — 概念上类似这样
class Queue<T> {
async push(item: T): Promise<void> {
await this.redis.lpush(this.key, JSON.stringify(item))
}
async pop(): Promise<T | null> {
const result = await this.redis.brpop(this.key, 0) // 阻塞弹出,0=一直等
if (!result) return null
return JSON.parse(result[1]) as T
}
}
有些项目悄悄地积累债务。Leverage OJ 不是其中之一——它大声地积累,表现为比赛中途冻结的排名系统、在 PM2 集群下崩溃的认证系统、每次请求都扫描整个提交表的排行榜,以及离全量凭证泄露只差一个配置泄漏的密码哈希方案。
这是完整重写的故事:我们替换了什么、为什么,以及在这个过程中学到了什么。
原版 Leverage OJ 是一个在真实约束下快速推进的项目。开发者是深思熟虑的——你可以从架构决策中看出来。但六年的功能添加、午夜修复和"在我机器上能跑"的补丁已经堆成了一座摇摇欲坠的塔。
每个代码库都有自己的故事。Leverage——我一直在维护的 Online Judge 平台——也有,只是这个故事不太好看。经历了多年的功能叠加、半夜推上去的临时修复、以及偶尔出现的"在我机器上能跑"式 hack 进了生产,这个代码库积累的技术债已经够开一家小公司了。
决定做全量重写不是一时冲动。重写有风险,"第二系统效应"是真实存在的。但当一次代码审查翻出 29 个 bug——包括一个"所有 AC/WA 提交计数都静默出错"的问题——你就会开始重新考虑了。