让我差点给修崩了。
P0-1 主题/回复 extern 命名空间隔离(双模型 Qwen H1+H2 合并)
问题: 主题与回复使用两条独立自增 ID 序列(均从 1 起),ID 区间完全重叠。extern 正文此前共用裸 ID 的 APCu 缓存键(extern_{id})与 .txt 文件名(extern/{Y}/{Q}/{桶}/{id}.txt):
- 启用 APCu 后读主题 5 会命中回复 5 的正文缓存(跨内容串读);
- 当父帖桶 ==
R2且同季度时,主题 R 与回复 R 写入同一文件路径,atomicRename静默互相覆盖、delete 互相误删(规模化必现正文损坏)。
修复内容:
| 位置 | 改动 |
|---|---|
app/SplitDB/ExternStorage.php |
write/read/delete 增加 type 参数('topic'|'reply');APCu 键改为 extern_t{id} / extern_r{id}(cacheKey() 经 ShardRouter::typePrefix() 统一生成) |
app/SplitDB/ShardRouter.php |
externRel()/externAbs()/ensureExternDir() 增加 type 参数,文件名改为 t{id}.txt / r{id}.txt |
.bin / .idx 同步处理(用户补充要求) |
存量二进制缓存与索引文件同样加类型前缀:t{id}.bin/r{id}.bin、t{id}.idx/r{id}.idx,防止旧数据继续与新数据串读/覆盖 |
| 调用点同步 | Thread::insert/update/hardDelete、Post::insert/update/softDelete、User::deleteWithCascade、Thread::batchDelete/deleteRepliesOf 全部传入 type |
cli/migrate_extern_namespace.php(新增,存量迁移) |
遍历 data/extern/**/*.txt 与裸 .bin/.idx,按 main_index 判定归属(topic_index→主题 t{id};reply_index→回复 r{id};两者皆无记日志待人工核对),重命名并拆分混合归档块,迁移后删除旧文件;脚本幂等可安全重跑 |
存量迁移已执行: 生产数据 data/extern/ 全部迁移完毕,0 裸文件残留;cli/smoke_extern_storage.php 17 项用例全部通过。
P0-2 搜索降级路径修复(双模型确认 Qwen H4 + M1 合并)
问题: searchLIKE() 降级路径存在双重失效:
- 默认
timeRange='all'时$since='',但 SQL 仅在$since !== ''时才含:since占位符,绑定数组却无条件塞入':since'—— 本项目DBFactory已强制ATTR_EMULATE_PREPARES=false,原生预处理下多余命名参数抛column index out of range,降级路径自身 500; - 帖子查询仍
FROM threads t—— 该表为 SplitDB 迁移后的退役表(0 行),降级搜索对新帖恒返回空,安全网完全报废。
修复内容(app/Helpers/Search.php):
- 条件绑定
:since:帖子 COUNT、博客 COUNT、帖子列表、博客列表 4 处,仅当$since !== ''时才加入绑定数组;帖子时间过滤改用topic_index.create_time(秒级 INTEGER),博客沿用blogs.created_at(字符串),分别绑定:sinceTs/:since,不再共用歧义参数; - 帖子分支改读
main_index.topic_index:title LIKE+status/deleted_at过滤 + 版块权限白名单 + 时间过滤,替代退役threads表;列表行用Thread::getByIds()+Thread::fillExternContent()补正文/用户名/分类(与已修好的索引搜索路径同口径); - 移除
t.content LIKE与content_snippet对threads的依赖,降级覆盖范围在代码注释中明确声明(标题全文为主)。