V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
notcome
V2EX  ›  Node.js

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

  •  
  •   notcome · 2014-05-06 16:10:25 +08:00 · 3784 次点击
    这是一个创建于 3861 天前的主题,其中的信息可能已经有所发展或是发生改变。
    有一组 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 所有对该文件的访问,直至更新结束后。但是似乎太复杂了啊…………
    12 条回复    2014-05-06 23:16:39 +08:00
    merlin852
        1
    merlin852  
       2014-05-06 17:05:44 +08:00
    2. mv -->cp
    不懂js,瞎猜的
    shiye515
        2
    shiye515  
       2014-05-06 17:11:55 +08:00
    我司的cms就能实现这种需求,不过我一个小前端没研究过是怎么实现的
    ibudao
        3
    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
        4
    notcome  
    OP
       2014-05-06 18:01:52 +08:00 via iPhone
    @ibudao 很好的方案……但是假如很倒霉……然后 foo 不存在,接着跑去试 foo.bak 的时候 foo 已删除怎么办……延迟删除吗?

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

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

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

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

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

    @Mutoo 感觉太复杂了,但是我最终打算采取类似的方案——突然想起我似乎还要实现一个权限管理系统,封装 fs 成最佳选择(“突然想起”,这个项目要坑啊)
    rrfeng
        10
    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
        11
    ibudao  
       2014-05-06 22:21:54 +08:00
    在高并发的情形中,冗余备份显然比同步有更好的响应速度。但如果并发量不高,并且磁盘容量吃紧时,则同步的方案更好。权衡一下空间和时间,最终还是看你的需求。。
    notcome
        12
    notcome  
    OP
       2014-05-06 23:16:39 +08:00
    我明白了一个问题……
    mv a b 能直接把 b 覆盖掉……
    那我直接:
    ln foo bar
    mv tmp foo
    就可以了……
    我还是太愚蠢,打扰各位了,对不起!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2686 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 12:25 · PVG 20:25 · LAX 04:25 · JFK 07:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.