Safe Haven 索引同步用 BITS 协议做增量传输,七天才拉一次全量
BITS 是什么
BITS(Background Incremental Transfer System,后台增量传输系统)是 Safe Haven 客户端和服务器之间同步应用目录的协议。它的核心目标是最小化数据开销——避免每次同步都下发整份 index.json。
两种同步模式
Safe Haven 客户端向服务器同步 endpoint 发送一个 since 参数(Unix 时间戳),服务器根据这个参数决定返回什么:
从未同步或 timestamp 超过 7 天
返回全量索引——整份 index.json,包含所有应用的完整数据。这是冷启动场景,只有第一次或长时间没同步才会触发。
timestamp 在 7 天以内
返回增量补丁——服务器遍历 7 天内的 changelog,计算出这期间发生了哪些变化,只返回变化的部分。
changelog 怎么维护
服务器在 S3 存储桶里和 index.json 同路径维护一个 index_changelog.json 文件。这是一个持久化的变更日志,每次 index.json 有变化时服务器会:
- 往 changelog 末尾追加一个事件记录:哪个包名变了、哪个包名被删了、变化的时间戳
- 每次写 changelog 时,自动删除7 天前的事件(滚动修剪)
这样 changelog 永远只保留最近 7 天的事件,大小可控。
补丁怎么计算
客户端带 since: 1715763600(假设是 4 小时前的时间戳)来同步:
- 服务器遍历 changelog 里所有
timestamp > 1715763600的事件 - 对同一个包名的多条事件做折叠——只保留最后一次的净效果(比如一个应用先被更新再被删除,折叠后就是"移除")
- 产出两个数组:
updates:变化过的应用(含完整元数据 + versions 数组)removes:被删除的包名
补丁结构示例:
{
"action": "patch",
"timestamp": 1715850000,
"removes": [
"com.example.oldapp"
],
"updates": [
{
"packageName": "com.example.updatedapp",
"name": "Updated App Name",
"summary": "A short summary",
"trustLevel": "verified_source",
"versions": [
{
"versionName": "2.1.0",
"versionCode": 42,
"apkPath": "apps/com.example.updatedapp/42/app.apk",
"sha256": "ab12cd...",
"scannedAt": 1715849000
}
]
}
]
}
补丁的过滤规则
全量索引和增量补丁都会过滤:
- 没有 version 记录的条目(placeholder 或不完整的 listings)不会下发到客户端
- 所以你在客户端搜不到的包名,不是你没搜到,是服务器根本没下发
客户端怎么处理补丁
客户端收到补丁后:
- 把
updates里的应用合并进本地索引(按 packageName 匹配,有就更新,没有就新增) - 把
removes里的包名从本地索引里删除 - 更新本地的同步时间戳,存为下次同步的
since参数
为什么用 7 天窗口
7 天是一个工程折中:
- 太短(比如 1 天):客户端如果断网 2 天就会因为 timestamp 过期被迫拉全量
- 太长(比如 30 天):changelog 文件会膨胀,补丁计算耗时增加
- 7 天足够覆盖"手机关机出差一周"这类最常见的离线场景
同步走的是什么通道
Safe Haven 的同步端点跑在 Cloudflare Workers 上——这是 Safe Haven 后端的三大组件之一(Routing Backend)。Workers 从 S3 存储桶读取 index.json 和 index_changelog.json,做增量计算,返回结果。
好处:
- Cloudflare Workers 的边缘计算,低延迟全球分发
- 静态 JSON 从 S3 读取 + Workers 做增量计算,逻辑简单可审计
- 不需要一个常驻的同步服务进程
和其他应用商店的同步对比
| 商店 | 同步方式 | 特点 |
|---|---|---|
| Safe Haven | BITS 七滚动增量 | 明确的 since 时间戳和 changelog 机制 |
| F-Droid | 全量索引 + 缓存 | 不做增量,每次拉完整 index-v1.json |
| Google Play | 黑盒(Protobuf) | 客户端做请求,增量由 Google 决定 |
F-Droid 客户端每次同步都拉全量 index-v1.json——文件不大(压缩后几 MB),但机制上比 Safe Haven 简单。Safe Haven 在增量这块做得更精细,代价是后端维护 changelog 的复杂度。