记忆 accessed_at 归一化

wesgine 记忆系统中 accessed_at 时间戳的零值陷阱与归一化设计。


零值问题

accessed_at 列定义为 NOT NULL DEFAULT 0。

零值(1970-01-01)不是中性状态——它是该字段最坏的取值:

影响说明
GC retentionScore衰减到 ~0,最先被淘汰
Hybrid ranker压到底部
Digest读作 stale
ORDER BY accessed_at DESC排到末尾

最坏的取值落在最新写入的行上。


曾经的缺陷

六个读者各自带一份补偿:

if AccessedAt.IsZero() { use CreatedAt }

第七个(context_index.go 的 ORDER BY)没带。

同一行在五处读作创建时间、在一处读作 epoch。


归一化设计(INV-MEM-51)

归一在 saveCore 一处,读者不补偿:

saveCore 落盘前:
  if entry.AccessedAt.IsZero() {
      entry.AccessedAt = entry.CreatedAt
  }

存量修复:ddlMemoriesV10 幂等更新。


为什么不用 DEFAULT CURRENT_TIMESTAMP

SQLite 的 DEFAULT 在 INSERT 时生效,Go 的 ORM 通常显式赋值。

即使改了默认值,Go 代码传 time.Time{} 仍会写入零值。

所以归一点必须在应用层(saveCore),不在 DDL。


第二排序键

accessed_at 并列时用 created_at 打破平局。

这不是整洁性:List 排序后切 Offset/Limit,并列时不确定的顺序移动的是页边界。


相关不变量


相关文档