跳转至

云端同步架构

背景:SPEC-004 Phase 3 要做"客户端众包 + 数据共享 + 贡献度"。这要求多类数据能高效增量同步到云端,不只是域名状态。 本文档解释为什么选 jsstore(IndexedDB)作为所有 sync-related 数据的统一存储,以及 Phase 3 的 store 设计预览。

1. 存储方案对比(云端协同视角)

能力 chrome.storage.local jsstore (IndexedDB) Dexie
增量 sync(只传变化的) ❌ 要 diff 整 Object(~MB 级) where: { syncedAt < lastSeen } 索引扫描(ms) ✅ 内置 sync 扩展
批量按字段查询 ❌ 全表 JS filter where: { state } / where: { in: [...] }
加 sync 字段 改 schema 软(每个 key 各 own) ✅ migrations 加列
数据量上限 ❌ 单 key 5MB / 总 10MB ✅ GB 级 ✅ 同
并发写(多 alarms 同时) ❌ JSON 替换 race ✅ transaction 隔离
现有项目 fit 简单 settings 用 ✅ MapTaskData 已用 ❌ 新 dep + 重构

选 jsstore(v0.10.94 起,DomainStats 是第 1 个 sync-related store)。

2. Phase 3 store 全景(已设计未实现)

按 SPEC-004 Phase 3.x 设计,云端协同需要至少 4 类数据:

2.1 DomainStats(v0.10.94 已实现)

// src/utils/jsstore/base.ts:tblDomainStats
{
  domain: string (primary key)
  state, recent[], fetchTotal, fetchOk, ..., firstSeen, lastSeen, stateChangedAt, stateExpiresAt,
  // Phase 3 预留
  syncedAt: number      // 增量 sync where 字段
  cloudConsensus: number // 0-1 N 客户端共识强度
}
  • 用途:域名健康度记忆 + 跨用户共享(dead/antibot-hard 域名一致性高,云端聚合后下发)
  • 量级:1-5k 域名 / 用户
  • sync 频率:中频(每 N 分钟批量)

2.2 ContactPool(Phase 3.1)

{
  urlHash: string (primary key)  // sha256(normalized url)
  domain: string                  // 索引(按域名批量查)
  emails: string[]
  phones: string[]
  socials: { fb, ins, li, tw, yt, wa }
  // 抓取来源(反作弊)
  contributorCount: number
  firstContributedAt: number
  lastVerifiedAt: number
  verifyTotal: number
  verifyMatch: number             // 数据一致性评分
  // sync
  syncedAt: number
  cloudHash: string               // 云端版本 hash(冲突检测)
}
  • 用途:本地缓存的"已抓过的页面 contact",先查云端命中则直接用 — 不抓
  • 量级:10w-100w URL / 用户(贡献者多)
  • sync 频率:高频(每次抓完就 enqueue 上传)

2.3 CloudDomainState(Phase 3.2)

{
  domain: string (primary key)
  state: DomainState              // 云端权威状态
  consensus: number               // 0-1 N 客户端共识强度
  updatedAt: number
  expiresAt: number               // 云端 TTL
  // 本地标记
  fetchedAt: number               // 本地最后拉云端的时间
}
  • 用途:云端权威域名状态缓存(启动时拉一次,过期前用)
  • 量级:1-5k
  • sync 频率:低频(每小时拉一次)

2.4 ContributionLedger(Phase 3.4)

{
  id: number (auto-increment)
  userId: string
  action: 'upload-new' | 'verify-match' | 'consume-query' | 'reset' | ...
  earnedAt: number
  amount: number                   // 贡献度变化(+/−)
  ref: string                      // 关联 urlHash / domain
  // sync
  syncedAt: number
  serverAcked: boolean             // 服务端是否已记账
}
  • 用途:贡献度账本(本地先入账 → 异步上传服务端 → 服务端 ack 后置 serverAcked=true)
  • 量级:随用户行为,可能数千-数万条
  • sync 频率:实时(每条 action 立即 enqueue)

3. 增量同步设计

3.1 通用 sync 流程(应用到 4 个 store)

本地写入 (saveStat / writeContact / writeLedger)
  ↓ 自动设 syncedAt=0(或保持 < lastSeen)
本地 syncer cron (每 N 秒/分钟)
1. select where: syncedAt < lastSeen LIMIT 100
2. upload to cloud API
3. on success: update syncedAt = now
4. on conflict: pull cloud version + merge + retry

3.2 拉云端:CloudDomainState / ContactPool

启动时 + 每小时:
1. cloud GET /sync?since={localMaxUpdatedAt}
2. 批量 jsstore.insert(upsert) 覆盖本地
3. UI 自动用新数据

3.3 冲突解决

  • DomainStats / ContactPool:last-write-wins(按 lastModified 时间戳)+ 云端共识算法(N 个客户端独立报告同结果 → 高置信度)
  • ContributionLedger:append-only(不冲突,服务端 ack 即可)

4. v0.10.94 已做 / 待做

状态 说明
DomainStats jsstore schema ✅ v0.10.94 base.ts tblDomainStats,version=3
domain-state.ts 改用 jsstore ✅ v0.10.94 旧 chrome.storage.local 数据自动迁移
syncedAt / cloudConsensus 字段 ✅ 预留 暂不读写,Phase 3 启用
ContactPool jsstore schema ⏸ Phase 3.1 设计已有,未建表
CloudDomainState jsstore schema ⏸ Phase 3.2 设计已有
ContributionLedger jsstore schema ⏸ Phase 3.4 设计已有
Sync API 客户端 ⏸ Phase 3 服务端先就绪
Sync API 服务端 ⏸ Phase 3 业务决策后启动

5. 为什么不用 dexie / pouchdb

  • Dexie + Dexie Cloud:sync 体验好,但是付费 / vendor lock。且需要把 MapTaskData 也迁移过去,重构成本高
  • PouchDB + CouchDB:双向 sync 天然,但服务端栈是 CouchDB(项目当前栈是 Express + MySQL 等通用栈,不想为此引入新数据库)
  • jsstore + 自建 sync API:现有 stack 一致 / 完全控制 / 学习成本零(已用 MapTaskData)。代价:自己实现增量 sync 逻辑(~1 周工程量)

6. 关键约束

所有 sync-related 数据必须: - 用 jsstore 表(不要 chrome.storage.local) - 加 syncedAt: number 字段(默认 0) - enableSearch 在 sync 字段上(syncedAt, state, domain 等过滤字段) - 单条 upsert(不要全表替换)

chrome.storage.local 留给: - settings(settingParams) - UI 偏好(zoom / theme) - 单 key 的小型状态(is_engine_running / lastForceReload 等) - watchdog / extension lifecycle 等无需 sync 的运行时状态

相关