题目重点:围绕两篇 Netflix 电面的文件管理题,澄清路径、单文件版本、删除 tombstone、历史读取、并发与崩溃恢复之间的边界。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练例选文件路径 `/a/b` 为 key,内容为 bytes,版本是每文件单调增加整数;题目未说明是否目录树、全局快照或内容类型。
- create 仅允许原来不存在,update 需要已有文件,delete 写 tombstone 并保留历史;这些错误规则是示例契约,真实题需面试澄清。
- 本文先讨论单机内存状态;持久化与跨节点复制是生产追问,不把它们冒充电面必须实现的原始接口。
解题路线
- 先画最小 API:create(path,data)、read(path,version?)、update(path,data,expected_version?)、delete(path,expected_version?)、list(prefix)。统一路径规范化,阻止 `..` 逃逸与同一路径别名。
- 每路径维护 append-only 版本序列,当前视图指向最新非 tombstone 版本;每次写在同一临界区验证 expected_version 并追加新版本。历史 read 用版本索引,不可用覆盖写丢失旧值;删除是否允许旧版可读由契约决定。
- 若目录语义存在,目录树和文件映射必须一起更新;rename 是多 key 原子操作,不能只改一处索引。高并发下可按路径锁或分片,跨路径事务另议。
- 生产化增加 WAL 先记录提交意图再确认成功,定期快照与重放恢复;版本内容大时可引用不可变 blob 加元数据索引,垃圾回收只清理已超过保留期且无快照引用的版本。
手动走一遍
训练例 create(`/x`,`A`) 得 v1;update 为 `B` 得 v2;read latest 为 `B`,read(v1) 为 `A`。两个客户端都读取 v2 后同时更新,只有第一个通过 expected_version=2,第二个收到冲突并重试。delete 写 v4 tombstone 后 latest read 为不存在;read(v2) 是否仍可读要按事先约定。
规模与取舍
单机哈希定位路径预期 O(1);某文件 V 个版本,用有序版本数组查历史版本 O(log V),写末尾追加 O(1) 均摊,内容存储按所有保留版本字节数增长。list(prefix) 若仅用哈希扫描 O(F),需要大量目录查询时应维护前缀树或目录索引。
易错点与追问
容易失分的边界
- 说支持 version 却覆盖旧内容,无法做历史读。
- 删除版本后物理抹掉历史,与回滚/审计要求矛盾。
- 未定义全局版本与文件版本,却跨文件比较版本号。
- 路径 `//`、`..` 规范化不一致造成重复键或越权。
- 把 的 crash-resilient file system 直接视为同一道题;题目明确以崩溃恢复为主目标。
继续深挖
- 若要求全局一致快照,需要怎样给多文件写分配提交序号?
- rename 与 delete 目录如何保持原子性?
- WAL 写入成功而 ACK 丢失后客户端重试,怎样幂等?
- 历史版本保留多久,如何做 GC 又不破坏活跃快照?
