用途
Windmill 在 CI 中使用 SQLX_OFFLINE=true,这要求所有 sqlx::query! / sqlx::query_as! 宏都能在 backend/.sqlx/ 中找到匹配的缓存查询数据。当在 Rust 源码中添加或编辑 SQL 查询后,必须更新该缓存,否则 CI 会报错:
error: `SQLX_OFFLINE=true` but there is no cached data for this query
本技能提供了一套安全更新 SQLx 离线查询缓存的操作流程。
主要能力
- 在运行 prepare 前后备份与恢复缓存,避免编译失败导致缓存被清空(文中记录观察到 2350 → 142 条目的情况)。
- 通过脚本
.agents/skills/update-sqlx/sqlx-cache.sh提供 backup、newq、restore 等操作。 - 提供保持 EE(企业版)缓存不被误删的安全流程,通过
git checkout origin/main -- .sqlx/恢复、运行 OSS feature 的 prepare、再补回缺失的 EE 缓存文件。 - 提供缓存完整性验证步骤,比较 origin/main 与当前缓存文件列表,确保缺失文件数为 0。
使用方式
何时运行
在 Rust 源码中添加或编辑 SQL 查询后运行。
当变更只是删除查询时不要运行——缓存对 CI 而言已经是完整的,剩下的是孤立条目,仅是外观问题,不会破坏构建。应在离线状态下删除它们,而不是运行 prepare 整理。
运行前准备
备份缓存,因为 prepare 会在重新生成前删除 .sqlx/,任何编译失败都会使缓存被掏空:
.agents/skills/update-sqlx/sqlx-cache.sh backup
备份状态按 worktree 隔离,因此同时运行的兄弟 worktree 的 prepare 无法覆盖你的备份。
将 DATABASE_URL 指向当前 worktree 的数据库。prepare 会将每个 sqlx::query! 对着实时数据库编译。另一个 worktree 的数据库缺少你的迁移,因此每个新表查询都会失败并连带破坏缓存。症状是 relation "<your_new_table>" does not exist——这是 DATABASE_URL 错误,不是查询本身有问题。
测试中的查询需要 --all-targets
prepare 只缓存它编译到的代码中的查询,而仅 --workspace 不会编译测试目标。因此 tests/*.rs 中的 sqlx::query! 不会获得缓存条目,即使 lib 构建干净,CI 也会在测试目标上以常见的“no cached data”错误失败。
添加 --all-targets 可以缓存它们,但在 CE checkout 中会中途中止:backend/tests/otel.rs 导入了 windmill_common::otel_ee,它仅存在于私有 feature 之后,因此编译在 prepare 已经清空 .sqlx/ 之后死亡(观察到 2435 → 4 条目)。
遇到这种情况不要对抗它——这是预先存在的 EE 缺口,不是你的变更引起的。取走你需要的条目并恢复备份:
.agents/skills/update-sqlx/sqlx-cache.sh backup
cd backend
DATABASE_URL=<this worktree's db> \
cargo sqlx prepare --workspace -- --workspace --features all_sqlx_features --all-targets
# 预期会失败;它仍然写入了死亡前获得的条目
cd ..
.agents/skills/update-sqlx/sqlx-cache.sh newq # prints each added query
.agents/skills/update-sqlx/sqlx-cache.sh restore # backup back, added entries grafted on
运行 restore 前先阅读 newq 打印的内容——它显示每个新增条目的 query 字段,每一条都应该是你的。集合很小(每个新测试查询一条);如果其中出现其他内容,说明运行走得比你想象的更远。
安全流程
始终从 origin/main 保留现有 EE 缓存:
cd backend
# 1. 从 main 恢复完整缓存(含 EE 缓存)
git checkout origin/main -- .sqlx/
# 2. 使用 OSS feature 运行 prepare(本地能编译的部分)
cargo sqlx prepare --workspace -- --workspace --features all_sqlx_features
# 3. 恢复在步骤 2 中被删除的 EE 缓存
git ls-tree origin/main backend/.sqlx/ \
| awk '{print $4}' | sed 's|backend/\.sqlx/||' | sort > /tmp/main_files.txt
find backend/.sqlx -name "*.json" -printf '%P\n' | sort > /tmp/current_files.txt
comm -23 /tmp/main_files.txt /tmp/current_files.txt > /tmp/missing_files.txt
while read f; do
git show "origin/main:backend/.sqlx/$f" > "backend/.sqlx/$f"
done < /tmp/missing_files.txt
# 4. 验证没有从 main 丢失任何内容
find backend/.sqlx -name "*.json" -printf '%P\n' | sort > /tmp/current_files.txt
comm -23 /tmp/main_files.txt /tmp/current_files.txt | wc -l
# 应输出:0
如果 EE 能在本地编译,可以使用完整脚本(更快):
cd backend
./update_sqlx.sh
但如果它以 EE 编译错误失败,则使用上述安全流程。
适用场景
- 在 Windmill 仓库中新增或修改 SQL 查询后更新 SQLx 离线缓存。
- 需要在 CI(SQLX_OFFLINE=true)下保持缓存完整。
- 需要避免误删 EE 查询缓存的情况。
限制与禁止事项
- 绝不要仅用 OSS feature 运行
cargo sqlx prepare --workspace并提交结果——它会删除 EE 缓存。 - 绝不要在本地 prepare 时设置 SQLX_OFFLINE=true——应使用实时数据库。
- 绝不要在未备份 .sqlx 或未确认 DATABASE_URL 属于当前 worktree 的情况下运行 prepare。
- 对于纯删除查询的变更绝不要运行 prepare。
- 绝不要跳过验证步骤(上述步骤 4)。
- 绝不要在 --all-targets 运行中止后保留其输出——那是一个近乎空的缓存。应恢复备份并仅嫁接已验证的条目。
- 步骤 4 与 origin/main 比较是因为步骤 1 从它恢复;如果未运行步骤 1,应与
git merge-base HEAD origin/main比较。
验证
提交后,与 origin/main 的 diff 应显示:几个新增缓存文件(对应你更改的查询)、几个删除的缓存文件(对应不再存在的旧查询)、EE 缓存集净删除为零。
git diff origin/main --stat backend/.sqlx/