更新一个随时可能被访问的文件

2014-05-06 16:10:25 +08:00
 notcome
有一组 Markdown 文档,用户请求、渲染、输出 HTML。

request /a => load /path/a.md => render => HTML => user
request /enoent => load /path/enoent.md => 404 => user

我希望实现一个可以动态更新的功能,

1. buffer => tmpfile
2. mv /path/foo => /history/bar
3. mv tmpfile => /path/foo

如果在 2、3 之间加载 /path/foo,那么可能会导致一个 404 错误。如果我同步而不是异步重命名,能解决这个问题吗?

fs.renameSync('/path/foo', '/history/bar');
fs.renameSync('tmpfile', '/path/foo');

我担心的问题是,如果之前已经有请求异步的读取 /path/foo,然后因为操作系统的调度原因,拖到 2、3 之间执行。这种情况可能发生吗?

1. fs.readFile('/path/foo', cb);
2. fs.renameSync('/path/foo', '/history/bar');
3. OS reads file /path/foo => not found => ENOENT
4. call cb of step 1.
5. fs.renameSync('tmpfile', '/path/foo');

以及,有没有异步更新该文件的方法?我想到的是建立一个 fs layer,当更新操作到达之后,block 所有对该文件的访问,直至更新结束后。但是似乎太复杂了啊…………
3802 次点击
所在节点    Node.js
12 条回复
merlin852
2014-05-06 17:05:44 +08:00
2. mv -->cp
不懂js,瞎猜的
shiye515
2014-05-06 17:11:55 +08:00
我司的cms就能实现这种需求,不过我一个小前端没研究过是怎么实现的
ibudao
2014-05-06 17:44:07 +08:00
主从备份不就是处理这种场景的么。。request的handler这样写:
if exists /path/foo then return;
else return /path/foo.bak;
你只需要保证每份资源有一个冗于备份/path/*.bak 。
notcome
2014-05-06 18:01:52 +08:00
@ibudao 很好的方案……但是假如很倒霉……然后 foo 不存在,接着跑去试 foo.bak 的时候 foo 已删除怎么办……延迟删除吗?

以及这样似乎拷贝了好多冗余信息。
rrfeng
2014-05-06 18:17:36 +08:00
mv foo foo.bak
mv tmp foo
rm foo.bak

再配合 if exists
Mutoo
2014-05-06 18:20:41 +08:00
先内存缓存,然后再文件IO.
Ever
2014-05-06 18:23:19 +08:00
写入filename.new, 再mv filename.new filename就行。

一般情况下, 打开一个文件获取到一个fd不关闭, 就算期间这个文件被删除或者被mv替换,通过保留的fd还是能读完原文件。
tangzx
2014-05-06 19:55:16 +08:00
楼主放心,如果调用sync自然会block整个程序,不用再额外写了
notcome
2014-05-06 20:12:09 +08:00
@Ever 这我明白,我担心的是 foo 不存在的那一刻查找 foo。

@rrfeng 我担心查找 foo 的请求在前两步 mv 之间出现。

@tangzx 谢谢,我主要担心 block 整个程序但是 kernel 还在以稀奇古怪地方式处理 I/O,可能我对进程、内核态之间的关系理解有误。

@Mutoo 感觉太复杂了,但是我最终打算采取类似的方案——突然想起我似乎还要实现一个权限管理系统,封装 fs 成最佳选择(“突然想起”,这个项目要坑啊)
rrfeng
2014-05-06 22:08:26 +08:00
@notcome
写错了,第一个 mv 改成 cp

仔细想想其实直接 mv / cp 覆盖被访问的文件,是不会存在 404 出现的状况的!
这是由文件系统(内核)决定的!
所以这样

1. buffer => tmpfile
2. cp /path/foo => /history/bar
3. mv tmpfile => /path/foo

把你的第二步 mv 换成 cp 就 OK 了……
ibudao
2014-05-06 22:21:54 +08:00
在高并发的情形中,冗余备份显然比同步有更好的响应速度。但如果并发量不高,并且磁盘容量吃紧时,则同步的方案更好。权衡一下空间和时间,最终还是看你的需求。。
notcome
2014-05-06 23:16:39 +08:00
我明白了一个问题……
mv a b 能直接把 b 覆盖掉……
那我直接:
ln foo bar
mv tmp foo
就可以了……
我还是太愚蠢,打扰各位了,对不起!

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/111364

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX