feat(sync): reconciliation scheduler with digest and bootstrap healing
P2 Task 4(inbox 对账):
- services/sync 新增 SyncReconciler(runOnce 可测;main 双循环,间隔
SYNC_RECONCILE_INTERVAL_MS env 可调,CN_API_URL 指向 cn 入口):每轮
先按 album/category/photo/asset 逐类分页拉取 cn reconcile「当前状态
事件」并直接签名推送到既有 content-batches 管道。sinceVersion 取 0
(全量当前态)而非 per-type 已应用最大版本——cn reconcile 以类型级
全局版本阈值过滤,类型内单实体滞后会被 max 掩蔽;ov 的 eventId 幂等
+ 版本单调把已追平实体全部判重(recon 确定性 eventId 重扫零成本),
偏差登记:计划原文对账返回「缺失清单」,契约已定为可重放 SyncEvent
数组(免建第二条拉取通道),openapi 描述同步更新
- cn 新增 POST /internal/sync/digest:每相册权威计数 {categoryCount,
publishedPhotoCount, photoCount, assetCount};ov 比对副本计数(照片
取 [publishedPhotoCount, photoCount] 区间口径——hidden/deleted 照片
若从未以 published 态同步过 ov 合法无行,缺席即撤下),不一致相册
自动触发 cn bootstrap 重建;ov-only 相册(cn 行已删,bootstrap 无法
表达删除)告警登记不自动处置
- 收敛语义两处落位:① sync-inbox 版本比较改「严格更旧才拒绝」——
同版本新 eventId(bootstrap/reconcile 快照重发)为当前态幂等重断言
应用,修复 ov 缺失副本行;A26 不受影响(更旧版本仍拒绝,cn 侧
entityVersion 与状态一一对应)② album.updated 快照载荷(bootstrap/
reconcile)携带分类全量清单,ov 存在即整体替换 albums_ov.categories
——修复错过的 category 墓碑(Task 3 登记边界:cn 行已删,无现存实体
事件可重放,唯全量替换可收敛)
- e2e(reconcile.e2e-spec):漏事件追赶(ov v1/cn v5 事件投后即失)→
清扫追平;digest 修复缺失 photos_ov 行(bootstrap 重发 → 同版本
重断言重建);digest 修复丢失的分类墓碑(全量清单替换收敛);稳态
空转(applied=0、无 bootstrap);Task 3 套件保持绿
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1369,6 +1369,40 @@ paths:
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalError'
|
||||
|
||||
/internal/sync/digest:
|
||||
post:
|
||||
operationId: syncDigest
|
||||
tags: [internal]
|
||||
summary: 每相册实体计数摘要
|
||||
description: |
|
||||
cn 侧权威计数(P2 Task 4):每相册 {categoryCount, publishedPhotoCount,
|
||||
photoCount, assetCount}。ov 对账调度定期拉取并与副本计数比对——不一致
|
||||
的相册触发该相册 bootstrap(当前状态快照重发,含分类全量清单整体替换),
|
||||
收敛错过的墓碑与漏掉的事件。albumId 缺省表示全部相册。鉴权见 [internal]
|
||||
tag 描述。
|
||||
security: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DigestRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: 每相册计数摘要(albumId 升序)。
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DigestResponse'
|
||||
'400':
|
||||
description: 请求体校验失败(VALIDATION_FAILED)。
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalError'
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
@@ -2719,12 +2753,16 @@ components:
|
||||
|
||||
ReconcileResponse:
|
||||
type: object
|
||||
description: 对账结果:按 entityVersion 升序的事件(无缺失为空数组)。
|
||||
description: |
|
||||
对账结果:按 entityVersion 升序的**当前状态事件**(可直接重放的完整
|
||||
SyncEvent,非仅版本清单——P2 Task 4 设计决策:重放走既有
|
||||
content-batches 管道,无需第二条拉取通道;sinceVersion 之后无实体时为
|
||||
空数组)。
|
||||
required: [events]
|
||||
properties:
|
||||
events:
|
||||
type: array
|
||||
description: 缺失事件(升序);请求方按序重放。
|
||||
description: 当前状态事件(升序);请求方按序经 content-batches 重放。
|
||||
items:
|
||||
$ref: '#/components/schemas/SyncEvent'
|
||||
|
||||
@@ -2750,3 +2788,57 @@ components:
|
||||
format: int64
|
||||
minimum: 0
|
||||
description: 快照水位:后续增量对账的 sinceVersion 起点。
|
||||
|
||||
DigestRequest:
|
||||
type: object
|
||||
description: 对账摘要请求(P2 Task 4)。
|
||||
properties:
|
||||
albumId:
|
||||
type: string
|
||||
description: 指定相册;缺省为全部相册。
|
||||
|
||||
AlbumDigest:
|
||||
type: object
|
||||
description: |
|
||||
单相册权威计数摘要。ov 侧比对口径:albums_ov.categories 元素数应等于
|
||||
categoryCount;photos_ov 行数落在 [publishedPhotoCount, photoCount]
|
||||
区间内即视为一致(hidden/deleted 照片若从未以 published 态同步过,ov
|
||||
合法地无行——缺席即撤下语义);相册行缺失即不一致。assetCount 供观测
|
||||
(ov 无 asset 副本行,不参与比对)。
|
||||
required:
|
||||
- albumId
|
||||
- categoryCount
|
||||
- publishedPhotoCount
|
||||
- photoCount
|
||||
- assetCount
|
||||
properties:
|
||||
albumId:
|
||||
type: string
|
||||
description: 相册 ID。
|
||||
categoryCount:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: 当前分类数(cn 权威行数)。
|
||||
publishedPhotoCount:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: 可见(published)照片数——ov 必须持有的副本行数下界。
|
||||
photoCount:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: published+hidden+deleted 照片总数——ov 副本行数上界。
|
||||
assetCount:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: 生效(effective)资产数(观测用;ov 无 asset 副本行)。
|
||||
|
||||
DigestResponse:
|
||||
type: object
|
||||
description: 每相册计数摘要(albumId 升序)。
|
||||
required: [albums]
|
||||
properties:
|
||||
albums:
|
||||
type: array
|
||||
description: 相册摘要清单;空库为空数组。
|
||||
items:
|
||||
$ref: '#/components/schemas/AlbumDigest'
|
||||
|
||||
Reference in New Issue
Block a user