Spacedrive 文件同步数据库 Schema 设计:SyncConduit / SyncGeneration 实体与 SeaORM 迁移全解析

发布时间:2026/9/19 7:04:31
Spacedrive 文件同步数据库 Schema 设计:SyncConduit / SyncGeneration 实体与 SeaORM 迁移全解析 Spacedrive 文件同步数据库 Schema 设计SyncConduit / SyncGeneration 实体与 SeaORM 迁移全解析【免费下载链接】spacedriveSpacedrive is an open source cross-platform file explorer, powered by a virtual distributed filesystem written in Rust.项目地址: https://gitcode.com/gh_mirrors/sp/spacedrive本文基于 Spacedrive 仓库中 FSYNC-002 任务文档.tasks/core/FSYNC-002-database-schema.md及其落盘实现系统讲解文件同步File Sync功能持久化层的数据模型设计sync_conduit与sync_generation两张表如何承载目录对目录的同步关系与每一次同步执行的完整历史以及它们如何通过 SeaORM 迁移、实体、外键与索引落地为可查询的 SQLite 结构。读完本文你将掌握这套 Schema 的字段语义、约束设计、验证状态机Trust Watcher与迁移验证方法并能直接对照仓库源码理解文件同步系统的可恢复性、冲突检测与历史追踪是如何从数据层获得支撑的。一、设计背景为什么文件同步需要持久化存储在 Spacedrive 的文件同步功能FSYNC 系列任务根任务见 .tasks/core/FSYNC-000-file-sync-system.md中同步不再是一次性的内存操作而是需要满足三个长期目标可恢复同步Resumable Sync同步中断后能依据持久化状态继续执行而不是重新全量扫描冲突检测Conflict Detection双向同步时能判断两侧自上次同步以来是否都修改过同一文件同步历史追踪Sync History Tracking记录每一次同步执行的时间、结果、统计与验证状态用于排查问题与审计。FSYNC-002 正是为此构建持久化层其核心交付物是SyncConduit与SyncGeneration两个实体外加对应的 SeaORM 迁移。目标非常明确Persistent storage enabling resumable sync, conflict detection, and sync history tracking.持久化存储使同步可恢复、可检测冲突、可追踪历史。从源码结构看这套持久化层实际落在core/src/infra/db/entities/与core/src/infra/db/migration/目录中并已由下游服务core/src/service/file_sync/FileSyncService、ConduitManager、SyncResolver消费说明该设计已从任务文档转化为真实可运行的实现。二、SyncConduit 实体一条目录到目录的同步关系SyncConduit同步管道代表两个目录之间的一条持久化同步关系。它在仓库中的实际实现位于 core/src/infra/db/entities/sync_conduit.rs通过 SeaORM 的DeriveEntityModel派生宏声明table_name sync_conduit约 130 行。完整字段如下#[sea_orm(table_name sync_conduit)] pub struct Model { #[sea_orm(primary_key)] pub id: i32, #[sea_orm(unique)] pub uuid: Uuid, // 全局唯一标识 pub source_entry_id: i32, // 源目录 entry ID pub target_entry_id: i32, // 目标目录 entry ID pub sync_mode: String, // mirror | bidirectional | selective pub enabled: bool, // 是否启用 pub schedule: String, // instant | interval:5m | manual pub use_index_rules: bool, // 是否应用索引规则过滤 pub index_mode_override: OptionString, // 可选覆盖索引模式如 shallow/deep pub parallel_transfers: i32, // 并行传输数 pub bandwidth_limit_mbps: Optioni32, // 可选带宽上限MB/s pub last_sync_completed_at: OptionDateTimeUtc, // 上次成功同步时间 pub sync_generation: i64, // 当前代际号每次同步 1 pub last_sync_error: OptionString, // 最近一次错误信息 pub total_syncs: i64, // 累计同步次数 pub files_synced: i64, // 累计同步文件数 pub bytes_transferred: i64, // 累计传输字节数 pub created_at: DateTimeUtc, pub updated_at: DateTimeUtc, }2.1 字段分组解读可以将这 20 个字段划分为五组职责分组字段说明主键与标识id、uuidid为自增主键uuid带unique约束用于跨实例/跨设备引用管道端点Endpointssource_entry_id、target_entry_id外键指向entry表且两侧必须是kind1的目录记录配置Configurationsync_mode、enabled、schedule、use_index_rules、index_mode_override、parallel_transfers、bandwidth_limit_mbps同步模式、启停、调度策略、索引规则开关、性能调优状态追踪State trackinglast_sync_completed_at、sync_generation、last_sync_error上次完成时间、单调递增的代际号、最近错误统计Statisticstotal_syncs、files_synced、bytes_transferred累计运行指标可直接用于 UI 展示与审计值得注意的设计点同步模式与调度都采用字符串枚举存储String而非数据库枚举配合 Rust 侧的SyncMode枚举做类型化解析。这样做的优势是数据库层保持简单、便于未来扩展新值同时把枚举校验前移到应用层。2.2 SyncMode 枚举三种同步语义实体文件内随附SyncMode枚举提供as_str()/from_str()/Display实现core/src/infra/db/entities/sync_conduit.rs#L100-L133pub enum SyncMode { Mirror, // 单向源 → 目标自动清理目标侧多余文件 Bidirectional, // 双向两侧同步带冲突检测 Selective, // 智能本地存储管理未来功能预留 }三种模式的语义差异直接影响下游SyncResolver的差异计算逻辑详见 FSYNC-003 任务文档 .tasks/core/FSYNC-003-sync-service-core.mdMirror镜像source_only → copy源有目标无则复制target_only → delete目标有源无则删除目标目录收敛为源的完整镜像Bidirectional双向需要检测自上次同步以来两侧的变更当同一文件两侧都变化时产生冲突SyncConflict交由冲突解析器处理Selective选择性设计文档中标注为智能本地存储管理未来当前为占位语义。2.3 关系定义与 entry 的双外键Relation枚举声明了两条belongs_to外键关系与一条has_many关系core/src/infra/db/entities/sync_conduit.rs#L68-L89SourceEntrysource_entry_id→entry.idTargetEntrytarget_entry_id→entry.idSyncGenerations一条 conduit 对应多条sync_generation记录一对多。也就是说一张 conduit 同时引用 entry 表两次源、目标这要求两条外键在迁移中用不同的约束名区分见下文迁移实现中的fk_sync_conduit_source_entry与fk_sync_conduit_target_entry。文档明确要求两端都必须是kind1的目录条目——kind是 entry 表区分文件类型的字段这一校验在ConduitManager.create_conduit中执行创建管道前会先验证目录身份并检查重复管道。三、SyncGeneration 实体一次同步执行的完整档案SyncGeneration同步代际记录单次同步操作的过程与结果用于历史查询、冲突检测与一致性验证。实际实现位于 core/src/infra/db/entities/sync_generation.rs约 105 行#[sea_orm(table_name sync_generation)] pub struct Model { #[sea_orm(primary_key)] pub id: i32, pub conduit_id: i32, // 外键 → sync_conduit.id pub generation: i64, // 代际号单调递增 pub started_at: DateTimeUtc, // 开始时间 pub completed_at: OptionDateTimeUtc, // 完成时间None 表示仍在进行 pub files_copied: i32, // 本次复制文件数 pub files_deleted: i32, // 本次删除文件数 pub conflicts_resolved: i32, // 本次解决冲突数 pub bytes_transferred: i64, // 本次传输字节数 pub errors_encountered: i32, // 本次错误数 pub verified_at: OptionDateTimeUtc, // 验证时间 pub verification_status: String, // 验证状态 }3.1 与 conduit 的联动generation 号generation字段是理解这套模型的关键每次同步执行时ConduitManager先将 conduit 的sync_generation自增active.sync_generation Set(active.sync_generation.unwrap() 1)见 core/src/service/file_sync/conduit.rs#L134然后以该代际号创建新的 generation 记录create_generation见 core/src/service/file_sync/conduit.rs#L157-L174。这样 conduit 表上的sync_generation是当前指针而 generation 表通过(conduit_id, generation)组合索引保存了每一次执行的快照。双向同步的冲突检测正是依赖这一历史比较同一文件在当前索引中的修改时间与最近一次已完成的 generation 的completed_at即可判断该文件是否在同步之后又被修改过。3.2 运行中的 generationcompleted_at 与 errors_encounteredcompleted_at为Option同步进行中为None完成后由complete_generation填充core/src/service/file_sync/conduit.rs#L182-L194。files_copied、files_deleted、conflicts_resolved、bytes_transferred、errors_encountered五个计数字段构成一次同步的操作摘要可在同步结束后一次性写入也可在执行过程中实时累加——从结构看它同时服务于进度监控与历史审计两种场景。四、验证状态机Trust Watcher 方案SyncGeneration的验证字段verified_atverification_status实现了文档中强调的Trust Watcher信任文件系统监视器验证策略。VerificationStatus枚举定义于 core/src/infra/db/entities/sync_generation.rs#L68-L105状态值含义unverified同步已完成尚未验证waiting_watcher等待文件系统监视器更新索引waiting_library_sync等待库同步library sync传播变更verified验证查询确认两侧一致failed:reason验证发现仍有差异动态拼接原因注意failed是一个动态状态Rust 侧枚举只覆盖前四个固定变体from_str对failed:...前缀返回None因为失败原因可变同时提供静态工厂VerificationStatus::failed(reason) - String来拼接出failed:reason字符串。这说明该列虽以字符串存储但应用层通过as_str/from_str保持了类型安全。4.1 为什么选择 Trust Watcher 而非 Eager Update任务文档给出了明确的设计取舍原样保留单一事实来源Spacedrive 的文件系统监视器watcher已经负责维护索引一致性同步服务无需重复实现文件系统语义无重复逻辑同步服务不需要关心文件系统语义细节专注差异计算与作业派发最终一致性系统天然收敛到一致状态验证只是确认而非驱动。对应的验证流程是同步完成后状态进入unverified→ 等待 watcher 处理事件进入waiting_watcher→ 等待库同步传播进入waiting_library_sync→ 执行一致性查询确认后置为verified若查询发现差异则置为failed:reason。update_verification_status方法core/src/service/file_sync/conduit.rs#L196-L208在状态被置为verified时同时写入verified_at Utc::now()使验证时间与验证结论保持一致。五、SeaORM 迁移从模型到 SQLite 表任务文档中的 SQL 设计目标已由迁移文件 core/src/infra/db/migration/m20251015_000002_create_sync_tables.rs约 280 行完整实现。该迁移名遵循 SeaORM 的mYYYYMMDD_序号_描述规范注册时通过DeriveMigrationName自动提取名称。5.1 up建表 索引 外键up方法按顺序执行四步操作创建sync_conduit表if_not_exists保证可重入id为自增主键uuid为binary().unique_key()SQLite 中 UUID 以 BLOB 存储时间列使用timestamp_with_time_zone映射为 TEXT统计列使用big_integer以支持大计数。创建idx_sync_conduit_enabled索引单列enabled加速查询所有启用的管道这一高频操作list_enabled。创建sync_generation表conduit_id非空generation为big_integer各计数字段带DEFAULT 0verification_status默认unverified。创建idx_sync_generation_conduit复合索引conduit_id, generation支撑按管道高效检索代际历史如get_last_completed_generation的排序查询。外键约束与级联删除是数据完整性的核心-- sync_conduit FOREIGN KEY (source_entry_id) REFERENCES entry(id) ON DELETE CASCADE FOREIGN KEY (target_entry_id) REFERENCES entry(id) ON DELETE CASCADE -- sync_generation FOREIGN KEY (conduit_id) REFERENCES sync_conduit(id) ON DELETE CASCADE删除一个 entry 会连带删除引用它的 conduit删除一个 conduit 会连带删除它的全部 generation 历史避免孤儿数据。两条指向 entry 的外键分别命名为fk_sync_conduit_source_entry/fk_sync_conduit_target_entrysync_generation的外键命名为fk_sync_generation_conduit。5.2 down按依赖序回滚down方法先删子表再删父表DROP TABLE sync_generation→DROP TABLE sync_conduit与建表顺序相反保证外键依赖不被破坏。5.3 迁移注册与实体导出迁移必须在迁移器Migrator中注册才能生效。注册位于 core/src/infra/db/migration/mod.rs第 17 行mod m20251015_000002_create_sync_tables;第 61 行将其实例加入Migrator::Migrations列表从而纳入整个数据库的迁移链。实体导出位于 core/src/infra/db/entities/mod.rs第 35-36 行声明模块第 60-61 行导出Entity别名SyncConduit/SyncGeneration第 93-94 行导出ActiveModel别名SyncConduitActive/SyncGenerationActive供业务层以类型安全方式使用。5.4 任务文档中的 SQL 对照任务文档给出了等价的 SQL DDLSQLite 方言与迁移代码相互印证enabled默认 1、schedule默认manual、use_index_rules默认 1、parallel_transfers默认 3、sync_generation/total_syncs/files_synced/bytes_transferred默认 0、verification_status默认unverified。这些默认值在迁移的default(...)声明中一一对应是理解新建管道时的初始状态的第一手资料CREATE TABLE sync_conduit ( id INTEGER PRIMARY KEY AUTOINCREMENT, uuid BLOB NOT NULL UNIQUE, source_entry_id INTEGER NOT NULL, target_entry_id INTEGER NOT NULL, sync_mode TEXT NOT NULL, enabled INTEGER NOT NULL DEFAULT 1, schedule TEXT NOT NULL DEFAULT manual, use_index_rules INTEGER NOT NULL DEFAULT 1, index_mode_override TEXT, parallel_transfers INTEGER NOT NULL DEFAULT 3, bandwidth_limit_mbps INTEGER, last_sync_completed_at TEXT, sync_generation INTEGER NOT NULL DEFAULT 0, last_sync_error TEXT, total_syncs INTEGER NOT NULL DEFAULT 0, files_synced INTEGER NOT NULL DEFAULT 0, bytes_transferred INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, FOREIGN KEY (source_entry_id) REFERENCES entry(id) ON DELETE CASCADE, FOREIGN KEY (target_entry_id) REFERENCES entry(id) ON DELETE CASCADE ); CREATE INDEX idx_sync_conduit_enabled ON sync_conduit(enabled);六、如何验证迁移test_migration 示例任务文档的验收标准要求Migration runs successfully via test_migration example仓库为此提供了专门的可执行示例 core/examples/test_migration.rs。它演示了验证这套 Schema 的标准流程在./data/test_migration.db创建临时 SQLite 数据库通过Database::connect(sqlite://...?moderwc)建立连接调用Migrator::up(db, None)执行全部迁移查询sqlite_master列出所有用户表确认sync_conduit、sync_generation及其余表创建成功结束后删除临时数据库文件。运行方式在仓库根目录执行cargo run --example test_migration --manifest-path core/Cargo.toml该示例同时佐证了任务进度记录中的结论Both tables created with foreign keys, indexes, and constraints——迁移链整体可用且建出的表能被 SeaORM 正常查询。若你需要在真实库上确认 schema也可用 sqlite3 打开 Spacedrive 的数据库文件后执行.tables与PRAGMA foreign_keys;检查。七、数据流与关系全貌任务文档末尾给出了实体关系图与实际代码完全一致sync_conduit ├─ entry (source_entry_id) ├─ entry (target_entry_id) └─ sync_generation (one-to-many)综合起来一条完整的同步生命周期是创建ConduitManager.create_conduit校验两个 entry 均为目录、无重复管道后写入sync_conduitsync_generation0各统计为 0执行同步开始时 conduit 的sync_generation自增并插入一条sync_generation记录started_at写入completed_atNULL状态unverified派发SyncResolver.calculate_operations基于索引查询计算to_copy/to_delete与冲突将工作派发给FileCopyJob/DeleteJob服务只派发作业、不直接执行职责分离收尾作业完成后complete_generation填充计数与completed_at并回写 conduit 的total_syncs、files_synced、bytes_transferred、last_sync_completed_at等统计验证按unverified → waiting_watcher → waiting_library_sync → verified / failed:reason的状态机流转verified_at在最终确认时写入。这套管道conduit 代际generation双层结构是 FSYNC-003 中FileSyncService的active_syncs追踪、防重复同步与进度监控的数据基础也是 FSYNC-005 验证流程所依赖的历史档案——两者都建立在本篇所述两张表之上。八、设计要点小结持久化是同步可靠性的地基sync_generation指针 generation 历史表让中断恢复与冲突检测都有了可查询的依据字符串枚举 应用层类型化sync_mode、schedule、verification_status以字符串存储、以 Rust 枚举解析兼顾扩展性与类型安全failed:reason的动态状态由此成为可能级联删除保证数据完整entry → conduit → generation的级联链避免了孤儿记录回滚顺序与依赖方向严格一致索引服务查询模式idx_sync_conduit_enabled服务列出启用管道idx_sync_generation_conduit服务按管道检索历史均直击下游最高频的查询路径验证可自动化test_migration示例让迁移的正确性验证成为一条命令的事任务文档的验收标准因此全部可勾选。如果你希望继续深入可以顺着三条线索阅读下游服务实现见 core/src/service/file_sync/mod.rs 与 core/src/service/file_sync/conduit.rs同步语义与 UI 层的整体说明见 docs/core/file-sync.mdxFSYNC 系列后续任务FSYNC-003-sync-service-core.md、FSYNC-005-advanced-features.md则展示了这些实体如何被同步编排与验证流程消费。【免费下载链接】spacedriveSpacedrive is an open source cross-platform file explorer, powered by a virtual distributed filesystem written in Rust.项目地址: https://gitcode.com/gh_mirrors/sp/spacedrive创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考