如何验证格式?
解读
“验证格式”在 CouchDB 语境下通常指两层含义:
- 文档级格式校验——确保写入的 JSON 文档字段、类型、取值范围符合业务规则;
- 视图/索引返回格式——确保 map/reduce 输出、 Mango 查询结果、复制过滤函数返回的数据结构符合调用方预期。
国内面试官问这一题,往往想确认候选人是否知道 CouchDB 没有 MySQL 那样的 CHECK 约束,必须自行实现校验逻辑,并且能否把校验流程无缝嵌入高并发、离线同步、多主复制的真实场景。若只回答“用 JSON Schema 库”会显得纸上谈兵;若能结合 validate_doc_update、设计文档版本管理、CICD 自动化回归、灰度双写 等国内互联网落地细节,则直接拉开差距。
知识点
- validate_doc_update 函数
唯一在写入路径原子执行的钩子,拒绝即回滚,适合做强一致校验。 - 设计文档版本化
国内大厂会要求 _id 形如 _design/v2.user,避免热更新直接覆盖老版本;同时用 git-flow 管理 json 文件,MR 合并后通过 Jenkins + Ansible 自动 PUT 到生产库。 - 双写校验与灰度
对存量大表直接加校验可能炸旧代码,国内常用 双写开关:新代码写新库并开校验,老代码写老库无校验,对比无误后全量切流。 - 离线端预校验
移动端(PouchDB)在本地就用 is-my-json-valid 预跑一遍,减少同步时 403 冲突;同时把校验规则放在 CDN 缓存的设计文档 里,App 启动时拉取最新版本。 - 错误指标与监控
在 validate_doc_update 里不要直接 throw 字符串,而是 throw{forbidden: message, errorCode: 10001},便于 SLS/ELK 统一采集并配置 阿里云告警;拒绝率突增可一键回滚设计文档。 - 性能陷阱
validate_doc_update 每次写入都跑,严禁 内部再发 HTTP 请求查其他库;若必须引用外部字典,用 ets 缓存 或 Redis 本地镜像 方案,并设置 300 ms 超时。 - 安全合规
国内等保 2.0 要求“数据完整性”, CouchDB 本身无行列级权限,需在 validate 里硬编码字段级脱敏规则,并配合 SM4 国密字段加密,审计时直接打印加密指纹。
答案
线上环境我采用“三层校验 + 灰度发布 + 监控回滚”方案:
- 把校验规则写成 validate_doc_update 函数,随设计文档一起版本化,git tag 与业务版本号绑定;
- 在持续集成阶段,用 couchdb-compile 把设计文档打成 json,通过 couchdb-vacuum 做静态检查,确认无语法错误;
- 预发环境打开 双写开关,新文档走校验、老文档无校验,对比 24 h 后错误率为 0 再全量切流;
- 移动端在本地用 PouchDB 先跑同一份校验脚本,同步遭拒时本地弹 Toast 并写 Sentry 埋点;
- 生产环境通过 阿里云日志服务 采集 forbidden 错误,拒绝率 >1% 就自动回滚设计文档到上一版本,回滚操作 30 秒内完成;
- 若业务需要跨字段复杂校验(如订单金额 = ∑明细),我会在 validate 里只验算 MD5 指纹,明细行改由 后置对账作业 兜底,避免阻塞写路径。
一句话总结:CouchDB 的格式验证只能靠自己写代码,但国内落地必须配套版本管理、灰度、监控、回滚四大件,否则就是给自己挖坑。
拓展思考
- 如果校验规则需要热更新且不能重启节点,你会如何确保 Erlang 热代码替换 不导致 validate 函数中间状态异常?
- 当业务要求字段级 AES 国密加密时,怎样在 validate_doc_update 里验证密文字段长度与算法标识,同时避免把密钥硬编码到设计文档?
- 多主复制场景下,A 节点 放宽了校验,B 节点 严格校验,复制时 B 持续拒绝 A 的文档,你会用 replication filter 还是 write quorum 来协调?请给出冲突解决策略。