Files
ShadowsocksX-NG/docs/adr/0031-node-creation-and-modification-timestamps.md
Qiu Yuzhou f23a24f203 feat(servers): add node creation and modification timestamps
CatalogEntry gains createdAt/updatedAt and the catalog document moves to
v5 (reads v1-v5, writes v5). Modification time is structure-sensitive:
editing a node's own fields bumps the node, a group's direct child set
or order changes bump the group, and a moved node keeps its timestamps.
No-op writes and unchanged subscription refreshes bump nothing; the
refresh rebuild compares per node, so surviving leaves keep theirs. All
times are passed in by callers as now: Date - one moment per logical
commit, the domain layer stays clock-free. Legacy documents load with
nil (unknown, never backfilled); duplicates and imports stamp fresh
times at the moment of the operation.

Semantics are recorded as glossary term 节点时间戳 and ADR 0031. No UI
in this change.
2026-10-09 18:04:48 +08:00

2.3 KiB

配置目录节点记录创建时间与修改时间

Status: Accepted (2026-10-09)

配置目录的每个节点(服务器配置与配置组)增加创建时间与修改时间事实,目录文档版本升至 v5(读取兼容 v1–v5,写入始终 v5)。修改时间采用结构敏感语义(类比目录 mtime):节点自身资料变化(服务器的连接字段与名称、分组的名称)更新自身;分组的直接子节点集合或顺序变化(增删子节点、子节点移入移出、重排)更新该分组;被移动节点自身内容不变、修改时间不动;内容未变的写操作(同名重命名、同值字段提交、内容未变的订阅刷新)不更新任何时间戳。时间一律由调用方传入(now: Date),领域层保持无时钟纯值类型;一次逻辑变更(导入、复制、订阅快照应用)共享同一时刻。

订阅子树同样维护时间戳:订阅刷新以快照整体重建,但逐节点与旧载荷比较——延续且内容未变的节点保留双时间戳,内容变化的保留创建时间、更新修改时间,新出现的节点双时间戳为写入时刻;固定分组身份客户端所有,其创建时间跨刷新保留。复制、URI/文件导入与 Legacy 导入产生的新节点,创建时间即操作时刻,与「全新本地身份」规则一致(ADR-0027)。历史文档(v1–v4)没有时间戳,读取为未知(nil)且不回填,与订阅 lastSucceededAt 的「旧文件缺失时不猜测」原则一致。

Considered options

  • 最窄语义(仅节点自身资料变化才更新,结构变化完全不动时间戳):实现最简,但把服务器移入/移出分组、组内重排不会在任何节点留下痕迹,与「分组内容被改过」的用户直觉不符。被否。
  • 写入即更新(不比较内容,任何写操作都刷新相关节点):无需比较逻辑,但内容未变的订阅刷新会刷新整棵子树的修改时间,同名重命名与同值保存也会伪造变化,事实失真。被否。

Consequences

  • v5 文档中未知的时间戳省略键;CatalogEntry 的 Equatable 包含时间戳,跨时间构造的目录夹具做整体相等比较时必须用受控时刻。
  • 展示 UI 不在本次范围内;后续直接读取节点的创建/修改时间即可,语义以本 ADR 与 GLOSSARY「节点时间戳」为准。