# DHT 元数据搜索服务计划 本文档记录项目当前规划实施顺序和完成状态 它是随需求实现结果性能数据和部署条件持续调整的活文档 ## 维护规则 - 已经通过验收的任务使用 `[x]` 标记 - 正在规划但尚未完成的任务使用 `[ ]` 标记 - 需求变化时允许新增删除拆分合并或调整阶段顺序 - 调整计划时同步修改任务说明依赖关系和验收标准 - 不因代码已经存在就标记完成必须满足对应验收标准 - 发现原方案不合适时记录新决策并更新后续阶段 - 每次完成一个可交付功能时同步更新本文档 ## 当前技术方向 - `src/crawler` 负责可复用的 DHT 协议节点发现 Peer 查找和 Metadata 下载 - `src/search` 负责持久化去重索引搜索接口配置和运行生命周期 - `src/web` 负责最终用户搜索诊断和配置管理界面 - RocksDB 保存权威数据去重信息和任务状态 - Tantivy 保存可以从 RocksDB 重建的搜索索引 - Axum 提供搜索详情统计和健康检查接口 - 用户配置使用强类型 DTO 表达 TOML 只是当前持久化适配器 - SQLite 保存有明确保留上限且可安全删除的运行诊断历史 - 所有长期任务通过有界队列和背压控制资源占用 如果实际运行证明 RocksDB 的构建部署或资源成本不合适可以重新评估 redb SQLite 或其他存储方案 ## 阶段零 项目基础 ### 目标 建立清晰的 workspace 边界开发规则和可持续验证的基础库 ### 任务 - [x] 将 crawler search 和 web 源码统一收纳到根目录 `src` - [x] 使用当前 Git 配置统一作者仓库许可证和 edition 元数据 - [x] 编写 `AGENTS.md` 记录架构边界和开发约定 - [x] 将最终应用与可复用 DHT 基础库分离 - [x] 删除被内容组索引替代的旧状态和无效兼容代码 - [x] 收紧仅供测试或存储内部使用的接口和依赖 - [x] 按运行统计调度队列 Peer 竞速和 RocksDB 生命周期职责拆分核心大模块 - [x] 实现 BEP-51 `sample_infohashes` 主动发现 - [x] 实现主动 Peer 查找和 Metadata 获取 - [x] 验证远程公网设备能够持续获取 Metadata - [x] 确认 Xray 全局代理会影响 Metadata TCP 连接并完成旁路验证 - [x] 保持基础库测试通过 ### 验收标准 - [x] `cargo check --workspace --all-targets` 通过 - [x] `dht-crawler` 单元测试通过 - [x] `opencodes` 参考项目不参与 workspace 构建 ## 阶段一 本地持久化基础 ### 目标 建立跨重启保留的权威数据源并完成精确去重和内容聚合基础 ### 任务 - [x] 定义二十字节 `InfoHash` 类型和十六进制转换 - [x] 定义 `TorrentRecord` `TorrentFile` 和内容组索引状态 - [x] 校验名称文件列表文件总大小和 infohash - [x] 使用 BLAKE3 计算规范化内容指纹 - [x] 规范化 Unicode 路径分隔符大小写和文件顺序 - [x] 保留真实子目录避免内容指纹碰撞 - [x] 定义 RocksDB 二进制键空间 - [x] 实现数据库格式检查 - [x] 实现 infohash 精确查询和存在性判断 - [x] 实现新记录 WriteBatch 原子写入 - [x] 实现重复 infohash 的 `last_seen` `seen_count` 和 Peer 更新 - [x] 实现相同内容不同 infohash 的聚合映射 - [x] 实现待索引记录查询和索引完成标记 - [x] 配置 Bloom Filter LZ4 压缩和有限 block cache - [x] 准备 Windows 本地 RocksDB 构建所需的 libclang - [x] 将本地构建工具目录排除出 Git ### 验收标准 - [x] 数据库关闭并重新打开后记录仍可读取 - [x] 重复写入不会创建第二条 torrent 记录 - [x] 重复写入会正确增加发现次数 - [x] 相同内容的不同 infohash 可以独立保存并聚合查询 - [x] Metadata 主体内容映射和待索引标记原子写入 - [x] RocksDB 功能测试通过 - [x] `dht-search` Clippy `-D warnings` 通过 ## 阶段二 采集持久化闭环 ### 目标 让 DHT 获取的真实 Metadata 自动进入有界持久化管线并支持安全停止和重新启动 ### 任务 - [x] 定义应用配置结构和默认配置文件 - [x] 支持通过配置指定固定数据目录 - [x] 支持配置 DHT 端口并发队列容量和 Metadata 限制 - [x] 初始化 RocksDB repository 并处理启动错误 - [x] 将 `TorrentInfo` callback 转换为 `TorrentRecord` - [x] 建立有界持久化队列并实现背压 - [x] 使用专用阻塞任务执行 RocksDB 操作避免阻塞 Tokio worker - [x] 在 Metadata 下载前查询持久化 infohash 状态减少重复下载 - [x] 在 BEP-51 Peer Lookup 前批量查询 RocksDB 并更新已有 infohash 发现状态 - [x] 将已存在记录更新为再次发现而不是重复创建 - [x] 增加接收写入重复拒绝失败和队列深度指标 - [x] 实现 `Ctrl+C` `SIGINT` 和 `SIGTERM` 优雅退出 - [x] 退出时停止接收新任务并排空或持久化剩余任务 - [x] 支持重新启动后继续使用原数据库 - [x] 将 example 运行方式替换为正式 `dht-search` 二进制 - [x] 支持通过运行时长参数进行间歇运行 ### 验收标准 - [x] 本地运行可以持续向 RocksDB 写入真实 Metadata - [x] 停止并重启后旧 infohash 不会作为新记录重复写入 - [x] 队列达到容量时内存不继续无界增长 - [x] 正常退出后已接受的任务不会静默丢失 - [ ] 远程设备运行一小时没有持续内存增长 - [x] 记录采集速度重复率数据库增长和写入延迟 ## 阶段三 Tantivy 搜索索引 ### 目标 让持久化 Metadata 支持快速全文搜索过滤排序和索引恢复 ### 任务 - [x] 定义 Tantivy schema 和索引版本 - [x] 索引名称文件路径扩展名 infohash 和内容指纹 - [x] 将大小文件数时间和发现次数定义为 fast fields - [x] 设计中英文数字和文件名子串 tokenizer - [x] 实现待索引任务批量消费 - [x] 实现按数量和时间间隔批量 commit - [x] commit 成功后原子更新 RocksDB 索引状态 - [x] 实现关键词短语和精确 infohash 查询 - [x] 实现大小时间扩展名文件数热度和可用性过滤 - [x] 实现大小范围和扩展名过滤 - [x] 实现相关性时间热度大小和发现次数排序 - [x] 建立带时间衰减的 DHT 活跃度分数和用户可读等级 - [x] 实现分页并限制最大翻页成本 - [x] 实现相同 `content_key` 结果精确折叠和变体分页 - [x] 实现从 RocksDB 全量重建 Tantivy 索引 - [x] 支持索引结构不兼容时直接重建 - [x] 使用影子索引保留旧搜索并在完整校验后原子切换 - [x] 持久化影子索引构建清单并支持跨重启继续重建 - [x] 暴露种子内容组待索引数量和全量重建状态进度 - [x] 按文件大小为大型种子选择最多 2048 个文件并限制路径文本预算 - [x] 优化完整名称别名文件名路径的相关性权重并使用热度时间稳定同分结果 - [x] 将标题文件名和路径拆分为有限 N-Gram 与路径分词策略并移除无用位置索引 - [x] 搜索写入器按需创建使影子重建期间旧活动索引保持纯查询占用 ### 验收标准 - [x] 新写入记录在目标延迟内可搜索 - [x] 搜索索引删除后可以从 RocksDB 完整重建 - [x] 索引过程中异常退出不会永久丢失文档 - [x] 全量重建期间旧索引继续提供完整旧结果且新数据在原子切换后可见 - [x] 百万级测试数据常用查询延迟达到 `README.md` 记录的目标 - [ ] 使用远端真实数据验证紧凑索引的最终体积重建峰值内存和稳态内存 ## 阶段四 HTTP 搜索服务 ### 目标 提供稳定可验证并且资源受限的搜索和详情接口 ### 任务 - [x] 使用 Axum 建立 HTTP 服务 - [x] 实现 `/health` 和 `/ready` 接口 - [x] 实现 `/stats` 运行状态接口 - [x] 实现 `/search` 搜索过滤和分页接口 - [x] 实现 `/torrents/{infohash}` 详情接口 - [x] 实现 `/contents/{content_key}` 内容变体接口 - [x] 使用 Bun Vue TypeScript Vite Tailwind CSS 和 shadcn-vue 建立 Web 基础环境 - [x] 实现简单现代并适配移动端的单页搜索界面 - [x] 接入搜索排序分页详情内容变体和磁力链接复制 - [x] 详情文件分页优先展示匹配搜索条件的文件并继承外层大小或名称排序 - [x] 实现名称别名和文件路径的有限状态自动机正则搜索 - [x] 根据输入语法自动识别普通文本通配符和正则表达式并移除独立模式开关 - [x] 品牌入口可清除搜索查询排序分页和详情状态并返回主页 - [x] 实现种子详情文件列表后端分页并限制浏览器单页节点数量 - [x] 保留种子详情顶部结构并使用 reka-ui 数字分页重构文件条目 - [x] 支持用户选择并持久化文件列表每页数量 - [x] 统一搜索结果数字分页并支持用户选择每页数量 - [x] 将采集索引持久化和验证运行状态集中到系统诊断页并仅在页面打开时每秒刷新 - [x] 将诊断时间范围放入历史趋势区域并精简图表卡片的单行摘要信息 - [x] 在 Web 顶栏提供持久化的 DHT 即时启停开关并保留离线索引搜索能力 - [x] 将配置页精简为设置与过滤两个页签并合并全部常用参数 - [x] 使用数值与单位选择编辑 Metadata 大小并自动换算内部字节数 - [x] 实现浏览器持久化明暗主题 - [x] 为加载空结果接口错误和失败重试提供明确界面状态 - [x] 将生产静态资源交给 Axum 提供并支持单页回退 - [x] 提供 Windows 一键启动后端和 Web 开发服务的脚本 - [x] 修复一键启动脚本只停止父进程导致 Vite 子进程残留的问题 - [x] 定义统一错误响应 - [x] 限制查询长度分页大小和最大 offset - [x] 增加请求延迟错误率和并发指标 - [x] 增加搜索详情字段和按需验证入队 API 端到端测试 - [x] 增加搜索过滤折叠精确哈希和变体接口测试 ### 验收标准 - [x] API 能搜索真实采集数据 - [x] 非法参数返回稳定的客户端错误 - [x] 搜索查询在独立阻塞任务执行不会阻塞异步 worker - [x] 健康检查能区分进程存活和服务可用 ## 阶段五 质量过滤和重复内容控制 ### 目标 减少垃圾数据和重复展示同时避免不可恢复的误删 ### 任务 - [x] 将种子可用性定义为最近通过 DHT 找到并完成 BitTorrent 握手 - [x] 区分 Metadata 结构有效和 swarm 当前可用性 - [x] 实现未验证活跃和可能失效三态模型 - [x] 实现详情高优先级和搜索普通优先级的仅按需验证 - [x] 使用持久化有界验证队列租约恢复去重和失败退避 - [x] Metadata 与验证握手共享 TCP 建连总预算 - [x] 开发阶段清理测试数据库并以当前数据结构重新采集 - [x] 搜索和详情接口返回热度与可用性数据 - [x] `/stats` 返回验证队列发现握手成功失败和拒绝指标 - [ ] 统计真实数据的 infohash 重复率和内容重复率 - [x] 在统一 `config.toml` 中定义种子标题和内部文件隐藏规则 - [x] 使用标题与内部文件双文本框按行管理不区分大小写的通配符和 `regex:` 规则 - [x] 保留 RocksDB 原始文件列表并将用户过滤从内容指纹和内容组身份中解耦 - [x] 使用内容组过滤投影哈希只增量更新真正变化的 Tantivy 文档 - [x] 持久化过滤扫描游标并支持连续修改采用最新规则和跨重启恢复 - [x] 在搜索页和系统诊断页展示过滤基线扫描更新提交和失败状态 - [x] 默认隐藏 BitComet padding 文件以及 `.pad` 和 `.____padding_file` 填充目录 - [x] 全部文件被隐藏的 Metadata 只保留原始记录且不进入公开索引 - [ ] 根据真实垃圾数据决定是否增加种子名称扩展名和大小准入规则 - [x] 定义可配置的 Metadata 最大大小文件数名称路径长度和目录层级限制 - [x] 识别空名称控制字符异常路径大小溢出总大小不一致和文件数量攻击 - [ ] 设计可解释的名称标准化规则 - [ ] 为模糊相似结果生成聚合候选但不自动删除 - [x] 使用带规则指纹的 RocksDB 轻量拒绝记录阻止相同异常 infohash 重复下载 - [x] 保留按原因分类的过滤指标但避免保存名称和大文件列表 - [ ] 增加误判测试和边界数据集 ### 验收标准 - [x] 搜索和详情响应不等待 DHT 或 Peer 网络验证 - [x] 进程重启后已接受的验证任务能够通过租约恢复 - [x] 一次验证失败不会删除记录或标记为绝对失效 - [x] 数据清空后能够建立新的内容组搜索文档 - [x] 精确重复不会重复下载和重复展示 - [x] 内容重复可以折叠并保留全部 infohash - [x] 过滤规则可以配置更新和回滚且不会删除原始 Metadata - [ ] 模糊去重不会直接造成数据丢失 ## 阶段六 性能资源和长期运行 ### 目标 以真实数据验证持续运行时的吞吐延迟磁盘放大和资源上限 ### 任务 - [x] 增加 `find_node` Peer Lookup 新目标和 Metadata 建连的显式配置 - [x] 为主动 `find_node` `get_peers` 和 `sample_infohashes` 增加共享 UDP 查询总预算 - [x] 为 Metadata TCP 建连增加独立每秒速率限制 - [x] 使用保守网络预算定位早期本机断网问题 - [x] 根据本机首次验证将主动 UDP 从 `40/s` 下调至 `10/s` 并将 Metadata 建连从 `5/s` 下调至 `2/s` 完成故障隔离 - [x] 对照 Bitmagnet 默认并发建立受全局预算和有界队列保护的激进配置 - [x] 根据两轮一分钟资源测试将 Bitmagnet 等效激进配置设为应用和运行模板默认值 - [x] 修复 Windows 临时索引文件占用导致整个服务退出的问题 - [x] 验证极保守配置运行三分钟不影响同机代理网络并安全退出 - [x] 将 BEP-51 采样准入压力反向传递到采样查询调度 - [x] 实现样本来源节点单点 `get_peers` 优先和失败后有限递归降级 - [x] 将 BEP-51 最大在途请求和采样失败后的迭代回退暴露为应用配置 - [x] 使用 Bitmagnet 等效并发完成一分钟资源测试并确认主网卡无丢包无错误且持久化无积压 - [x] 将新发现但尚未验证的节点按地址稳定分流到有界 BEP-51 通道并避免与 `find_node` 重复探测 - [x] 增加直接采样候选队列请求响应重复过滤丢弃和 Metadata 成功来源转化指标 - [x] 使用相同网络预算对旧快照采样和新节点直接采样完成十分钟对比 - [x] 确认直接采样在相近 UDP 流量下 Metadata 成功数提高约百分之六点六且单条成功 UDP 成本降低约百分之六点六 - [x] 完成首轮三分钟对比并验证 Peer Lookup UDP 从 `278` 降至 `254` 且网络稳定 - [ ] 通过多轮或更长时间运行评估随机 DHT 样本下的 Metadata 成功率 - [ ] 统计按需验证的 Peer 发现率握手成功率和平均验证耗时 - [x] 完成首轮真实按需验证并确认旧记录两次握手均成功更新为活跃 - [x] 验证新 Metadata 记录直接继承成功来源 Peer 的活跃状态 - [x] 验证启用按需可用性功能后保守预算运行三分钟并安全停止 - [ ] 根据真实验证数据校准热度权重等级边界和失败退避时间 - [ ] 根据公网设备长期实测设计超时率自动降速 - [x] 建立可重复的采集存储索引和查询规模基准工具 - [x] 完成一万十万和一百万条 release 基线并记录查询 P50 P95 P99 - [x] 记录每条元数据和每个索引文档的平均磁盘占用 - [x] 记录百万级基准进程峰值内存和索引总吞吐 - [x] 记录 RocksDB block cache memtable 和 compaction 指标 - [x] 记录 Tantivy IndexWriter 内存和 commit 延迟 - [ ] 根据实测调整批量大小队列容量和并发 - [x] 增加带排空阶段恢复滞回和探测失败保护的磁盘只读降级策略 - [x] 固定每分钟检查磁盘并按文件系统容量自动计算保护和恢复阈值 - [x] 增加在线 RocksDB 检查点保留上限只读校验和带旧库保留的离线恢复 - [x] 默认关闭自动检查点并从 Web 隐藏全部备份调度参数 - [x] 始终保留终端日志并仅在 Web 暴露滚动文件日志开关 - [x] 移除 Docker 对文件日志的命令行覆盖并隐藏无关的基础设施覆盖提示 - [x] 验证间歇运行和正常退出恢复 - [x] 完成本机约七小时真实持续运行并确认采集索引和搜索服务可用 - [ ] 验证二十四小时和七天连续运行 - [ ] 根据规模决定是否继续使用 RocksDB ### 验收标准 - [ ] 内存使用在目标上限内稳定 - [ ] 队列和缓存不会随运行时间无限增长 - [x] 磁盘不足时能够拒绝新任务排空持久化队列并保留搜索能力 - [x] 备份可以在独立目录恢复并从 RocksDB 重建索引搜索 - [ ] 连续运行期间没有数据格式损坏和不可恢复任务 ## 阶段七 部署和运维 ### 目标 让应用可以在公网 Linux 设备上重复构建部署监控停止和恢复 ### 任务 - [x] 固化 Linux 目标构建方式和 RocksDB 构建依赖 - [x] 生成 release 二进制并使用 SHA-256 校验部署 - [x] 定义 Docker 配置数据日志索引和静态资源目录布局 - [x] 编写 Debian 三阶段最小运行镜像并使用非 root 用户完成构建启动和重启恢复测试 - [x] 编写 Compose 配置管理端口数据卷重启策略和文件句柄限制 - [x] Docker 将根目录唯一 `config.toml` 映射到容器并使用单独数据卷保存全部运行数据 - [x] 使用专用低权限 UID 运行验证 - [x] 在公网 Debian 设备验证镜像传输内部 Docker 网络 Caddy 反向代理 Compose 重启数据复用和优雅停止 - [x] 固化 Xray 环境下的最小范围网络旁路 - [x] 为 Docker DHT 容器增加独立直连网络和持久化 Xray 来源网段旁路 - [x] 验证旁路后一分钟内 Metadata 成功入库且 Xray 文件描述符和主机 UDP socket 恢复稳定 - [x] 验证高并发运行需要 `LimitNOFILE=65536` - [ ] 实现启动前数据目录权限检查 - [ ] 实现优雅升级和回滚流程 - [ ] 编写备份恢复和故障排查文档 ### 验收标准 - [ ] 新设备可以按 Docker 文档完成部署 - [x] 容器重建并复用数据卷不会丢失已提交数据和诊断历史 - [ ] Xray 旁路只影响爬虫进程 - [ ] 更新失败时可以恢复上一版本二进制和数据 ## 当前下一步 - [x] 按职责拆分应用编排索引 worker 运行监控搜索查询构建和 Tantivy 文档映射 - [x] 按领域边界拆分 infohash Metadata 校验内容聚合和种子活跃状态 - [x] 将 DHT 响应限流器从服务器编排中提取为独立组合组件 - [x] 将规模基准拆分为参数数据集工作负载采样报告和编排模块 - [x] 明确单元组件集成端到端和性能测试层级并增加公开 API 集成测试 - [x] 实现可回滚的无效文件过滤并重建有效内容聚合和搜索索引 - [x] 将 crawler search 和 web 统一迁移到根目录 `src` 并修复构建脚本文档路径 - [x] 使用领域 Metadata DTO 切断领域层对 DHT 传输 DTO 的直接依赖 - [x] 将配置拆分为可序列化 DTO TOML 读取适配器和运行时解析结果 - [x] 使用独立 SQLite 建立有界运行诊断历史存储 - [x] 采集进程 RocksDB Tantivy DHT 队列和磁盘资源快照 - [x] 在系统诊断中持续展示已保存种子总量并以历史趋势替代低价值 HTTP 请求图表 - [x] 提供当前诊断快照和原始或分钟历史查询接口 - [x] 将 HTTP 请求并发客户端错误服务端错误和延迟分布写入诊断历史 - [x] 增加配置查询完整校验原子保存并发修订和统一重启提示 - [x] 增加 Web 诊断页和配置管理页 - [x] 精简诊断页即时指标并由趋势图承担重复的资源和吞吐数据 - [x] 在系统诊断中区分 Peer 下载结果和 Metadata 内容有效性并展示 Peer 失败原因构成 - [x] 使用页签拆分配置分类并隐藏底层配置存储位置 - [x] 将主配置和内容过滤规则合并为唯一 `config.toml` - [x] 将 Docker 测试和性能基线核心内容合并到根目录 `README.md` - [x] 将 Xray 透明代理异常环境的判断处理和回滚方案独立到 `docs` - [x] 实现可恢复的影子索引原子切换和 Web 重建进度 - [x] 将大型种子文件路径覆盖扩大到按大小选择的 2048 个文件和 256 KiB - [x] 使用文本优先热度时间同分的稳定相关性排序 完成二十四小时持续运行并继续观察私有内存 Metadata 成功率候选队列深度和每条成功 Metadata 的网络成本 RocksDB Tantivy HTTP 和进程资源指标已经接入诊断历史 后续根据长期实测继续调优 下一步持续观察公网设备 Metadata 成功率 Xray 文件描述符主机 socket 和磁盘增长是否长期稳定